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(name, info, source,
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(name, info, "'%s'" % meta, 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(value, info, source,
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(key, info, "member of %s" % source,
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 check_known_keys(arg, info, "member '%s' of %s" % (key, source),
818 check_type(arg['type'], info, "member '%s' of %s" % (key, source),
820 allow_metas=['built-in', 'union', 'alternate', 'struct',
824 def check_command(expr, info):
825 name = expr['command']
826 boxed = expr.get('boxed', False)
828 args_meta = ['struct']
830 args_meta += ['union']
831 check_type(expr.get('data'), info,
832 "'data' for command '%s'" % name,
833 allow_dict=not boxed, allow_metas=args_meta)
834 returns_meta = ['union', 'struct']
835 if name in returns_whitelist:
836 returns_meta += ['built-in', 'alternate', 'enum']
837 check_type(expr.get('returns'), info,
838 "'returns' for command '%s'" % name,
839 allow_array=True, allow_metas=returns_meta)
842 def check_event(expr, info):
844 boxed = expr.get('boxed', False)
849 check_type(expr.get('data'), info,
850 "'data' for event '%s'" % name,
851 allow_dict=not boxed, allow_metas=meta)
854 def enum_get_names(expr):
855 return [e['name'] for e in expr['data']]
858 def check_union(expr, info):
860 base = expr.get('base')
861 discriminator = expr.get('discriminator')
862 members = expr['data']
864 # Two types of unions, determined by discriminator.
866 # With no discriminator it is a simple union.
867 if discriminator is None:
868 enum_values = members.keys()
869 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
872 info, "simple union '%s' must not have a base" % name)
874 # Else, it's a flat union.
876 # The object must have a string or dictionary 'base'.
877 check_type(base, info, "'base' for union '%s'" % name,
878 allow_dict=name, allow_metas=['struct'])
881 info, "flat union '%s' must have a base" % name)
882 base_members = find_base_members(base)
883 assert base_members is not None
885 # The value of member 'discriminator' must name a non-optional
886 # member of the base struct.
887 check_name(discriminator, info,
888 "discriminator of flat union '%s'" % name)
889 discriminator_value = base_members.get(discriminator)
890 if not discriminator_value:
891 raise QAPISemError(info,
892 "discriminator '%s' is not a member of 'base'"
894 if discriminator_value.get('if'):
897 "the discriminator '%s' for union %s must not be conditional"
898 % (discriminator, name))
899 enum_define = enum_types.get(discriminator_value['type'])
900 # Do not allow string discriminator
904 "discriminator '%s' must be of enumeration type"
906 enum_values = enum_get_names(enum_define)
907 allow_metas = ['struct']
909 if (len(enum_values) == 0):
910 raise QAPISemError(info, "union '%s' has no branches" % name)
912 for (key, value) in members.items():
913 check_name(key, info, "member of union '%s'" % name)
915 check_known_keys(value, info,
916 "member '%s' of union '%s'" % (key, name),
918 check_if(value, info)
920 # Each value must name a known type
921 check_type(value['type'], info,
922 "member '%s' of union '%s'" % (key, name),
923 allow_array=not base, allow_metas=allow_metas)
925 # If the discriminator names an enum type, then all members
926 # of 'data' must also be members of the enum type.
927 if discriminator is not None:
928 if key not in enum_values:
931 "discriminator value '%s' is not found in enum '%s'"
932 % (key, enum_define['enum']))
935 def check_alternate(expr, info):
936 name = expr['alternate']
937 members = expr['data']
940 if len(members) == 0:
941 raise QAPISemError(info,
942 "alternate '%s' cannot have empty 'data'" % name)
943 for (key, value) in members.items():
944 check_name(key, info, "member of alternate '%s'" % name)
945 check_known_keys(value, info,
946 "member '%s' of alternate '%s'" % (key, name),
948 check_if(value, info)
952 # Ensure alternates have no type conflicts.
953 check_type(typ, info, "member '%s' of alternate '%s'" % (key, name),
954 allow_metas=['built-in', 'union', 'struct', 'enum'])
955 qtype = find_alternate_member_qtype(typ)
959 "alternate '%s' member '%s' cannot use type '%s'"
961 conflicting = set([qtype])
962 if qtype == 'QTYPE_QSTRING':
963 enum_expr = enum_types.get(typ)
965 for v in enum_get_names(enum_expr):
966 if v in ['on', 'off']:
967 conflicting.add('QTYPE_QBOOL')
968 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
969 conflicting.add('QTYPE_QNUM')
971 conflicting.add('QTYPE_QNUM')
972 conflicting.add('QTYPE_QBOOL')
973 for qt in conflicting:
977 "alternate '%s' member '%s' can't be distinguished "
979 % (name, key, types_seen[qt]))
983 def check_enum(expr, info):
985 members = expr['data']
986 prefix = expr.get('prefix')
988 if not isinstance(members, list):
989 raise QAPISemError(info,
990 "enum '%s' requires an array for 'data'" % name)
991 if prefix is not None and not isinstance(prefix, str):
992 raise QAPISemError(info,
993 "enum '%s' requires a string for 'prefix'" % name)
995 permit_upper = name in name_case_whitelist
997 for member in members:
998 check_known_keys(member, info, "member of enum '%s'" % name,
1000 check_if(member, info)
1001 normalize_if(member)
1002 check_name(member['name'], info, "member of enum '%s'" % name,
1003 enum_member=True, permit_upper=permit_upper)
1006 def check_struct(expr, info):
1007 name = expr['struct']
1008 members = expr['data']
1009 features = expr.get('features')
1011 check_type(members, info, "'data' for struct '%s'" % name,
1013 check_type(expr.get('base'), info, "'base' for struct '%s'" % name,
1014 allow_metas=['struct'])
1017 if not isinstance(features, list):
1019 info, "struct '%s' requires an array for 'features'" % name)
1021 assert isinstance(f, dict)
1022 check_known_keys(f, info, "feature of struct %s" % name,
1027 check_name(f['name'], info, "feature of struct %s" % name)
1030 def check_known_keys(value, info, source, required, optional):
1033 return ', '.join("'" + e + "'" for e in sorted(elems))
1035 missing = set(required) - set(value)
1039 "key%s %s %s missing from %s"
1040 % ('s' if len(missing) > 1 else '', pprint(missing),
1041 'are' if len(missing) > 1 else 'is', source))
1042 allowed = set(required + optional)
1043 unknown = set(value) - allowed
1047 "unknown key%s %s in %s\nValid keys are %s."
1048 % ('s' if len(unknown) > 1 else '', pprint(unknown),
1049 source, pprint(allowed)))
1052 def check_keys(expr, info, meta, required, optional=[]):
1054 if not isinstance(name, str):
1055 raise QAPISemError(info, "'%s' key must have a string value" % meta)
1056 required = required + [meta]
1057 source = "%s '%s'" % (meta, name)
1058 check_known_keys(expr, info, source, required, optional)
1059 for (key, value) in expr.items():
1060 if key in ['gen', 'success-response'] and value is not False:
1061 raise QAPISemError(info,
1062 "'%s' of %s '%s' should only use false value"
1063 % (key, meta, name))
1064 if (key in ['boxed', 'allow-oob', 'allow-preconfig']
1065 and value is not True):
1066 raise QAPISemError(info,
1067 "'%s' of %s '%s' should only use true value"
1068 % (key, meta, name))
1070 check_if(expr, info)
1073 def normalize_enum(expr):
1074 if isinstance(expr['data'], list):
1075 expr['data'] = [m if isinstance(m, dict) else {'name': m}
1076 for m in expr['data']]
1079 def normalize_members(members):
1080 if isinstance(members, OrderedDict):
1081 for key, arg in members.items():
1082 if isinstance(arg, dict):
1084 members[key] = {'type': arg}
1087 def normalize_features(features):
1088 if isinstance(features, list):
1089 features[:] = [f if isinstance(f, dict) else {'name': f}
1093 def normalize_if(expr):
1094 ifcond = expr.get('if')
1095 if isinstance(ifcond, str):
1096 expr['if'] = [ifcond]
1099 def check_exprs(exprs):
1102 # Populate name table with names of built-in types
1103 for builtin in builtin_types.keys():
1104 all_names[builtin] = 'built-in'
1106 # Learn the types and check for valid expression keys
1107 for expr_elem in exprs:
1108 expr = expr_elem['expr']
1109 info = expr_elem['info']
1110 doc = expr_elem.get('doc')
1112 if 'include' in expr:
1115 if not doc and doc_required:
1116 raise QAPISemError(info,
1117 "definition missing documentation comment")
1121 check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
1122 normalize_enum(expr)
1123 enum_types[expr[meta]] = expr
1124 elif 'union' in expr:
1126 check_keys(expr, info, 'union', ['data'],
1127 ['base', 'discriminator', 'if'])
1128 normalize_members(expr.get('base'))
1129 normalize_members(expr['data'])
1130 union_types[expr[meta]] = expr
1131 elif 'alternate' in expr:
1133 check_keys(expr, info, 'alternate', ['data'], ['if'])
1134 normalize_members(expr['data'])
1135 elif 'struct' in expr:
1137 check_keys(expr, info, 'struct', ['data'],
1138 ['base', 'if', 'features'])
1139 normalize_members(expr['data'])
1140 normalize_features(expr.get('features'))
1141 struct_types[expr[meta]] = expr
1142 elif 'command' in expr:
1144 check_keys(expr, info, 'command', [],
1145 ['data', 'returns', 'gen', 'success-response',
1146 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
1147 normalize_members(expr.get('data'))
1148 elif 'event' in expr:
1150 check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
1151 normalize_members(expr.get('data'))
1153 raise QAPISemError(info, "expression is missing metatype")
1156 add_name(name, info, meta)
1157 info.set_defn(meta, name)
1158 if doc and doc.symbol != name:
1161 "definition of '%s' follows documentation for '%s'"
1162 % (name, doc.symbol))
1164 # Validate that exprs make sense
1165 for expr_elem in exprs:
1166 expr = expr_elem['expr']
1167 info = expr_elem['info']
1168 doc = expr_elem.get('doc')
1170 if 'include' in expr:
1173 check_enum(expr, info)
1174 elif 'union' in expr:
1175 check_union(expr, info)
1176 elif 'alternate' in expr:
1177 check_alternate(expr, info)
1178 elif 'struct' in expr:
1179 check_struct(expr, info)
1180 elif 'command' in expr:
1181 check_command(expr, info)
1182 elif 'event' in expr:
1183 check_event(expr, info)
1185 assert False, 'unexpected meta type'
1188 doc.check_expr(expr)
1194 # Schema compiler frontend
1197 class QAPISchemaEntity(object):
1198 def __init__(self, name, info, doc, ifcond=None):
1199 assert name is None or isinstance(name, str)
1202 # For explicitly defined entities, info points to the (explicit)
1203 # definition. For builtins (and their arrays), info is None.
1204 # For implicitly defined entities, info points to a place that
1205 # triggered the implicit definition (there may be more than one
1209 self._ifcond = ifcond or []
1210 self._checked = False
1213 return c_name(self.name)
1215 def check(self, schema):
1216 assert not self._checked
1218 self._module = os.path.relpath(self.info.fname,
1219 os.path.dirname(schema.fname))
1220 self._checked = True
1224 assert self._checked
1229 assert self._checked
1232 def is_implicit(self):
1233 return not self.info
1235 def visit(self, visitor):
1236 assert self._checked
1239 class QAPISchemaVisitor(object):
1240 def visit_begin(self, schema):
1243 def visit_end(self):
1246 def visit_module(self, fname):
1249 def visit_needed(self, entity):
1250 # Default to visiting everything
1253 def visit_include(self, fname, info):
1256 def visit_builtin_type(self, name, info, json_type):
1259 def visit_enum_type(self, name, info, ifcond, members, prefix):
1262 def visit_array_type(self, name, info, ifcond, element_type):
1265 def visit_object_type(self, name, info, ifcond, base, members, variants,
1269 def visit_object_type_flat(self, name, info, ifcond, members, variants,
1273 def visit_alternate_type(self, name, info, ifcond, variants):
1276 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1277 success_response, boxed, allow_oob, allow_preconfig):
1280 def visit_event(self, name, info, ifcond, arg_type, boxed):
1284 class QAPISchemaInclude(QAPISchemaEntity):
1286 def __init__(self, fname, info):
1287 QAPISchemaEntity.__init__(self, None, info, None)
1290 def visit(self, visitor):
1291 QAPISchemaEntity.visit(self, visitor)
1292 visitor.visit_include(self.fname, self.info)
1295 class QAPISchemaType(QAPISchemaEntity):
1296 # Return the C type for common use.
1297 # For the types we commonly box, this is a pointer type.
1301 # Return the C type to be used in a parameter list.
1302 def c_param_type(self):
1303 return self.c_type()
1305 # Return the C type to be used where we suppress boxing.
1306 def c_unboxed_type(self):
1307 return self.c_type()
1309 def json_type(self):
1312 def alternate_qtype(self):
1314 'null': 'QTYPE_QNULL',
1315 'string': 'QTYPE_QSTRING',
1316 'number': 'QTYPE_QNUM',
1317 'int': 'QTYPE_QNUM',
1318 'boolean': 'QTYPE_QBOOL',
1319 'object': 'QTYPE_QDICT'
1321 return json2qtype.get(self.json_type())
1324 if self.is_implicit():
1329 class QAPISchemaBuiltinType(QAPISchemaType):
1330 def __init__(self, name, json_type, c_type):
1331 QAPISchemaType.__init__(self, name, None, None)
1332 assert not c_type or isinstance(c_type, str)
1333 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1335 self._json_type_name = json_type
1336 self._c_type_name = c_type
1342 return self._c_type_name
1344 def c_param_type(self):
1345 if self.name == 'str':
1346 return 'const ' + self._c_type_name
1347 return self._c_type_name
1349 def json_type(self):
1350 return self._json_type_name
1353 return self.json_type()
1355 def visit(self, visitor):
1356 QAPISchemaType.visit(self, visitor)
1357 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1360 class QAPISchemaEnumType(QAPISchemaType):
1361 def __init__(self, name, info, doc, ifcond, members, prefix):
1362 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1364 assert isinstance(m, QAPISchemaEnumMember)
1365 m.set_defined_in(name)
1366 assert prefix is None or isinstance(prefix, str)
1367 self.members = members
1368 self.prefix = prefix
1370 def check(self, schema):
1371 QAPISchemaType.check(self, schema)
1373 for m in self.members:
1374 m.check_clash(self.info, seen)
1376 self.doc.connect_member(m)
1378 def is_implicit(self):
1379 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1380 return self.name.endswith('Kind') or self.name == 'QType'
1383 return c_name(self.name)
1385 def member_names(self):
1386 return [m.name for m in self.members]
1388 def json_type(self):
1391 def visit(self, visitor):
1392 QAPISchemaType.visit(self, visitor)
1393 visitor.visit_enum_type(self.name, self.info, self.ifcond,
1394 self.members, self.prefix)
1397 class QAPISchemaArrayType(QAPISchemaType):
1398 def __init__(self, name, info, element_type):
1399 QAPISchemaType.__init__(self, name, info, None, None)
1400 assert isinstance(element_type, str)
1401 self._element_type_name = element_type
1402 self.element_type = None
1404 def check(self, schema):
1405 QAPISchemaType.check(self, schema)
1406 self.element_type = schema.lookup_type(self._element_type_name)
1407 assert self.element_type
1408 assert not isinstance(self.element_type, QAPISchemaArrayType)
1412 assert self._checked
1413 return self.element_type.ifcond
1417 assert self._checked
1418 return self.element_type.module
1420 def is_implicit(self):
1424 return c_name(self.name) + pointer_suffix
1426 def json_type(self):
1430 elt_doc_type = self.element_type.doc_type()
1431 if not elt_doc_type:
1433 return 'array of ' + elt_doc_type
1435 def visit(self, visitor):
1436 QAPISchemaType.visit(self, visitor)
1437 visitor.visit_array_type(self.name, self.info, self.ifcond,
1441 class QAPISchemaObjectType(QAPISchemaType):
1442 def __init__(self, name, info, doc, ifcond,
1443 base, local_members, variants, features):
1444 # struct has local_members, optional base, and no variants
1445 # flat union has base, variants, and no local_members
1446 # simple union has local_members, variants, and no base
1447 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1448 assert base is None or isinstance(base, str)
1449 for m in local_members:
1450 assert isinstance(m, QAPISchemaObjectTypeMember)
1451 m.set_defined_in(name)
1452 if variants is not None:
1453 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1454 variants.set_defined_in(name)
1456 assert isinstance(f, QAPISchemaFeature)
1457 f.set_defined_in(name)
1458 self._base_name = base
1460 self.local_members = local_members
1461 self.variants = variants
1463 self.features = features
1465 def check(self, schema):
1466 # This calls another type T's .check() exactly when the C
1467 # struct emitted by gen_object() contains that T's C struct
1468 # (pointers don't count).
1469 if self.members is not None:
1470 # A previous .check() completed: nothing to do
1473 # Recursed: C struct contains itself
1474 raise QAPISemError(self.info,
1475 "object %s contains itself" % self.name)
1477 QAPISchemaType.check(self, schema)
1478 assert self._checked and self.members is None
1480 seen = OrderedDict()
1482 self.base = schema.lookup_type(self._base_name)
1483 assert isinstance(self.base, QAPISchemaObjectType)
1484 self.base.check(schema)
1485 self.base.check_clash(self.info, seen)
1486 for m in self.local_members:
1488 m.check_clash(self.info, seen)
1490 self.doc.connect_member(m)
1491 members = seen.values()
1494 self.variants.check(schema, seen)
1495 assert self.variants.tag_member in members
1496 self.variants.check_clash(self.info, seen)
1498 # Features are in a name space separate from members
1500 for f in self.features:
1501 f.check_clash(self.info, seen)
1506 self.members = members # mark completed
1508 # Check that the members of this type do not cause duplicate JSON members,
1509 # and update seen to track the members seen so far. Report any errors
1510 # on behalf of info, which is not necessarily self.info
1511 def check_clash(self, info, seen):
1512 assert self._checked
1513 assert not self.variants # not implemented
1514 for m in self.members:
1515 m.check_clash(info, seen)
1519 assert self._checked
1520 if isinstance(self._ifcond, QAPISchemaType):
1521 # Simple union wrapper type inherits from wrapped type;
1522 # see _make_implicit_object_type()
1523 return self._ifcond.ifcond
1526 def is_implicit(self):
1527 # See QAPISchema._make_implicit_object_type(), as well as
1528 # _def_predefineds()
1529 return self.name.startswith('q_')
1532 assert self.members is not None
1533 return not self.members and not self.variants
1536 assert self.name != 'q_empty'
1537 return QAPISchemaType.c_name(self)
1540 assert not self.is_implicit()
1541 return c_name(self.name) + pointer_suffix
1543 def c_unboxed_type(self):
1544 return c_name(self.name)
1546 def json_type(self):
1549 def visit(self, visitor):
1550 QAPISchemaType.visit(self, visitor)
1551 visitor.visit_object_type(self.name, self.info, self.ifcond,
1552 self.base, self.local_members, self.variants,
1554 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1555 self.members, self.variants,
1559 class QAPISchemaMember(object):
1560 """ Represents object members, enum members and features """
1563 def __init__(self, name, ifcond=None):
1564 assert isinstance(name, str)
1566 self.ifcond = ifcond or []
1567 self.defined_in = None
1569 def set_defined_in(self, name):
1570 assert not self.defined_in
1571 self.defined_in = name
1573 def check_clash(self, info, seen):
1574 cname = c_name(self.name)
1578 "%s collides with %s"
1579 % (self.describe(info), seen[cname].describe(info)))
1582 def describe(self, info):
1584 defined_in = self.defined_in
1587 if defined_in.startswith('q_obj_'):
1588 # See QAPISchema._make_implicit_object_type() - reverse the
1589 # mapping there to create a nice human-readable description
1590 defined_in = defined_in[6:]
1591 if defined_in.endswith('-arg'):
1592 # Implicit type created for a command's dict 'data'
1593 assert role == 'member'
1595 elif defined_in.endswith('-base'):
1596 # Implicit type created for a flat union's dict 'base'
1597 role = 'base ' + role
1599 # Implicit type created for a simple union's branch
1600 assert defined_in.endswith('-wrapper')
1601 # Unreachable and not implemented
1603 elif defined_in.endswith('Kind'):
1604 # See QAPISchema._make_implicit_enum_type()
1605 # Implicit enum created for simple union's branches
1606 assert role == 'value'
1608 elif defined_in != info.defn_name:
1609 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
1610 return "%s '%s'" % (role, self.name)
1613 class QAPISchemaEnumMember(QAPISchemaMember):
1617 class QAPISchemaFeature(QAPISchemaMember):
1621 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1622 def __init__(self, name, typ, optional, ifcond=None):
1623 QAPISchemaMember.__init__(self, name, ifcond)
1624 assert isinstance(typ, str)
1625 assert isinstance(optional, bool)
1626 self._type_name = typ
1628 self.optional = optional
1630 def check(self, schema):
1631 assert self.defined_in
1632 self.type = schema.lookup_type(self._type_name)
1636 class QAPISchemaObjectTypeVariants(object):
1637 def __init__(self, tag_name, tag_member, variants):
1638 # Flat unions pass tag_name but not tag_member.
1639 # Simple unions and alternates pass tag_member but not tag_name.
1640 # After check(), tag_member is always set, and tag_name remains
1641 # a reliable witness of being used by a flat union.
1642 assert bool(tag_member) != bool(tag_name)
1643 assert (isinstance(tag_name, str) or
1644 isinstance(tag_member, QAPISchemaObjectTypeMember))
1646 assert isinstance(v, QAPISchemaObjectTypeVariant)
1647 self._tag_name = tag_name
1648 self.tag_member = tag_member
1649 self.variants = variants
1651 def set_defined_in(self, name):
1652 for v in self.variants:
1653 v.set_defined_in(name)
1655 def check(self, schema, seen):
1656 if not self.tag_member: # flat union
1657 self.tag_member = seen[c_name(self._tag_name)]
1658 assert self._tag_name == self.tag_member.name
1659 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1660 assert not self.tag_member.optional
1661 assert self.tag_member.ifcond == []
1662 if self._tag_name: # flat union
1663 # branches that are not explicitly covered get an empty type
1664 cases = set([v.name for v in self.variants])
1665 for m in self.tag_member.type.members:
1666 if m.name not in cases:
1667 v = QAPISchemaObjectTypeVariant(m.name, 'q_empty',
1669 v.set_defined_in(self.tag_member.defined_in)
1670 self.variants.append(v)
1671 assert self.variants
1672 for v in self.variants:
1674 # Union names must match enum values; alternate names are
1675 # checked separately. Use 'seen' to tell the two apart.
1677 assert v.name in self.tag_member.type.member_names()
1678 assert (isinstance(v.type, QAPISchemaObjectType)
1679 and not v.type.variants)
1680 v.type.check(schema)
1682 def check_clash(self, info, seen):
1683 for v in self.variants:
1684 # Reset seen map for each variant, since qapi names from one
1685 # branch do not affect another branch
1686 v.type.check_clash(info, dict(seen))
1689 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1692 def __init__(self, name, typ, ifcond=None):
1693 QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
1696 class QAPISchemaAlternateType(QAPISchemaType):
1697 def __init__(self, name, info, doc, ifcond, variants):
1698 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1699 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1700 assert variants.tag_member
1701 variants.set_defined_in(name)
1702 variants.tag_member.set_defined_in(self.name)
1703 self.variants = variants
1705 def check(self, schema):
1706 QAPISchemaType.check(self, schema)
1707 self.variants.tag_member.check(schema)
1708 # Not calling self.variants.check_clash(), because there's nothing
1710 self.variants.check(schema, {})
1711 # Alternate branch names have no relation to the tag enum values;
1712 # so we have to check for potential name collisions ourselves.
1714 for v in self.variants.variants:
1715 v.check_clash(self.info, seen)
1716 # TODO check conflicting qtypes
1718 self.doc.connect_member(v)
1723 return c_name(self.name) + pointer_suffix
1725 def json_type(self):
1728 def visit(self, visitor):
1729 QAPISchemaType.visit(self, visitor)
1730 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1734 class QAPISchemaCommand(QAPISchemaEntity):
1735 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1736 gen, success_response, boxed, allow_oob, allow_preconfig):
1737 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1738 assert not arg_type or isinstance(arg_type, str)
1739 assert not ret_type or isinstance(ret_type, str)
1740 self._arg_type_name = arg_type
1741 self.arg_type = None
1742 self._ret_type_name = ret_type
1743 self.ret_type = None
1745 self.success_response = success_response
1747 self.allow_oob = allow_oob
1748 self.allow_preconfig = allow_preconfig
1750 def check(self, schema):
1751 QAPISchemaEntity.check(self, schema)
1752 if self._arg_type_name:
1753 self.arg_type = schema.lookup_type(self._arg_type_name)
1754 assert isinstance(self.arg_type, QAPISchemaObjectType)
1755 assert not self.arg_type.variants or self.boxed
1757 raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
1758 if self._ret_type_name:
1759 self.ret_type = schema.lookup_type(self._ret_type_name)
1760 assert isinstance(self.ret_type, QAPISchemaType)
1762 def visit(self, visitor):
1763 QAPISchemaEntity.visit(self, visitor)
1764 visitor.visit_command(self.name, self.info, self.ifcond,
1765 self.arg_type, self.ret_type,
1766 self.gen, self.success_response,
1767 self.boxed, self.allow_oob,
1768 self.allow_preconfig)
1771 class QAPISchemaEvent(QAPISchemaEntity):
1772 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1773 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1774 assert not arg_type or isinstance(arg_type, str)
1775 self._arg_type_name = arg_type
1776 self.arg_type = None
1779 def check(self, schema):
1780 QAPISchemaEntity.check(self, schema)
1781 if self._arg_type_name:
1782 self.arg_type = schema.lookup_type(self._arg_type_name)
1783 assert isinstance(self.arg_type, QAPISchemaObjectType)
1784 assert not self.arg_type.variants or self.boxed
1786 raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
1788 def visit(self, visitor):
1789 QAPISchemaEntity.visit(self, visitor)
1790 visitor.visit_event(self.name, self.info, self.ifcond,
1791 self.arg_type, self.boxed)
1794 class QAPISchema(object):
1795 def __init__(self, fname):
1797 if sys.version_info[0] >= 3:
1798 f = open(fname, 'r', encoding='utf-8')
1800 f = open(fname, 'r')
1801 parser = QAPISchemaParser(f)
1802 exprs = check_exprs(parser.exprs)
1803 self.docs = parser.docs
1804 self._entity_list = []
1805 self._entity_dict = {}
1806 self._predefining = True
1807 self._def_predefineds()
1808 self._predefining = False
1809 self._def_exprs(exprs)
1812 def _def_entity(self, ent):
1813 # Only the predefined types are allowed to not have info
1814 assert ent.info or self._predefining
1815 assert ent.name is None or ent.name not in self._entity_dict
1816 self._entity_list.append(ent)
1817 if ent.name is not None:
1818 self._entity_dict[ent.name] = ent
1820 def lookup_entity(self, name, typ=None):
1821 ent = self._entity_dict.get(name)
1822 if typ and not isinstance(ent, typ):
1826 def lookup_type(self, name):
1827 return self.lookup_entity(name, QAPISchemaType)
1829 def _def_include(self, expr, info, doc):
1830 include = expr['include']
1833 while main_info.parent:
1834 main_info = main_info.parent
1835 fname = os.path.relpath(include, os.path.dirname(main_info.fname))
1836 self._def_entity(QAPISchemaInclude(fname, info))
1838 def _def_builtin_type(self, name, json_type, c_type):
1839 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1840 # Instantiating only the arrays that are actually used would
1841 # be nice, but we can't as long as their generated code
1842 # (qapi-builtin-types.[ch]) may be shared by some other
1844 self._make_array_type(name, None)
1846 def _def_predefineds(self):
1847 for t in [('str', 'string', 'char' + pointer_suffix),
1848 ('number', 'number', 'double'),
1849 ('int', 'int', 'int64_t'),
1850 ('int8', 'int', 'int8_t'),
1851 ('int16', 'int', 'int16_t'),
1852 ('int32', 'int', 'int32_t'),
1853 ('int64', 'int', 'int64_t'),
1854 ('uint8', 'int', 'uint8_t'),
1855 ('uint16', 'int', 'uint16_t'),
1856 ('uint32', 'int', 'uint32_t'),
1857 ('uint64', 'int', 'uint64_t'),
1858 ('size', 'int', 'uint64_t'),
1859 ('bool', 'boolean', 'bool'),
1860 ('any', 'value', 'QObject' + pointer_suffix),
1861 ('null', 'null', 'QNull' + pointer_suffix)]:
1862 self._def_builtin_type(*t)
1863 self.the_empty_object_type = QAPISchemaObjectType(
1864 'q_empty', None, None, None, None, [], None, [])
1865 self._def_entity(self.the_empty_object_type)
1867 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1869 qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
1871 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1872 qtype_values, 'QTYPE'))
1874 def _make_features(self, features):
1875 return [QAPISchemaFeature(f['name'], f.get('if')) for f in features]
1877 def _make_enum_members(self, values):
1878 return [QAPISchemaEnumMember(v['name'], v.get('if'))
1881 def _make_implicit_enum_type(self, name, info, ifcond, values):
1882 # See also QAPISchemaObjectTypeMember.describe()
1883 name = name + 'Kind' # Use namespace reserved by add_name()
1884 self._def_entity(QAPISchemaEnumType(
1885 name, info, None, ifcond, self._make_enum_members(values), None))
1888 def _make_array_type(self, element_type, info):
1889 name = element_type + 'List' # Use namespace reserved by add_name()
1890 if not self.lookup_type(name):
1891 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1894 def _make_implicit_object_type(self, name, info, doc, ifcond,
1898 # See also QAPISchemaObjectTypeMember.describe()
1899 name = 'q_obj_%s-%s' % (name, role)
1900 typ = self.lookup_entity(name, QAPISchemaObjectType)
1902 # The implicit object type has multiple users. This can
1903 # happen only for simple unions' implicit wrapper types.
1904 # Its ifcond should be the disjunction of its user's
1905 # ifconds. Not implemented. Instead, we always pass the
1906 # wrapped type's ifcond, which is trivially the same for all
1907 # users. It's also necessary for the wrapper to compile.
1908 # But it's not tight: the disjunction need not imply it. We
1909 # may end up compiling useless wrapper types.
1910 # TODO kill simple unions or implement the disjunction
1911 assert ifcond == typ._ifcond # pylint: disable=protected-access
1913 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1914 None, members, None, []))
1917 def _def_enum_type(self, expr, info, doc):
1920 prefix = expr.get('prefix')
1921 ifcond = expr.get('if')
1922 self._def_entity(QAPISchemaEnumType(
1923 name, info, doc, ifcond,
1924 self._make_enum_members(data), prefix))
1926 def _make_member(self, name, typ, ifcond, info):
1928 if name.startswith('*'):
1931 if isinstance(typ, list):
1932 assert len(typ) == 1
1933 typ = self._make_array_type(typ[0], info)
1934 return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
1936 def _make_members(self, data, info):
1937 return [self._make_member(key, value['type'], value.get('if'), info)
1938 for (key, value) in data.items()]
1940 def _def_struct_type(self, expr, info, doc):
1941 name = expr['struct']
1942 base = expr.get('base')
1944 ifcond = expr.get('if')
1945 features = expr.get('features', [])
1946 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1947 self._make_members(data, info),
1949 self._make_features(features)))
1951 def _make_variant(self, case, typ, ifcond):
1952 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1954 def _make_simple_variant(self, case, typ, ifcond, info):
1955 if isinstance(typ, list):
1956 assert len(typ) == 1
1957 typ = self._make_array_type(typ[0], info)
1958 typ = self._make_implicit_object_type(
1959 typ, info, None, self.lookup_type(typ),
1960 'wrapper', [self._make_member('data', typ, None, info)])
1961 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1963 def _def_union_type(self, expr, info, doc):
1964 name = expr['union']
1966 base = expr.get('base')
1967 ifcond = expr.get('if')
1968 tag_name = expr.get('discriminator')
1970 if isinstance(base, dict):
1971 base = self._make_implicit_object_type(
1972 name, info, doc, ifcond,
1973 'base', self._make_members(base, info))
1975 variants = [self._make_variant(key, value['type'], value.get('if'))
1976 for (key, value) in data.items()]
1979 variants = [self._make_simple_variant(key, value['type'],
1980 value.get('if'), info)
1981 for (key, value) in data.items()]
1982 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1983 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1984 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1985 members = [tag_member]
1987 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1988 QAPISchemaObjectTypeVariants(tag_name,
1992 def _def_alternate_type(self, expr, info, doc):
1993 name = expr['alternate']
1995 ifcond = expr.get('if')
1996 variants = [self._make_variant(key, value['type'], value.get('if'))
1997 for (key, value) in data.items()]
1998 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
2000 QAPISchemaAlternateType(name, info, doc, ifcond,
2001 QAPISchemaObjectTypeVariants(None,
2005 def _def_command(self, expr, info, doc):
2006 name = expr['command']
2007 data = expr.get('data')
2008 rets = expr.get('returns')
2009 gen = expr.get('gen', True)
2010 success_response = expr.get('success-response', True)
2011 boxed = expr.get('boxed', False)
2012 allow_oob = expr.get('allow-oob', False)
2013 allow_preconfig = expr.get('allow-preconfig', False)
2014 ifcond = expr.get('if')
2015 if isinstance(data, OrderedDict):
2016 data = self._make_implicit_object_type(
2017 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2018 if isinstance(rets, list):
2019 assert len(rets) == 1
2020 rets = self._make_array_type(rets[0], info)
2021 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
2022 gen, success_response,
2023 boxed, allow_oob, allow_preconfig))
2025 def _def_event(self, expr, info, doc):
2026 name = expr['event']
2027 data = expr.get('data')
2028 boxed = expr.get('boxed', False)
2029 ifcond = expr.get('if')
2030 if isinstance(data, OrderedDict):
2031 data = self._make_implicit_object_type(
2032 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2033 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
2035 def _def_exprs(self, exprs):
2036 for expr_elem in exprs:
2037 expr = expr_elem['expr']
2038 info = expr_elem['info']
2039 doc = expr_elem.get('doc')
2041 self._def_enum_type(expr, info, doc)
2042 elif 'struct' in expr:
2043 self._def_struct_type(expr, info, doc)
2044 elif 'union' in expr:
2045 self._def_union_type(expr, info, doc)
2046 elif 'alternate' in expr:
2047 self._def_alternate_type(expr, info, doc)
2048 elif 'command' in expr:
2049 self._def_command(expr, info, doc)
2050 elif 'event' in expr:
2051 self._def_event(expr, info, doc)
2052 elif 'include' in expr:
2053 self._def_include(expr, info, doc)
2058 for ent in self._entity_list:
2061 def visit(self, visitor):
2062 visitor.visit_begin(self)
2064 visitor.visit_module(module)
2065 for entity in self._entity_list:
2066 if visitor.visit_needed(entity):
2067 if entity.module != module:
2068 module = entity.module
2069 visitor.visit_module(module)
2070 entity.visit(visitor)
2075 # Code generation helpers
2078 def camel_case(name):
2082 if ch in ['_', '-']:
2085 new_name += ch.upper()
2088 new_name += ch.lower()
2092 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2093 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2094 # ENUM24_Name -> ENUM24_NAME
2095 def camel_to_upper(value):
2096 c_fun_str = c_name(value, False)
2101 length = len(c_fun_str)
2102 for i in range(length):
2104 # When c is upper and no '_' appears before, do more checks
2105 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
2106 if i < length - 1 and c_fun_str[i + 1].islower():
2108 elif c_fun_str[i - 1].isdigit():
2111 return new_name.lstrip('_').upper()
2114 def c_enum_const(type_name, const_name, prefix=None):
2115 if prefix is not None:
2117 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
2120 if hasattr(str, 'maketrans'):
2121 c_name_trans = str.maketrans('.-', '__')
2123 c_name_trans = string.maketrans('.-', '__')
2126 # Map @name to a valid C identifier.
2127 # If @protect, avoid returning certain ticklish identifiers (like
2128 # C keywords) by prepending 'q_'.
2130 # Used for converting 'name' from a 'name':'type' qapi definition
2131 # into a generated struct member, as well as converting type names
2132 # into substrings of a generated C function name.
2133 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2134 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2135 def c_name(name, protect=True):
2136 # ANSI X3J11/88-090, 3.1.1
2137 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
2138 'default', 'do', 'double', 'else', 'enum', 'extern',
2139 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2140 'return', 'short', 'signed', 'sizeof', 'static',
2141 'struct', 'switch', 'typedef', 'union', 'unsigned',
2142 'void', 'volatile', 'while'])
2143 # ISO/IEC 9899:1999, 6.4.1
2144 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2145 # ISO/IEC 9899:2011, 6.4.1
2146 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2147 '_Noreturn', '_Static_assert', '_Thread_local'])
2148 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2150 gcc_words = set(['asm', 'typeof'])
2151 # C++ ISO/IEC 14882:2003 2.11
2152 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
2153 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2154 'namespace', 'new', 'operator', 'private', 'protected',
2155 'public', 'reinterpret_cast', 'static_cast', 'template',
2156 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2157 'using', 'virtual', 'wchar_t',
2158 # alternative representations
2159 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2160 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2161 # namespace pollution:
2162 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2163 name = name.translate(c_name_trans)
2164 if protect and (name in c89_words | c99_words | c11_words | gcc_words
2165 | cpp_words | polluted_words):
2170 eatspace = '\033EATSPACE.'
2171 pointer_suffix = ' *' + eatspace
2174 def genindent(count):
2176 for _ in range(count):
2184 def push_indent(indent_amount=4):
2186 indent_level += indent_amount
2189 def pop_indent(indent_amount=4):
2191 indent_level -= indent_amount
2194 # Generate @code with @kwds interpolated.
2195 # Obey indent_level, and strip eatspace.
2196 def cgen(code, **kwds):
2199 indent = genindent(indent_level)
2200 # re.subn() lacks flags support before Python 2.7, use re.compile()
2201 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2204 return re.sub(re.escape(eatspace) + r' *', '', raw)
2207 def mcgen(code, **kwds):
2210 return cgen(code, **kwds)
2213 def c_fname(filename):
2214 return re.sub(r'[^A-Za-z0-9_]', '_', filename)
2217 def guardstart(name):
2223 name=c_fname(name).upper())
2229 #endif /* %(name)s */
2231 name=c_fname(name).upper())
2243 def gen_endif(ifcond):
2245 for ifc in reversed(ifcond):
2247 #endif /* %(cond)s */
2252 def _wrap_ifcond(ifcond, before, after):
2254 return after # suppress empty #if ... #endif
2256 assert after.startswith(before)
2258 added = after[len(before):]
2259 if added[0] == '\n':
2262 out += gen_if(ifcond)
2264 out += gen_endif(ifcond)
2268 def gen_enum_lookup(name, members, prefix=None):
2271 const QEnumLookup %(c_name)s_lookup = {
2272 .array = (const char *const[]) {
2274 c_name=c_name(name))
2276 ret += gen_if(m.ifcond)
2277 index = c_enum_const(name, m.name, prefix)
2279 [%(index)s] = "%(name)s",
2281 index=index, name=m.name)
2282 ret += gen_endif(m.ifcond)
2286 .size = %(max_index)s
2289 max_index=c_enum_const(name, '_MAX', prefix))
2293 def gen_enum(name, members, prefix=None):
2294 # append automatically generated _MAX value
2295 enum_members = members + [QAPISchemaEnumMember('_MAX')]
2299 typedef enum %(c_name)s {
2301 c_name=c_name(name))
2303 for m in enum_members:
2304 ret += gen_if(m.ifcond)
2308 c_enum=c_enum_const(name, m.name, prefix))
2309 ret += gen_endif(m.ifcond)
2314 c_name=c_name(name))
2318 #define %(c_name)s_str(val) \\
2319 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2321 extern const QEnumLookup %(c_name)s_lookup;
2323 c_name=c_name(name))
2327 def build_params(arg_type, boxed, extra=None):
2332 ret += '%s arg' % arg_type.c_param_type()
2335 assert not arg_type.variants
2336 for memb in arg_type.members:
2340 ret += 'bool has_%s, ' % c_name(memb.name)
2341 ret += '%s %s' % (memb.type.c_param_type(),
2345 return ret if ret else 'void'
2349 # Accumulate and write output
2352 class QAPIGen(object):
2354 def __init__(self, fname):
2359 def preamble_add(self, text):
2360 self._preamble += text
2362 def add(self, text):
2365 def get_content(self):
2366 return self._top() + self._preamble + self._body + self._bottom()
2374 def write(self, output_dir):
2375 pathname = os.path.join(output_dir, self.fname)
2376 dir = os.path.dirname(pathname)
2380 except os.error as e:
2381 if e.errno != errno.EEXIST:
2383 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2384 if sys.version_info[0] >= 3:
2385 f = open(fd, 'r+', encoding='utf-8')
2387 f = os.fdopen(fd, 'r+')
2388 text = self.get_content()
2389 oldtext = f.read(len(text) + 1)
2398 def ifcontext(ifcond, *args):
2399 """A 'with' statement context manager to wrap with start_if()/end_if()
2401 *args: any number of QAPIGenCCode
2405 with ifcontext(ifcond, self._genh, self._genc):
2406 modify self._genh and self._genc ...
2408 Is equivalent to calling::
2410 self._genh.start_if(ifcond)
2411 self._genc.start_if(ifcond)
2412 modify self._genh and self._genc ...
2417 arg.start_if(ifcond)
2423 class QAPIGenCCode(QAPIGen):
2425 def __init__(self, fname):
2426 QAPIGen.__init__(self, fname)
2427 self._start_if = None
2429 def start_if(self, ifcond):
2430 assert self._start_if is None
2431 self._start_if = (ifcond, self._body, self._preamble)
2434 assert self._start_if
2436 self._start_if = None
2438 def _wrap_ifcond(self):
2439 self._body = _wrap_ifcond(self._start_if[0],
2440 self._start_if[1], self._body)
2441 self._preamble = _wrap_ifcond(self._start_if[0],
2442 self._start_if[2], self._preamble)
2444 def get_content(self):
2445 assert self._start_if is None
2446 return QAPIGen.get_content(self)
2449 class QAPIGenC(QAPIGenCCode):
2451 def __init__(self, fname, blurb, pydoc):
2452 QAPIGenCCode.__init__(self, fname)
2454 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2459 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2466 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2467 * See the COPYING.LIB file in the top-level directory.
2471 blurb=self._blurb, copyright=self._copyright)
2476 /* Dummy declaration to prevent empty .o file */
2477 char qapi_dummy_%(name)s;
2479 name=c_fname(self.fname))
2482 class QAPIGenH(QAPIGenC):
2485 return QAPIGenC._top(self) + guardstart(self.fname)
2488 return guardend(self.fname)
2491 class QAPIGenDoc(QAPIGen):
2494 return (QAPIGen._top(self)
2495 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2498 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2500 def __init__(self, prefix, what, blurb, pydoc):
2501 self._prefix = prefix
2503 self._genc = QAPIGenC(self._prefix + self._what + '.c',
2505 self._genh = QAPIGenH(self._prefix + self._what + '.h',
2508 def write(self, output_dir):
2509 self._genc.write(output_dir)
2510 self._genh.write(output_dir)
2513 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2515 def __init__(self, prefix, what, blurb, pydoc):
2516 self._prefix = prefix
2523 self._main_module = None
2526 def _is_user_module(name):
2527 return name and not name.startswith('./')
2530 def _is_builtin_module(name):
2533 def _module_dirname(self, what, name):
2534 if self._is_user_module(name):
2535 return os.path.dirname(name)
2538 def _module_basename(self, what, name):
2539 ret = '' if self._is_builtin_module(name) else self._prefix
2540 if self._is_user_module(name):
2541 basename = os.path.basename(name)
2543 if name != self._main_module:
2544 ret += '-' + os.path.splitext(basename)[0]
2546 name = name[2:] if name else 'builtin'
2547 ret += re.sub(r'-', '-' + name + '-', what)
2550 def _module_filename(self, what, name):
2551 return os.path.join(self._module_dirname(what, name),
2552 self._module_basename(what, name))
2554 def _add_module(self, name, blurb):
2555 basename = self._module_filename(self._what, name)
2556 genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
2557 genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
2558 self._module[name] = (genc, genh)
2559 self._set_module(name)
2561 def _add_user_module(self, name, blurb):
2562 assert self._is_user_module(name)
2563 if self._main_module is None:
2564 self._main_module = name
2565 self._add_module(name, blurb)
2567 def _add_system_module(self, name, blurb):
2568 self._add_module(name and './' + name, blurb)
2570 def _set_module(self, name):
2571 self._genc, self._genh = self._module[name]
2573 def write(self, output_dir, opt_builtins=False):
2574 for name in self._module:
2575 if self._is_builtin_module(name) and not opt_builtins:
2577 (genc, genh) = self._module[name]
2578 genc.write(output_dir)
2579 genh.write(output_dir)
2581 def _begin_user_module(self, name):
2584 def visit_module(self, name):
2585 if name in self._module:
2586 self._set_module(name)
2587 elif self._is_builtin_module(name):
2588 # The built-in module has not been created. No code may
2593 self._add_user_module(name, self._blurb)
2594 self._begin_user_module(name)
2596 def visit_include(self, name, info):
2597 relname = os.path.relpath(self._module_filename(self._what, name),
2598 os.path.dirname(self._genh.fname))
2599 self._genh.preamble_add(mcgen('''
2600 #include "%(relname)s.h"