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
26 # Parsing the schema into expressions
30 class QAPISchemaPragma(object):
32 # Are documentation comments required?
33 self.doc_required = False
34 # Whitelist of commands allowed to return a non-dictionary
35 self.returns_whitelist = []
36 # Whitelist of entities allowed to violate case conventions
37 self.name_case_whitelist = []
40 class QAPISourceInfo(object):
41 def __init__(self, fname, line, parent):
45 self.pragma = parent.pragma if parent else QAPISchemaPragma()
49 def set_defn(self, meta, name):
54 info = copy.copy(self)
59 if self.fname is None:
62 if self.line is not None:
63 ret += ':%d' % self.line
68 return "%s: In %s '%s':\n" % (self.fname,
69 self.defn_meta, self.defn_name)
72 def include_path(self):
76 ret = 'In file included from %s:\n' % parent.loc() + ret
77 parent = parent.parent
81 return self.include_path() + self.in_defn() + self.loc()
84 class QAPIError(Exception):
85 def __init__(self, info, col, msg):
86 Exception.__init__(self)
93 if self.col is not None:
94 assert self.info.line is not None
95 loc += ':%s' % self.col
96 return loc + ': ' + self.msg
99 class QAPIParseError(QAPIError):
100 def __init__(self, parser, msg):
102 for ch in parser.src[parser.line_pos:parser.pos]:
104 col = (col + 7) % 8 + 1
107 QAPIError.__init__(self, parser.info, col, msg)
110 class QAPISemError(QAPIError):
111 def __init__(self, info, msg):
112 QAPIError.__init__(self, info, None, msg)
115 class QAPIDoc(object):
117 A documentation comment block, either definition or free-form
119 Definition documentation blocks consist of
121 * a body section: one line naming the definition, followed by an
122 overview (any number of lines)
124 * argument sections: a description of each argument (for commands
125 and events) or member (for structs, unions and alternates)
127 * features sections: a description of each feature flag
129 * additional (non-argument) sections, possibly tagged
131 Free-form documentation blocks consist only of a body section.
134 class Section(object):
135 def __init__(self, name=None):
136 # optional section name (argument/member or section name)
138 # the list of lines for this section
141 def append(self, line):
142 self.text += line.rstrip() + '\n'
144 class ArgSection(Section):
145 def __init__(self, name):
146 QAPIDoc.Section.__init__(self, name)
149 def connect(self, member):
152 def __init__(self, parser, info):
153 # self._parser is used to report errors with QAPIParseError. The
154 # resulting error position depends on the state of the parser.
155 # It happens to be the beginning of the comment. More or less
156 # servicable, but action at a distance.
157 self._parser = parser
160 self.body = QAPIDoc.Section()
161 # dict mapping parameter name to ArgSection
162 self.args = OrderedDict()
163 self.features = OrderedDict()
166 # the current section
167 self._section = self.body
168 self._append_line = self._append_body_line
170 def has_section(self, name):
171 """Return True if we have a section with this name."""
172 for i in self.sections:
177 def append(self, line):
179 Parse a comment line and add it to the documentation.
181 The way that the line is dealt with depends on which part of
182 the documentation we're parsing right now:
183 * The body section: ._append_line is ._append_body_line
184 * An argument section: ._append_line is ._append_args_line
185 * A features section: ._append_line is ._append_features_line
186 * An additional section: ._append_line is ._append_various_line
190 self._append_freeform(line)
194 raise QAPIParseError(self._parser, "missing space after #")
196 self._append_line(line)
198 def end_comment(self):
202 def _is_section_tag(name):
203 return name in ('Returns:', 'Since:',
204 # those are often singular or plural
206 'Example:', 'Examples:',
209 def _append_body_line(self, line):
211 Process a line of documentation text in the body section.
213 If this a symbol line and it is the section's first line, this
214 is a definition documentation block for that symbol.
216 If it's a definition documentation block, another symbol line
217 begins the argument section for the argument named by it, and
218 a section tag begins an additional section. Start that
219 section and append the line to it.
221 Else, append the line to the current section.
223 name = line.split(' ', 1)[0]
224 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
225 # recognized, and get silently treated as ordinary text
226 if not self.symbol and not self.body.text and line.startswith('@'):
227 if not line.endswith(':'):
228 raise QAPIParseError(self._parser, "line should end with ':'")
229 self.symbol = line[1:-1]
230 # FIXME invalid names other than the empty string aren't flagged
232 raise QAPIParseError(self._parser, "invalid name")
234 # This is a definition documentation block
235 if name.startswith('@') and name.endswith(':'):
236 self._append_line = self._append_args_line
237 self._append_args_line(line)
238 elif line == 'Features:':
239 self._append_line = self._append_features_line
240 elif self._is_section_tag(name):
241 self._append_line = self._append_various_line
242 self._append_various_line(line)
244 self._append_freeform(line.strip())
246 # This is a free-form documentation block
247 self._append_freeform(line.strip())
249 def _append_args_line(self, line):
251 Process a line of documentation text in an argument section.
253 A symbol line begins the next argument section, a section tag
254 section or a non-indented line after a blank line begins an
255 additional section. Start that section and append the line to
258 Else, append the line to the current section.
261 name = line.split(' ', 1)[0]
263 if name.startswith('@') and name.endswith(':'):
264 line = line[len(name)+1:]
265 self._start_args_section(name[1:-1])
266 elif self._is_section_tag(name):
267 self._append_line = self._append_various_line
268 self._append_various_line(line)
270 elif (self._section.text.endswith('\n\n')
271 and line and not line[0].isspace()):
272 if line == 'Features:':
273 self._append_line = self._append_features_line
275 self._start_section()
276 self._append_line = self._append_various_line
277 self._append_various_line(line)
280 self._append_freeform(line.strip())
282 def _append_features_line(self, line):
283 name = line.split(' ', 1)[0]
285 if name.startswith('@') and name.endswith(':'):
286 line = line[len(name)+1:]
287 self._start_features_section(name[1:-1])
288 elif self._is_section_tag(name):
289 self._append_line = self._append_various_line
290 self._append_various_line(line)
292 elif (self._section.text.endswith('\n\n')
293 and line and not line[0].isspace()):
294 self._start_section()
295 self._append_line = self._append_various_line
296 self._append_various_line(line)
299 self._append_freeform(line.strip())
301 def _append_various_line(self, line):
303 Process a line of documentation text in an additional section.
305 A symbol line is an error.
307 A section tag begins an additional section. Start that
308 section and append the line to it.
310 Else, append the line to the current section.
312 name = line.split(' ', 1)[0]
314 if name.startswith('@') and name.endswith(':'):
315 raise QAPIParseError(self._parser,
316 "'%s' can't follow '%s' section"
317 % (name, self.sections[0].name))
318 elif self._is_section_tag(name):
319 line = line[len(name)+1:]
320 self._start_section(name[:-1])
322 if (not self._section.name or
323 not self._section.name.startswith('Example')):
326 self._append_freeform(line)
328 def _start_symbol_section(self, symbols_dict, name):
329 # FIXME invalid names other than the empty string aren't flagged
331 raise QAPIParseError(self._parser, "invalid parameter name")
332 if name in symbols_dict:
333 raise QAPIParseError(self._parser,
334 "'%s' parameter name duplicated" % name)
335 assert not self.sections
337 self._section = QAPIDoc.ArgSection(name)
338 symbols_dict[name] = self._section
340 def _start_args_section(self, name):
341 self._start_symbol_section(self.args, name)
343 def _start_features_section(self, name):
344 self._start_symbol_section(self.features, name)
346 def _start_section(self, name=None):
347 if name in ('Returns', 'Since') and self.has_section(name):
348 raise QAPIParseError(self._parser,
349 "duplicated '%s' section" % name)
351 self._section = QAPIDoc.Section(name)
352 self.sections.append(self._section)
354 def _end_section(self):
356 text = self._section.text = self._section.text.strip()
357 if self._section.name and (not text or text.isspace()):
358 raise QAPIParseError(
360 "empty doc section '%s'" % self._section.name)
363 def _append_freeform(self, line):
364 match = re.match(r'(@\S+:)', line)
366 raise QAPIParseError(self._parser,
367 "'%s' not allowed in free-form documentation"
369 self._section.append(line)
371 def connect_member(self, member):
372 if member.name not in self.args:
373 # Undocumented TODO outlaw
374 self.args[member.name] = QAPIDoc.ArgSection(member.name)
375 self.args[member.name].connect(member)
377 def check_expr(self, expr):
378 if self.has_section('Returns') and 'command' not in expr:
379 raise QAPISemError(self.info,
380 "'Returns:' is only valid for commands")
383 bogus = [name for name, section in self.args.items()
384 if not section.member]
388 "the following documented members are not in "
389 "the declaration: %s" % ", ".join(bogus))
392 class QAPISchemaParser(object):
394 def __init__(self, fname, previously_included=[], incl_info=None):
395 previously_included.append(os.path.abspath(fname))
398 if sys.version_info[0] >= 3:
399 fp = open(fname, 'r', encoding='utf-8')
401 fp = open(fname, 'r')
404 raise QAPISemError(incl_info or QAPISourceInfo(None, None, None),
405 "can't read %s file '%s': %s"
406 % ("include" if incl_info else "schema",
410 if self.src == '' or self.src[-1] != '\n':
413 self.info = QAPISourceInfo(fname, 1, incl_info)
420 while self.tok is not None:
423 self.reject_expr_doc(cur_doc)
424 cur_doc = self.get_doc(info)
425 self.docs.append(cur_doc)
428 expr = self.get_expr(False)
429 if 'include' in expr:
430 self.reject_expr_doc(cur_doc)
432 raise QAPISemError(info, "invalid 'include' directive")
433 include = expr['include']
434 if not isinstance(include, str):
435 raise QAPISemError(info,
436 "value of 'include' must be a string")
437 incl_fname = os.path.join(os.path.dirname(fname),
439 self.exprs.append({'expr': {'include': incl_fname},
441 exprs_include = self._include(include, info, incl_fname,
444 self.exprs.extend(exprs_include.exprs)
445 self.docs.extend(exprs_include.docs)
446 elif "pragma" in expr:
447 self.reject_expr_doc(cur_doc)
449 raise QAPISemError(info, "invalid 'pragma' directive")
450 pragma = expr['pragma']
451 if not isinstance(pragma, dict):
453 info, "value of 'pragma' must be an object")
454 for name, value in pragma.items():
455 self._pragma(name, value, info)
457 expr_elem = {'expr': expr,
460 if not cur_doc.symbol:
462 cur_doc.info, "definition documentation required")
463 expr_elem['doc'] = cur_doc
464 self.exprs.append(expr_elem)
466 self.reject_expr_doc(cur_doc)
469 def reject_expr_doc(doc):
470 if doc and doc.symbol:
473 "documentation for '%s' is not followed by the definition"
476 def _include(self, include, info, incl_fname, previously_included):
477 incl_abs_fname = os.path.abspath(incl_fname)
478 # catch inclusion cycle
481 if incl_abs_fname == os.path.abspath(inf.fname):
482 raise QAPISemError(info, "inclusion loop for %s" % include)
485 # skip multiple include of the same file
486 if incl_abs_fname in previously_included:
489 return QAPISchemaParser(incl_fname, previously_included, info)
491 def _pragma(self, name, value, info):
492 if name == 'doc-required':
493 if not isinstance(value, bool):
494 raise QAPISemError(info,
495 "pragma 'doc-required' must be boolean")
496 info.pragma.doc_required = value
497 elif name == 'returns-whitelist':
498 if (not isinstance(value, list)
499 or any([not isinstance(elt, str) for elt in value])):
502 "pragma returns-whitelist must be a list of strings")
503 info.pragma.returns_whitelist = value
504 elif name == 'name-case-whitelist':
505 if (not isinstance(value, list)
506 or any([not isinstance(elt, str) for elt in value])):
509 "pragma name-case-whitelist must be a list of strings")
510 info.pragma.name_case_whitelist = value
512 raise QAPISemError(info, "unknown pragma '%s'" % name)
514 def accept(self, skip_comment=True):
516 self.tok = self.src[self.cursor]
517 self.pos = self.cursor
522 if self.src[self.cursor] == '#':
523 # Start of doc comment
525 self.cursor = self.src.find('\n', self.cursor)
527 self.val = self.src[self.pos:self.cursor]
529 elif self.tok in '{}:,[]':
531 elif self.tok == "'":
532 # Note: we accept only printable ASCII
536 ch = self.src[self.cursor]
539 raise QAPIParseError(self, "missing terminating \"'\"")
541 # Note: we recognize only \\ because we have
542 # no use for funny characters in strings
544 raise QAPIParseError(self,
545 "unknown escape \\%s" % ch)
553 if ord(ch) < 32 or ord(ch) >= 127:
554 raise QAPIParseError(
555 self, "funny character in string")
557 elif self.src.startswith('true', self.pos):
561 elif self.src.startswith('false', self.pos):
565 elif self.tok == '\n':
566 if self.cursor == len(self.src):
569 self.info = self.info.next_line()
570 self.line_pos = self.cursor
571 elif not self.tok.isspace():
572 # Show up to next structural, whitespace or quote
574 match = re.match('[^[\\]{}:,\\s\'"]+',
575 self.src[self.cursor-1:])
576 raise QAPIParseError(self, "stray '%s'" % match.group(0))
578 def get_members(self):
584 raise QAPIParseError(self, "expected string or '}'")
589 raise QAPIParseError(self, "expected ':'")
592 raise QAPIParseError(self, "duplicate key '%s'" % key)
593 expr[key] = self.get_expr(True)
598 raise QAPIParseError(self, "expected ',' or '}'")
601 raise QAPIParseError(self, "expected string")
603 def get_values(self):
608 if self.tok not in "{['tfn":
609 raise QAPIParseError(
610 self, "expected '{', '[', ']', string, boolean or 'null'")
612 expr.append(self.get_expr(True))
617 raise QAPIParseError(self, "expected ',' or ']'")
620 def get_expr(self, nested):
621 if self.tok != '{' and not nested:
622 raise QAPIParseError(self, "expected '{'")
625 expr = self.get_members()
626 elif self.tok == '[':
628 expr = self.get_values()
629 elif self.tok in "'tfn":
633 raise QAPIParseError(
634 self, "expected '{', '[', string, boolean or 'null'")
637 def get_doc(self, info):
639 raise QAPIParseError(
640 self, "junk after '##' at start of documentation comment")
642 doc = QAPIDoc(self, info)
644 while self.tok == '#':
645 if self.val.startswith('##'):
648 raise QAPIParseError(
650 "junk after '##' at end of documentation comment")
658 raise QAPIParseError(self, "documentation comment must end with '##'")
662 # Check (context-free) schema expression structure
665 # Names must be letters, numbers, -, and _. They must start with letter,
666 # except for downstream extensions which must start with __RFQDN_.
667 # Dots are only valid in the downstream extension prefix.
668 valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
669 '[a-zA-Z][a-zA-Z0-9_-]*$')
672 def check_name_is_str(name, info, source):
673 if not isinstance(name, str):
674 raise QAPISemError(info, "%s requires a string name" % source)
677 def check_name_str(name, info, source,
678 allow_optional=False, enum_member=False,
683 if allow_optional and name.startswith('*'):
684 membername = name[1:]
685 # Enum members can start with a digit, because the generated C
686 # code always prefixes it with the enum name
687 if enum_member and membername[0].isdigit():
688 membername = 'D' + membername
689 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
690 # and 'q_obj_*' implicit type names.
691 if not valid_name.match(membername) or \
692 c_name(membername, False).startswith('q_'):
693 raise QAPISemError(info, "%s has an invalid name" % source)
694 if not permit_upper and name.lower() != name:
696 info, "%s uses uppercase in name" % source)
697 assert not membername.startswith('*')
700 def check_defn_name_str(name, info, meta):
701 check_name_str(name, info, meta, permit_upper=True)
702 if name.endswith('Kind') or name.endswith('List'):
704 info, "%s name should not end in '%s'" % (meta, name[-4:]))
707 def check_if(expr, info, source):
709 def check_if_str(ifcond, info):
710 if not isinstance(ifcond, str):
713 "'if' condition of %s must be a string or a list of strings"
715 if ifcond.strip() == '':
718 "'if' condition '%s' of %s makes no sense"
721 ifcond = expr.get('if')
724 if isinstance(ifcond, list):
727 info, "'if' condition [] of %s is useless" % source)
729 check_if_str(elt, info)
731 check_if_str(ifcond, info)
734 def check_type(value, info, source,
735 allow_array=False, allow_dict=False):
740 if isinstance(value, list):
742 raise QAPISemError(info, "%s cannot be an array" % source)
743 if len(value) != 1 or not isinstance(value[0], str):
744 raise QAPISemError(info,
745 "%s: array type must contain single type name" %
750 if isinstance(value, str):
756 raise QAPISemError(info, "%s should be a type name" % source)
758 if not isinstance(value, OrderedDict):
759 raise QAPISemError(info,
760 "%s should be an object or type name" % source)
762 permit_upper = allow_dict in info.pragma.name_case_whitelist
764 # value is a dictionary, check that each member is okay
765 for (key, arg) in value.items():
766 key_source = "%s member '%s'" % (source, key)
767 check_name_str(key, info, key_source,
768 allow_optional=True, permit_upper=permit_upper)
769 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
770 raise QAPISemError(info, "%s uses reserved name" % key_source)
771 check_keys(arg, info, key_source, ['type'], ['if'])
772 check_if(arg, info, key_source)
774 check_type(arg['type'], info, key_source, allow_array=True)
777 def check_command(expr, info):
778 args = expr.get('data')
779 rets = expr.get('returns')
780 boxed = expr.get('boxed', False)
782 if boxed and args is None:
783 raise QAPISemError(info, "'boxed': true requires 'data'")
784 check_type(args, info, "'data'", allow_dict=not boxed)
785 check_type(rets, info, "'returns'", allow_array=True)
788 def check_event(expr, info):
789 args = expr.get('data')
790 boxed = expr.get('boxed', False)
792 if boxed and args is None:
793 raise QAPISemError(info, "'boxed': true requires 'data'")
794 check_type(args, info, "'data'", allow_dict=not boxed)
797 def check_union(expr, info):
799 base = expr.get('base')
800 discriminator = expr.get('discriminator')
801 members = expr['data']
803 if discriminator is None: # simple union
805 raise QAPISemError(info, "'base' requires 'discriminator'")
807 check_type(base, info, "'base'", allow_dict=name)
809 raise QAPISemError(info, "'discriminator' requires 'base'")
810 check_name_is_str(discriminator, info, "'discriminator'")
812 for (key, value) in members.items():
813 source = "'data' member '%s'" % key
814 check_name_str(key, info, source)
815 check_keys(value, info, source, ['type'], ['if'])
816 check_if(value, info, source)
818 check_type(value['type'], info, source, allow_array=not base)
821 def check_alternate(expr, info):
822 members = expr['data']
824 if len(members) == 0:
825 raise QAPISemError(info, "'data' must not be empty")
826 for (key, value) in members.items():
827 source = "'data' member '%s'" % key
828 check_name_str(key, info, source)
829 check_keys(value, info, source, ['type'], ['if'])
830 check_if(value, info, source)
832 check_type(value['type'], info, source)
835 def check_enum(expr, info):
837 members = expr['data']
838 prefix = expr.get('prefix')
840 if not isinstance(members, list):
841 raise QAPISemError(info, "'data' must be an array")
842 if prefix is not None and not isinstance(prefix, str):
843 raise QAPISemError(info, "'prefix' must be a string")
845 permit_upper = name in info.pragma.name_case_whitelist
847 for member in members:
848 source = "'data' member"
849 check_keys(member, info, source, ['name'], ['if'])
850 check_name_is_str(member['name'], info, source)
851 source = "%s '%s'" % (source, member['name'])
852 check_name_str(member['name'], info, source,
853 enum_member=True, permit_upper=permit_upper)
854 check_if(member, info, source)
858 def check_struct(expr, info):
859 name = expr['struct']
860 members = expr['data']
861 features = expr.get('features')
863 check_type(members, info, "'data'", allow_dict=name)
864 check_type(expr.get('base'), info, "'base'")
867 if not isinstance(features, list):
868 raise QAPISemError(info, "'features' must be an array")
870 source = "'features' member"
871 assert isinstance(f, dict)
872 check_keys(f, info, source, ['name'], ['if'])
873 check_name_is_str(f['name'], info, source)
874 source = "%s '%s'" % (source, f['name'])
875 check_name_str(f['name'], info, source)
876 check_if(f, info, source)
880 def check_keys(value, info, source, required, optional):
883 return ', '.join("'" + e + "'" for e in sorted(elems))
885 missing = set(required) - set(value)
890 % (source, 's' if len(missing) > 1 else '',
892 allowed = set(required + optional)
893 unknown = set(value) - allowed
897 "%s has unknown key%s %s\nValid keys are %s."
898 % (source, 's' if len(unknown) > 1 else '',
899 pprint(unknown), pprint(allowed)))
902 def check_flags(expr, info):
903 for key in ['gen', 'success-response']:
904 if key in expr and expr[key] is not False:
906 info, "flag '%s' may only use false value" % key)
907 for key in ['boxed', 'allow-oob', 'allow-preconfig']:
908 if key in expr and expr[key] is not True:
910 info, "flag '%s' may only use true value" % key)
913 def normalize_enum(expr):
914 if isinstance(expr['data'], list):
915 expr['data'] = [m if isinstance(m, dict) else {'name': m}
916 for m in expr['data']]
919 def normalize_members(members):
920 if isinstance(members, OrderedDict):
921 for key, arg in members.items():
922 if isinstance(arg, dict):
924 members[key] = {'type': arg}
927 def normalize_features(features):
928 if isinstance(features, list):
929 features[:] = [f if isinstance(f, dict) else {'name': f}
933 def normalize_if(expr):
934 ifcond = expr.get('if')
935 if isinstance(ifcond, str):
936 expr['if'] = [ifcond]
939 def check_exprs(exprs):
940 for expr_elem in exprs:
941 expr = expr_elem['expr']
942 info = expr_elem['info']
943 doc = expr_elem.get('doc')
945 if 'include' in expr:
950 elif 'union' in expr:
952 elif 'alternate' in expr:
954 elif 'struct' in expr:
956 elif 'command' in expr:
958 elif 'event' in expr:
961 raise QAPISemError(info, "expression is missing metatype")
964 check_name_is_str(name, info, "'%s'" % meta)
965 info.set_defn(meta, name)
966 check_defn_name_str(name, info, meta)
969 if doc.symbol != name:
971 info, "documentation comment is for '%s'" % doc.symbol)
973 elif info.pragma.doc_required:
974 raise QAPISemError(info,
975 "documentation comment required")
978 check_keys(expr, info, meta,
979 ['enum', 'data'], ['if', 'prefix'])
981 check_enum(expr, info)
982 elif meta == 'union':
983 check_keys(expr, info, meta,
985 ['base', 'discriminator', 'if'])
986 normalize_members(expr.get('base'))
987 normalize_members(expr['data'])
988 check_union(expr, info)
989 elif meta == 'alternate':
990 check_keys(expr, info, meta,
991 ['alternate', 'data'], ['if'])
992 normalize_members(expr['data'])
993 check_alternate(expr, info)
994 elif meta == 'struct':
995 check_keys(expr, info, meta,
996 ['struct', 'data'], ['base', 'if', 'features'])
997 normalize_members(expr['data'])
998 normalize_features(expr.get('features'))
999 check_struct(expr, info)
1000 elif meta == 'command':
1001 check_keys(expr, info, meta,
1003 ['data', 'returns', 'boxed', 'if',
1004 'gen', 'success-response', 'allow-oob',
1006 normalize_members(expr.get('data'))
1007 check_command(expr, info)
1008 elif meta == 'event':
1009 check_keys(expr, info, meta,
1010 ['event'], ['data', 'boxed', 'if'])
1011 normalize_members(expr.get('data'))
1012 check_event(expr, info)
1014 assert False, 'unexpected meta type'
1017 check_if(expr, info, meta)
1018 check_flags(expr, info)
1024 # Schema compiler frontend
1025 # TODO catching name collisions in generated code would be nice
1028 class QAPISchemaEntity(object):
1031 def __init__(self, name, info, doc, ifcond=None):
1032 assert name is None or isinstance(name, str)
1035 # For explicitly defined entities, info points to the (explicit)
1036 # definition. For builtins (and their arrays), info is None.
1037 # For implicitly defined entities, info points to a place that
1038 # triggered the implicit definition (there may be more than one
1042 self._ifcond = ifcond or []
1043 self._checked = False
1046 return c_name(self.name)
1048 def check(self, schema):
1049 assert not self._checked
1051 self._module = os.path.relpath(self.info.fname,
1052 os.path.dirname(schema.fname))
1053 self._checked = True
1057 assert self._checked
1062 assert self._checked
1065 def is_implicit(self):
1066 return not self.info
1068 def visit(self, visitor):
1069 assert self._checked
1073 return "%s '%s'" % (self.meta, self.name)
1076 class QAPISchemaVisitor(object):
1077 def visit_begin(self, schema):
1080 def visit_end(self):
1083 def visit_module(self, fname):
1086 def visit_needed(self, entity):
1087 # Default to visiting everything
1090 def visit_include(self, fname, info):
1093 def visit_builtin_type(self, name, info, json_type):
1096 def visit_enum_type(self, name, info, ifcond, members, prefix):
1099 def visit_array_type(self, name, info, ifcond, element_type):
1102 def visit_object_type(self, name, info, ifcond, base, members, variants,
1106 def visit_object_type_flat(self, name, info, ifcond, members, variants,
1110 def visit_alternate_type(self, name, info, ifcond, variants):
1113 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1114 success_response, boxed, allow_oob, allow_preconfig):
1117 def visit_event(self, name, info, ifcond, arg_type, boxed):
1121 class QAPISchemaInclude(QAPISchemaEntity):
1123 def __init__(self, fname, info):
1124 QAPISchemaEntity.__init__(self, None, info, None)
1127 def visit(self, visitor):
1128 QAPISchemaEntity.visit(self, visitor)
1129 visitor.visit_include(self.fname, self.info)
1132 class QAPISchemaType(QAPISchemaEntity):
1133 # Return the C type for common use.
1134 # For the types we commonly box, this is a pointer type.
1138 # Return the C type to be used in a parameter list.
1139 def c_param_type(self):
1140 return self.c_type()
1142 # Return the C type to be used where we suppress boxing.
1143 def c_unboxed_type(self):
1144 return self.c_type()
1146 def json_type(self):
1149 def alternate_qtype(self):
1151 'null': 'QTYPE_QNULL',
1152 'string': 'QTYPE_QSTRING',
1153 'number': 'QTYPE_QNUM',
1154 'int': 'QTYPE_QNUM',
1155 'boolean': 'QTYPE_QBOOL',
1156 'object': 'QTYPE_QDICT'
1158 return json2qtype.get(self.json_type())
1161 if self.is_implicit():
1167 return "%s type '%s'" % (self.meta, self.name)
1170 class QAPISchemaBuiltinType(QAPISchemaType):
1173 def __init__(self, name, json_type, c_type):
1174 QAPISchemaType.__init__(self, name, None, None)
1175 assert not c_type or isinstance(c_type, str)
1176 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1178 self._json_type_name = json_type
1179 self._c_type_name = c_type
1185 return self._c_type_name
1187 def c_param_type(self):
1188 if self.name == 'str':
1189 return 'const ' + self._c_type_name
1190 return self._c_type_name
1192 def json_type(self):
1193 return self._json_type_name
1196 return self.json_type()
1198 def visit(self, visitor):
1199 QAPISchemaType.visit(self, visitor)
1200 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1203 class QAPISchemaEnumType(QAPISchemaType):
1206 def __init__(self, name, info, doc, ifcond, members, prefix):
1207 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1209 assert isinstance(m, QAPISchemaEnumMember)
1210 m.set_defined_in(name)
1211 assert prefix is None or isinstance(prefix, str)
1212 self.members = members
1213 self.prefix = prefix
1215 def check(self, schema):
1216 QAPISchemaType.check(self, schema)
1218 for m in self.members:
1219 m.check_clash(self.info, seen)
1221 self.doc.connect_member(m)
1223 def is_implicit(self):
1224 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1225 return self.name.endswith('Kind') or self.name == 'QType'
1228 return c_name(self.name)
1230 def member_names(self):
1231 return [m.name for m in self.members]
1233 def json_type(self):
1236 def visit(self, visitor):
1237 QAPISchemaType.visit(self, visitor)
1238 visitor.visit_enum_type(self.name, self.info, self.ifcond,
1239 self.members, self.prefix)
1242 class QAPISchemaArrayType(QAPISchemaType):
1245 def __init__(self, name, info, element_type):
1246 QAPISchemaType.__init__(self, name, info, None, None)
1247 assert isinstance(element_type, str)
1248 self._element_type_name = element_type
1249 self.element_type = None
1251 def check(self, schema):
1252 QAPISchemaType.check(self, schema)
1253 self.element_type = schema.resolve_type(
1254 self._element_type_name, self.info,
1255 self.info and self.info.defn_meta)
1256 assert not isinstance(self.element_type, QAPISchemaArrayType)
1260 assert self._checked
1261 return self.element_type.ifcond
1265 assert self._checked
1266 return self.element_type.module
1268 def is_implicit(self):
1272 return c_name(self.name) + pointer_suffix
1274 def json_type(self):
1278 elt_doc_type = self.element_type.doc_type()
1279 if not elt_doc_type:
1281 return 'array of ' + elt_doc_type
1283 def visit(self, visitor):
1284 QAPISchemaType.visit(self, visitor)
1285 visitor.visit_array_type(self.name, self.info, self.ifcond,
1290 return "%s type ['%s']" % (self.meta, self._element_type_name)
1293 class QAPISchemaObjectType(QAPISchemaType):
1294 def __init__(self, name, info, doc, ifcond,
1295 base, local_members, variants, features):
1296 # struct has local_members, optional base, and no variants
1297 # flat union has base, variants, and no local_members
1298 # simple union has local_members, variants, and no base
1299 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1300 self.meta = 'union' if variants else 'struct'
1301 assert base is None or isinstance(base, str)
1302 for m in local_members:
1303 assert isinstance(m, QAPISchemaObjectTypeMember)
1304 m.set_defined_in(name)
1305 if variants is not None:
1306 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1307 variants.set_defined_in(name)
1309 assert isinstance(f, QAPISchemaFeature)
1310 f.set_defined_in(name)
1311 self._base_name = base
1313 self.local_members = local_members
1314 self.variants = variants
1316 self.features = features
1318 def check(self, schema):
1319 # This calls another type T's .check() exactly when the C
1320 # struct emitted by gen_object() contains that T's C struct
1321 # (pointers don't count).
1322 if self.members is not None:
1323 # A previous .check() completed: nothing to do
1326 # Recursed: C struct contains itself
1327 raise QAPISemError(self.info,
1328 "object %s contains itself" % self.name)
1330 QAPISchemaType.check(self, schema)
1331 assert self._checked and self.members is None
1333 seen = OrderedDict()
1335 self.base = schema.resolve_type(self._base_name, self.info,
1337 if (not isinstance(self.base, QAPISchemaObjectType)
1338 or self.base.variants):
1341 "'base' requires a struct type, %s isn't"
1342 % self.base.describe())
1343 self.base.check(schema)
1344 self.base.check_clash(self.info, seen)
1345 for m in self.local_members:
1347 m.check_clash(self.info, seen)
1349 self.doc.connect_member(m)
1350 members = seen.values()
1353 self.variants.check(schema, seen)
1354 self.variants.check_clash(self.info, seen)
1356 # Features are in a name space separate from members
1358 for f in self.features:
1359 f.check_clash(self.info, seen)
1364 self.members = members # mark completed
1366 # Check that the members of this type do not cause duplicate JSON members,
1367 # and update seen to track the members seen so far. Report any errors
1368 # on behalf of info, which is not necessarily self.info
1369 def check_clash(self, info, seen):
1370 assert self._checked
1371 assert not self.variants # not implemented
1372 for m in self.members:
1373 m.check_clash(info, seen)
1377 assert self._checked
1378 if isinstance(self._ifcond, QAPISchemaType):
1379 # Simple union wrapper type inherits from wrapped type;
1380 # see _make_implicit_object_type()
1381 return self._ifcond.ifcond
1384 def is_implicit(self):
1385 # See QAPISchema._make_implicit_object_type(), as well as
1386 # _def_predefineds()
1387 return self.name.startswith('q_')
1390 assert self.members is not None
1391 return not self.members and not self.variants
1394 assert self.name != 'q_empty'
1395 return QAPISchemaType.c_name(self)
1398 assert not self.is_implicit()
1399 return c_name(self.name) + pointer_suffix
1401 def c_unboxed_type(self):
1402 return c_name(self.name)
1404 def json_type(self):
1407 def visit(self, visitor):
1408 QAPISchemaType.visit(self, visitor)
1409 visitor.visit_object_type(self.name, self.info, self.ifcond,
1410 self.base, self.local_members, self.variants,
1412 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1413 self.members, self.variants,
1417 class QAPISchemaMember(object):
1418 """ Represents object members, enum members and features """
1421 def __init__(self, name, info, ifcond=None):
1422 assert isinstance(name, str)
1425 self.ifcond = ifcond or []
1426 self.defined_in = None
1428 def set_defined_in(self, name):
1429 assert not self.defined_in
1430 self.defined_in = name
1432 def check_clash(self, info, seen):
1433 cname = c_name(self.name)
1437 "%s collides with %s"
1438 % (self.describe(info), seen[cname].describe(info)))
1441 def describe(self, info):
1443 defined_in = self.defined_in
1446 if defined_in.startswith('q_obj_'):
1447 # See QAPISchema._make_implicit_object_type() - reverse the
1448 # mapping there to create a nice human-readable description
1449 defined_in = defined_in[6:]
1450 if defined_in.endswith('-arg'):
1451 # Implicit type created for a command's dict 'data'
1452 assert role == 'member'
1454 elif defined_in.endswith('-base'):
1455 # Implicit type created for a flat union's dict 'base'
1456 role = 'base ' + role
1458 # Implicit type created for a simple union's branch
1459 assert defined_in.endswith('-wrapper')
1460 # Unreachable and not implemented
1462 elif defined_in.endswith('Kind'):
1463 # See QAPISchema._make_implicit_enum_type()
1464 # Implicit enum created for simple union's branches
1465 assert role == 'value'
1467 elif defined_in != info.defn_name:
1468 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
1469 return "%s '%s'" % (role, self.name)
1472 class QAPISchemaEnumMember(QAPISchemaMember):
1476 class QAPISchemaFeature(QAPISchemaMember):
1480 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1481 def __init__(self, name, info, typ, optional, ifcond=None):
1482 QAPISchemaMember.__init__(self, name, info, ifcond)
1483 assert isinstance(typ, str)
1484 assert isinstance(optional, bool)
1485 self._type_name = typ
1487 self.optional = optional
1489 def check(self, schema):
1490 assert self.defined_in
1491 self.type = schema.resolve_type(self._type_name, self.info,
1495 class QAPISchemaObjectTypeVariants(object):
1496 def __init__(self, tag_name, info, tag_member, variants):
1497 # Flat unions pass tag_name but not tag_member.
1498 # Simple unions and alternates pass tag_member but not tag_name.
1499 # After check(), tag_member is always set, and tag_name remains
1500 # a reliable witness of being used by a flat union.
1501 assert bool(tag_member) != bool(tag_name)
1502 assert (isinstance(tag_name, str) or
1503 isinstance(tag_member, QAPISchemaObjectTypeMember))
1505 assert isinstance(v, QAPISchemaObjectTypeVariant)
1506 self._tag_name = tag_name
1508 self.tag_member = tag_member
1509 self.variants = variants
1511 def set_defined_in(self, name):
1512 for v in self.variants:
1513 v.set_defined_in(name)
1515 def check(self, schema, seen):
1516 if not self.tag_member: # flat union
1517 self.tag_member = seen.get(c_name(self._tag_name))
1519 # Pointing to the base type when not implicit would be
1520 # nice, but we don't know it here
1521 if not self.tag_member or self._tag_name != self.tag_member.name:
1524 "discriminator '%s' is not a member of %s"
1525 % (self._tag_name, base))
1527 base_type = schema.lookup_type(self.tag_member.defined_in)
1529 if not base_type.is_implicit():
1530 base = "base type '%s'" % self.tag_member.defined_in
1531 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
1534 "discriminator member '%s' of %s must be of enum type"
1535 % (self._tag_name, base))
1536 if self.tag_member.optional:
1539 "discriminator member '%s' of %s must not be optional"
1540 % (self._tag_name, base))
1541 if self.tag_member.ifcond:
1544 "discriminator member '%s' of %s must not be conditional"
1545 % (self._tag_name, base))
1546 else: # simple union
1547 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1548 assert not self.tag_member.optional
1549 assert self.tag_member.ifcond == []
1550 if self._tag_name: # flat union
1551 # branches that are not explicitly covered get an empty type
1552 cases = set([v.name for v in self.variants])
1553 for m in self.tag_member.type.members:
1554 if m.name not in cases:
1555 v = QAPISchemaObjectTypeVariant(m.name, self.info,
1556 'q_empty', m.ifcond)
1557 v.set_defined_in(self.tag_member.defined_in)
1558 self.variants.append(v)
1559 if not self.variants:
1560 raise QAPISemError(self.info, "union has no branches")
1561 for v in self.variants:
1563 # Union names must match enum values; alternate names are
1564 # checked separately. Use 'seen' to tell the two apart.
1566 if v.name not in self.tag_member.type.member_names():
1569 "branch '%s' is not a value of %s"
1570 % (v.name, self.tag_member.type.describe()))
1571 if (not isinstance(v.type, QAPISchemaObjectType)
1572 or v.type.variants):
1576 % (v.describe(self.info), v.type.describe()))
1577 v.type.check(schema)
1579 def check_clash(self, info, seen):
1580 for v in self.variants:
1581 # Reset seen map for each variant, since qapi names from one
1582 # branch do not affect another branch
1583 v.type.check_clash(info, dict(seen))
1586 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1589 def __init__(self, name, info, typ, ifcond=None):
1590 QAPISchemaObjectTypeMember.__init__(self, name, info, typ,
1594 class QAPISchemaAlternateType(QAPISchemaType):
1597 def __init__(self, name, info, doc, ifcond, variants):
1598 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1599 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1600 assert variants.tag_member
1601 variants.set_defined_in(name)
1602 variants.tag_member.set_defined_in(self.name)
1603 self.variants = variants
1605 def check(self, schema):
1606 QAPISchemaType.check(self, schema)
1607 self.variants.tag_member.check(schema)
1608 # Not calling self.variants.check_clash(), because there's nothing
1610 self.variants.check(schema, {})
1611 # Alternate branch names have no relation to the tag enum values;
1612 # so we have to check for potential name collisions ourselves.
1615 for v in self.variants.variants:
1616 v.check_clash(self.info, seen)
1617 qtype = v.type.alternate_qtype()
1622 % (v.describe(self.info), v.type.describe()))
1623 conflicting = set([qtype])
1624 if qtype == 'QTYPE_QSTRING':
1625 if isinstance(v.type, QAPISchemaEnumType):
1626 for m in v.type.members:
1627 if m.name in ['on', 'off']:
1628 conflicting.add('QTYPE_QBOOL')
1629 if re.match(r'[-+0-9.]', m.name):
1630 # lazy, could be tightened
1631 conflicting.add('QTYPE_QNUM')
1633 conflicting.add('QTYPE_QNUM')
1634 conflicting.add('QTYPE_QBOOL')
1635 for qt in conflicting:
1636 if qt in types_seen:
1639 "%s can't be distinguished from '%s'"
1640 % (v.describe(self.info), types_seen[qt]))
1641 types_seen[qt] = v.name
1643 self.doc.connect_member(v)
1648 return c_name(self.name) + pointer_suffix
1650 def json_type(self):
1653 def visit(self, visitor):
1654 QAPISchemaType.visit(self, visitor)
1655 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1659 class QAPISchemaCommand(QAPISchemaEntity):
1662 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1663 gen, success_response, boxed, allow_oob, allow_preconfig):
1664 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1665 assert not arg_type or isinstance(arg_type, str)
1666 assert not ret_type or isinstance(ret_type, str)
1667 self._arg_type_name = arg_type
1668 self.arg_type = None
1669 self._ret_type_name = ret_type
1670 self.ret_type = None
1672 self.success_response = success_response
1674 self.allow_oob = allow_oob
1675 self.allow_preconfig = allow_preconfig
1677 def check(self, schema):
1678 QAPISchemaEntity.check(self, schema)
1679 if self._arg_type_name:
1680 self.arg_type = schema.resolve_type(
1681 self._arg_type_name, self.info, "command's 'data'")
1682 if not isinstance(self.arg_type, QAPISchemaObjectType):
1685 "command's 'data' cannot take %s"
1686 % self.arg_type.describe())
1687 if self.arg_type.variants and not self.boxed:
1690 "command's 'data' can take %s only with 'boxed': true"
1691 % self.arg_type.describe())
1692 if self._ret_type_name:
1693 self.ret_type = schema.resolve_type(
1694 self._ret_type_name, self.info, "command's 'returns'")
1695 if self.name not in self.info.pragma.returns_whitelist:
1696 if not (isinstance(self.ret_type, QAPISchemaObjectType)
1697 or (isinstance(self.ret_type, QAPISchemaArrayType)
1698 and isinstance(self.ret_type.element_type,
1699 QAPISchemaObjectType))):
1702 "command's 'returns' cannot take %s"
1703 % self.ret_type.describe())
1705 def visit(self, visitor):
1706 QAPISchemaEntity.visit(self, visitor)
1707 visitor.visit_command(self.name, self.info, self.ifcond,
1708 self.arg_type, self.ret_type,
1709 self.gen, self.success_response,
1710 self.boxed, self.allow_oob,
1711 self.allow_preconfig)
1714 class QAPISchemaEvent(QAPISchemaEntity):
1717 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1718 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1719 assert not arg_type or isinstance(arg_type, str)
1720 self._arg_type_name = arg_type
1721 self.arg_type = None
1724 def check(self, schema):
1725 QAPISchemaEntity.check(self, schema)
1726 if self._arg_type_name:
1727 self.arg_type = schema.resolve_type(
1728 self._arg_type_name, self.info, "event's 'data'")
1729 if not isinstance(self.arg_type, QAPISchemaObjectType):
1732 "event's 'data' cannot take %s"
1733 % self.arg_type.describe())
1734 if self.arg_type.variants and not self.boxed:
1737 "event's 'data' can take %s only with 'boxed': true"
1738 % self.arg_type.describe())
1740 def visit(self, visitor):
1741 QAPISchemaEntity.visit(self, visitor)
1742 visitor.visit_event(self.name, self.info, self.ifcond,
1743 self.arg_type, self.boxed)
1746 class QAPISchema(object):
1747 def __init__(self, fname):
1749 parser = QAPISchemaParser(fname)
1750 exprs = check_exprs(parser.exprs)
1751 self.docs = parser.docs
1752 self._entity_list = []
1753 self._entity_dict = {}
1754 self._predefining = True
1755 self._def_predefineds()
1756 self._predefining = False
1757 self._def_exprs(exprs)
1760 def _def_entity(self, ent):
1761 # Only the predefined types are allowed to not have info
1762 assert ent.info or self._predefining
1763 self._entity_list.append(ent)
1764 if ent.name is None:
1766 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1767 # because they're liable to clash in generated C.
1768 other_ent = self._entity_dict.get(ent.name)
1771 where = QAPIError(other_ent.info, None, "previous definition")
1774 "'%s' is already defined\n%s" % (ent.name, where))
1776 ent.info, "%s is already defined" % other_ent.describe())
1777 self._entity_dict[ent.name] = ent
1779 def lookup_entity(self, name, typ=None):
1780 ent = self._entity_dict.get(name)
1781 if typ and not isinstance(ent, typ):
1785 def lookup_type(self, name):
1786 return self.lookup_entity(name, QAPISchemaType)
1788 def resolve_type(self, name, info, what):
1789 typ = self.lookup_type(name)
1794 info, "%s uses unknown type '%s'" % (what, name))
1797 def _def_include(self, expr, info, doc):
1798 include = expr['include']
1801 while main_info.parent:
1802 main_info = main_info.parent
1803 fname = os.path.relpath(include, os.path.dirname(main_info.fname))
1804 self._def_entity(QAPISchemaInclude(fname, info))
1806 def _def_builtin_type(self, name, json_type, c_type):
1807 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1808 # Instantiating only the arrays that are actually used would
1809 # be nice, but we can't as long as their generated code
1810 # (qapi-builtin-types.[ch]) may be shared by some other
1812 self._make_array_type(name, None)
1814 def _def_predefineds(self):
1815 for t in [('str', 'string', 'char' + pointer_suffix),
1816 ('number', 'number', 'double'),
1817 ('int', 'int', 'int64_t'),
1818 ('int8', 'int', 'int8_t'),
1819 ('int16', 'int', 'int16_t'),
1820 ('int32', 'int', 'int32_t'),
1821 ('int64', 'int', 'int64_t'),
1822 ('uint8', 'int', 'uint8_t'),
1823 ('uint16', 'int', 'uint16_t'),
1824 ('uint32', 'int', 'uint32_t'),
1825 ('uint64', 'int', 'uint64_t'),
1826 ('size', 'int', 'uint64_t'),
1827 ('bool', 'boolean', 'bool'),
1828 ('any', 'value', 'QObject' + pointer_suffix),
1829 ('null', 'null', 'QNull' + pointer_suffix)]:
1830 self._def_builtin_type(*t)
1831 self.the_empty_object_type = QAPISchemaObjectType(
1832 'q_empty', None, None, None, None, [], None, [])
1833 self._def_entity(self.the_empty_object_type)
1835 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1837 qtype_values = self._make_enum_members(
1838 [{'name': n} for n in qtypes], None)
1840 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1841 qtype_values, 'QTYPE'))
1843 def _make_features(self, features, info):
1844 return [QAPISchemaFeature(f['name'], info, f.get('if'))
1847 def _make_enum_members(self, values, info):
1848 return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
1851 def _make_implicit_enum_type(self, name, info, ifcond, values):
1852 # See also QAPISchemaObjectTypeMember.describe()
1853 name = name + 'Kind' # reserved by check_defn_name_str()
1854 self._def_entity(QAPISchemaEnumType(
1855 name, info, None, ifcond, self._make_enum_members(values, info),
1859 def _make_array_type(self, element_type, info):
1860 name = element_type + 'List' # reserved by check_defn_name_str()
1861 if not self.lookup_type(name):
1862 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1865 def _make_implicit_object_type(self, name, info, doc, ifcond,
1869 # See also QAPISchemaObjectTypeMember.describe()
1870 name = 'q_obj_%s-%s' % (name, role)
1871 typ = self.lookup_entity(name, QAPISchemaObjectType)
1873 # The implicit object type has multiple users. This can
1874 # happen only for simple unions' implicit wrapper types.
1875 # Its ifcond should be the disjunction of its user's
1876 # ifconds. Not implemented. Instead, we always pass the
1877 # wrapped type's ifcond, which is trivially the same for all
1878 # users. It's also necessary for the wrapper to compile.
1879 # But it's not tight: the disjunction need not imply it. We
1880 # may end up compiling useless wrapper types.
1881 # TODO kill simple unions or implement the disjunction
1882 assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access
1884 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1885 None, members, None, []))
1888 def _def_enum_type(self, expr, info, doc):
1891 prefix = expr.get('prefix')
1892 ifcond = expr.get('if')
1893 self._def_entity(QAPISchemaEnumType(
1894 name, info, doc, ifcond,
1895 self._make_enum_members(data, info), prefix))
1897 def _make_member(self, name, typ, ifcond, info):
1899 if name.startswith('*'):
1902 if isinstance(typ, list):
1903 assert len(typ) == 1
1904 typ = self._make_array_type(typ[0], info)
1905 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond)
1907 def _make_members(self, data, info):
1908 return [self._make_member(key, value['type'], value.get('if'), info)
1909 for (key, value) in data.items()]
1911 def _def_struct_type(self, expr, info, doc):
1912 name = expr['struct']
1913 base = expr.get('base')
1915 ifcond = expr.get('if')
1916 features = expr.get('features', [])
1917 self._def_entity(QAPISchemaObjectType(
1918 name, info, doc, ifcond, base,
1919 self._make_members(data, info),
1921 self._make_features(features, info)))
1923 def _make_variant(self, case, typ, ifcond, info):
1924 return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
1926 def _make_simple_variant(self, case, typ, ifcond, info):
1927 if isinstance(typ, list):
1928 assert len(typ) == 1
1929 typ = self._make_array_type(typ[0], info)
1930 typ = self._make_implicit_object_type(
1931 typ, info, None, self.lookup_type(typ),
1932 'wrapper', [self._make_member('data', typ, None, info)])
1933 return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
1935 def _def_union_type(self, expr, info, doc):
1936 name = expr['union']
1938 base = expr.get('base')
1939 ifcond = expr.get('if')
1940 tag_name = expr.get('discriminator')
1942 if isinstance(base, dict):
1943 base = self._make_implicit_object_type(
1944 name, info, doc, ifcond,
1945 'base', self._make_members(base, info))
1947 variants = [self._make_variant(key, value['type'],
1948 value.get('if'), info)
1949 for (key, value) in data.items()]
1952 variants = [self._make_simple_variant(key, value['type'],
1953 value.get('if'), info)
1954 for (key, value) in data.items()]
1955 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1956 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1957 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
1958 members = [tag_member]
1960 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1961 QAPISchemaObjectTypeVariants(
1962 tag_name, info, tag_member, variants),
1965 def _def_alternate_type(self, expr, info, doc):
1966 name = expr['alternate']
1968 ifcond = expr.get('if')
1969 variants = [self._make_variant(key, value['type'], value.get('if'),
1971 for (key, value) in data.items()]
1972 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1974 QAPISchemaAlternateType(name, info, doc, ifcond,
1975 QAPISchemaObjectTypeVariants(
1976 None, info, tag_member, variants)))
1978 def _def_command(self, expr, info, doc):
1979 name = expr['command']
1980 data = expr.get('data')
1981 rets = expr.get('returns')
1982 gen = expr.get('gen', True)
1983 success_response = expr.get('success-response', True)
1984 boxed = expr.get('boxed', False)
1985 allow_oob = expr.get('allow-oob', False)
1986 allow_preconfig = expr.get('allow-preconfig', False)
1987 ifcond = expr.get('if')
1988 if isinstance(data, OrderedDict):
1989 data = self._make_implicit_object_type(
1990 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1991 if isinstance(rets, list):
1992 assert len(rets) == 1
1993 rets = self._make_array_type(rets[0], info)
1994 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1995 gen, success_response,
1996 boxed, allow_oob, allow_preconfig))
1998 def _def_event(self, expr, info, doc):
1999 name = expr['event']
2000 data = expr.get('data')
2001 boxed = expr.get('boxed', False)
2002 ifcond = expr.get('if')
2003 if isinstance(data, OrderedDict):
2004 data = self._make_implicit_object_type(
2005 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2006 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
2008 def _def_exprs(self, exprs):
2009 for expr_elem in exprs:
2010 expr = expr_elem['expr']
2011 info = expr_elem['info']
2012 doc = expr_elem.get('doc')
2014 self._def_enum_type(expr, info, doc)
2015 elif 'struct' in expr:
2016 self._def_struct_type(expr, info, doc)
2017 elif 'union' in expr:
2018 self._def_union_type(expr, info, doc)
2019 elif 'alternate' in expr:
2020 self._def_alternate_type(expr, info, doc)
2021 elif 'command' in expr:
2022 self._def_command(expr, info, doc)
2023 elif 'event' in expr:
2024 self._def_event(expr, info, doc)
2025 elif 'include' in expr:
2026 self._def_include(expr, info, doc)
2031 for ent in self._entity_list:
2034 def visit(self, visitor):
2035 visitor.visit_begin(self)
2037 visitor.visit_module(module)
2038 for entity in self._entity_list:
2039 if visitor.visit_needed(entity):
2040 if entity.module != module:
2041 module = entity.module
2042 visitor.visit_module(module)
2043 entity.visit(visitor)
2048 # Code generation helpers
2051 def camel_case(name):
2055 if ch in ['_', '-']:
2058 new_name += ch.upper()
2061 new_name += ch.lower()
2065 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2066 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2067 # ENUM24_Name -> ENUM24_NAME
2068 def camel_to_upper(value):
2069 c_fun_str = c_name(value, False)
2074 length = len(c_fun_str)
2075 for i in range(length):
2077 # When c is upper and no '_' appears before, do more checks
2078 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
2079 if i < length - 1 and c_fun_str[i + 1].islower():
2081 elif c_fun_str[i - 1].isdigit():
2084 return new_name.lstrip('_').upper()
2087 def c_enum_const(type_name, const_name, prefix=None):
2088 if prefix is not None:
2090 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
2093 if hasattr(str, 'maketrans'):
2094 c_name_trans = str.maketrans('.-', '__')
2096 c_name_trans = string.maketrans('.-', '__')
2099 # Map @name to a valid C identifier.
2100 # If @protect, avoid returning certain ticklish identifiers (like
2101 # C keywords) by prepending 'q_'.
2103 # Used for converting 'name' from a 'name':'type' qapi definition
2104 # into a generated struct member, as well as converting type names
2105 # into substrings of a generated C function name.
2106 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2107 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2108 def c_name(name, protect=True):
2109 # ANSI X3J11/88-090, 3.1.1
2110 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
2111 'default', 'do', 'double', 'else', 'enum', 'extern',
2112 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2113 'return', 'short', 'signed', 'sizeof', 'static',
2114 'struct', 'switch', 'typedef', 'union', 'unsigned',
2115 'void', 'volatile', 'while'])
2116 # ISO/IEC 9899:1999, 6.4.1
2117 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2118 # ISO/IEC 9899:2011, 6.4.1
2119 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2120 '_Noreturn', '_Static_assert', '_Thread_local'])
2121 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2123 gcc_words = set(['asm', 'typeof'])
2124 # C++ ISO/IEC 14882:2003 2.11
2125 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
2126 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2127 'namespace', 'new', 'operator', 'private', 'protected',
2128 'public', 'reinterpret_cast', 'static_cast', 'template',
2129 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2130 'using', 'virtual', 'wchar_t',
2131 # alternative representations
2132 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2133 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2134 # namespace pollution:
2135 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2136 name = name.translate(c_name_trans)
2137 if protect and (name in c89_words | c99_words | c11_words | gcc_words
2138 | cpp_words | polluted_words):
2143 eatspace = '\033EATSPACE.'
2144 pointer_suffix = ' *' + eatspace
2147 def genindent(count):
2149 for _ in range(count):
2157 def push_indent(indent_amount=4):
2159 indent_level += indent_amount
2162 def pop_indent(indent_amount=4):
2164 indent_level -= indent_amount
2167 # Generate @code with @kwds interpolated.
2168 # Obey indent_level, and strip eatspace.
2169 def cgen(code, **kwds):
2172 indent = genindent(indent_level)
2173 # re.subn() lacks flags support before Python 2.7, use re.compile()
2174 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2177 return re.sub(re.escape(eatspace) + r' *', '', raw)
2180 def mcgen(code, **kwds):
2183 return cgen(code, **kwds)
2186 def c_fname(filename):
2187 return re.sub(r'[^A-Za-z0-9_]', '_', filename)
2190 def guardstart(name):
2196 name=c_fname(name).upper())
2202 #endif /* %(name)s */
2204 name=c_fname(name).upper())
2216 def gen_endif(ifcond):
2218 for ifc in reversed(ifcond):
2220 #endif /* %(cond)s */
2225 def _wrap_ifcond(ifcond, before, after):
2227 return after # suppress empty #if ... #endif
2229 assert after.startswith(before)
2231 added = after[len(before):]
2232 if added[0] == '\n':
2235 out += gen_if(ifcond)
2237 out += gen_endif(ifcond)
2241 def gen_enum_lookup(name, members, prefix=None):
2244 const QEnumLookup %(c_name)s_lookup = {
2245 .array = (const char *const[]) {
2247 c_name=c_name(name))
2249 ret += gen_if(m.ifcond)
2250 index = c_enum_const(name, m.name, prefix)
2252 [%(index)s] = "%(name)s",
2254 index=index, name=m.name)
2255 ret += gen_endif(m.ifcond)
2259 .size = %(max_index)s
2262 max_index=c_enum_const(name, '_MAX', prefix))
2266 def gen_enum(name, members, prefix=None):
2267 # append automatically generated _MAX value
2268 enum_members = members + [QAPISchemaEnumMember('_MAX', None)]
2272 typedef enum %(c_name)s {
2274 c_name=c_name(name))
2276 for m in enum_members:
2277 ret += gen_if(m.ifcond)
2281 c_enum=c_enum_const(name, m.name, prefix))
2282 ret += gen_endif(m.ifcond)
2287 c_name=c_name(name))
2291 #define %(c_name)s_str(val) \\
2292 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2294 extern const QEnumLookup %(c_name)s_lookup;
2296 c_name=c_name(name))
2300 def build_params(arg_type, boxed, extra=None):
2305 ret += '%s arg' % arg_type.c_param_type()
2308 assert not arg_type.variants
2309 for memb in arg_type.members:
2313 ret += 'bool has_%s, ' % c_name(memb.name)
2314 ret += '%s %s' % (memb.type.c_param_type(),
2318 return ret if ret else 'void'
2322 # Accumulate and write output
2325 class QAPIGen(object):
2327 def __init__(self, fname):
2332 def preamble_add(self, text):
2333 self._preamble += text
2335 def add(self, text):
2338 def get_content(self):
2339 return self._top() + self._preamble + self._body + self._bottom()
2347 def write(self, output_dir):
2348 pathname = os.path.join(output_dir, self.fname)
2349 dir = os.path.dirname(pathname)
2353 except os.error as e:
2354 if e.errno != errno.EEXIST:
2356 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2357 if sys.version_info[0] >= 3:
2358 f = open(fd, 'r+', encoding='utf-8')
2360 f = os.fdopen(fd, 'r+')
2361 text = self.get_content()
2362 oldtext = f.read(len(text) + 1)
2371 def ifcontext(ifcond, *args):
2372 """A 'with' statement context manager to wrap with start_if()/end_if()
2374 *args: any number of QAPIGenCCode
2378 with ifcontext(ifcond, self._genh, self._genc):
2379 modify self._genh and self._genc ...
2381 Is equivalent to calling::
2383 self._genh.start_if(ifcond)
2384 self._genc.start_if(ifcond)
2385 modify self._genh and self._genc ...
2390 arg.start_if(ifcond)
2396 class QAPIGenCCode(QAPIGen):
2398 def __init__(self, fname):
2399 QAPIGen.__init__(self, fname)
2400 self._start_if = None
2402 def start_if(self, ifcond):
2403 assert self._start_if is None
2404 self._start_if = (ifcond, self._body, self._preamble)
2407 assert self._start_if
2409 self._start_if = None
2411 def _wrap_ifcond(self):
2412 self._body = _wrap_ifcond(self._start_if[0],
2413 self._start_if[1], self._body)
2414 self._preamble = _wrap_ifcond(self._start_if[0],
2415 self._start_if[2], self._preamble)
2417 def get_content(self):
2418 assert self._start_if is None
2419 return QAPIGen.get_content(self)
2422 class QAPIGenC(QAPIGenCCode):
2424 def __init__(self, fname, blurb, pydoc):
2425 QAPIGenCCode.__init__(self, fname)
2427 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2432 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2439 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2440 * See the COPYING.LIB file in the top-level directory.
2444 blurb=self._blurb, copyright=self._copyright)
2449 /* Dummy declaration to prevent empty .o file */
2450 char qapi_dummy_%(name)s;
2452 name=c_fname(self.fname))
2455 class QAPIGenH(QAPIGenC):
2458 return QAPIGenC._top(self) + guardstart(self.fname)
2461 return guardend(self.fname)
2464 class QAPIGenDoc(QAPIGen):
2467 return (QAPIGen._top(self)
2468 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2471 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2473 def __init__(self, prefix, what, blurb, pydoc):
2474 self._prefix = prefix
2476 self._genc = QAPIGenC(self._prefix + self._what + '.c',
2478 self._genh = QAPIGenH(self._prefix + self._what + '.h',
2481 def write(self, output_dir):
2482 self._genc.write(output_dir)
2483 self._genh.write(output_dir)
2486 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2488 def __init__(self, prefix, what, blurb, pydoc):
2489 self._prefix = prefix
2496 self._main_module = None
2499 def _is_user_module(name):
2500 return name and not name.startswith('./')
2503 def _is_builtin_module(name):
2506 def _module_dirname(self, what, name):
2507 if self._is_user_module(name):
2508 return os.path.dirname(name)
2511 def _module_basename(self, what, name):
2512 ret = '' if self._is_builtin_module(name) else self._prefix
2513 if self._is_user_module(name):
2514 basename = os.path.basename(name)
2516 if name != self._main_module:
2517 ret += '-' + os.path.splitext(basename)[0]
2519 name = name[2:] if name else 'builtin'
2520 ret += re.sub(r'-', '-' + name + '-', what)
2523 def _module_filename(self, what, name):
2524 return os.path.join(self._module_dirname(what, name),
2525 self._module_basename(what, name))
2527 def _add_module(self, name, blurb):
2528 basename = self._module_filename(self._what, name)
2529 genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
2530 genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
2531 self._module[name] = (genc, genh)
2532 self._set_module(name)
2534 def _add_user_module(self, name, blurb):
2535 assert self._is_user_module(name)
2536 if self._main_module is None:
2537 self._main_module = name
2538 self._add_module(name, blurb)
2540 def _add_system_module(self, name, blurb):
2541 self._add_module(name and './' + name, blurb)
2543 def _set_module(self, name):
2544 self._genc, self._genh = self._module[name]
2546 def write(self, output_dir, opt_builtins=False):
2547 for name in self._module:
2548 if self._is_builtin_module(name) and not opt_builtins:
2550 (genc, genh) = self._module[name]
2551 genc.write(output_dir)
2552 genh.write(output_dir)
2554 def _begin_user_module(self, name):
2557 def visit_module(self, name):
2558 if name in self._module:
2559 self._set_module(name)
2560 elif self._is_builtin_module(name):
2561 # The built-in module has not been created. No code may
2566 self._add_user_module(name, self._blurb)
2567 self._begin_user_module(name)
2569 def visit_include(self, name, info):
2570 relname = os.path.relpath(self._module_filename(self._what, name),
2571 os.path.dirname(self._genh.fname))
2572 self._genh.preamble_add(mcgen('''
2573 #include "%(relname)s.h"