4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2018 Red Hat Inc.
8 # Anthony Liguori <aliguori@us.ibm.com>
9 # Markus Armbruster <armbru@redhat.com>
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
19 from collections
import OrderedDict
22 'null': 'QTYPE_QNULL',
23 'str': 'QTYPE_QSTRING',
25 'number': 'QTYPE_QNUM',
26 'bool': 'QTYPE_QBOOL',
28 'int16': 'QTYPE_QNUM',
29 'int32': 'QTYPE_QNUM',
30 'int64': 'QTYPE_QNUM',
31 'uint8': 'QTYPE_QNUM',
32 'uint16': 'QTYPE_QNUM',
33 'uint32': 'QTYPE_QNUM',
34 'uint64': 'QTYPE_QNUM',
36 'any': None, # any QType possible, actually
37 'QType': 'QTYPE_QSTRING',
40 # Are documentation comments required?
43 # Whitelist of commands allowed to return a non-dictionary
44 returns_whitelist
= []
46 # Whitelist of entities allowed to violate case conventions
47 name_case_whitelist
= []
55 # Parsing the schema into expressions
59 def error_path(parent
):
62 res
= ('In file included from %s:%d:\n' % (parent
['file'],
63 parent
['line'])) + res
64 parent
= parent
['parent']
68 class QAPIError(Exception):
69 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
70 Exception.__init
__(self
)
78 loc
= '%s:%d' % (self
.fname
, self
.line
)
79 if self
.col
is not None:
80 loc
+= ':%s' % self
.col
81 return error_path(self
.info
) + '%s: %s' % (loc
, self
.msg
)
84 class QAPIParseError(QAPIError
):
85 def __init__(self
, parser
, msg
):
87 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
89 col
= (col
+ 7) % 8 + 1
92 QAPIError
.__init
__(self
, parser
.fname
, parser
.line
, col
,
93 parser
.incl_info
, msg
)
96 class QAPISemError(QAPIError
):
97 def __init__(self
, info
, msg
):
98 QAPIError
.__init
__(self
, info
['file'], info
['line'], None,
102 class QAPIDoc(object):
103 class Section(object):
104 def __init__(self
, name
=None):
105 # optional section name (argument/member or section name)
107 # the list of lines for this section
110 def append(self
, line
):
111 self
.text
+= line
.rstrip() + '\n'
113 class ArgSection(Section
):
114 def __init__(self
, name
):
115 QAPIDoc
.Section
.__init
__(self
, name
)
118 def connect(self
, member
):
121 def __init__(self
, parser
, info
):
122 # self._parser is used to report errors with QAPIParseError. The
123 # resulting error position depends on the state of the parser.
124 # It happens to be the beginning of the comment. More or less
125 # servicable, but action at a distance.
126 self
._parser
= parser
129 self
.body
= QAPIDoc
.Section()
130 # dict mapping parameter name to ArgSection
131 self
.args
= OrderedDict()
134 # the current section
135 self
._section
= self
.body
137 def has_section(self
, name
):
138 """Return True if we have a section with this name."""
139 for i
in self
.sections
:
144 def append(self
, line
):
145 """Parse a comment line and add it to the documentation."""
148 self
._append
_freeform
(line
)
152 raise QAPIParseError(self
._parser
, "Missing space after #")
155 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
156 # recognized, and get silently treated as ordinary text
158 self
._append
_symbol
_line
(line
)
159 elif not self
.body
.text
and line
.startswith('@'):
160 if not line
.endswith(':'):
161 raise QAPIParseError(self
._parser
, "Line should end with :")
162 self
.symbol
= line
[1:-1]
163 # FIXME invalid names other than the empty string aren't flagged
165 raise QAPIParseError(self
._parser
, "Invalid name")
167 self
._append
_freeform
(line
)
169 def end_comment(self
):
172 def _append_symbol_line(self
, line
):
173 name
= line
.split(' ', 1)[0]
175 if name
.startswith('@') and name
.endswith(':'):
176 line
= line
[len(name
)+1:]
177 self
._start
_args
_section
(name
[1:-1])
178 elif name
in ('Returns:', 'Since:',
179 # those are often singular or plural
181 'Example:', 'Examples:',
183 line
= line
[len(name
)+1:]
184 self
._start
_section
(name
[:-1])
186 self
._append
_freeform
(line
)
188 def _start_args_section(self
, name
):
189 # FIXME invalid names other than the empty string aren't flagged
191 raise QAPIParseError(self
._parser
, "Invalid parameter name")
192 if name
in self
.args
:
193 raise QAPIParseError(self
._parser
,
194 "'%s' parameter name duplicated" % name
)
196 raise QAPIParseError(self
._parser
,
197 "'@%s:' can't follow '%s' section"
198 % (name
, self
.sections
[0].name
))
200 self
._section
= QAPIDoc
.ArgSection(name
)
201 self
.args
[name
] = self
._section
203 def _start_section(self
, name
=None):
204 if name
in ('Returns', 'Since') and self
.has_section(name
):
205 raise QAPIParseError(self
._parser
,
206 "Duplicated '%s' section" % name
)
208 self
._section
= QAPIDoc
.Section(name
)
209 self
.sections
.append(self
._section
)
211 def _end_section(self
):
213 text
= self
._section
.text
= self
._section
.text
.strip()
214 if self
._section
.name
and (not text
or text
.isspace()):
215 raise QAPIParseError(self
._parser
, "Empty doc section '%s'"
216 % self
._section
.name
)
219 def _append_freeform(self
, line
):
220 in_arg
= isinstance(self
._section
, QAPIDoc
.ArgSection
)
221 if (in_arg
and self
._section
.text
.endswith('\n\n')
222 and line
and not line
[0].isspace()):
223 self
._start
_section
()
224 if (in_arg
or not self
._section
.name
225 or not self
._section
.name
.startswith('Example')):
227 match
= re
.match(r
'(@\S+:)', line
)
229 raise QAPIParseError(self
._parser
,
230 "'%s' not allowed in free-form documentation"
232 self
._section
.append(line
)
234 def connect_member(self
, member
):
235 if member
.name
not in self
.args
:
236 # Undocumented TODO outlaw
237 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
238 self
.args
[member
.name
].connect(member
)
240 def check_expr(self
, expr
):
241 if self
.has_section('Returns') and 'command' not in expr
:
242 raise QAPISemError(self
.info
,
243 "'Returns:' is only valid for commands")
246 bogus
= [name
for name
, section
in self
.args
.items()
247 if not section
.member
]
251 "The following documented members are not in "
252 "the declaration: %s" % ", ".join(bogus
))
255 class QAPISchemaParser(object):
257 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
259 previously_included
.append(os
.path
.abspath(fp
.name
))
260 self
.incl_info
= incl_info
262 if self
.src
== '' or self
.src
[-1] != '\n':
272 while self
.tok
is not None:
273 info
= {'file': self
.fname
, 'line': self
.line
,
274 'parent': self
.incl_info
}
276 self
.reject_expr_doc(cur_doc
)
277 cur_doc
= self
.get_doc(info
)
278 self
.docs
.append(cur_doc
)
281 expr
= self
.get_expr(False)
282 if 'include' in expr
:
283 self
.reject_expr_doc(cur_doc
)
285 raise QAPISemError(info
, "Invalid 'include' directive")
286 include
= expr
['include']
287 if not isinstance(include
, str):
288 raise QAPISemError(info
,
289 "Value of 'include' must be a string")
290 incl_fname
= os
.path
.join(os
.path
.dirname(self
.fname
),
292 self
.exprs
.append({'expr': {'include': incl_fname
},
294 exprs_include
= self
._include
(include
, info
, incl_fname
,
297 self
.exprs
.extend(exprs_include
.exprs
)
298 self
.docs
.extend(exprs_include
.docs
)
299 elif "pragma" in expr
:
300 self
.reject_expr_doc(cur_doc
)
302 raise QAPISemError(info
, "Invalid 'pragma' directive")
303 pragma
= expr
['pragma']
304 if not isinstance(pragma
, dict):
306 info
, "Value of 'pragma' must be a dictionary")
307 for name
, value
in pragma
.items():
308 self
._pragma
(name
, value
, info
)
310 expr_elem
= {'expr': expr
,
313 if not cur_doc
.symbol
:
315 cur_doc
.info
, "Expression documentation required")
316 expr_elem
['doc'] = cur_doc
317 self
.exprs
.append(expr_elem
)
319 self
.reject_expr_doc(cur_doc
)
322 def reject_expr_doc(doc
):
323 if doc
and doc
.symbol
:
326 "Documentation for '%s' is not followed by the definition"
329 def _include(self
, include
, info
, incl_fname
, previously_included
):
330 incl_abs_fname
= os
.path
.abspath(incl_fname
)
331 # catch inclusion cycle
334 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
335 raise QAPISemError(info
, "Inclusion loop for %s" % include
)
338 # skip multiple include of the same file
339 if incl_abs_fname
in previously_included
:
343 fobj
= open(incl_fname
, 'r')
345 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, incl_fname
))
346 return QAPISchemaParser(fobj
, previously_included
, info
)
348 def _pragma(self
, name
, value
, info
):
349 global doc_required
, returns_whitelist
, name_case_whitelist
350 if name
== 'doc-required':
351 if not isinstance(value
, bool):
352 raise QAPISemError(info
,
353 "Pragma 'doc-required' must be boolean")
355 elif name
== 'returns-whitelist':
356 if (not isinstance(value
, list)
357 or any([not isinstance(elt
, str) for elt
in value
])):
358 raise QAPISemError(info
,
359 "Pragma returns-whitelist must be"
360 " a list of strings")
361 returns_whitelist
= value
362 elif name
== 'name-case-whitelist':
363 if (not isinstance(value
, list)
364 or any([not isinstance(elt
, str) for elt
in value
])):
365 raise QAPISemError(info
,
366 "Pragma name-case-whitelist must be"
367 " a list of strings")
368 name_case_whitelist
= value
370 raise QAPISemError(info
, "Unknown pragma '%s'" % name
)
372 def accept(self
, skip_comment
=True):
374 self
.tok
= self
.src
[self
.cursor
]
375 self
.pos
= self
.cursor
380 if self
.src
[self
.cursor
] == '#':
381 # Start of doc comment
383 self
.cursor
= self
.src
.find('\n', self
.cursor
)
385 self
.val
= self
.src
[self
.pos
:self
.cursor
]
387 elif self
.tok
in '{}:,[]':
389 elif self
.tok
== "'":
393 ch
= self
.src
[self
.cursor
]
396 raise QAPIParseError(self
, 'Missing terminating "\'"')
410 for _
in range(0, 4):
411 ch
= self
.src
[self
.cursor
]
413 if ch
not in '0123456789abcdefABCDEF':
414 raise QAPIParseError(self
,
415 '\\u escape needs 4 '
417 value
= (value
<< 4) + int(ch
, 16)
418 # If Python 2 and 3 didn't disagree so much on
419 # how to handle Unicode, then we could allow
420 # Unicode string defaults. But most of QAPI is
421 # ASCII-only, so we aren't losing much for now.
422 if not value
or value
> 0x7f:
423 raise QAPIParseError(self
,
424 'For now, \\u escape '
425 'only supports non-zero '
426 'values up to \\u007f')
431 raise QAPIParseError(self
,
432 "Unknown escape \\%s" % ch
)
441 elif self
.src
.startswith('true', self
.pos
):
445 elif self
.src
.startswith('false', self
.pos
):
449 elif self
.src
.startswith('null', self
.pos
):
453 elif self
.tok
== '\n':
454 if self
.cursor
== len(self
.src
):
458 self
.line_pos
= self
.cursor
459 elif not self
.tok
.isspace():
460 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
462 def get_members(self
):
468 raise QAPIParseError(self
, 'Expected string or "}"')
473 raise QAPIParseError(self
, 'Expected ":"')
476 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
477 expr
[key
] = self
.get_expr(True)
482 raise QAPIParseError(self
, 'Expected "," or "}"')
485 raise QAPIParseError(self
, 'Expected string')
487 def get_values(self
):
492 if self
.tok
not in "{['tfn":
493 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
496 expr
.append(self
.get_expr(True))
501 raise QAPIParseError(self
, 'Expected "," or "]"')
504 def get_expr(self
, nested
):
505 if self
.tok
!= '{' and not nested
:
506 raise QAPIParseError(self
, 'Expected "{"')
509 expr
= self
.get_members()
510 elif self
.tok
== '[':
512 expr
= self
.get_values()
513 elif self
.tok
in "'tfn":
517 raise QAPIParseError(self
, 'Expected "{", "[", string, '
521 def get_doc(self
, info
):
523 raise QAPIParseError(self
, "Junk after '##' at start of "
524 "documentation comment")
526 doc
= QAPIDoc(self
, info
)
528 while self
.tok
== '#':
529 if self
.val
.startswith('##'):
532 raise QAPIParseError(self
, "Junk after '##' at end of "
533 "documentation comment")
541 raise QAPIParseError(self
, "Documentation comment must end with '##'")
545 # Semantic analysis of schema expressions
546 # TODO fold into QAPISchema
547 # TODO catching name collisions in generated code would be nice
551 def find_base_members(base
):
552 if isinstance(base
, dict):
554 base_struct_define
= struct_types
.get(base
)
555 if not base_struct_define
:
557 return base_struct_define
['data']
560 # Return the qtype of an alternate branch, or None on error.
561 def find_alternate_member_qtype(qapi_type
):
562 if qapi_type
in builtin_types
:
563 return builtin_types
[qapi_type
]
564 elif qapi_type
in struct_types
:
566 elif qapi_type
in enum_types
:
567 return 'QTYPE_QSTRING'
568 elif qapi_type
in union_types
:
573 # Return the discriminator enum define if discriminator is specified as an
574 # enum type, otherwise return None.
575 def discriminator_find_enum_define(expr
):
576 base
= expr
.get('base')
577 discriminator
= expr
.get('discriminator')
579 if not (discriminator
and base
):
582 base_members
= find_base_members(base
)
586 discriminator_type
= base_members
.get(discriminator
)
587 if not discriminator_type
:
590 return enum_types
.get(discriminator_type
)
593 # Names must be letters, numbers, -, and _. They must start with letter,
594 # except for downstream extensions which must start with __RFQDN_.
595 # Dots are only valid in the downstream extension prefix.
596 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
597 '[a-zA-Z][a-zA-Z0-9_-]*$')
600 def check_name(info
, source
, name
, allow_optional
=False,
605 if not isinstance(name
, str):
606 raise QAPISemError(info
, "%s requires a string name" % source
)
607 if name
.startswith('*'):
608 membername
= name
[1:]
609 if not allow_optional
:
610 raise QAPISemError(info
, "%s does not allow optional name '%s'"
612 # Enum members can start with a digit, because the generated C
613 # code always prefixes it with the enum name
614 if enum_member
and membername
[0].isdigit():
615 membername
= 'D' + membername
616 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
617 # and 'q_obj_*' implicit type names.
618 if not valid_name
.match(membername
) or \
619 c_name(membername
, False).startswith('q_'):
620 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
623 def add_name(name
, info
, meta
, implicit
=False):
625 check_name(info
, "'%s'" % meta
, name
)
626 # FIXME should reject names that differ only in '_' vs. '.'
627 # vs. '-', because they're liable to clash in generated C.
628 if name
in all_names
:
629 raise QAPISemError(info
, "%s '%s' is already defined"
630 % (all_names
[name
], name
))
631 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
632 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
633 % (meta
, name
, name
[-4:]))
634 all_names
[name
] = meta
637 def check_type(info
, source
, value
, allow_array
=False,
638 allow_dict
=False, allow_optional
=False,
645 # Check if array type for value is okay
646 if isinstance(value
, list):
648 raise QAPISemError(info
, "%s cannot be an array" % source
)
649 if len(value
) != 1 or not isinstance(value
[0], str):
650 raise QAPISemError(info
,
651 "%s: array type must contain single type name" %
655 # Check if type name for value is okay
656 if isinstance(value
, str):
657 if value
not in all_names
:
658 raise QAPISemError(info
, "%s uses unknown type '%s'"
660 if not all_names
[value
] in allow_metas
:
661 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
662 (source
, all_names
[value
], value
))
666 raise QAPISemError(info
, "%s should be a type name" % source
)
668 if not isinstance(value
, OrderedDict
):
669 raise QAPISemError(info
,
670 "%s should be a dictionary or type name" % source
)
672 # value is a dictionary, check that each member is okay
673 for (key
, arg
) in value
.items():
674 check_name(info
, "Member of %s" % source
, key
,
675 allow_optional
=allow_optional
)
676 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
677 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
679 # Todo: allow dictionaries to represent default values of
680 # an optional argument.
681 check_type(info
, "Member '%s' of %s" % (key
, source
), arg
,
683 allow_metas
=['built-in', 'union', 'alternate', 'struct',
687 def check_command(expr
, info
):
688 name
= expr
['command']
689 boxed
= expr
.get('boxed', False)
691 args_meta
= ['struct']
693 args_meta
+= ['union', 'alternate']
694 check_type(info
, "'data' for command '%s'" % name
,
695 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
696 allow_metas
=args_meta
)
697 returns_meta
= ['union', 'struct']
698 if name
in returns_whitelist
:
699 returns_meta
+= ['built-in', 'alternate', 'enum']
700 check_type(info
, "'returns' for command '%s'" % name
,
701 expr
.get('returns'), allow_array
=True,
702 allow_optional
=True, allow_metas
=returns_meta
)
705 def check_event(expr
, info
):
707 boxed
= expr
.get('boxed', False)
711 meta
+= ['union', 'alternate']
712 check_type(info
, "'data' for event '%s'" % name
,
713 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
717 def check_union(expr
, info
):
719 base
= expr
.get('base')
720 discriminator
= expr
.get('discriminator')
721 members
= expr
['data']
723 # Two types of unions, determined by discriminator.
725 # With no discriminator it is a simple union.
726 if discriminator
is None:
728 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
730 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
733 # Else, it's a flat union.
735 # The object must have a string or dictionary 'base'.
736 check_type(info
, "'base' for union '%s'" % name
,
737 base
, allow_dict
=True, allow_optional
=True,
738 allow_metas
=['struct'])
740 raise QAPISemError(info
, "Flat union '%s' must have a base"
742 base_members
= find_base_members(base
)
743 assert base_members
is not None
745 # The value of member 'discriminator' must name a non-optional
746 # member of the base struct.
747 check_name(info
, "Discriminator of flat union '%s'" % name
,
749 discriminator_type
= base_members
.get(discriminator
)
750 if not discriminator_type
:
751 raise QAPISemError(info
,
752 "Discriminator '%s' is not a member of base "
754 % (discriminator
, base
))
755 enum_define
= enum_types
.get(discriminator_type
)
756 allow_metas
= ['struct']
757 # Do not allow string discriminator
759 raise QAPISemError(info
,
760 "Discriminator '%s' must be of enumeration "
761 "type" % discriminator
)
763 # Check every branch; don't allow an empty union
764 if len(members
) == 0:
765 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
766 for (key
, value
) in members
.items():
767 check_name(info
, "Member of union '%s'" % name
, key
)
769 # Each value must name a known type
770 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
771 value
, allow_array
=not base
, allow_metas
=allow_metas
)
773 # If the discriminator names an enum type, then all members
774 # of 'data' must also be members of the enum type.
776 if key
not in enum_define
['data']:
777 raise QAPISemError(info
,
778 "Discriminator value '%s' is not found in "
780 % (key
, enum_define
['enum']))
782 # If discriminator is user-defined, ensure all values are covered
784 for value
in enum_define
['data']:
785 if value
not in members
.keys():
786 raise QAPISemError(info
, "Union '%s' data missing '%s' branch"
790 def check_alternate(expr
, info
):
791 name
= expr
['alternate']
792 members
= expr
['data']
795 # Check every branch; require at least two branches
797 raise QAPISemError(info
,
798 "Alternate '%s' should have at least two branches "
800 for (key
, value
) in members
.items():
801 check_name(info
, "Member of alternate '%s'" % name
, key
)
803 # Ensure alternates have no type conflicts.
804 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
),
806 allow_metas
=['built-in', 'union', 'struct', 'enum'])
807 qtype
= find_alternate_member_qtype(value
)
809 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
810 "type '%s'" % (name
, key
, value
))
811 conflicting
= set([qtype
])
812 if qtype
== 'QTYPE_QSTRING':
813 enum_expr
= enum_types
.get(value
)
815 for v
in enum_expr
['data']:
816 if v
in ['on', 'off']:
817 conflicting
.add('QTYPE_QBOOL')
818 if re
.match(r
'[-+0-9.]', v
): # lazy, could be tightened
819 conflicting
.add('QTYPE_QNUM')
821 conflicting
.add('QTYPE_QNUM')
822 conflicting
.add('QTYPE_QBOOL')
823 for qt
in conflicting
:
825 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
826 "be distinguished from member '%s'"
827 % (name
, key
, types_seen
[qt
]))
831 def check_enum(expr
, info
):
833 members
= expr
.get('data')
834 prefix
= expr
.get('prefix')
836 if not isinstance(members
, list):
837 raise QAPISemError(info
,
838 "Enum '%s' requires an array for 'data'" % name
)
839 if prefix
is not None and not isinstance(prefix
, str):
840 raise QAPISemError(info
,
841 "Enum '%s' requires a string for 'prefix'" % name
)
842 for member
in members
:
843 check_name(info
, "Member of enum '%s'" % name
, member
,
847 def check_struct(expr
, info
):
848 name
= expr
['struct']
849 members
= expr
['data']
851 check_type(info
, "'data' for struct '%s'" % name
, members
,
852 allow_dict
=True, allow_optional
=True)
853 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
854 allow_metas
=['struct'])
857 def check_keys(expr_elem
, meta
, required
, optional
=[]):
858 expr
= expr_elem
['expr']
859 info
= expr_elem
['info']
861 if not isinstance(name
, str):
862 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
863 required
= required
+ [meta
]
864 for (key
, value
) in expr
.items():
865 if key
not in required
and key
not in optional
:
866 raise QAPISemError(info
, "Unknown key '%s' in %s '%s'"
868 if (key
== 'gen' or key
== 'success-response') and value
is not False:
869 raise QAPISemError(info
,
870 "'%s' of %s '%s' should only use false value"
872 if (key
== 'boxed' or key
== 'allow-oob' or
873 key
== 'allow-preconfig') and value
is not True:
874 raise QAPISemError(info
,
875 "'%s' of %s '%s' should only use true value"
879 raise QAPISemError(info
, "Key '%s' is missing from %s '%s'"
883 def check_exprs(exprs
):
886 # Populate name table with names of built-in types
887 for builtin
in builtin_types
.keys():
888 all_names
[builtin
] = 'built-in'
890 # Learn the types and check for valid expression keys
891 for expr_elem
in exprs
:
892 expr
= expr_elem
['expr']
893 info
= expr_elem
['info']
894 doc
= expr_elem
.get('doc')
896 if 'include' in expr
:
899 if not doc
and doc_required
:
900 raise QAPISemError(info
,
901 "Expression missing documentation comment")
905 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
906 enum_types
[expr
[meta
]] = expr
907 elif 'union' in expr
:
909 check_keys(expr_elem
, 'union', ['data'],
910 ['base', 'discriminator'])
911 union_types
[expr
[meta
]] = expr
912 elif 'alternate' in expr
:
914 check_keys(expr_elem
, 'alternate', ['data'])
915 elif 'struct' in expr
:
917 check_keys(expr_elem
, 'struct', ['data'], ['base'])
918 struct_types
[expr
[meta
]] = expr
919 elif 'command' in expr
:
921 check_keys(expr_elem
, 'command', [],
922 ['data', 'returns', 'gen', 'success-response',
923 'boxed', 'allow-oob', 'allow-preconfig'])
924 elif 'event' in expr
:
926 check_keys(expr_elem
, 'event', [], ['data', 'boxed'])
928 raise QAPISemError(expr_elem
['info'],
929 "Expression is missing metatype")
931 add_name(name
, info
, meta
)
932 if doc
and doc
.symbol
!= name
:
933 raise QAPISemError(info
, "Definition of '%s' follows documentation"
934 " for '%s'" % (name
, doc
.symbol
))
936 # Try again for hidden UnionKind enum
937 for expr_elem
in exprs
:
938 expr
= expr_elem
['expr']
940 if 'include' in expr
:
942 if 'union' in expr
and not discriminator_find_enum_define(expr
):
943 name
= '%sKind' % expr
['union']
944 elif 'alternate' in expr
:
945 name
= '%sKind' % expr
['alternate']
948 enum_types
[name
] = {'enum': name
}
949 add_name(name
, info
, 'enum', implicit
=True)
951 # Validate that exprs make sense
952 for expr_elem
in exprs
:
953 expr
= expr_elem
['expr']
954 info
= expr_elem
['info']
955 doc
= expr_elem
.get('doc')
957 if 'include' in expr
:
960 check_enum(expr
, info
)
961 elif 'union' in expr
:
962 check_union(expr
, info
)
963 elif 'alternate' in expr
:
964 check_alternate(expr
, info
)
965 elif 'struct' in expr
:
966 check_struct(expr
, info
)
967 elif 'command' in expr
:
968 check_command(expr
, info
)
969 elif 'event' in expr
:
970 check_event(expr
, info
)
972 assert False, 'unexpected meta type'
981 # Schema compiler frontend
984 class QAPISchemaEntity(object):
985 def __init__(self
, name
, info
, doc
):
986 assert name
is None or isinstance(name
, str)
989 # For explicitly defined entities, info points to the (explicit)
990 # definition. For builtins (and their arrays), info is None.
991 # For implicitly defined entities, info points to a place that
992 # triggered the implicit definition (there may be more than one
998 return c_name(self
.name
)
1000 def check(self
, schema
):
1003 def is_implicit(self
):
1004 return not self
.info
1006 def visit(self
, visitor
):
1010 class QAPISchemaVisitor(object):
1011 def visit_begin(self
, schema
):
1014 def visit_end(self
):
1017 def visit_module(self
, fname
):
1020 def visit_needed(self
, entity
):
1021 # Default to visiting everything
1024 def visit_include(self
, fname
, info
):
1027 def visit_builtin_type(self
, name
, info
, json_type
):
1030 def visit_enum_type(self
, name
, info
, values
, prefix
):
1033 def visit_array_type(self
, name
, info
, element_type
):
1036 def visit_object_type(self
, name
, info
, base
, members
, variants
):
1039 def visit_object_type_flat(self
, name
, info
, members
, variants
):
1042 def visit_alternate_type(self
, name
, info
, variants
):
1045 def visit_command(self
, name
, info
, arg_type
, ret_type
, gen
,
1046 success_response
, boxed
, allow_oob
, allow_preconfig
):
1049 def visit_event(self
, name
, info
, arg_type
, boxed
):
1053 class QAPISchemaInclude(QAPISchemaEntity
):
1055 def __init__(self
, fname
, info
):
1056 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1059 def visit(self
, visitor
):
1060 visitor
.visit_include(self
.fname
, self
.info
)
1063 class QAPISchemaType(QAPISchemaEntity
):
1064 # Return the C type for common use.
1065 # For the types we commonly box, this is a pointer type.
1069 # Return the C type to be used in a parameter list.
1070 def c_param_type(self
):
1071 return self
.c_type()
1073 # Return the C type to be used where we suppress boxing.
1074 def c_unboxed_type(self
):
1075 return self
.c_type()
1077 def json_type(self
):
1080 def alternate_qtype(self
):
1082 'null': 'QTYPE_QNULL',
1083 'string': 'QTYPE_QSTRING',
1084 'number': 'QTYPE_QNUM',
1085 'int': 'QTYPE_QNUM',
1086 'boolean': 'QTYPE_QBOOL',
1087 'object': 'QTYPE_QDICT'
1089 return json2qtype
.get(self
.json_type())
1092 if self
.is_implicit():
1097 class QAPISchemaBuiltinType(QAPISchemaType
):
1098 def __init__(self
, name
, json_type
, c_type
):
1099 QAPISchemaType
.__init
__(self
, name
, None, None)
1100 assert not c_type
or isinstance(c_type
, str)
1101 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1103 self
._json
_type
_name
= json_type
1104 self
._c
_type
_name
= c_type
1110 return self
._c
_type
_name
1112 def c_param_type(self
):
1113 if self
.name
== 'str':
1114 return 'const ' + self
._c
_type
_name
1115 return self
._c
_type
_name
1117 def json_type(self
):
1118 return self
._json
_type
_name
1121 return self
.json_type()
1123 def visit(self
, visitor
):
1124 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1127 class QAPISchemaEnumType(QAPISchemaType
):
1128 def __init__(self
, name
, info
, doc
, values
, prefix
):
1129 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1131 assert isinstance(v
, QAPISchemaMember
)
1133 assert prefix
is None or isinstance(prefix
, str)
1134 self
.values
= values
1135 self
.prefix
= prefix
1137 def check(self
, schema
):
1139 for v
in self
.values
:
1140 v
.check_clash(self
.info
, seen
)
1142 self
.doc
.connect_member(v
)
1144 def is_implicit(self
):
1145 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1146 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1149 return c_name(self
.name
)
1151 def member_names(self
):
1152 return [v
.name
for v
in self
.values
]
1154 def json_type(self
):
1157 def visit(self
, visitor
):
1158 visitor
.visit_enum_type(self
.name
, self
.info
,
1159 self
.member_names(), self
.prefix
)
1162 class QAPISchemaArrayType(QAPISchemaType
):
1163 def __init__(self
, name
, info
, element_type
):
1164 QAPISchemaType
.__init
__(self
, name
, info
, None)
1165 assert isinstance(element_type
, str)
1166 self
._element
_type
_name
= element_type
1167 self
.element_type
= None
1169 def check(self
, schema
):
1170 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1171 assert self
.element_type
1173 def is_implicit(self
):
1177 return c_name(self
.name
) + pointer_suffix
1179 def json_type(self
):
1183 elt_doc_type
= self
.element_type
.doc_type()
1184 if not elt_doc_type
:
1186 return 'array of ' + elt_doc_type
1188 def visit(self
, visitor
):
1189 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
1192 class QAPISchemaObjectType(QAPISchemaType
):
1193 def __init__(self
, name
, info
, doc
, base
, local_members
, variants
):
1194 # struct has local_members, optional base, and no variants
1195 # flat union has base, variants, and no local_members
1196 # simple union has local_members, variants, and no base
1197 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1198 assert base
is None or isinstance(base
, str)
1199 for m
in local_members
:
1200 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1202 if variants
is not None:
1203 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1204 variants
.set_owner(name
)
1205 self
._base
_name
= base
1207 self
.local_members
= local_members
1208 self
.variants
= variants
1211 def check(self
, schema
):
1212 if self
.members
is False: # check for cycles
1213 raise QAPISemError(self
.info
,
1214 "Object %s contains itself" % self
.name
)
1217 self
.members
= False # mark as being checked
1218 seen
= OrderedDict()
1220 self
.base
= schema
.lookup_type(self
._base
_name
)
1221 assert isinstance(self
.base
, QAPISchemaObjectType
)
1222 self
.base
.check(schema
)
1223 self
.base
.check_clash(self
.info
, seen
)
1224 for m
in self
.local_members
:
1226 m
.check_clash(self
.info
, seen
)
1228 self
.doc
.connect_member(m
)
1229 self
.members
= seen
.values()
1231 self
.variants
.check(schema
, seen
)
1232 assert self
.variants
.tag_member
in self
.members
1233 self
.variants
.check_clash(self
.info
, seen
)
1237 # Check that the members of this type do not cause duplicate JSON members,
1238 # and update seen to track the members seen so far. Report any errors
1239 # on behalf of info, which is not necessarily self.info
1240 def check_clash(self
, info
, seen
):
1241 assert not self
.variants
# not implemented
1242 for m
in self
.members
:
1243 m
.check_clash(info
, seen
)
1245 def is_implicit(self
):
1246 # See QAPISchema._make_implicit_object_type(), as well as
1247 # _def_predefineds()
1248 return self
.name
.startswith('q_')
1251 assert self
.members
is not None
1252 return not self
.members
and not self
.variants
1255 assert self
.name
!= 'q_empty'
1256 return QAPISchemaType
.c_name(self
)
1259 assert not self
.is_implicit()
1260 return c_name(self
.name
) + pointer_suffix
1262 def c_unboxed_type(self
):
1263 return c_name(self
.name
)
1265 def json_type(self
):
1268 def visit(self
, visitor
):
1269 visitor
.visit_object_type(self
.name
, self
.info
,
1270 self
.base
, self
.local_members
, self
.variants
)
1271 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1272 self
.members
, self
.variants
)
1275 class QAPISchemaMember(object):
1278 def __init__(self
, name
):
1279 assert isinstance(name
, str)
1283 def set_owner(self
, name
):
1284 assert not self
.owner
1287 def check_clash(self
, info
, seen
):
1288 cname
= c_name(self
.name
)
1289 if cname
.lower() != cname
and self
.owner
not in name_case_whitelist
:
1290 raise QAPISemError(info
,
1291 "%s should not use uppercase" % self
.describe())
1293 raise QAPISemError(info
, "%s collides with %s" %
1294 (self
.describe(), seen
[cname
].describe()))
1297 def _pretty_owner(self
):
1299 if owner
.startswith('q_obj_'):
1300 # See QAPISchema._make_implicit_object_type() - reverse the
1301 # mapping there to create a nice human-readable description
1303 if owner
.endswith('-arg'):
1304 return '(parameter of %s)' % owner
[:-4]
1305 elif owner
.endswith('-base'):
1306 return '(base of %s)' % owner
[:-5]
1308 assert owner
.endswith('-wrapper')
1309 # Unreachable and not implemented
1311 if owner
.endswith('Kind'):
1312 # See QAPISchema._make_implicit_enum_type()
1313 return '(branch of %s)' % owner
[:-4]
1314 return '(%s of %s)' % (self
.role
, owner
)
1317 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1320 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1321 def __init__(self
, name
, typ
, optional
):
1322 QAPISchemaMember
.__init
__(self
, name
)
1323 assert isinstance(typ
, str)
1324 assert isinstance(optional
, bool)
1325 self
._type
_name
= typ
1327 self
.optional
= optional
1329 def check(self
, schema
):
1331 self
.type = schema
.lookup_type(self
._type
_name
)
1335 class QAPISchemaObjectTypeVariants(object):
1336 def __init__(self
, tag_name
, tag_member
, variants
):
1337 # Flat unions pass tag_name but not tag_member.
1338 # Simple unions and alternates pass tag_member but not tag_name.
1339 # After check(), tag_member is always set, and tag_name remains
1340 # a reliable witness of being used by a flat union.
1341 assert bool(tag_member
) != bool(tag_name
)
1342 assert (isinstance(tag_name
, str) or
1343 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1344 assert len(variants
) > 0
1346 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1347 self
._tag
_name
= tag_name
1348 self
.tag_member
= tag_member
1349 self
.variants
= variants
1351 def set_owner(self
, name
):
1352 for v
in self
.variants
:
1355 def check(self
, schema
, seen
):
1356 if not self
.tag_member
: # flat union
1357 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1358 assert self
._tag
_name
== self
.tag_member
.name
1359 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1360 for v
in self
.variants
:
1362 # Union names must match enum values; alternate names are
1363 # checked separately. Use 'seen' to tell the two apart.
1365 assert v
.name
in self
.tag_member
.type.member_names()
1366 assert isinstance(v
.type, QAPISchemaObjectType
)
1367 v
.type.check(schema
)
1369 def check_clash(self
, info
, seen
):
1370 for v
in self
.variants
:
1371 # Reset seen map for each variant, since qapi names from one
1372 # branch do not affect another branch
1373 assert isinstance(v
.type, QAPISchemaObjectType
)
1374 v
.type.check_clash(info
, dict(seen
))
1377 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1380 def __init__(self
, name
, typ
):
1381 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1384 class QAPISchemaAlternateType(QAPISchemaType
):
1385 def __init__(self
, name
, info
, doc
, variants
):
1386 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1387 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1388 assert variants
.tag_member
1389 variants
.set_owner(name
)
1390 variants
.tag_member
.set_owner(self
.name
)
1391 self
.variants
= variants
1393 def check(self
, schema
):
1394 self
.variants
.tag_member
.check(schema
)
1395 # Not calling self.variants.check_clash(), because there's nothing
1397 self
.variants
.check(schema
, {})
1398 # Alternate branch names have no relation to the tag enum values;
1399 # so we have to check for potential name collisions ourselves.
1401 for v
in self
.variants
.variants
:
1402 v
.check_clash(self
.info
, seen
)
1404 self
.doc
.connect_member(v
)
1409 return c_name(self
.name
) + pointer_suffix
1411 def json_type(self
):
1414 def visit(self
, visitor
):
1415 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1421 class QAPISchemaCommand(QAPISchemaEntity
):
1422 def __init__(self
, name
, info
, doc
, arg_type
, ret_type
,
1423 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
1424 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1425 assert not arg_type
or isinstance(arg_type
, str)
1426 assert not ret_type
or isinstance(ret_type
, str)
1427 self
._arg
_type
_name
= arg_type
1428 self
.arg_type
= None
1429 self
._ret
_type
_name
= ret_type
1430 self
.ret_type
= None
1432 self
.success_response
= success_response
1434 self
.allow_oob
= allow_oob
1435 self
.allow_preconfig
= allow_preconfig
1437 def check(self
, schema
):
1438 if self
._arg
_type
_name
:
1439 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1440 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1441 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1442 self
.arg_type
.check(schema
)
1444 if self
.arg_type
.is_empty():
1445 raise QAPISemError(self
.info
,
1446 "Cannot use 'boxed' with empty type")
1448 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1449 assert not self
.arg_type
.variants
1451 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1452 if self
._ret
_type
_name
:
1453 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1454 assert isinstance(self
.ret_type
, QAPISchemaType
)
1456 def visit(self
, visitor
):
1457 visitor
.visit_command(self
.name
, self
.info
,
1458 self
.arg_type
, self
.ret_type
,
1459 self
.gen
, self
.success_response
,
1460 self
.boxed
, self
.allow_oob
,
1461 self
.allow_preconfig
)
1464 class QAPISchemaEvent(QAPISchemaEntity
):
1465 def __init__(self
, name
, info
, doc
, arg_type
, boxed
):
1466 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1467 assert not arg_type
or isinstance(arg_type
, str)
1468 self
._arg
_type
_name
= arg_type
1469 self
.arg_type
= None
1472 def check(self
, schema
):
1473 if self
._arg
_type
_name
:
1474 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1475 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1476 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1477 self
.arg_type
.check(schema
)
1479 if self
.arg_type
.is_empty():
1480 raise QAPISemError(self
.info
,
1481 "Cannot use 'boxed' with empty type")
1483 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1484 assert not self
.arg_type
.variants
1486 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1488 def visit(self
, visitor
):
1489 visitor
.visit_event(self
.name
, self
.info
, self
.arg_type
, self
.boxed
)
1492 class QAPISchema(object):
1493 def __init__(self
, fname
):
1495 parser
= QAPISchemaParser(open(fname
, 'r'))
1496 exprs
= check_exprs(parser
.exprs
)
1497 self
.docs
= parser
.docs
1498 self
._entity
_list
= []
1499 self
._entity
_dict
= {}
1500 self
._predefining
= True
1501 self
._def
_predefineds
()
1502 self
._predefining
= False
1503 self
._def
_exprs
(exprs
)
1506 def _def_entity(self
, ent
):
1507 # Only the predefined types are allowed to not have info
1508 assert ent
.info
or self
._predefining
1509 assert ent
.name
is None or ent
.name
not in self
._entity
_dict
1510 self
._entity
_list
.append(ent
)
1511 if ent
.name
is not None:
1512 self
._entity
_dict
[ent
.name
] = ent
1514 ent
.module
= os
.path
.relpath(ent
.info
['file'],
1515 os
.path
.dirname(self
._fname
))
1517 def lookup_entity(self
, name
, typ
=None):
1518 ent
= self
._entity
_dict
.get(name
)
1519 if typ
and not isinstance(ent
, typ
):
1523 def lookup_type(self
, name
):
1524 return self
.lookup_entity(name
, QAPISchemaType
)
1526 def _def_include(self
, expr
, info
, doc
):
1527 include
= expr
['include']
1530 while main_info
['parent']:
1531 main_info
= main_info
['parent']
1532 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
['file']))
1533 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
1535 def _def_builtin_type(self
, name
, json_type
, c_type
):
1536 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1537 # Instantiating only the arrays that are actually used would
1538 # be nice, but we can't as long as their generated code
1539 # (qapi-builtin-types.[ch]) may be shared by some other
1541 self
._make
_array
_type
(name
, None)
1543 def _def_predefineds(self
):
1544 for t
in [('str', 'string', 'char' + pointer_suffix
),
1545 ('number', 'number', 'double'),
1546 ('int', 'int', 'int64_t'),
1547 ('int8', 'int', 'int8_t'),
1548 ('int16', 'int', 'int16_t'),
1549 ('int32', 'int', 'int32_t'),
1550 ('int64', 'int', 'int64_t'),
1551 ('uint8', 'int', 'uint8_t'),
1552 ('uint16', 'int', 'uint16_t'),
1553 ('uint32', 'int', 'uint32_t'),
1554 ('uint64', 'int', 'uint64_t'),
1555 ('size', 'int', 'uint64_t'),
1556 ('bool', 'boolean', 'bool'),
1557 ('any', 'value', 'QObject' + pointer_suffix
),
1558 ('null', 'null', 'QNull' + pointer_suffix
)]:
1559 self
._def
_builtin
_type
(*t
)
1560 self
.the_empty_object_type
= QAPISchemaObjectType(
1561 'q_empty', None, None, None, [], None)
1562 self
._def
_entity
(self
.the_empty_object_type
)
1563 qtype_values
= self
._make
_enum
_members
(['none', 'qnull', 'qnum',
1564 'qstring', 'qdict', 'qlist',
1566 self
._def
_entity
(QAPISchemaEnumType('QType', None, None,
1567 qtype_values
, 'QTYPE'))
1569 def _make_enum_members(self
, values
):
1570 return [QAPISchemaMember(v
) for v
in values
]
1572 def _make_implicit_enum_type(self
, name
, info
, values
):
1573 # See also QAPISchemaObjectTypeMember._pretty_owner()
1574 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1575 self
._def
_entity
(QAPISchemaEnumType(
1576 name
, info
, None, self
._make
_enum
_members
(values
), None))
1579 def _make_array_type(self
, element_type
, info
):
1580 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1581 if not self
.lookup_type(name
):
1582 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1585 def _make_implicit_object_type(self
, name
, info
, doc
, role
, members
):
1588 # See also QAPISchemaObjectTypeMember._pretty_owner()
1589 name
= 'q_obj_%s-%s' % (name
, role
)
1590 if not self
.lookup_entity(name
, QAPISchemaObjectType
):
1591 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, None,
1595 def _def_enum_type(self
, expr
, info
, doc
):
1598 prefix
= expr
.get('prefix')
1599 self
._def
_entity
(QAPISchemaEnumType(
1600 name
, info
, doc
, self
._make
_enum
_members
(data
), prefix
))
1602 def _make_member(self
, name
, typ
, info
):
1604 if name
.startswith('*'):
1607 if isinstance(typ
, list):
1608 assert len(typ
) == 1
1609 typ
= self
._make
_array
_type
(typ
[0], info
)
1610 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1612 def _make_members(self
, data
, info
):
1613 return [self
._make
_member
(key
, value
, info
)
1614 for (key
, value
) in data
.items()]
1616 def _def_struct_type(self
, expr
, info
, doc
):
1617 name
= expr
['struct']
1618 base
= expr
.get('base')
1620 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, base
,
1621 self
._make
_members
(data
, info
),
1624 def _make_variant(self
, case
, typ
):
1625 return QAPISchemaObjectTypeVariant(case
, typ
)
1627 def _make_simple_variant(self
, case
, typ
, info
):
1628 if isinstance(typ
, list):
1629 assert len(typ
) == 1
1630 typ
= self
._make
_array
_type
(typ
[0], info
)
1631 typ
= self
._make
_implicit
_object
_type
(
1632 typ
, info
, None, 'wrapper', [self
._make
_member
('data', typ
, info
)])
1633 return QAPISchemaObjectTypeVariant(case
, typ
)
1635 def _def_union_type(self
, expr
, info
, doc
):
1636 name
= expr
['union']
1638 base
= expr
.get('base')
1639 tag_name
= expr
.get('discriminator')
1641 if isinstance(base
, dict):
1642 base
= (self
._make
_implicit
_object
_type
(
1643 name
, info
, doc
, 'base', self
._make
_members
(base
, info
)))
1645 variants
= [self
._make
_variant
(key
, value
)
1646 for (key
, value
) in data
.items()]
1649 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1650 for (key
, value
) in data
.items()]
1651 typ
= self
._make
_implicit
_enum
_type
(name
, info
,
1652 [v
.name
for v
in variants
])
1653 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1654 members
= [tag_member
]
1656 QAPISchemaObjectType(name
, info
, doc
, base
, members
,
1657 QAPISchemaObjectTypeVariants(tag_name
,
1661 def _def_alternate_type(self
, expr
, info
, doc
):
1662 name
= expr
['alternate']
1664 variants
= [self
._make
_variant
(key
, value
)
1665 for (key
, value
) in data
.items()]
1666 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1668 QAPISchemaAlternateType(name
, info
, doc
,
1669 QAPISchemaObjectTypeVariants(None,
1673 def _def_command(self
, expr
, info
, doc
):
1674 name
= expr
['command']
1675 data
= expr
.get('data')
1676 rets
= expr
.get('returns')
1677 gen
= expr
.get('gen', True)
1678 success_response
= expr
.get('success-response', True)
1679 boxed
= expr
.get('boxed', False)
1680 allow_oob
= expr
.get('allow-oob', False)
1681 allow_preconfig
= expr
.get('allow-preconfig', False)
1682 if isinstance(data
, OrderedDict
):
1683 data
= self
._make
_implicit
_object
_type
(
1684 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1685 if isinstance(rets
, list):
1686 assert len(rets
) == 1
1687 rets
= self
._make
_array
_type
(rets
[0], info
)
1688 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, data
, rets
,
1689 gen
, success_response
,
1690 boxed
, allow_oob
, allow_preconfig
))
1692 def _def_event(self
, expr
, info
, doc
):
1693 name
= expr
['event']
1694 data
= expr
.get('data')
1695 boxed
= expr
.get('boxed', False)
1696 if isinstance(data
, OrderedDict
):
1697 data
= self
._make
_implicit
_object
_type
(
1698 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1699 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, data
, boxed
))
1701 def _def_exprs(self
, exprs
):
1702 for expr_elem
in exprs
:
1703 expr
= expr_elem
['expr']
1704 info
= expr_elem
['info']
1705 doc
= expr_elem
.get('doc')
1707 self
._def
_enum
_type
(expr
, info
, doc
)
1708 elif 'struct' in expr
:
1709 self
._def
_struct
_type
(expr
, info
, doc
)
1710 elif 'union' in expr
:
1711 self
._def
_union
_type
(expr
, info
, doc
)
1712 elif 'alternate' in expr
:
1713 self
._def
_alternate
_type
(expr
, info
, doc
)
1714 elif 'command' in expr
:
1715 self
._def
_command
(expr
, info
, doc
)
1716 elif 'event' in expr
:
1717 self
._def
_event
(expr
, info
, doc
)
1718 elif 'include' in expr
:
1719 self
._def
_include
(expr
, info
, doc
)
1724 for ent
in self
._entity
_list
:
1727 def visit(self
, visitor
):
1728 visitor
.visit_begin(self
)
1730 for entity
in self
._entity
_list
:
1731 if visitor
.visit_needed(entity
):
1732 if entity
.module
!= module
:
1733 module
= entity
.module
1734 visitor
.visit_module(module
)
1735 entity
.visit(visitor
)
1740 # Code generation helpers
1743 def camel_case(name
):
1747 if ch
in ['_', '-']:
1750 new_name
+= ch
.upper()
1753 new_name
+= ch
.lower()
1757 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1758 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1759 # ENUM24_Name -> ENUM24_NAME
1760 def camel_to_upper(value
):
1761 c_fun_str
= c_name(value
, False)
1769 # When c is upper and no '_' appears before, do more checks
1770 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
1771 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1773 elif c_fun_str
[i
- 1].isdigit():
1776 return new_name
.lstrip('_').upper()
1779 def c_enum_const(type_name
, const_name
, prefix
=None):
1780 if prefix
is not None:
1782 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1784 if hasattr(str, 'maketrans'):
1785 c_name_trans
= str.maketrans('.-', '__')
1787 c_name_trans
= string
.maketrans('.-', '__')
1790 # Map @name to a valid C identifier.
1791 # If @protect, avoid returning certain ticklish identifiers (like
1792 # C keywords) by prepending 'q_'.
1794 # Used for converting 'name' from a 'name':'type' qapi definition
1795 # into a generated struct member, as well as converting type names
1796 # into substrings of a generated C function name.
1797 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1798 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1799 def c_name(name
, protect
=True):
1800 # ANSI X3J11/88-090, 3.1.1
1801 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1802 'default', 'do', 'double', 'else', 'enum', 'extern',
1803 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1804 'return', 'short', 'signed', 'sizeof', 'static',
1805 'struct', 'switch', 'typedef', 'union', 'unsigned',
1806 'void', 'volatile', 'while'])
1807 # ISO/IEC 9899:1999, 6.4.1
1808 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1809 # ISO/IEC 9899:2011, 6.4.1
1810 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1811 '_Noreturn', '_Static_assert', '_Thread_local'])
1812 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1814 gcc_words
= set(['asm', 'typeof'])
1815 # C++ ISO/IEC 14882:2003 2.11
1816 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1817 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1818 'namespace', 'new', 'operator', 'private', 'protected',
1819 'public', 'reinterpret_cast', 'static_cast', 'template',
1820 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1821 'using', 'virtual', 'wchar_t',
1822 # alternative representations
1823 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1824 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1825 # namespace pollution:
1826 polluted_words
= set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1827 name
= name
.translate(c_name_trans
)
1828 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1829 | cpp_words | polluted_words
):
1833 eatspace
= '\033EATSPACE.'
1834 pointer_suffix
= ' *' + eatspace
1837 def genindent(count
):
1839 for _
in range(count
):
1846 def push_indent(indent_amount
=4):
1848 indent_level
+= indent_amount
1851 def pop_indent(indent_amount
=4):
1853 indent_level
-= indent_amount
1856 # Generate @code with @kwds interpolated.
1857 # Obey indent_level, and strip eatspace.
1858 def cgen(code
, **kwds
):
1861 indent
= genindent(indent_level
)
1862 # re.subn() lacks flags support before Python 2.7, use re.compile()
1863 raw
= re
.subn(re
.compile(r
'^.', re
.MULTILINE
),
1864 indent
+ r
'\g<0>', raw
)
1866 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
1869 def mcgen(code
, **kwds
):
1872 return cgen(code
, **kwds
)
1875 def guardname(filename
):
1876 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
).upper()
1879 def guardstart(name
):
1885 name
=guardname(name
))
1891 #endif /* %(name)s */
1893 name
=guardname(name
))
1896 def gen_enum_lookup(name
, values
, prefix
=None):
1899 const QEnumLookup %(c_name)s_lookup = {
1900 .array = (const char *const[]) {
1902 c_name
=c_name(name
))
1903 for value
in values
:
1904 index
= c_enum_const(name
, value
, prefix
)
1906 [%(index)s] = "%(value)s",
1908 index
=index
, value
=value
)
1912 .size = %(max_index)s
1915 max_index
=c_enum_const(name
, '_MAX', prefix
))
1919 def gen_enum(name
, values
, prefix
=None):
1920 # append automatically generated _MAX value
1921 enum_values
= values
+ ['_MAX']
1925 typedef enum %(c_name)s {
1927 c_name
=c_name(name
))
1930 for value
in enum_values
:
1934 c_enum
=c_enum_const(name
, value
, prefix
),
1941 c_name
=c_name(name
))
1945 #define %(c_name)s_str(val) \\
1946 qapi_enum_lookup(&%(c_name)s_lookup, (val))
1948 extern const QEnumLookup %(c_name)s_lookup;
1950 c_name
=c_name(name
))
1954 def build_params(arg_type
, boxed
, extra
):
1961 ret
+= '%s arg' % arg_type
.c_param_type()
1964 assert not arg_type
.variants
1965 for memb
in arg_type
.members
:
1969 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1970 ret
+= '%s %s' % (memb
.type.c_param_type(),
1978 # Accumulate and write output
1981 class QAPIGen(object):
1987 def preamble_add(self
, text
):
1988 self
._preamble
+= text
1990 def add(self
, text
):
1993 def _top(self
, fname
):
1996 def _bottom(self
, fname
):
1999 def write(self
, output_dir
, fname
):
2000 pathname
= os
.path
.join(output_dir
, fname
)
2001 dir = os
.path
.dirname(pathname
)
2005 except os
.error
as e
:
2006 if e
.errno
!= errno
.EEXIST
:
2008 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2009 f
= os
.fdopen(fd
, 'r+')
2010 text
= (self
._top
(fname
) + self
._preamble
+ self
._body
2011 + self
._bottom
(fname
))
2012 oldtext
= f
.read(len(text
) + 1)
2020 class QAPIGenC(QAPIGen
):
2022 def __init__(self
, blurb
, pydoc
):
2023 QAPIGen
.__init
__(self
)
2025 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2028 def _top(self
, fname
):
2030 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2037 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2038 * See the COPYING.LIB file in the top-level directory.
2042 blurb
=self
._blurb
, copyright
=self
._copyright
)
2044 def _bottom(self
, fname
):
2046 /* Dummy declaration to prevent empty .o file */
2047 char dummy_%(name)s;
2052 class QAPIGenH(QAPIGenC
):
2054 def _top(self
, fname
):
2055 return QAPIGenC
._top
(self
, fname
) + guardstart(fname
)
2057 def _bottom(self
, fname
):
2058 return guardend(fname
)
2061 class QAPIGenDoc(QAPIGen
):
2063 def _top(self
, fname
):
2064 return (QAPIGen
._top
(self
, fname
)
2065 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2068 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2070 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2071 self
._prefix
= prefix
2073 self
._genc
= QAPIGenC(blurb
, pydoc
)
2074 self
._genh
= QAPIGenH(blurb
, pydoc
)
2076 def write(self
, output_dir
):
2077 self
._genc
.write(output_dir
, self
._prefix
+ self
._what
+ '.c')
2078 self
._genh
.write(output_dir
, self
._prefix
+ self
._what
+ '.h')
2081 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2083 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2084 self
._prefix
= prefix
2089 self
._main
_module
= None
2091 def _module_basename(self
, what
, name
):
2093 return re
.sub(r
'-', '-builtin-', what
)
2094 basename
= os
.path
.join(os
.path
.dirname(name
),
2095 self
._prefix
+ what
)
2096 if name
== self
._main
_module
:
2098 return basename
+ '-' + os
.path
.splitext(os
.path
.basename(name
))[0]
2100 def _add_module(self
, name
, blurb
):
2101 if self
._main
_module
is None and name
is not None:
2102 self
._main
_module
= name
2103 genc
= QAPIGenC(blurb
, self
._pydoc
)
2104 genh
= QAPIGenH(blurb
, self
._pydoc
)
2105 self
._module
[name
] = (genc
, genh
)
2106 self
._set
_module
(name
)
2108 def _set_module(self
, name
):
2109 self
._genc
, self
._genh
= self
._module
[name
]
2111 def write(self
, output_dir
, opt_builtins
=False):
2112 for name
in self
._module
:
2113 if name
is None and not opt_builtins
:
2115 basename
= self
._module
_basename
(self
._what
, name
)
2116 (genc
, genh
) = self
._module
[name
]
2117 genc
.write(output_dir
, basename
+ '.c')
2118 genh
.write(output_dir
, basename
+ '.h')
2120 def _begin_module(self
, name
):
2123 def visit_module(self
, name
):
2124 if name
in self
._module
:
2125 self
._set
_module
(name
)
2127 self
._add
_module
(name
, self
._blurb
)
2128 self
._begin
_module
(name
)
2130 def visit_include(self
, name
, info
):
2131 basename
= self
._module
_basename
(self
._what
, name
)
2132 self
._genh
.preamble_add(mcgen('''
2133 #include "%(basename)s.h"