4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2018 Red Hat Inc.
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
14 from __future__ import print_function
15 from contextlib import contextmanager
22 from collections import OrderedDict
25 'null': 'QTYPE_QNULL',
26 'str': 'QTYPE_QSTRING',
28 'number': 'QTYPE_QNUM',
29 'bool': 'QTYPE_QBOOL',
31 'int16': 'QTYPE_QNUM',
32 'int32': 'QTYPE_QNUM',
33 'int64': 'QTYPE_QNUM',
34 'uint8': 'QTYPE_QNUM',
35 'uint16': 'QTYPE_QNUM',
36 'uint32': 'QTYPE_QNUM',
37 'uint64': 'QTYPE_QNUM',
39 'any': None, # any QType possible, actually
40 'QType': 'QTYPE_QSTRING',
43 # Are documentation comments required?
46 # Whitelist of commands allowed to return a non-dictionary
47 returns_whitelist = []
49 # Whitelist of entities allowed to violate case conventions
50 name_case_whitelist = []
59 # Parsing the schema into expressions
62 class QAPISourceInfo(object):
63 def __init__(self, fname, line, parent):
70 def set_defn(self, meta, name):
75 info = copy.copy(self)
80 return '%s:%d' % (self.fname, self.line)
84 return "%s: In %s '%s':\n" % (self.fname,
85 self.defn_meta, self.defn_name)
88 def include_path(self):
92 ret = 'In file included from %s:\n' % parent.loc() + ret
93 parent = parent.parent
97 return self.include_path() + self.in_defn() + self.loc()
100 class QAPIError(Exception):
101 def __init__(self, info, col, msg):
102 Exception.__init__(self)
109 if self.col is not None:
110 assert self.info.line is not None
111 loc += ':%s' % self.col
112 return loc + ': ' + self.msg
115 class QAPIParseError(QAPIError):
116 def __init__(self, parser, msg):
118 for ch in parser.src[parser.line_pos:parser.pos]:
120 col = (col + 7) % 8 + 1
123 QAPIError.__init__(self, parser.info, col, msg)
126 class QAPISemError(QAPIError):
127 def __init__(self, info, msg):
128 QAPIError.__init__(self, info, None, msg)
131 class QAPIDoc(object):
133 A documentation comment block, either definition or free-form
135 Definition documentation blocks consist of
137 * a body section: one line naming the definition, followed by an
138 overview (any number of lines)
140 * argument sections: a description of each argument (for commands
141 and events) or member (for structs, unions and alternates)
143 * features sections: a description of each feature flag
145 * additional (non-argument) sections, possibly tagged
147 Free-form documentation blocks consist only of a body section.
150 class Section(object):
151 def __init__(self, name=None):
152 # optional section name (argument/member or section name)
154 # the list of lines for this section
157 def append(self, line):
158 self.text += line.rstrip() + '\n'
160 class ArgSection(Section):
161 def __init__(self, name):
162 QAPIDoc.Section.__init__(self, name)
165 def connect(self, member):
168 def __init__(self, parser, info):
169 # self._parser is used to report errors with QAPIParseError. The
170 # resulting error position depends on the state of the parser.
171 # It happens to be the beginning of the comment. More or less
172 # servicable, but action at a distance.
173 self._parser = parser
176 self.body = QAPIDoc.Section()
177 # dict mapping parameter name to ArgSection
178 self.args = OrderedDict()
179 self.features = OrderedDict()
182 # the current section
183 self._section = self.body
184 self._append_line = self._append_body_line
186 def has_section(self, name):
187 """Return True if we have a section with this name."""
188 for i in self.sections:
193 def append(self, line):
195 Parse a comment line and add it to the documentation.
197 The way that the line is dealt with depends on which part of
198 the documentation we're parsing right now:
199 * The body section: ._append_line is ._append_body_line
200 * An argument section: ._append_line is ._append_args_line
201 * A features section: ._append_line is ._append_features_line
202 * An additional section: ._append_line is ._append_various_line
206 self._append_freeform(line)
210 raise QAPIParseError(self._parser, "missing space after #")
212 self._append_line(line)
214 def end_comment(self):
218 def _is_section_tag(name):
219 return name in ('Returns:', 'Since:',
220 # those are often singular or plural
222 'Example:', 'Examples:',
225 def _append_body_line(self, line):
227 Process a line of documentation text in the body section.
229 If this a symbol line and it is the section's first line, this
230 is a definition documentation block for that symbol.
232 If it's a definition documentation block, another symbol line
233 begins the argument section for the argument named by it, and
234 a section tag begins an additional section. Start that
235 section and append the line to it.
237 Else, append the line to the current section.
239 name = line.split(' ', 1)[0]
240 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
241 # recognized, and get silently treated as ordinary text
242 if not self.symbol and not self.body.text and line.startswith('@'):
243 if not line.endswith(':'):
244 raise QAPIParseError(self._parser, "line should end with ':'")
245 self.symbol = line[1:-1]
246 # FIXME invalid names other than the empty string aren't flagged
248 raise QAPIParseError(self._parser, "invalid name")
250 # This is a definition documentation block
251 if name.startswith('@') and name.endswith(':'):
252 self._append_line = self._append_args_line
253 self._append_args_line(line)
254 elif line == 'Features:':
255 self._append_line = self._append_features_line
256 elif self._is_section_tag(name):
257 self._append_line = self._append_various_line
258 self._append_various_line(line)
260 self._append_freeform(line.strip())
262 # This is a free-form documentation block
263 self._append_freeform(line.strip())
265 def _append_args_line(self, line):
267 Process a line of documentation text in an argument section.
269 A symbol line begins the next argument section, a section tag
270 section or a non-indented line after a blank line begins an
271 additional section. Start that section and append the line to
274 Else, append the line to the current section.
277 name = line.split(' ', 1)[0]
279 if name.startswith('@') and name.endswith(':'):
280 line = line[len(name)+1:]
281 self._start_args_section(name[1:-1])
282 elif self._is_section_tag(name):
283 self._append_line = self._append_various_line
284 self._append_various_line(line)
286 elif (self._section.text.endswith('\n\n')
287 and line and not line[0].isspace()):
288 if line == 'Features:':
289 self._append_line = self._append_features_line
291 self._start_section()
292 self._append_line = self._append_various_line
293 self._append_various_line(line)
296 self._append_freeform(line.strip())
298 def _append_features_line(self, line):
299 name = line.split(' ', 1)[0]
301 if name.startswith('@') and name.endswith(':'):
302 line = line[len(name)+1:]
303 self._start_features_section(name[1:-1])
304 elif self._is_section_tag(name):
305 self._append_line = self._append_various_line
306 self._append_various_line(line)
308 elif (self._section.text.endswith('\n\n')
309 and line and not line[0].isspace()):
310 self._start_section()
311 self._append_line = self._append_various_line
312 self._append_various_line(line)
315 self._append_freeform(line.strip())
317 def _append_various_line(self, line):
319 Process a line of documentation text in an additional section.
321 A symbol line is an error.
323 A section tag begins an additional section. Start that
324 section and append the line to it.
326 Else, append the line to the current section.
328 name = line.split(' ', 1)[0]
330 if name.startswith('@') and name.endswith(':'):
331 raise QAPIParseError(self._parser,
332 "'%s' can't follow '%s' section"
333 % (name, self.sections[0].name))
334 elif self._is_section_tag(name):
335 line = line[len(name)+1:]
336 self._start_section(name[:-1])
338 if (not self._section.name or
339 not self._section.name.startswith('Example')):
342 self._append_freeform(line)
344 def _start_symbol_section(self, symbols_dict, name):
345 # FIXME invalid names other than the empty string aren't flagged
347 raise QAPIParseError(self._parser, "invalid parameter name")
348 if name in symbols_dict:
349 raise QAPIParseError(self._parser,
350 "'%s' parameter name duplicated" % name)
351 assert not self.sections
353 self._section = QAPIDoc.ArgSection(name)
354 symbols_dict[name] = self._section
356 def _start_args_section(self, name):
357 self._start_symbol_section(self.args, name)
359 def _start_features_section(self, name):
360 self._start_symbol_section(self.features, name)
362 def _start_section(self, name=None):
363 if name in ('Returns', 'Since') and self.has_section(name):
364 raise QAPIParseError(self._parser,
365 "duplicated '%s' section" % name)
367 self._section = QAPIDoc.Section(name)
368 self.sections.append(self._section)
370 def _end_section(self):
372 text = self._section.text = self._section.text.strip()
373 if self._section.name and (not text or text.isspace()):
374 raise QAPIParseError(
376 "empty doc section '%s'" % self._section.name)
379 def _append_freeform(self, line):
380 match = re.match(r'(@\S+:)', line)
382 raise QAPIParseError(self._parser,
383 "'%s' not allowed in free-form documentation"
385 self._section.append(line)
387 def connect_member(self, member):
388 if member.name not in self.args:
389 # Undocumented TODO outlaw
390 self.args[member.name] = QAPIDoc.ArgSection(member.name)
391 self.args[member.name].connect(member)
393 def check_expr(self, expr):
394 if self.has_section('Returns') and 'command' not in expr:
395 raise QAPISemError(self.info,
396 "'Returns:' is only valid for commands")
399 bogus = [name for name, section in self.args.items()
400 if not section.member]
404 "the following documented members are not in "
405 "the declaration: %s" % ", ".join(bogus))
408 class QAPISchemaParser(object):
410 def __init__(self, fp, previously_included=[], incl_info=None):
412 previously_included.append(os.path.abspath(fp.name))
414 if self.src == '' or self.src[-1] != '\n':
417 self.info = QAPISourceInfo(self.fname, 1, incl_info)
424 while self.tok is not None:
427 self.reject_expr_doc(cur_doc)
428 cur_doc = self.get_doc(info)
429 self.docs.append(cur_doc)
432 expr = self.get_expr(False)
433 if 'include' in expr:
434 self.reject_expr_doc(cur_doc)
436 raise QAPISemError(info, "invalid 'include' directive")
437 include = expr['include']
438 if not isinstance(include, str):
439 raise QAPISemError(info,
440 "value of 'include' must be a string")
441 incl_fname = os.path.join(os.path.dirname(self.fname),
443 self.exprs.append({'expr': {'include': incl_fname},
445 exprs_include = self._include(include, info, incl_fname,
448 self.exprs.extend(exprs_include.exprs)
449 self.docs.extend(exprs_include.docs)
450 elif "pragma" in expr:
451 self.reject_expr_doc(cur_doc)
453 raise QAPISemError(info, "invalid 'pragma' directive")
454 pragma = expr['pragma']
455 if not isinstance(pragma, dict):
457 info, "value of 'pragma' must be an object")
458 for name, value in pragma.items():
459 self._pragma(name, value, info)
461 expr_elem = {'expr': expr,
464 if not cur_doc.symbol:
466 cur_doc.info, "definition documentation required")
467 expr_elem['doc'] = cur_doc
468 self.exprs.append(expr_elem)
470 self.reject_expr_doc(cur_doc)
473 def reject_expr_doc(doc):
474 if doc and doc.symbol:
477 "documentation for '%s' is not followed by the definition"
480 def _include(self, include, info, incl_fname, previously_included):
481 incl_abs_fname = os.path.abspath(incl_fname)
482 # catch inclusion cycle
485 if incl_abs_fname == os.path.abspath(inf.fname):
486 raise QAPISemError(info, "inclusion loop for %s" % include)
489 # skip multiple include of the same file
490 if incl_abs_fname in previously_included:
494 if sys.version_info[0] >= 3:
495 fobj = open(incl_fname, 'r', encoding='utf-8')
497 fobj = open(incl_fname, 'r')
499 raise QAPISemError(info, "%s: %s" % (e.strerror, incl_fname))
500 return QAPISchemaParser(fobj, previously_included, info)
502 def _pragma(self, name, value, info):
503 global doc_required, returns_whitelist, name_case_whitelist
504 if name == 'doc-required':
505 if not isinstance(value, bool):
506 raise QAPISemError(info,
507 "pragma 'doc-required' must be boolean")
509 elif name == 'returns-whitelist':
510 if (not isinstance(value, list)
511 or any([not isinstance(elt, str) for elt in value])):
514 "pragma returns-whitelist must be a list of strings")
515 returns_whitelist = value
516 elif name == 'name-case-whitelist':
517 if (not isinstance(value, list)
518 or any([not isinstance(elt, str) for elt in value])):
521 "pragma name-case-whitelist must be a list of strings")
522 name_case_whitelist = value
524 raise QAPISemError(info, "unknown pragma '%s'" % name)
526 def accept(self, skip_comment=True):
528 self.tok = self.src[self.cursor]
529 self.pos = self.cursor
534 if self.src[self.cursor] == '#':
535 # Start of doc comment
537 self.cursor = self.src.find('\n', self.cursor)
539 self.val = self.src[self.pos:self.cursor]
541 elif self.tok in '{}:,[]':
543 elif self.tok == "'":
544 # Note: we accept only printable ASCII
548 ch = self.src[self.cursor]
551 raise QAPIParseError(self, "missing terminating \"'\"")
553 # Note: we recognize only \\ because we have
554 # no use for funny characters in strings
556 raise QAPIParseError(self,
557 "unknown escape \\%s" % ch)
565 if ord(ch) < 32 or ord(ch) >= 127:
566 raise QAPIParseError(
567 self, "funny character in string")
569 elif self.src.startswith('true', self.pos):
573 elif self.src.startswith('false', self.pos):
577 elif self.tok == '\n':
578 if self.cursor == len(self.src):
581 self.info = self.info.next_line()
582 self.line_pos = self.cursor
583 elif not self.tok.isspace():
584 # Show up to next structural, whitespace or quote
586 match = re.match('[^[\\]{}:,\\s\'"]+',
587 self.src[self.cursor-1:])
588 raise QAPIParseError(self, "stray '%s'" % match.group(0))
590 def get_members(self):
596 raise QAPIParseError(self, "expected string or '}'")
601 raise QAPIParseError(self, "expected ':'")
604 raise QAPIParseError(self, "duplicate key '%s'" % key)
605 expr[key] = self.get_expr(True)
610 raise QAPIParseError(self, "expected ',' or '}'")
613 raise QAPIParseError(self, "expected string")
615 def get_values(self):
620 if self.tok not in "{['tfn":
621 raise QAPIParseError(
622 self, "expected '{', '[', ']', string, boolean or 'null'")
624 expr.append(self.get_expr(True))
629 raise QAPIParseError(self, "expected ',' or ']'")
632 def get_expr(self, nested):
633 if self.tok != '{' and not nested:
634 raise QAPIParseError(self, "expected '{'")
637 expr = self.get_members()
638 elif self.tok == '[':
640 expr = self.get_values()
641 elif self.tok in "'tfn":
645 raise QAPIParseError(
646 self, "expected '{', '[', string, boolean or 'null'")
649 def get_doc(self, info):
651 raise QAPIParseError(
652 self, "junk after '##' at start of documentation comment")
654 doc = QAPIDoc(self, info)
656 while self.tok == '#':
657 if self.val.startswith('##'):
660 raise QAPIParseError(
662 "junk after '##' at end of documentation comment")
670 raise QAPIParseError(self, "documentation comment must end with '##'")
674 # Semantic analysis of schema expressions
675 # TODO fold into QAPISchema
676 # TODO catching name collisions in generated code would be nice
680 def find_base_members(base):
681 if isinstance(base, dict):
683 base_struct_define = struct_types.get(base)
684 if not base_struct_define:
686 return base_struct_define['data']
689 # Return the qtype of an alternate branch, or None on error.
690 def find_alternate_member_qtype(qapi_type):
691 if qapi_type in builtin_types:
692 return builtin_types[qapi_type]
693 elif qapi_type in struct_types:
695 elif qapi_type in enum_types:
696 return 'QTYPE_QSTRING'
697 elif qapi_type in union_types:
702 # Names must be letters, numbers, -, and _. They must start with letter,
703 # except for downstream extensions which must start with __RFQDN_.
704 # Dots are only valid in the downstream extension prefix.
705 valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
706 '[a-zA-Z][a-zA-Z0-9_-]*$')
709 def check_name(info, source, name,
710 allow_optional=False, enum_member=False, permit_upper=False):
714 if not isinstance(name, str):
715 raise QAPISemError(info, "%s requires a string name" % source)
716 if name.startswith('*'):
717 membername = name[1:]
718 if not allow_optional:
719 raise QAPISemError(info, "%s does not allow optional name '%s'"
721 # Enum members can start with a digit, because the generated C
722 # code always prefixes it with the enum name
723 if enum_member and membername[0].isdigit():
724 membername = 'D' + membername
725 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
726 # and 'q_obj_*' implicit type names.
727 if not valid_name.match(membername) or \
728 c_name(membername, False).startswith('q_'):
729 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
730 if not permit_upper and name.lower() != name:
732 info, "%s uses uppercase in name '%s'" % (source, name))
735 def add_name(name, info, meta):
737 check_name(info, "'%s'" % meta, name, permit_upper=True)
738 # FIXME should reject names that differ only in '_' vs. '.'
739 # vs. '-', because they're liable to clash in generated C.
740 if name in all_names:
741 raise QAPISemError(info, "%s '%s' is already defined"
742 % (all_names[name], name))
743 if name.endswith('Kind') or name.endswith('List'):
744 raise QAPISemError(info, "%s '%s' should not end in '%s'"
745 % (meta, name, name[-4:]))
746 all_names[name] = meta
749 def check_if(expr, info):
751 def check_if_str(ifcond, info):
752 if not isinstance(ifcond, str):
754 info, "'if' condition must be a string or a list of strings")
755 if ifcond.strip() == '':
756 raise QAPISemError(info, "'if' condition '%s' makes no sense"
759 ifcond = expr.get('if')
762 if isinstance(ifcond, list):
764 raise QAPISemError(info, "'if' condition [] is useless")
766 check_if_str(elt, info)
768 check_if_str(ifcond, info)
771 def check_type(info, source, value,
772 allow_array=False, allow_dict=False, allow_metas=[]):
778 # Check if array type for value is okay
779 if isinstance(value, list):
781 raise QAPISemError(info, "%s cannot be an array" % source)
782 if len(value) != 1 or not isinstance(value[0], str):
783 raise QAPISemError(info,
784 "%s: array type must contain single type name" %
788 # Check if type name for value is okay
789 if isinstance(value, str):
790 if value not in all_names:
791 raise QAPISemError(info, "%s uses unknown type '%s'"
793 if not all_names[value] in allow_metas:
794 raise QAPISemError(info, "%s cannot use %s type '%s'" %
795 (source, all_names[value], value))
799 raise QAPISemError(info, "%s should be a type name" % source)
801 if not isinstance(value, OrderedDict):
802 raise QAPISemError(info,
803 "%s should be an object or type name" % source)
805 permit_upper = allow_dict in name_case_whitelist
807 # value is a dictionary, check that each member is okay
808 for (key, arg) in value.items():
809 check_name(info, "member of %s" % source, key,
810 allow_optional=True, permit_upper=permit_upper)
811 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
813 info, "member of %s uses reserved name '%s'" % (source, key))
814 # Todo: allow dictionaries to represent default values of
815 # an optional argument.
816 check_known_keys(info, "member '%s' of %s" % (key, source),
817 arg, ['type'], ['if'])
820 check_type(info, "member '%s' of %s" % (key, source),
821 arg['type'], allow_array=True,
822 allow_metas=['built-in', 'union', 'alternate', 'struct',
826 def check_command(expr, info):
827 name = expr['command']
828 boxed = expr.get('boxed', False)
830 args_meta = ['struct']
832 args_meta += ['union']
833 check_type(info, "'data' for command '%s'" % name,
834 expr.get('data'), allow_dict=not boxed,
835 allow_metas=args_meta)
836 returns_meta = ['union', 'struct']
837 if name in returns_whitelist:
838 returns_meta += ['built-in', 'alternate', 'enum']
839 check_type(info, "'returns' for command '%s'" % name,
840 expr.get('returns'), allow_array=True,
841 allow_metas=returns_meta)
844 def check_event(expr, info):
846 boxed = expr.get('boxed', False)
851 check_type(info, "'data' for event '%s'" % name,
852 expr.get('data'), allow_dict=not boxed,
856 def enum_get_names(expr):
857 return [e['name'] for e in expr['data']]
860 def check_union(expr, info):
862 base = expr.get('base')
863 discriminator = expr.get('discriminator')
864 members = expr['data']
866 # Two types of unions, determined by discriminator.
868 # With no discriminator it is a simple union.
869 if discriminator is None:
870 enum_values = members.keys()
871 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
874 info, "simple union '%s' must not have a base" % name)
876 # Else, it's a flat union.
878 # The object must have a string or dictionary 'base'.
879 check_type(info, "'base' for union '%s'" % name,
880 base, allow_dict=name,
881 allow_metas=['struct'])
884 info, "flat union '%s' must have a base" % name)
885 base_members = find_base_members(base)
886 assert base_members is not None
888 # The value of member 'discriminator' must name a non-optional
889 # member of the base struct.
890 check_name(info, "discriminator of flat union '%s'" % name,
892 discriminator_value = base_members.get(discriminator)
893 if not discriminator_value:
894 raise QAPISemError(info,
895 "discriminator '%s' is not a member of 'base'"
897 if discriminator_value.get('if'):
900 "the discriminator '%s' for union %s must not be conditional"
901 % (discriminator, name))
902 enum_define = enum_types.get(discriminator_value['type'])
903 # Do not allow string discriminator
907 "discriminator '%s' must be of enumeration type"
909 enum_values = enum_get_names(enum_define)
910 allow_metas = ['struct']
912 if (len(enum_values) == 0):
913 raise QAPISemError(info, "union '%s' has no branches" % name)
915 for (key, value) in members.items():
916 check_name(info, "member of union '%s'" % name, key)
918 check_known_keys(info, "member '%s' of union '%s'" % (key, name),
919 value, ['type'], ['if'])
920 check_if(value, info)
922 # Each value must name a known type
923 check_type(info, "member '%s' of union '%s'" % (key, name),
925 allow_array=not base, allow_metas=allow_metas)
927 # If the discriminator names an enum type, then all members
928 # of 'data' must also be members of the enum type.
929 if discriminator is not None:
930 if key not in enum_values:
933 "discriminator value '%s' is not found in enum '%s'"
934 % (key, enum_define['enum']))
937 def check_alternate(expr, info):
938 name = expr['alternate']
939 members = expr['data']
942 if len(members) == 0:
943 raise QAPISemError(info,
944 "alternate '%s' cannot have empty 'data'" % name)
945 for (key, value) in members.items():
946 check_name(info, "member of alternate '%s'" % name, key)
947 check_known_keys(info,
948 "member '%s' of alternate '%s'" % (key, name),
949 value, ['type'], ['if'])
950 check_if(value, info)
954 # Ensure alternates have no type conflicts.
955 check_type(info, "member '%s' of alternate '%s'" % (key, name), typ,
956 allow_metas=['built-in', 'union', 'struct', 'enum'])
957 qtype = find_alternate_member_qtype(typ)
961 "alternate '%s' member '%s' cannot use type '%s'"
963 conflicting = set([qtype])
964 if qtype == 'QTYPE_QSTRING':
965 enum_expr = enum_types.get(typ)
967 for v in enum_get_names(enum_expr):
968 if v in ['on', 'off']:
969 conflicting.add('QTYPE_QBOOL')
970 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
971 conflicting.add('QTYPE_QNUM')
973 conflicting.add('QTYPE_QNUM')
974 conflicting.add('QTYPE_QBOOL')
975 for qt in conflicting:
979 "alternate '%s' member '%s' can't be distinguished "
981 % (name, key, types_seen[qt]))
985 def check_enum(expr, info):
987 members = expr['data']
988 prefix = expr.get('prefix')
990 if not isinstance(members, list):
991 raise QAPISemError(info,
992 "enum '%s' requires an array for 'data'" % name)
993 if prefix is not None and not isinstance(prefix, str):
994 raise QAPISemError(info,
995 "enum '%s' requires a string for 'prefix'" % name)
997 permit_upper = name in name_case_whitelist
999 for member in members:
1000 check_known_keys(info, "member of enum '%s'" % name, member,
1002 check_if(member, info)
1003 normalize_if(member)
1004 check_name(info, "member of enum '%s'" % name, member['name'],
1005 enum_member=True, permit_upper=permit_upper)
1008 def check_struct(expr, info):
1009 name = expr['struct']
1010 members = expr['data']
1011 features = expr.get('features')
1013 check_type(info, "'data' for struct '%s'" % name, members,
1015 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
1016 allow_metas=['struct'])
1019 if not isinstance(features, list):
1021 info, "struct '%s' requires an array for 'features'" % name)
1023 assert isinstance(f, dict)
1024 check_known_keys(info, "feature of struct %s" % name, f,
1029 check_name(info, "feature of struct %s" % name, f['name'])
1032 def check_known_keys(info, source, value, required, optional):
1035 return ', '.join("'" + e + "'" for e in sorted(elems))
1037 missing = set(required) - set(value)
1041 "key%s %s %s missing from %s"
1042 % ('s' if len(missing) > 1 else '', pprint(missing),
1043 'are' if len(missing) > 1 else 'is', source))
1044 allowed = set(required + optional)
1045 unknown = set(value) - allowed
1049 "unknown key%s %s in %s\nValid keys are %s."
1050 % ('s' if len(unknown) > 1 else '', pprint(unknown),
1051 source, pprint(allowed)))
1054 def check_keys(expr, info, meta, required, optional=[]):
1056 if not isinstance(name, str):
1057 raise QAPISemError(info, "'%s' key must have a string value" % meta)
1058 required = required + [meta]
1059 source = "%s '%s'" % (meta, name)
1060 check_known_keys(info, source, expr, required, optional)
1061 for (key, value) in expr.items():
1062 if key in ['gen', 'success-response'] and value is not False:
1063 raise QAPISemError(info,
1064 "'%s' of %s '%s' should only use false value"
1065 % (key, meta, name))
1066 if (key in ['boxed', 'allow-oob', 'allow-preconfig']
1067 and value is not True):
1068 raise QAPISemError(info,
1069 "'%s' of %s '%s' should only use true value"
1070 % (key, meta, name))
1072 check_if(expr, info)
1075 def normalize_enum(expr):
1076 if isinstance(expr['data'], list):
1077 expr['data'] = [m if isinstance(m, dict) else {'name': m}
1078 for m in expr['data']]
1081 def normalize_members(members):
1082 if isinstance(members, OrderedDict):
1083 for key, arg in members.items():
1084 if isinstance(arg, dict):
1086 members[key] = {'type': arg}
1089 def normalize_features(features):
1090 if isinstance(features, list):
1091 features[:] = [f if isinstance(f, dict) else {'name': f}
1095 def normalize_if(expr):
1096 ifcond = expr.get('if')
1097 if isinstance(ifcond, str):
1098 expr['if'] = [ifcond]
1101 def check_exprs(exprs):
1104 # Populate name table with names of built-in types
1105 for builtin in builtin_types.keys():
1106 all_names[builtin] = 'built-in'
1108 # Learn the types and check for valid expression keys
1109 for expr_elem in exprs:
1110 expr = expr_elem['expr']
1111 info = expr_elem['info']
1112 doc = expr_elem.get('doc')
1114 if 'include' in expr:
1117 if not doc and doc_required:
1118 raise QAPISemError(info,
1119 "definition missing documentation comment")
1123 check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
1124 normalize_enum(expr)
1125 enum_types[expr[meta]] = expr
1126 elif 'union' in expr:
1128 check_keys(expr, info, 'union', ['data'],
1129 ['base', 'discriminator', 'if'])
1130 normalize_members(expr.get('base'))
1131 normalize_members(expr['data'])
1132 union_types[expr[meta]] = expr
1133 elif 'alternate' in expr:
1135 check_keys(expr, info, 'alternate', ['data'], ['if'])
1136 normalize_members(expr['data'])
1137 elif 'struct' in expr:
1139 check_keys(expr, info, 'struct', ['data'],
1140 ['base', 'if', 'features'])
1141 normalize_members(expr['data'])
1142 normalize_features(expr.get('features'))
1143 struct_types[expr[meta]] = expr
1144 elif 'command' in expr:
1146 check_keys(expr, info, 'command', [],
1147 ['data', 'returns', 'gen', 'success-response',
1148 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
1149 normalize_members(expr.get('data'))
1150 elif 'event' in expr:
1152 check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
1153 normalize_members(expr.get('data'))
1155 raise QAPISemError(info, "expression is missing metatype")
1158 add_name(name, info, meta)
1159 info.set_defn(meta, name)
1160 if doc and doc.symbol != name:
1163 "definition of '%s' follows documentation for '%s'"
1164 % (name, doc.symbol))
1166 # Validate that exprs make sense
1167 for expr_elem in exprs:
1168 expr = expr_elem['expr']
1169 info = expr_elem['info']
1170 doc = expr_elem.get('doc')
1172 if 'include' in expr:
1175 check_enum(expr, info)
1176 elif 'union' in expr:
1177 check_union(expr, info)
1178 elif 'alternate' in expr:
1179 check_alternate(expr, info)
1180 elif 'struct' in expr:
1181 check_struct(expr, info)
1182 elif 'command' in expr:
1183 check_command(expr, info)
1184 elif 'event' in expr:
1185 check_event(expr, info)
1187 assert False, 'unexpected meta type'
1190 doc.check_expr(expr)
1196 # Schema compiler frontend
1199 class QAPISchemaEntity(object):
1200 def __init__(self, name, info, doc, ifcond=None):
1201 assert name is None or isinstance(name, str)
1204 # For explicitly defined entities, info points to the (explicit)
1205 # definition. For builtins (and their arrays), info is None.
1206 # For implicitly defined entities, info points to a place that
1207 # triggered the implicit definition (there may be more than one
1211 self._ifcond = ifcond or []
1212 self._checked = False
1215 return c_name(self.name)
1217 def check(self, schema):
1218 assert not self._checked
1220 self._module = os.path.relpath(self.info.fname,
1221 os.path.dirname(schema.fname))
1222 self._checked = True
1226 assert self._checked
1231 assert self._checked
1234 def is_implicit(self):
1235 return not self.info
1237 def visit(self, visitor):
1238 assert self._checked
1241 class QAPISchemaVisitor(object):
1242 def visit_begin(self, schema):
1245 def visit_end(self):
1248 def visit_module(self, fname):
1251 def visit_needed(self, entity):
1252 # Default to visiting everything
1255 def visit_include(self, fname, info):
1258 def visit_builtin_type(self, name, info, json_type):
1261 def visit_enum_type(self, name, info, ifcond, members, prefix):
1264 def visit_array_type(self, name, info, ifcond, element_type):
1267 def visit_object_type(self, name, info, ifcond, base, members, variants,
1271 def visit_object_type_flat(self, name, info, ifcond, members, variants,
1275 def visit_alternate_type(self, name, info, ifcond, variants):
1278 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1279 success_response, boxed, allow_oob, allow_preconfig):
1282 def visit_event(self, name, info, ifcond, arg_type, boxed):
1286 class QAPISchemaInclude(QAPISchemaEntity):
1288 def __init__(self, fname, info):
1289 QAPISchemaEntity.__init__(self, None, info, None)
1292 def visit(self, visitor):
1293 QAPISchemaEntity.visit(self, visitor)
1294 visitor.visit_include(self.fname, self.info)
1297 class QAPISchemaType(QAPISchemaEntity):
1298 # Return the C type for common use.
1299 # For the types we commonly box, this is a pointer type.
1303 # Return the C type to be used in a parameter list.
1304 def c_param_type(self):
1305 return self.c_type()
1307 # Return the C type to be used where we suppress boxing.
1308 def c_unboxed_type(self):
1309 return self.c_type()
1311 def json_type(self):
1314 def alternate_qtype(self):
1316 'null': 'QTYPE_QNULL',
1317 'string': 'QTYPE_QSTRING',
1318 'number': 'QTYPE_QNUM',
1319 'int': 'QTYPE_QNUM',
1320 'boolean': 'QTYPE_QBOOL',
1321 'object': 'QTYPE_QDICT'
1323 return json2qtype.get(self.json_type())
1326 if self.is_implicit():
1331 class QAPISchemaBuiltinType(QAPISchemaType):
1332 def __init__(self, name, json_type, c_type):
1333 QAPISchemaType.__init__(self, name, None, None)
1334 assert not c_type or isinstance(c_type, str)
1335 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1337 self._json_type_name = json_type
1338 self._c_type_name = c_type
1344 return self._c_type_name
1346 def c_param_type(self):
1347 if self.name == 'str':
1348 return 'const ' + self._c_type_name
1349 return self._c_type_name
1351 def json_type(self):
1352 return self._json_type_name
1355 return self.json_type()
1357 def visit(self, visitor):
1358 QAPISchemaType.visit(self, visitor)
1359 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1362 class QAPISchemaEnumType(QAPISchemaType):
1363 def __init__(self, name, info, doc, ifcond, members, prefix):
1364 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1366 assert isinstance(m, QAPISchemaEnumMember)
1367 m.set_defined_in(name)
1368 assert prefix is None or isinstance(prefix, str)
1369 self.members = members
1370 self.prefix = prefix
1372 def check(self, schema):
1373 QAPISchemaType.check(self, schema)
1375 for m in self.members:
1376 m.check_clash(self.info, seen)
1378 self.doc.connect_member(m)
1380 def is_implicit(self):
1381 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1382 return self.name.endswith('Kind') or self.name == 'QType'
1385 return c_name(self.name)
1387 def member_names(self):
1388 return [m.name for m in self.members]
1390 def json_type(self):
1393 def visit(self, visitor):
1394 QAPISchemaType.visit(self, visitor)
1395 visitor.visit_enum_type(self.name, self.info, self.ifcond,
1396 self.members, self.prefix)
1399 class QAPISchemaArrayType(QAPISchemaType):
1400 def __init__(self, name, info, element_type):
1401 QAPISchemaType.__init__(self, name, info, None, None)
1402 assert isinstance(element_type, str)
1403 self._element_type_name = element_type
1404 self.element_type = None
1406 def check(self, schema):
1407 QAPISchemaType.check(self, schema)
1408 self.element_type = schema.lookup_type(self._element_type_name)
1409 assert self.element_type
1410 assert not isinstance(self.element_type, QAPISchemaArrayType)
1414 assert self._checked
1415 return self.element_type.ifcond
1419 assert self._checked
1420 return self.element_type.module
1422 def is_implicit(self):
1426 return c_name(self.name) + pointer_suffix
1428 def json_type(self):
1432 elt_doc_type = self.element_type.doc_type()
1433 if not elt_doc_type:
1435 return 'array of ' + elt_doc_type
1437 def visit(self, visitor):
1438 QAPISchemaType.visit(self, visitor)
1439 visitor.visit_array_type(self.name, self.info, self.ifcond,
1443 class QAPISchemaObjectType(QAPISchemaType):
1444 def __init__(self, name, info, doc, ifcond,
1445 base, local_members, variants, features):
1446 # struct has local_members, optional base, and no variants
1447 # flat union has base, variants, and no local_members
1448 # simple union has local_members, variants, and no base
1449 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1450 assert base is None or isinstance(base, str)
1451 for m in local_members:
1452 assert isinstance(m, QAPISchemaObjectTypeMember)
1453 m.set_defined_in(name)
1454 if variants is not None:
1455 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1456 variants.set_defined_in(name)
1458 assert isinstance(f, QAPISchemaFeature)
1459 f.set_defined_in(name)
1460 self._base_name = base
1462 self.local_members = local_members
1463 self.variants = variants
1465 self.features = features
1467 def check(self, schema):
1468 # This calls another type T's .check() exactly when the C
1469 # struct emitted by gen_object() contains that T's C struct
1470 # (pointers don't count).
1471 if self.members is not None:
1472 # A previous .check() completed: nothing to do
1475 # Recursed: C struct contains itself
1476 raise QAPISemError(self.info,
1477 "object %s contains itself" % self.name)
1479 QAPISchemaType.check(self, schema)
1480 assert self._checked and self.members is None
1482 seen = OrderedDict()
1484 self.base = schema.lookup_type(self._base_name)
1485 assert isinstance(self.base, QAPISchemaObjectType)
1486 self.base.check(schema)
1487 self.base.check_clash(self.info, seen)
1488 for m in self.local_members:
1490 m.check_clash(self.info, seen)
1492 self.doc.connect_member(m)
1493 members = seen.values()
1496 self.variants.check(schema, seen)
1497 assert self.variants.tag_member in members
1498 self.variants.check_clash(self.info, seen)
1500 # Features are in a name space separate from members
1502 for f in self.features:
1503 f.check_clash(self.info, seen)
1508 self.members = members # mark completed
1510 # Check that the members of this type do not cause duplicate JSON members,
1511 # and update seen to track the members seen so far. Report any errors
1512 # on behalf of info, which is not necessarily self.info
1513 def check_clash(self, info, seen):
1514 assert self._checked
1515 assert not self.variants # not implemented
1516 for m in self.members:
1517 m.check_clash(info, seen)
1521 assert self._checked
1522 if isinstance(self._ifcond, QAPISchemaType):
1523 # Simple union wrapper type inherits from wrapped type;
1524 # see _make_implicit_object_type()
1525 return self._ifcond.ifcond
1528 def is_implicit(self):
1529 # See QAPISchema._make_implicit_object_type(), as well as
1530 # _def_predefineds()
1531 return self.name.startswith('q_')
1534 assert self.members is not None
1535 return not self.members and not self.variants
1538 assert self.name != 'q_empty'
1539 return QAPISchemaType.c_name(self)
1542 assert not self.is_implicit()
1543 return c_name(self.name) + pointer_suffix
1545 def c_unboxed_type(self):
1546 return c_name(self.name)
1548 def json_type(self):
1551 def visit(self, visitor):
1552 QAPISchemaType.visit(self, visitor)
1553 visitor.visit_object_type(self.name, self.info, self.ifcond,
1554 self.base, self.local_members, self.variants,
1556 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1557 self.members, self.variants,
1561 class QAPISchemaMember(object):
1562 """ Represents object members, enum members and features """
1565 def __init__(self, name, ifcond=None):
1566 assert isinstance(name, str)
1568 self.ifcond = ifcond or []
1569 self.defined_in = None
1571 def set_defined_in(self, name):
1572 assert not self.defined_in
1573 self.defined_in = name
1575 def check_clash(self, info, seen):
1576 cname = c_name(self.name)
1580 "%s collides with %s"
1581 % (self.describe(info), seen[cname].describe(info)))
1584 def describe(self, info):
1586 defined_in = self.defined_in
1589 if defined_in.startswith('q_obj_'):
1590 # See QAPISchema._make_implicit_object_type() - reverse the
1591 # mapping there to create a nice human-readable description
1592 defined_in = defined_in[6:]
1593 if defined_in.endswith('-arg'):
1594 # Implicit type created for a command's dict 'data'
1595 assert role == 'member'
1597 elif defined_in.endswith('-base'):
1598 # Implicit type created for a flat union's dict 'base'
1599 role = 'base ' + role
1601 # Implicit type created for a simple union's branch
1602 assert defined_in.endswith('-wrapper')
1603 # Unreachable and not implemented
1605 elif defined_in.endswith('Kind'):
1606 # See QAPISchema._make_implicit_enum_type()
1607 # Implicit enum created for simple union's branches
1608 assert role == 'value'
1610 elif defined_in != info.defn_name:
1611 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
1612 return "%s '%s'" % (role, self.name)
1615 class QAPISchemaEnumMember(QAPISchemaMember):
1619 class QAPISchemaFeature(QAPISchemaMember):
1623 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1624 def __init__(self, name, typ, optional, ifcond=None):
1625 QAPISchemaMember.__init__(self, name, ifcond)
1626 assert isinstance(typ, str)
1627 assert isinstance(optional, bool)
1628 self._type_name = typ
1630 self.optional = optional
1632 def check(self, schema):
1633 assert self.defined_in
1634 self.type = schema.lookup_type(self._type_name)
1638 class QAPISchemaObjectTypeVariants(object):
1639 def __init__(self, tag_name, tag_member, variants):
1640 # Flat unions pass tag_name but not tag_member.
1641 # Simple unions and alternates pass tag_member but not tag_name.
1642 # After check(), tag_member is always set, and tag_name remains
1643 # a reliable witness of being used by a flat union.
1644 assert bool(tag_member) != bool(tag_name)
1645 assert (isinstance(tag_name, str) or
1646 isinstance(tag_member, QAPISchemaObjectTypeMember))
1648 assert isinstance(v, QAPISchemaObjectTypeVariant)
1649 self._tag_name = tag_name
1650 self.tag_member = tag_member
1651 self.variants = variants
1653 def set_defined_in(self, name):
1654 for v in self.variants:
1655 v.set_defined_in(name)
1657 def check(self, schema, seen):
1658 if not self.tag_member: # flat union
1659 self.tag_member = seen[c_name(self._tag_name)]
1660 assert self._tag_name == self.tag_member.name
1661 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1662 assert not self.tag_member.optional
1663 assert self.tag_member.ifcond == []
1664 if self._tag_name: # flat union
1665 # branches that are not explicitly covered get an empty type
1666 cases = set([v.name for v in self.variants])
1667 for m in self.tag_member.type.members:
1668 if m.name not in cases:
1669 v = QAPISchemaObjectTypeVariant(m.name, 'q_empty',
1671 v.set_defined_in(self.tag_member.defined_in)
1672 self.variants.append(v)
1673 assert self.variants
1674 for v in self.variants:
1676 # Union names must match enum values; alternate names are
1677 # checked separately. Use 'seen' to tell the two apart.
1679 assert v.name in self.tag_member.type.member_names()
1680 assert (isinstance(v.type, QAPISchemaObjectType)
1681 and not v.type.variants)
1682 v.type.check(schema)
1684 def check_clash(self, info, seen):
1685 for v in self.variants:
1686 # Reset seen map for each variant, since qapi names from one
1687 # branch do not affect another branch
1688 v.type.check_clash(info, dict(seen))
1691 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1694 def __init__(self, name, typ, ifcond=None):
1695 QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
1698 class QAPISchemaAlternateType(QAPISchemaType):
1699 def __init__(self, name, info, doc, ifcond, variants):
1700 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1701 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1702 assert variants.tag_member
1703 variants.set_defined_in(name)
1704 variants.tag_member.set_defined_in(self.name)
1705 self.variants = variants
1707 def check(self, schema):
1708 QAPISchemaType.check(self, schema)
1709 self.variants.tag_member.check(schema)
1710 # Not calling self.variants.check_clash(), because there's nothing
1712 self.variants.check(schema, {})
1713 # Alternate branch names have no relation to the tag enum values;
1714 # so we have to check for potential name collisions ourselves.
1716 for v in self.variants.variants:
1717 v.check_clash(self.info, seen)
1718 # TODO check conflicting qtypes
1720 self.doc.connect_member(v)
1725 return c_name(self.name) + pointer_suffix
1727 def json_type(self):
1730 def visit(self, visitor):
1731 QAPISchemaType.visit(self, visitor)
1732 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1736 class QAPISchemaCommand(QAPISchemaEntity):
1737 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1738 gen, success_response, boxed, allow_oob, allow_preconfig):
1739 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1740 assert not arg_type or isinstance(arg_type, str)
1741 assert not ret_type or isinstance(ret_type, str)
1742 self._arg_type_name = arg_type
1743 self.arg_type = None
1744 self._ret_type_name = ret_type
1745 self.ret_type = None
1747 self.success_response = success_response
1749 self.allow_oob = allow_oob
1750 self.allow_preconfig = allow_preconfig
1752 def check(self, schema):
1753 QAPISchemaEntity.check(self, schema)
1754 if self._arg_type_name:
1755 self.arg_type = schema.lookup_type(self._arg_type_name)
1756 assert isinstance(self.arg_type, QAPISchemaObjectType)
1757 assert not self.arg_type.variants or self.boxed
1759 raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
1760 if self._ret_type_name:
1761 self.ret_type = schema.lookup_type(self._ret_type_name)
1762 assert isinstance(self.ret_type, QAPISchemaType)
1764 def visit(self, visitor):
1765 QAPISchemaEntity.visit(self, visitor)
1766 visitor.visit_command(self.name, self.info, self.ifcond,
1767 self.arg_type, self.ret_type,
1768 self.gen, self.success_response,
1769 self.boxed, self.allow_oob,
1770 self.allow_preconfig)
1773 class QAPISchemaEvent(QAPISchemaEntity):
1774 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1775 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1776 assert not arg_type or isinstance(arg_type, str)
1777 self._arg_type_name = arg_type
1778 self.arg_type = None
1781 def check(self, schema):
1782 QAPISchemaEntity.check(self, schema)
1783 if self._arg_type_name:
1784 self.arg_type = schema.lookup_type(self._arg_type_name)
1785 assert isinstance(self.arg_type, QAPISchemaObjectType)
1786 assert not self.arg_type.variants or self.boxed
1788 raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
1790 def visit(self, visitor):
1791 QAPISchemaEntity.visit(self, visitor)
1792 visitor.visit_event(self.name, self.info, self.ifcond,
1793 self.arg_type, self.boxed)
1796 class QAPISchema(object):
1797 def __init__(self, fname):
1799 if sys.version_info[0] >= 3:
1800 f = open(fname, 'r', encoding='utf-8')
1802 f = open(fname, 'r')
1803 parser = QAPISchemaParser(f)
1804 exprs = check_exprs(parser.exprs)
1805 self.docs = parser.docs
1806 self._entity_list = []
1807 self._entity_dict = {}
1808 self._predefining = True
1809 self._def_predefineds()
1810 self._predefining = False
1811 self._def_exprs(exprs)
1814 def _def_entity(self, ent):
1815 # Only the predefined types are allowed to not have info
1816 assert ent.info or self._predefining
1817 assert ent.name is None or ent.name not in self._entity_dict
1818 self._entity_list.append(ent)
1819 if ent.name is not None:
1820 self._entity_dict[ent.name] = ent
1822 def lookup_entity(self, name, typ=None):
1823 ent = self._entity_dict.get(name)
1824 if typ and not isinstance(ent, typ):
1828 def lookup_type(self, name):
1829 return self.lookup_entity(name, QAPISchemaType)
1831 def _def_include(self, expr, info, doc):
1832 include = expr['include']
1835 while main_info.parent:
1836 main_info = main_info.parent
1837 fname = os.path.relpath(include, os.path.dirname(main_info.fname))
1838 self._def_entity(QAPISchemaInclude(fname, info))
1840 def _def_builtin_type(self, name, json_type, c_type):
1841 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1842 # Instantiating only the arrays that are actually used would
1843 # be nice, but we can't as long as their generated code
1844 # (qapi-builtin-types.[ch]) may be shared by some other
1846 self._make_array_type(name, None)
1848 def _def_predefineds(self):
1849 for t in [('str', 'string', 'char' + pointer_suffix),
1850 ('number', 'number', 'double'),
1851 ('int', 'int', 'int64_t'),
1852 ('int8', 'int', 'int8_t'),
1853 ('int16', 'int', 'int16_t'),
1854 ('int32', 'int', 'int32_t'),
1855 ('int64', 'int', 'int64_t'),
1856 ('uint8', 'int', 'uint8_t'),
1857 ('uint16', 'int', 'uint16_t'),
1858 ('uint32', 'int', 'uint32_t'),
1859 ('uint64', 'int', 'uint64_t'),
1860 ('size', 'int', 'uint64_t'),
1861 ('bool', 'boolean', 'bool'),
1862 ('any', 'value', 'QObject' + pointer_suffix),
1863 ('null', 'null', 'QNull' + pointer_suffix)]:
1864 self._def_builtin_type(*t)
1865 self.the_empty_object_type = QAPISchemaObjectType(
1866 'q_empty', None, None, None, None, [], None, [])
1867 self._def_entity(self.the_empty_object_type)
1869 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1871 qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
1873 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1874 qtype_values, 'QTYPE'))
1876 def _make_features(self, features):
1877 return [QAPISchemaFeature(f['name'], f.get('if')) for f in features]
1879 def _make_enum_members(self, values):
1880 return [QAPISchemaEnumMember(v['name'], v.get('if'))
1883 def _make_implicit_enum_type(self, name, info, ifcond, values):
1884 # See also QAPISchemaObjectTypeMember.describe()
1885 name = name + 'Kind' # Use namespace reserved by add_name()
1886 self._def_entity(QAPISchemaEnumType(
1887 name, info, None, ifcond, self._make_enum_members(values), None))
1890 def _make_array_type(self, element_type, info):
1891 name = element_type + 'List' # Use namespace reserved by add_name()
1892 if not self.lookup_type(name):
1893 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1896 def _make_implicit_object_type(self, name, info, doc, ifcond,
1900 # See also QAPISchemaObjectTypeMember.describe()
1901 name = 'q_obj_%s-%s' % (name, role)
1902 typ = self.lookup_entity(name, QAPISchemaObjectType)
1904 # The implicit object type has multiple users. This can
1905 # happen only for simple unions' implicit wrapper types.
1906 # Its ifcond should be the disjunction of its user's
1907 # ifconds. Not implemented. Instead, we always pass the
1908 # wrapped type's ifcond, which is trivially the same for all
1909 # users. It's also necessary for the wrapper to compile.
1910 # But it's not tight: the disjunction need not imply it. We
1911 # may end up compiling useless wrapper types.
1912 # TODO kill simple unions or implement the disjunction
1913 assert ifcond == typ._ifcond # pylint: disable=protected-access
1915 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1916 None, members, None, []))
1919 def _def_enum_type(self, expr, info, doc):
1922 prefix = expr.get('prefix')
1923 ifcond = expr.get('if')
1924 self._def_entity(QAPISchemaEnumType(
1925 name, info, doc, ifcond,
1926 self._make_enum_members(data), prefix))
1928 def _make_member(self, name, typ, ifcond, info):
1930 if name.startswith('*'):
1933 if isinstance(typ, list):
1934 assert len(typ) == 1
1935 typ = self._make_array_type(typ[0], info)
1936 return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
1938 def _make_members(self, data, info):
1939 return [self._make_member(key, value['type'], value.get('if'), info)
1940 for (key, value) in data.items()]
1942 def _def_struct_type(self, expr, info, doc):
1943 name = expr['struct']
1944 base = expr.get('base')
1946 ifcond = expr.get('if')
1947 features = expr.get('features', [])
1948 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1949 self._make_members(data, info),
1951 self._make_features(features)))
1953 def _make_variant(self, case, typ, ifcond):
1954 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1956 def _make_simple_variant(self, case, typ, ifcond, info):
1957 if isinstance(typ, list):
1958 assert len(typ) == 1
1959 typ = self._make_array_type(typ[0], info)
1960 typ = self._make_implicit_object_type(
1961 typ, info, None, self.lookup_type(typ),
1962 'wrapper', [self._make_member('data', typ, None, info)])
1963 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1965 def _def_union_type(self, expr, info, doc):
1966 name = expr['union']
1968 base = expr.get('base')
1969 ifcond = expr.get('if')
1970 tag_name = expr.get('discriminator')
1972 if isinstance(base, dict):
1973 base = self._make_implicit_object_type(
1974 name, info, doc, ifcond,
1975 'base', self._make_members(base, info))
1977 variants = [self._make_variant(key, value['type'], value.get('if'))
1978 for (key, value) in data.items()]
1981 variants = [self._make_simple_variant(key, value['type'],
1982 value.get('if'), info)
1983 for (key, value) in data.items()]
1984 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1985 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1986 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1987 members = [tag_member]
1989 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1990 QAPISchemaObjectTypeVariants(tag_name,
1994 def _def_alternate_type(self, expr, info, doc):
1995 name = expr['alternate']
1997 ifcond = expr.get('if')
1998 variants = [self._make_variant(key, value['type'], value.get('if'))
1999 for (key, value) in data.items()]
2000 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
2002 QAPISchemaAlternateType(name, info, doc, ifcond,
2003 QAPISchemaObjectTypeVariants(None,
2007 def _def_command(self, expr, info, doc):
2008 name = expr['command']
2009 data = expr.get('data')
2010 rets = expr.get('returns')
2011 gen = expr.get('gen', True)
2012 success_response = expr.get('success-response', True)
2013 boxed = expr.get('boxed', False)
2014 allow_oob = expr.get('allow-oob', False)
2015 allow_preconfig = expr.get('allow-preconfig', False)
2016 ifcond = expr.get('if')
2017 if isinstance(data, OrderedDict):
2018 data = self._make_implicit_object_type(
2019 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2020 if isinstance(rets, list):
2021 assert len(rets) == 1
2022 rets = self._make_array_type(rets[0], info)
2023 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
2024 gen, success_response,
2025 boxed, allow_oob, allow_preconfig))
2027 def _def_event(self, expr, info, doc):
2028 name = expr['event']
2029 data = expr.get('data')
2030 boxed = expr.get('boxed', False)
2031 ifcond = expr.get('if')
2032 if isinstance(data, OrderedDict):
2033 data = self._make_implicit_object_type(
2034 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2035 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
2037 def _def_exprs(self, exprs):
2038 for expr_elem in exprs:
2039 expr = expr_elem['expr']
2040 info = expr_elem['info']
2041 doc = expr_elem.get('doc')
2043 self._def_enum_type(expr, info, doc)
2044 elif 'struct' in expr:
2045 self._def_struct_type(expr, info, doc)
2046 elif 'union' in expr:
2047 self._def_union_type(expr, info, doc)
2048 elif 'alternate' in expr:
2049 self._def_alternate_type(expr, info, doc)
2050 elif 'command' in expr:
2051 self._def_command(expr, info, doc)
2052 elif 'event' in expr:
2053 self._def_event(expr, info, doc)
2054 elif 'include' in expr:
2055 self._def_include(expr, info, doc)
2060 for ent in self._entity_list:
2063 def visit(self, visitor):
2064 visitor.visit_begin(self)
2066 visitor.visit_module(module)
2067 for entity in self._entity_list:
2068 if visitor.visit_needed(entity):
2069 if entity.module != module:
2070 module = entity.module
2071 visitor.visit_module(module)
2072 entity.visit(visitor)
2077 # Code generation helpers
2080 def camel_case(name):
2084 if ch in ['_', '-']:
2087 new_name += ch.upper()
2090 new_name += ch.lower()
2094 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2095 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2096 # ENUM24_Name -> ENUM24_NAME
2097 def camel_to_upper(value):
2098 c_fun_str = c_name(value, False)
2103 length = len(c_fun_str)
2104 for i in range(length):
2106 # When c is upper and no '_' appears before, do more checks
2107 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
2108 if i < length - 1 and c_fun_str[i + 1].islower():
2110 elif c_fun_str[i - 1].isdigit():
2113 return new_name.lstrip('_').upper()
2116 def c_enum_const(type_name, const_name, prefix=None):
2117 if prefix is not None:
2119 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
2122 if hasattr(str, 'maketrans'):
2123 c_name_trans = str.maketrans('.-', '__')
2125 c_name_trans = string.maketrans('.-', '__')
2128 # Map @name to a valid C identifier.
2129 # If @protect, avoid returning certain ticklish identifiers (like
2130 # C keywords) by prepending 'q_'.
2132 # Used for converting 'name' from a 'name':'type' qapi definition
2133 # into a generated struct member, as well as converting type names
2134 # into substrings of a generated C function name.
2135 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2136 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2137 def c_name(name, protect=True):
2138 # ANSI X3J11/88-090, 3.1.1
2139 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
2140 'default', 'do', 'double', 'else', 'enum', 'extern',
2141 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2142 'return', 'short', 'signed', 'sizeof', 'static',
2143 'struct', 'switch', 'typedef', 'union', 'unsigned',
2144 'void', 'volatile', 'while'])
2145 # ISO/IEC 9899:1999, 6.4.1
2146 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2147 # ISO/IEC 9899:2011, 6.4.1
2148 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2149 '_Noreturn', '_Static_assert', '_Thread_local'])
2150 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2152 gcc_words = set(['asm', 'typeof'])
2153 # C++ ISO/IEC 14882:2003 2.11
2154 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
2155 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2156 'namespace', 'new', 'operator', 'private', 'protected',
2157 'public', 'reinterpret_cast', 'static_cast', 'template',
2158 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2159 'using', 'virtual', 'wchar_t',
2160 # alternative representations
2161 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2162 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2163 # namespace pollution:
2164 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2165 name = name.translate(c_name_trans)
2166 if protect and (name in c89_words | c99_words | c11_words | gcc_words
2167 | cpp_words | polluted_words):
2172 eatspace = '\033EATSPACE.'
2173 pointer_suffix = ' *' + eatspace
2176 def genindent(count):
2178 for _ in range(count):
2186 def push_indent(indent_amount=4):
2188 indent_level += indent_amount
2191 def pop_indent(indent_amount=4):
2193 indent_level -= indent_amount
2196 # Generate @code with @kwds interpolated.
2197 # Obey indent_level, and strip eatspace.
2198 def cgen(code, **kwds):
2201 indent = genindent(indent_level)
2202 # re.subn() lacks flags support before Python 2.7, use re.compile()
2203 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2206 return re.sub(re.escape(eatspace) + r' *', '', raw)
2209 def mcgen(code, **kwds):
2212 return cgen(code, **kwds)
2215 def c_fname(filename):
2216 return re.sub(r'[^A-Za-z0-9_]', '_', filename)
2219 def guardstart(name):
2225 name=c_fname(name).upper())
2231 #endif /* %(name)s */
2233 name=c_fname(name).upper())
2245 def gen_endif(ifcond):
2247 for ifc in reversed(ifcond):
2249 #endif /* %(cond)s */
2254 def _wrap_ifcond(ifcond, before, after):
2256 return after # suppress empty #if ... #endif
2258 assert after.startswith(before)
2260 added = after[len(before):]
2261 if added[0] == '\n':
2264 out += gen_if(ifcond)
2266 out += gen_endif(ifcond)
2270 def gen_enum_lookup(name, members, prefix=None):
2273 const QEnumLookup %(c_name)s_lookup = {
2274 .array = (const char *const[]) {
2276 c_name=c_name(name))
2278 ret += gen_if(m.ifcond)
2279 index = c_enum_const(name, m.name, prefix)
2281 [%(index)s] = "%(name)s",
2283 index=index, name=m.name)
2284 ret += gen_endif(m.ifcond)
2288 .size = %(max_index)s
2291 max_index=c_enum_const(name, '_MAX', prefix))
2295 def gen_enum(name, members, prefix=None):
2296 # append automatically generated _MAX value
2297 enum_members = members + [QAPISchemaEnumMember('_MAX')]
2301 typedef enum %(c_name)s {
2303 c_name=c_name(name))
2305 for m in enum_members:
2306 ret += gen_if(m.ifcond)
2310 c_enum=c_enum_const(name, m.name, prefix))
2311 ret += gen_endif(m.ifcond)
2316 c_name=c_name(name))
2320 #define %(c_name)s_str(val) \\
2321 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2323 extern const QEnumLookup %(c_name)s_lookup;
2325 c_name=c_name(name))
2329 def build_params(arg_type, boxed, extra=None):
2334 ret += '%s arg' % arg_type.c_param_type()
2337 assert not arg_type.variants
2338 for memb in arg_type.members:
2342 ret += 'bool has_%s, ' % c_name(memb.name)
2343 ret += '%s %s' % (memb.type.c_param_type(),
2347 return ret if ret else 'void'
2351 # Accumulate and write output
2354 class QAPIGen(object):
2356 def __init__(self, fname):
2361 def preamble_add(self, text):
2362 self._preamble += text
2364 def add(self, text):
2367 def get_content(self):
2368 return self._top() + self._preamble + self._body + self._bottom()
2376 def write(self, output_dir):
2377 pathname = os.path.join(output_dir, self.fname)
2378 dir = os.path.dirname(pathname)
2382 except os.error as e:
2383 if e.errno != errno.EEXIST:
2385 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2386 if sys.version_info[0] >= 3:
2387 f = open(fd, 'r+', encoding='utf-8')
2389 f = os.fdopen(fd, 'r+')
2390 text = self.get_content()
2391 oldtext = f.read(len(text) + 1)
2400 def ifcontext(ifcond, *args):
2401 """A 'with' statement context manager to wrap with start_if()/end_if()
2403 *args: any number of QAPIGenCCode
2407 with ifcontext(ifcond, self._genh, self._genc):
2408 modify self._genh and self._genc ...
2410 Is equivalent to calling::
2412 self._genh.start_if(ifcond)
2413 self._genc.start_if(ifcond)
2414 modify self._genh and self._genc ...
2419 arg.start_if(ifcond)
2425 class QAPIGenCCode(QAPIGen):
2427 def __init__(self, fname):
2428 QAPIGen.__init__(self, fname)
2429 self._start_if = None
2431 def start_if(self, ifcond):
2432 assert self._start_if is None
2433 self._start_if = (ifcond, self._body, self._preamble)
2436 assert self._start_if
2438 self._start_if = None
2440 def _wrap_ifcond(self):
2441 self._body = _wrap_ifcond(self._start_if[0],
2442 self._start_if[1], self._body)
2443 self._preamble = _wrap_ifcond(self._start_if[0],
2444 self._start_if[2], self._preamble)
2446 def get_content(self):
2447 assert self._start_if is None
2448 return QAPIGen.get_content(self)
2451 class QAPIGenC(QAPIGenCCode):
2453 def __init__(self, fname, blurb, pydoc):
2454 QAPIGenCCode.__init__(self, fname)
2456 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2461 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2468 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2469 * See the COPYING.LIB file in the top-level directory.
2473 blurb=self._blurb, copyright=self._copyright)
2478 /* Dummy declaration to prevent empty .o file */
2479 char qapi_dummy_%(name)s;
2481 name=c_fname(self.fname))
2484 class QAPIGenH(QAPIGenC):
2487 return QAPIGenC._top(self) + guardstart(self.fname)
2490 return guardend(self.fname)
2493 class QAPIGenDoc(QAPIGen):
2496 return (QAPIGen._top(self)
2497 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2500 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2502 def __init__(self, prefix, what, blurb, pydoc):
2503 self._prefix = prefix
2505 self._genc = QAPIGenC(self._prefix + self._what + '.c',
2507 self._genh = QAPIGenH(self._prefix + self._what + '.h',
2510 def write(self, output_dir):
2511 self._genc.write(output_dir)
2512 self._genh.write(output_dir)
2515 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2517 def __init__(self, prefix, what, blurb, pydoc):
2518 self._prefix = prefix
2525 self._main_module = None
2528 def _is_user_module(name):
2529 return name and not name.startswith('./')
2532 def _is_builtin_module(name):
2535 def _module_dirname(self, what, name):
2536 if self._is_user_module(name):
2537 return os.path.dirname(name)
2540 def _module_basename(self, what, name):
2541 ret = '' if self._is_builtin_module(name) else self._prefix
2542 if self._is_user_module(name):
2543 basename = os.path.basename(name)
2545 if name != self._main_module:
2546 ret += '-' + os.path.splitext(basename)[0]
2548 name = name[2:] if name else 'builtin'
2549 ret += re.sub(r'-', '-' + name + '-', what)
2552 def _module_filename(self, what, name):
2553 return os.path.join(self._module_dirname(what, name),
2554 self._module_basename(what, name))
2556 def _add_module(self, name, blurb):
2557 basename = self._module_filename(self._what, name)
2558 genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
2559 genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
2560 self._module[name] = (genc, genh)
2561 self._set_module(name)
2563 def _add_user_module(self, name, blurb):
2564 assert self._is_user_module(name)
2565 if self._main_module is None:
2566 self._main_module = name
2567 self._add_module(name, blurb)
2569 def _add_system_module(self, name, blurb):
2570 self._add_module(name and './' + name, blurb)
2572 def _set_module(self, name):
2573 self._genc, self._genh = self._module[name]
2575 def write(self, output_dir, opt_builtins=False):
2576 for name in self._module:
2577 if self._is_builtin_module(name) and not opt_builtins:
2579 (genc, genh) = self._module[name]
2580 genc.write(output_dir)
2581 genh.write(output_dir)
2583 def _begin_user_module(self, name):
2586 def visit_module(self, name):
2587 if name in self._module:
2588 self._set_module(name)
2589 elif self._is_builtin_module(name):
2590 # The built-in module has not been created. No code may
2595 self._add_user_module(name, self._blurb)
2596 self._begin_user_module(name)
2598 def visit_include(self, name, info):
2599 relname = os.path.relpath(self._module_filename(self._what, name),
2600 os.path.dirname(self._genh.fname))
2601 self._genh.preamble_add(mcgen('''
2602 #include "%(relname)s.h"