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
20 from collections
import OrderedDict
22 from ordereddict
import OrderedDict
25 'null': 'QTYPE_QNULL',
26 'str': 'QTYPE_QSTRING',
28 'number': 'QTYPE_QNUM',
29 'bool': 'QTYPE_QBOOL',
31 'int16': 'QTYPE_QNUM',
32 'int32': 'QTYPE_QNUM',
33 'int64': 'QTYPE_QNUM',
34 'uint8': 'QTYPE_QNUM',
35 'uint16': 'QTYPE_QNUM',
36 'uint32': 'QTYPE_QNUM',
37 'uint64': 'QTYPE_QNUM',
39 'any': None, # any QType possible, actually
40 'QType': 'QTYPE_QSTRING',
43 # Are documentation comments required?
46 # Whitelist of commands allowed to return a non-dictionary
47 returns_whitelist
= []
49 # Whitelist of entities allowed to violate case conventions
50 name_case_whitelist
= []
58 # Parsing the schema into expressions
62 def error_path(parent
):
65 res
= ('In file included from %s:%d:\n' % (parent
['file'],
66 parent
['line'])) + res
67 parent
= parent
['parent']
71 class QAPIError(Exception):
72 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
73 Exception.__init
__(self
)
81 loc
= '%s:%d' % (self
.fname
, self
.line
)
82 if self
.col
is not None:
83 loc
+= ':%s' % self
.col
84 return error_path(self
.info
) + '%s: %s' % (loc
, self
.msg
)
87 class QAPIParseError(QAPIError
):
88 def __init__(self
, parser
, msg
):
90 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
92 col
= (col
+ 7) % 8 + 1
95 QAPIError
.__init
__(self
, parser
.fname
, parser
.line
, col
,
96 parser
.incl_info
, msg
)
99 class QAPISemError(QAPIError
):
100 def __init__(self
, info
, msg
):
101 QAPIError
.__init
__(self
, info
['file'], info
['line'], None,
105 class QAPIDoc(object):
106 class Section(object):
107 def __init__(self
, name
=None):
108 # optional section name (argument/member or section name)
110 # the list of lines for this section
113 def append(self
, line
):
114 self
.text
+= line
.rstrip() + '\n'
116 class ArgSection(Section
):
117 def __init__(self
, name
):
118 QAPIDoc
.Section
.__init
__(self
, name
)
121 def connect(self
, member
):
124 def __init__(self
, parser
, info
):
125 # self._parser is used to report errors with QAPIParseError. The
126 # resulting error position depends on the state of the parser.
127 # It happens to be the beginning of the comment. More or less
128 # servicable, but action at a distance.
129 self
._parser
= parser
132 self
.body
= QAPIDoc
.Section()
133 # dict mapping parameter name to ArgSection
134 self
.args
= OrderedDict()
137 # the current section
138 self
._section
= self
.body
140 def has_section(self
, name
):
141 """Return True if we have a section with this name."""
142 for i
in self
.sections
:
147 def append(self
, line
):
148 """Parse a comment line and add it to the documentation."""
151 self
._append
_freeform
(line
)
155 raise QAPIParseError(self
._parser
, "Missing space after #")
158 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
159 # recognized, and get silently treated as ordinary text
161 self
._append
_symbol
_line
(line
)
162 elif not self
.body
.text
and line
.startswith('@'):
163 if not line
.endswith(':'):
164 raise QAPIParseError(self
._parser
, "Line should end with :")
165 self
.symbol
= line
[1:-1]
166 # FIXME invalid names other than the empty string aren't flagged
168 raise QAPIParseError(self
._parser
, "Invalid name")
170 self
._append
_freeform
(line
)
172 def end_comment(self
):
175 def _append_symbol_line(self
, line
):
176 name
= line
.split(' ', 1)[0]
178 if name
.startswith('@') and name
.endswith(':'):
179 line
= line
[len(name
)+1:]
180 self
._start
_args
_section
(name
[1:-1])
181 elif name
in ('Returns:', 'Since:',
182 # those are often singular or plural
184 'Example:', 'Examples:',
186 line
= line
[len(name
)+1:]
187 self
._start
_section
(name
[:-1])
189 self
._append
_freeform
(line
)
191 def _start_args_section(self
, name
):
192 # FIXME invalid names other than the empty string aren't flagged
194 raise QAPIParseError(self
._parser
, "Invalid parameter name")
195 if name
in self
.args
:
196 raise QAPIParseError(self
._parser
,
197 "'%s' parameter name duplicated" % name
)
199 raise QAPIParseError(self
._parser
,
200 "'@%s:' can't follow '%s' section"
201 % (name
, self
.sections
[0].name
))
203 self
._section
= QAPIDoc
.ArgSection(name
)
204 self
.args
[name
] = self
._section
206 def _start_section(self
, name
=None):
207 if name
in ('Returns', 'Since') and self
.has_section(name
):
208 raise QAPIParseError(self
._parser
,
209 "Duplicated '%s' section" % name
)
211 self
._section
= QAPIDoc
.Section(name
)
212 self
.sections
.append(self
._section
)
214 def _end_section(self
):
216 text
= self
._section
.text
= self
._section
.text
.strip()
217 if self
._section
.name
and (not text
or text
.isspace()):
218 raise QAPIParseError(self
._parser
, "Empty doc section '%s'"
219 % self
._section
.name
)
222 def _append_freeform(self
, line
):
223 in_arg
= isinstance(self
._section
, QAPIDoc
.ArgSection
)
224 if (in_arg
and self
._section
.text
.endswith('\n\n')
225 and line
and not line
[0].isspace()):
226 self
._start
_section
()
227 if (in_arg
or not self
._section
.name
228 or not self
._section
.name
.startswith('Example')):
230 match
= re
.match(r
'(@\S+:)', line
)
232 raise QAPIParseError(self
._parser
,
233 "'%s' not allowed in free-form documentation"
235 self
._section
.append(line
)
237 def connect_member(self
, member
):
238 if member
.name
not in self
.args
:
239 # Undocumented TODO outlaw
240 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
241 self
.args
[member
.name
].connect(member
)
243 def check_expr(self
, expr
):
244 if self
.has_section('Returns') and 'command' not in expr
:
245 raise QAPISemError(self
.info
,
246 "'Returns:' is only valid for commands")
249 bogus
= [name
for name
, section
in self
.args
.items()
250 if not section
.member
]
254 "The following documented members are not in "
255 "the declaration: %s" % ", ".join(bogus
))
258 class QAPISchemaParser(object):
260 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
262 previously_included
.append(os
.path
.abspath(fp
.name
))
263 self
.incl_info
= incl_info
265 if self
.src
== '' or self
.src
[-1] != '\n':
275 while self
.tok
is not None:
276 info
= {'file': self
.fname
, 'line': self
.line
,
277 'parent': self
.incl_info
}
279 self
.reject_expr_doc(cur_doc
)
280 cur_doc
= self
.get_doc(info
)
281 self
.docs
.append(cur_doc
)
284 expr
= self
.get_expr(False)
285 if 'include' in expr
:
286 self
.reject_expr_doc(cur_doc
)
288 raise QAPISemError(info
, "Invalid 'include' directive")
289 include
= expr
['include']
290 if not isinstance(include
, str):
291 raise QAPISemError(info
,
292 "Value of 'include' must be a string")
293 incl_fname
= os
.path
.join(os
.path
.dirname(self
.fname
),
295 self
.exprs
.append({'expr': {'include': incl_fname
},
297 exprs_include
= self
._include
(include
, info
, incl_fname
,
300 self
.exprs
.extend(exprs_include
.exprs
)
301 self
.docs
.extend(exprs_include
.docs
)
302 elif "pragma" in expr
:
303 self
.reject_expr_doc(cur_doc
)
305 raise QAPISemError(info
, "Invalid 'pragma' directive")
306 pragma
= expr
['pragma']
307 if not isinstance(pragma
, dict):
309 info
, "Value of 'pragma' must be a dictionary")
310 for name
, value
in pragma
.items():
311 self
._pragma
(name
, value
, info
)
313 expr_elem
= {'expr': expr
,
316 if not cur_doc
.symbol
:
318 cur_doc
.info
, "Expression documentation required")
319 expr_elem
['doc'] = cur_doc
320 self
.exprs
.append(expr_elem
)
322 self
.reject_expr_doc(cur_doc
)
325 def reject_expr_doc(doc
):
326 if doc
and doc
.symbol
:
329 "Documentation for '%s' is not followed by the definition"
332 def _include(self
, include
, info
, incl_fname
, previously_included
):
333 incl_abs_fname
= os
.path
.abspath(incl_fname
)
334 # catch inclusion cycle
337 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
338 raise QAPISemError(info
, "Inclusion loop for %s" % include
)
341 # skip multiple include of the same file
342 if incl_abs_fname
in previously_included
:
346 fobj
= open(incl_fname
, 'r')
348 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, incl_fname
))
349 return QAPISchemaParser(fobj
, previously_included
, info
)
351 def _pragma(self
, name
, value
, info
):
352 global doc_required
, returns_whitelist
, name_case_whitelist
353 if name
== 'doc-required':
354 if not isinstance(value
, bool):
355 raise QAPISemError(info
,
356 "Pragma 'doc-required' must be boolean")
358 elif name
== 'returns-whitelist':
359 if (not isinstance(value
, list)
360 or any([not isinstance(elt
, str) for elt
in value
])):
361 raise QAPISemError(info
,
362 "Pragma returns-whitelist must be"
363 " a list of strings")
364 returns_whitelist
= value
365 elif name
== 'name-case-whitelist':
366 if (not isinstance(value
, list)
367 or any([not isinstance(elt
, str) for elt
in value
])):
368 raise QAPISemError(info
,
369 "Pragma name-case-whitelist must be"
370 " a list of strings")
371 name_case_whitelist
= value
373 raise QAPISemError(info
, "Unknown pragma '%s'" % name
)
375 def accept(self
, skip_comment
=True):
377 self
.tok
= self
.src
[self
.cursor
]
378 self
.pos
= self
.cursor
383 if self
.src
[self
.cursor
] == '#':
384 # Start of doc comment
386 self
.cursor
= self
.src
.find('\n', self
.cursor
)
388 self
.val
= self
.src
[self
.pos
:self
.cursor
]
390 elif self
.tok
in '{}:,[]':
392 elif self
.tok
== "'":
396 ch
= self
.src
[self
.cursor
]
399 raise QAPIParseError(self
, 'Missing terminating "\'"')
413 for _
in range(0, 4):
414 ch
= self
.src
[self
.cursor
]
416 if ch
not in '0123456789abcdefABCDEF':
417 raise QAPIParseError(self
,
418 '\\u escape needs 4 '
420 value
= (value
<< 4) + int(ch
, 16)
421 # If Python 2 and 3 didn't disagree so much on
422 # how to handle Unicode, then we could allow
423 # Unicode string defaults. But most of QAPI is
424 # ASCII-only, so we aren't losing much for now.
425 if not value
or value
> 0x7f:
426 raise QAPIParseError(self
,
427 'For now, \\u escape '
428 'only supports non-zero '
429 'values up to \\u007f')
434 raise QAPIParseError(self
,
435 "Unknown escape \\%s" % ch
)
444 elif self
.src
.startswith('true', self
.pos
):
448 elif self
.src
.startswith('false', self
.pos
):
452 elif self
.src
.startswith('null', self
.pos
):
456 elif self
.tok
== '\n':
457 if self
.cursor
== len(self
.src
):
461 self
.line_pos
= self
.cursor
462 elif not self
.tok
.isspace():
463 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
465 def get_members(self
):
471 raise QAPIParseError(self
, 'Expected string or "}"')
476 raise QAPIParseError(self
, 'Expected ":"')
479 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
480 expr
[key
] = self
.get_expr(True)
485 raise QAPIParseError(self
, 'Expected "," or "}"')
488 raise QAPIParseError(self
, 'Expected string')
490 def get_values(self
):
495 if self
.tok
not in "{['tfn":
496 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
499 expr
.append(self
.get_expr(True))
504 raise QAPIParseError(self
, 'Expected "," or "]"')
507 def get_expr(self
, nested
):
508 if self
.tok
!= '{' and not nested
:
509 raise QAPIParseError(self
, 'Expected "{"')
512 expr
= self
.get_members()
513 elif self
.tok
== '[':
515 expr
= self
.get_values()
516 elif self
.tok
in "'tfn":
520 raise QAPIParseError(self
, 'Expected "{", "[", string, '
524 def get_doc(self
, info
):
526 raise QAPIParseError(self
, "Junk after '##' at start of "
527 "documentation comment")
529 doc
= QAPIDoc(self
, info
)
531 while self
.tok
== '#':
532 if self
.val
.startswith('##'):
535 raise QAPIParseError(self
, "Junk after '##' at end of "
536 "documentation comment")
544 raise QAPIParseError(self
, "Documentation comment must end with '##'")
548 # Semantic analysis of schema expressions
549 # TODO fold into QAPISchema
550 # TODO catching name collisions in generated code would be nice
554 def find_base_members(base
):
555 if isinstance(base
, dict):
557 base_struct_define
= struct_types
.get(base
)
558 if not base_struct_define
:
560 return base_struct_define
['data']
563 # Return the qtype of an alternate branch, or None on error.
564 def find_alternate_member_qtype(qapi_type
):
565 if qapi_type
in builtin_types
:
566 return builtin_types
[qapi_type
]
567 elif qapi_type
in struct_types
:
569 elif qapi_type
in enum_types
:
570 return 'QTYPE_QSTRING'
571 elif qapi_type
in union_types
:
576 # Return the discriminator enum define if discriminator is specified as an
577 # enum type, otherwise return None.
578 def discriminator_find_enum_define(expr
):
579 base
= expr
.get('base')
580 discriminator
= expr
.get('discriminator')
582 if not (discriminator
and base
):
585 base_members
= find_base_members(base
)
589 discriminator_type
= base_members
.get(discriminator
)
590 if not discriminator_type
:
593 return enum_types
.get(discriminator_type
)
596 # Names must be letters, numbers, -, and _. They must start with letter,
597 # except for downstream extensions which must start with __RFQDN_.
598 # Dots are only valid in the downstream extension prefix.
599 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
600 '[a-zA-Z][a-zA-Z0-9_-]*$')
603 def check_name(info
, source
, name
, allow_optional
=False,
608 if not isinstance(name
, str):
609 raise QAPISemError(info
, "%s requires a string name" % source
)
610 if name
.startswith('*'):
611 membername
= name
[1:]
612 if not allow_optional
:
613 raise QAPISemError(info
, "%s does not allow optional name '%s'"
615 # Enum members can start with a digit, because the generated C
616 # code always prefixes it with the enum name
617 if enum_member
and membername
[0].isdigit():
618 membername
= 'D' + membername
619 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
620 # and 'q_obj_*' implicit type names.
621 if not valid_name
.match(membername
) or \
622 c_name(membername
, False).startswith('q_'):
623 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
626 def add_name(name
, info
, meta
, implicit
=False):
628 check_name(info
, "'%s'" % meta
, name
)
629 # FIXME should reject names that differ only in '_' vs. '.'
630 # vs. '-', because they're liable to clash in generated C.
631 if name
in all_names
:
632 raise QAPISemError(info
, "%s '%s' is already defined"
633 % (all_names
[name
], name
))
634 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
635 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
636 % (meta
, name
, name
[-4:]))
637 all_names
[name
] = meta
640 def check_type(info
, source
, value
, allow_array
=False,
641 allow_dict
=False, allow_optional
=False,
648 # Check if array type for value is okay
649 if isinstance(value
, list):
651 raise QAPISemError(info
, "%s cannot be an array" % source
)
652 if len(value
) != 1 or not isinstance(value
[0], str):
653 raise QAPISemError(info
,
654 "%s: array type must contain single type name" %
658 # Check if type name for value is okay
659 if isinstance(value
, str):
660 if value
not in all_names
:
661 raise QAPISemError(info
, "%s uses unknown type '%s'"
663 if not all_names
[value
] in allow_metas
:
664 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
665 (source
, all_names
[value
], value
))
669 raise QAPISemError(info
, "%s should be a type name" % source
)
671 if not isinstance(value
, OrderedDict
):
672 raise QAPISemError(info
,
673 "%s should be a dictionary or type name" % source
)
675 # value is a dictionary, check that each member is okay
676 for (key
, arg
) in value
.items():
677 check_name(info
, "Member of %s" % source
, key
,
678 allow_optional
=allow_optional
)
679 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
680 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
682 # Todo: allow dictionaries to represent default values of
683 # an optional argument.
684 check_type(info
, "Member '%s' of %s" % (key
, source
), arg
,
686 allow_metas
=['built-in', 'union', 'alternate', 'struct',
690 def check_command(expr
, info
):
691 name
= expr
['command']
692 boxed
= expr
.get('boxed', False)
694 args_meta
= ['struct']
696 args_meta
+= ['union', 'alternate']
697 check_type(info
, "'data' for command '%s'" % name
,
698 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
699 allow_metas
=args_meta
)
700 returns_meta
= ['union', 'struct']
701 if name
in returns_whitelist
:
702 returns_meta
+= ['built-in', 'alternate', 'enum']
703 check_type(info
, "'returns' for command '%s'" % name
,
704 expr
.get('returns'), allow_array
=True,
705 allow_optional
=True, allow_metas
=returns_meta
)
708 def check_event(expr
, info
):
710 boxed
= expr
.get('boxed', False)
714 meta
+= ['union', 'alternate']
715 check_type(info
, "'data' for event '%s'" % name
,
716 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
720 def check_union(expr
, info
):
722 base
= expr
.get('base')
723 discriminator
= expr
.get('discriminator')
724 members
= expr
['data']
726 # Two types of unions, determined by discriminator.
728 # With no discriminator it is a simple union.
729 if discriminator
is None:
731 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
733 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
736 # Else, it's a flat union.
738 # The object must have a string or dictionary 'base'.
739 check_type(info
, "'base' for union '%s'" % name
,
740 base
, allow_dict
=True, allow_optional
=True,
741 allow_metas
=['struct'])
743 raise QAPISemError(info
, "Flat union '%s' must have a base"
745 base_members
= find_base_members(base
)
746 assert base_members
is not None
748 # The value of member 'discriminator' must name a non-optional
749 # member of the base struct.
750 check_name(info
, "Discriminator of flat union '%s'" % name
,
752 discriminator_type
= base_members
.get(discriminator
)
753 if not discriminator_type
:
754 raise QAPISemError(info
,
755 "Discriminator '%s' is not a member of base "
757 % (discriminator
, base
))
758 enum_define
= enum_types
.get(discriminator_type
)
759 allow_metas
= ['struct']
760 # Do not allow string discriminator
762 raise QAPISemError(info
,
763 "Discriminator '%s' must be of enumeration "
764 "type" % discriminator
)
766 # Check every branch; don't allow an empty union
767 if len(members
) == 0:
768 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
769 for (key
, value
) in members
.items():
770 check_name(info
, "Member of union '%s'" % name
, key
)
772 # Each value must name a known type
773 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
774 value
, allow_array
=not base
, allow_metas
=allow_metas
)
776 # If the discriminator names an enum type, then all members
777 # of 'data' must also be members of the enum type.
779 if key
not in enum_define
['data']:
780 raise QAPISemError(info
,
781 "Discriminator value '%s' is not found in "
783 % (key
, enum_define
['enum']))
785 # If discriminator is user-defined, ensure all values are covered
787 for value
in enum_define
['data']:
788 if value
not in members
.keys():
789 raise QAPISemError(info
, "Union '%s' data missing '%s' branch"
793 def check_alternate(expr
, info
):
794 name
= expr
['alternate']
795 members
= expr
['data']
798 # Check every branch; require at least two branches
800 raise QAPISemError(info
,
801 "Alternate '%s' should have at least two branches "
803 for (key
, value
) in members
.items():
804 check_name(info
, "Member of alternate '%s'" % name
, key
)
806 # Ensure alternates have no type conflicts.
807 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
),
809 allow_metas
=['built-in', 'union', 'struct', 'enum'])
810 qtype
= find_alternate_member_qtype(value
)
812 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
813 "type '%s'" % (name
, key
, value
))
814 conflicting
= set([qtype
])
815 if qtype
== 'QTYPE_QSTRING':
816 enum_expr
= enum_types
.get(value
)
818 for v
in enum_expr
['data']:
819 if v
in ['on', 'off']:
820 conflicting
.add('QTYPE_QBOOL')
821 if re
.match(r
'[-+0-9.]', v
): # lazy, could be tightened
822 conflicting
.add('QTYPE_QNUM')
824 conflicting
.add('QTYPE_QNUM')
825 conflicting
.add('QTYPE_QBOOL')
826 for qt
in conflicting
:
828 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
829 "be distinguished from member '%s'"
830 % (name
, key
, types_seen
[qt
]))
834 def check_enum(expr
, info
):
836 members
= expr
.get('data')
837 prefix
= expr
.get('prefix')
839 if not isinstance(members
, list):
840 raise QAPISemError(info
,
841 "Enum '%s' requires an array for 'data'" % name
)
842 if prefix
is not None and not isinstance(prefix
, str):
843 raise QAPISemError(info
,
844 "Enum '%s' requires a string for 'prefix'" % name
)
845 for member
in members
:
846 check_name(info
, "Member of enum '%s'" % name
, member
,
850 def check_struct(expr
, info
):
851 name
= expr
['struct']
852 members
= expr
['data']
854 check_type(info
, "'data' for struct '%s'" % name
, members
,
855 allow_dict
=True, allow_optional
=True)
856 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
857 allow_metas
=['struct'])
860 def check_keys(expr_elem
, meta
, required
, optional
=[]):
861 expr
= expr_elem
['expr']
862 info
= expr_elem
['info']
864 if not isinstance(name
, str):
865 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
866 required
= required
+ [meta
]
867 for (key
, value
) in expr
.items():
868 if key
not in required
and key
not in optional
:
869 raise QAPISemError(info
, "Unknown key '%s' in %s '%s'"
871 if (key
== 'gen' or key
== 'success-response') and value
is not False:
872 raise QAPISemError(info
,
873 "'%s' of %s '%s' should only use false value"
875 if key
== 'boxed' and value
is not True:
876 raise QAPISemError(info
,
877 "'%s' of %s '%s' should only use true value"
881 raise QAPISemError(info
, "Key '%s' is missing from %s '%s'"
885 def check_exprs(exprs
):
888 # Populate name table with names of built-in types
889 for builtin
in builtin_types
.keys():
890 all_names
[builtin
] = 'built-in'
892 # Learn the types and check for valid expression keys
893 for expr_elem
in exprs
:
894 expr
= expr_elem
['expr']
895 info
= expr_elem
['info']
896 doc
= expr_elem
.get('doc')
898 if 'include' in expr
:
901 if not doc
and doc_required
:
902 raise QAPISemError(info
,
903 "Expression missing documentation comment")
907 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
908 enum_types
[expr
[meta
]] = expr
909 elif 'union' in expr
:
911 check_keys(expr_elem
, 'union', ['data'],
912 ['base', 'discriminator'])
913 union_types
[expr
[meta
]] = expr
914 elif 'alternate' in expr
:
916 check_keys(expr_elem
, 'alternate', ['data'])
917 elif 'struct' in expr
:
919 check_keys(expr_elem
, 'struct', ['data'], ['base'])
920 struct_types
[expr
[meta
]] = expr
921 elif 'command' in expr
:
923 check_keys(expr_elem
, 'command', [],
924 ['data', 'returns', 'gen', 'success-response',
925 'boxed', 'allow-oob'])
926 elif 'event' in expr
:
928 check_keys(expr_elem
, 'event', [], ['data', 'boxed'])
930 raise QAPISemError(expr_elem
['info'],
931 "Expression is missing metatype")
933 add_name(name
, info
, meta
)
934 if doc
and doc
.symbol
!= name
:
935 raise QAPISemError(info
, "Definition of '%s' follows documentation"
936 " for '%s'" % (name
, doc
.symbol
))
938 # Try again for hidden UnionKind enum
939 for expr_elem
in exprs
:
940 expr
= expr_elem
['expr']
942 if 'include' in expr
:
944 if 'union' in expr
and not discriminator_find_enum_define(expr
):
945 name
= '%sKind' % expr
['union']
946 elif 'alternate' in expr
:
947 name
= '%sKind' % expr
['alternate']
950 enum_types
[name
] = {'enum': name
}
951 add_name(name
, info
, 'enum', implicit
=True)
953 # Validate that exprs make sense
954 for expr_elem
in exprs
:
955 expr
= expr_elem
['expr']
956 info
= expr_elem
['info']
957 doc
= expr_elem
.get('doc')
959 if 'include' in expr
:
962 check_enum(expr
, info
)
963 elif 'union' in expr
:
964 check_union(expr
, info
)
965 elif 'alternate' in expr
:
966 check_alternate(expr
, info
)
967 elif 'struct' in expr
:
968 check_struct(expr
, info
)
969 elif 'command' in expr
:
970 check_command(expr
, info
)
971 elif 'event' in expr
:
972 check_event(expr
, info
)
974 assert False, 'unexpected meta type'
983 # Schema compiler frontend
986 class QAPISchemaEntity(object):
987 def __init__(self
, name
, info
, doc
):
988 assert name
is None or isinstance(name
, str)
991 # For explicitly defined entities, info points to the (explicit)
992 # definition. For builtins (and their arrays), info is None.
993 # For implicitly defined entities, info points to a place that
994 # triggered the implicit definition (there may be more than one
1000 return c_name(self
.name
)
1002 def check(self
, schema
):
1005 def is_implicit(self
):
1006 return not self
.info
1008 def visit(self
, visitor
):
1012 class QAPISchemaVisitor(object):
1013 def visit_begin(self
, schema
):
1016 def visit_end(self
):
1019 def visit_module(self
, fname
):
1022 def visit_needed(self
, entity
):
1023 # Default to visiting everything
1026 def visit_include(self
, fname
, info
):
1029 def visit_builtin_type(self
, name
, info
, json_type
):
1032 def visit_enum_type(self
, name
, info
, values
, prefix
):
1035 def visit_array_type(self
, name
, info
, element_type
):
1038 def visit_object_type(self
, name
, info
, base
, members
, variants
):
1041 def visit_object_type_flat(self
, name
, info
, members
, variants
):
1044 def visit_alternate_type(self
, name
, info
, variants
):
1047 def visit_command(self
, name
, info
, arg_type
, ret_type
,
1048 gen
, success_response
, boxed
, allow_oob
):
1051 def visit_event(self
, name
, info
, arg_type
, boxed
):
1055 class QAPISchemaInclude(QAPISchemaEntity
):
1057 def __init__(self
, fname
, info
):
1058 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1061 def visit(self
, visitor
):
1062 visitor
.visit_include(self
.fname
, self
.info
)
1065 class QAPISchemaType(QAPISchemaEntity
):
1066 # Return the C type for common use.
1067 # For the types we commonly box, this is a pointer type.
1071 # Return the C type to be used in a parameter list.
1072 def c_param_type(self
):
1073 return self
.c_type()
1075 # Return the C type to be used where we suppress boxing.
1076 def c_unboxed_type(self
):
1077 return self
.c_type()
1079 def json_type(self
):
1082 def alternate_qtype(self
):
1084 'null': 'QTYPE_QNULL',
1085 'string': 'QTYPE_QSTRING',
1086 'number': 'QTYPE_QNUM',
1087 'int': 'QTYPE_QNUM',
1088 'boolean': 'QTYPE_QBOOL',
1089 'object': 'QTYPE_QDICT'
1091 return json2qtype
.get(self
.json_type())
1094 if self
.is_implicit():
1099 class QAPISchemaBuiltinType(QAPISchemaType
):
1100 def __init__(self
, name
, json_type
, c_type
):
1101 QAPISchemaType
.__init
__(self
, name
, None, None)
1102 assert not c_type
or isinstance(c_type
, str)
1103 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1105 self
._json
_type
_name
= json_type
1106 self
._c
_type
_name
= c_type
1112 return self
._c
_type
_name
1114 def c_param_type(self
):
1115 if self
.name
== 'str':
1116 return 'const ' + self
._c
_type
_name
1117 return self
._c
_type
_name
1119 def json_type(self
):
1120 return self
._json
_type
_name
1123 return self
.json_type()
1125 def visit(self
, visitor
):
1126 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1129 class QAPISchemaEnumType(QAPISchemaType
):
1130 def __init__(self
, name
, info
, doc
, values
, prefix
):
1131 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1133 assert isinstance(v
, QAPISchemaMember
)
1135 assert prefix
is None or isinstance(prefix
, str)
1136 self
.values
= values
1137 self
.prefix
= prefix
1139 def check(self
, schema
):
1141 for v
in self
.values
:
1142 v
.check_clash(self
.info
, seen
)
1144 self
.doc
.connect_member(v
)
1146 def is_implicit(self
):
1147 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1148 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1151 return c_name(self
.name
)
1153 def member_names(self
):
1154 return [v
.name
for v
in self
.values
]
1156 def json_type(self
):
1159 def visit(self
, visitor
):
1160 visitor
.visit_enum_type(self
.name
, self
.info
,
1161 self
.member_names(), self
.prefix
)
1164 class QAPISchemaArrayType(QAPISchemaType
):
1165 def __init__(self
, name
, info
, element_type
):
1166 QAPISchemaType
.__init
__(self
, name
, info
, None)
1167 assert isinstance(element_type
, str)
1168 self
._element
_type
_name
= element_type
1169 self
.element_type
= None
1171 def check(self
, schema
):
1172 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1173 assert self
.element_type
1175 def is_implicit(self
):
1179 return c_name(self
.name
) + pointer_suffix
1181 def json_type(self
):
1185 elt_doc_type
= self
.element_type
.doc_type()
1186 if not elt_doc_type
:
1188 return 'array of ' + elt_doc_type
1190 def visit(self
, visitor
):
1191 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
1194 class QAPISchemaObjectType(QAPISchemaType
):
1195 def __init__(self
, name
, info
, doc
, base
, local_members
, variants
):
1196 # struct has local_members, optional base, and no variants
1197 # flat union has base, variants, and no local_members
1198 # simple union has local_members, variants, and no base
1199 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1200 assert base
is None or isinstance(base
, str)
1201 for m
in local_members
:
1202 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1204 if variants
is not None:
1205 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1206 variants
.set_owner(name
)
1207 self
._base
_name
= base
1209 self
.local_members
= local_members
1210 self
.variants
= variants
1213 def check(self
, schema
):
1214 if self
.members
is False: # check for cycles
1215 raise QAPISemError(self
.info
,
1216 "Object %s contains itself" % self
.name
)
1219 self
.members
= False # mark as being checked
1220 seen
= OrderedDict()
1222 self
.base
= schema
.lookup_type(self
._base
_name
)
1223 assert isinstance(self
.base
, QAPISchemaObjectType
)
1224 self
.base
.check(schema
)
1225 self
.base
.check_clash(self
.info
, seen
)
1226 for m
in self
.local_members
:
1228 m
.check_clash(self
.info
, seen
)
1230 self
.doc
.connect_member(m
)
1231 self
.members
= seen
.values()
1233 self
.variants
.check(schema
, seen
)
1234 assert self
.variants
.tag_member
in self
.members
1235 self
.variants
.check_clash(self
.info
, seen
)
1239 # Check that the members of this type do not cause duplicate JSON members,
1240 # and update seen to track the members seen so far. Report any errors
1241 # on behalf of info, which is not necessarily self.info
1242 def check_clash(self
, info
, seen
):
1243 assert not self
.variants
# not implemented
1244 for m
in self
.members
:
1245 m
.check_clash(info
, seen
)
1247 def is_implicit(self
):
1248 # See QAPISchema._make_implicit_object_type(), as well as
1249 # _def_predefineds()
1250 return self
.name
.startswith('q_')
1253 assert self
.members
is not None
1254 return not self
.members
and not self
.variants
1257 assert self
.name
!= 'q_empty'
1258 return QAPISchemaType
.c_name(self
)
1261 assert not self
.is_implicit()
1262 return c_name(self
.name
) + pointer_suffix
1264 def c_unboxed_type(self
):
1265 return c_name(self
.name
)
1267 def json_type(self
):
1270 def visit(self
, visitor
):
1271 visitor
.visit_object_type(self
.name
, self
.info
,
1272 self
.base
, self
.local_members
, self
.variants
)
1273 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1274 self
.members
, self
.variants
)
1277 class QAPISchemaMember(object):
1280 def __init__(self
, name
):
1281 assert isinstance(name
, str)
1285 def set_owner(self
, name
):
1286 assert not self
.owner
1289 def check_clash(self
, info
, seen
):
1290 cname
= c_name(self
.name
)
1291 if cname
.lower() != cname
and self
.owner
not in name_case_whitelist
:
1292 raise QAPISemError(info
,
1293 "%s should not use uppercase" % self
.describe())
1295 raise QAPISemError(info
, "%s collides with %s" %
1296 (self
.describe(), seen
[cname
].describe()))
1299 def _pretty_owner(self
):
1301 if owner
.startswith('q_obj_'):
1302 # See QAPISchema._make_implicit_object_type() - reverse the
1303 # mapping there to create a nice human-readable description
1305 if owner
.endswith('-arg'):
1306 return '(parameter of %s)' % owner
[:-4]
1307 elif owner
.endswith('-base'):
1308 return '(base of %s)' % owner
[:-5]
1310 assert owner
.endswith('-wrapper')
1311 # Unreachable and not implemented
1313 if owner
.endswith('Kind'):
1314 # See QAPISchema._make_implicit_enum_type()
1315 return '(branch of %s)' % owner
[:-4]
1316 return '(%s of %s)' % (self
.role
, owner
)
1319 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1322 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1323 def __init__(self
, name
, typ
, optional
):
1324 QAPISchemaMember
.__init
__(self
, name
)
1325 assert isinstance(typ
, str)
1326 assert isinstance(optional
, bool)
1327 self
._type
_name
= typ
1329 self
.optional
= optional
1331 def check(self
, schema
):
1333 self
.type = schema
.lookup_type(self
._type
_name
)
1337 class QAPISchemaObjectTypeVariants(object):
1338 def __init__(self
, tag_name
, tag_member
, variants
):
1339 # Flat unions pass tag_name but not tag_member.
1340 # Simple unions and alternates pass tag_member but not tag_name.
1341 # After check(), tag_member is always set, and tag_name remains
1342 # a reliable witness of being used by a flat union.
1343 assert bool(tag_member
) != bool(tag_name
)
1344 assert (isinstance(tag_name
, str) or
1345 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1346 assert len(variants
) > 0
1348 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1349 self
._tag
_name
= tag_name
1350 self
.tag_member
= tag_member
1351 self
.variants
= variants
1353 def set_owner(self
, name
):
1354 for v
in self
.variants
:
1357 def check(self
, schema
, seen
):
1358 if not self
.tag_member
: # flat union
1359 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1360 assert self
._tag
_name
== self
.tag_member
.name
1361 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1362 for v
in self
.variants
:
1364 # Union names must match enum values; alternate names are
1365 # checked separately. Use 'seen' to tell the two apart.
1367 assert v
.name
in self
.tag_member
.type.member_names()
1368 assert isinstance(v
.type, QAPISchemaObjectType
)
1369 v
.type.check(schema
)
1371 def check_clash(self
, info
, seen
):
1372 for v
in self
.variants
:
1373 # Reset seen map for each variant, since qapi names from one
1374 # branch do not affect another branch
1375 assert isinstance(v
.type, QAPISchemaObjectType
)
1376 v
.type.check_clash(info
, dict(seen
))
1379 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1382 def __init__(self
, name
, typ
):
1383 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1386 class QAPISchemaAlternateType(QAPISchemaType
):
1387 def __init__(self
, name
, info
, doc
, variants
):
1388 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1389 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1390 assert variants
.tag_member
1391 variants
.set_owner(name
)
1392 variants
.tag_member
.set_owner(self
.name
)
1393 self
.variants
= variants
1395 def check(self
, schema
):
1396 self
.variants
.tag_member
.check(schema
)
1397 # Not calling self.variants.check_clash(), because there's nothing
1399 self
.variants
.check(schema
, {})
1400 # Alternate branch names have no relation to the tag enum values;
1401 # so we have to check for potential name collisions ourselves.
1403 for v
in self
.variants
.variants
:
1404 v
.check_clash(self
.info
, seen
)
1406 self
.doc
.connect_member(v
)
1411 return c_name(self
.name
) + pointer_suffix
1413 def json_type(self
):
1416 def visit(self
, visitor
):
1417 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1423 class QAPISchemaCommand(QAPISchemaEntity
):
1424 def __init__(self
, name
, info
, doc
, arg_type
, ret_type
,
1425 gen
, success_response
, boxed
, allow_oob
):
1426 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1427 assert not arg_type
or isinstance(arg_type
, str)
1428 assert not ret_type
or isinstance(ret_type
, str)
1429 self
._arg
_type
_name
= arg_type
1430 self
.arg_type
= None
1431 self
._ret
_type
_name
= ret_type
1432 self
.ret_type
= None
1434 self
.success_response
= success_response
1436 self
.allow_oob
= allow_oob
1438 def check(self
, schema
):
1439 if self
._arg
_type
_name
:
1440 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1441 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1442 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1443 self
.arg_type
.check(schema
)
1445 if self
.arg_type
.is_empty():
1446 raise QAPISemError(self
.info
,
1447 "Cannot use 'boxed' with empty type")
1449 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1450 assert not self
.arg_type
.variants
1452 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1453 if self
._ret
_type
_name
:
1454 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1455 assert isinstance(self
.ret_type
, QAPISchemaType
)
1457 def visit(self
, visitor
):
1458 visitor
.visit_command(self
.name
, self
.info
,
1459 self
.arg_type
, self
.ret_type
,
1460 self
.gen
, self
.success_response
,
1461 self
.boxed
, self
.allow_oob
)
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 if isinstance(data
, OrderedDict
):
1682 data
= self
._make
_implicit
_object
_type
(
1683 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1684 if isinstance(rets
, list):
1685 assert len(rets
) == 1
1686 rets
= self
._make
_array
_type
(rets
[0], info
)
1687 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, data
, rets
,
1688 gen
, success_response
,
1691 def _def_event(self
, expr
, info
, doc
):
1692 name
= expr
['event']
1693 data
= expr
.get('data')
1694 boxed
= expr
.get('boxed', False)
1695 if isinstance(data
, OrderedDict
):
1696 data
= self
._make
_implicit
_object
_type
(
1697 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1698 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, data
, boxed
))
1700 def _def_exprs(self
, exprs
):
1701 for expr_elem
in exprs
:
1702 expr
= expr_elem
['expr']
1703 info
= expr_elem
['info']
1704 doc
= expr_elem
.get('doc')
1706 self
._def
_enum
_type
(expr
, info
, doc
)
1707 elif 'struct' in expr
:
1708 self
._def
_struct
_type
(expr
, info
, doc
)
1709 elif 'union' in expr
:
1710 self
._def
_union
_type
(expr
, info
, doc
)
1711 elif 'alternate' in expr
:
1712 self
._def
_alternate
_type
(expr
, info
, doc
)
1713 elif 'command' in expr
:
1714 self
._def
_command
(expr
, info
, doc
)
1715 elif 'event' in expr
:
1716 self
._def
_event
(expr
, info
, doc
)
1717 elif 'include' in expr
:
1718 self
._def
_include
(expr
, info
, doc
)
1723 for ent
in self
._entity
_list
:
1726 def visit(self
, visitor
):
1727 visitor
.visit_begin(self
)
1729 for entity
in self
._entity
_list
:
1730 if visitor
.visit_needed(entity
):
1731 if entity
.module
!= module
:
1732 module
= entity
.module
1733 visitor
.visit_module(module
)
1734 entity
.visit(visitor
)
1739 # Code generation helpers
1742 def camel_case(name
):
1746 if ch
in ['_', '-']:
1749 new_name
+= ch
.upper()
1752 new_name
+= ch
.lower()
1756 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1757 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1758 # ENUM24_Name -> ENUM24_NAME
1759 def camel_to_upper(value
):
1760 c_fun_str
= c_name(value
, False)
1768 # When c is upper and no '_' appears before, do more checks
1769 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
1770 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1772 elif c_fun_str
[i
- 1].isdigit():
1775 return new_name
.lstrip('_').upper()
1778 def c_enum_const(type_name
, const_name
, prefix
=None):
1779 if prefix
is not None:
1781 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1783 if hasattr(str, 'maketrans'):
1784 c_name_trans
= str.maketrans('.-', '__')
1786 c_name_trans
= string
.maketrans('.-', '__')
1789 # Map @name to a valid C identifier.
1790 # If @protect, avoid returning certain ticklish identifiers (like
1791 # C keywords) by prepending 'q_'.
1793 # Used for converting 'name' from a 'name':'type' qapi definition
1794 # into a generated struct member, as well as converting type names
1795 # into substrings of a generated C function name.
1796 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1797 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1798 def c_name(name
, protect
=True):
1799 # ANSI X3J11/88-090, 3.1.1
1800 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1801 'default', 'do', 'double', 'else', 'enum', 'extern',
1802 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1803 'return', 'short', 'signed', 'sizeof', 'static',
1804 'struct', 'switch', 'typedef', 'union', 'unsigned',
1805 'void', 'volatile', 'while'])
1806 # ISO/IEC 9899:1999, 6.4.1
1807 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1808 # ISO/IEC 9899:2011, 6.4.1
1809 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1810 '_Noreturn', '_Static_assert', '_Thread_local'])
1811 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1813 gcc_words
= set(['asm', 'typeof'])
1814 # C++ ISO/IEC 14882:2003 2.11
1815 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1816 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1817 'namespace', 'new', 'operator', 'private', 'protected',
1818 'public', 'reinterpret_cast', 'static_cast', 'template',
1819 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1820 'using', 'virtual', 'wchar_t',
1821 # alternative representations
1822 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1823 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1824 # namespace pollution:
1825 polluted_words
= set(['unix', 'errno', 'mips', 'sparc'])
1826 name
= name
.translate(c_name_trans
)
1827 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1828 | cpp_words | polluted_words
):
1832 eatspace
= '\033EATSPACE.'
1833 pointer_suffix
= ' *' + eatspace
1836 def genindent(count
):
1838 for _
in range(count
):
1845 def push_indent(indent_amount
=4):
1847 indent_level
+= indent_amount
1850 def pop_indent(indent_amount
=4):
1852 indent_level
-= indent_amount
1855 # Generate @code with @kwds interpolated.
1856 # Obey indent_level, and strip eatspace.
1857 def cgen(code
, **kwds
):
1860 indent
= genindent(indent_level
)
1861 # re.subn() lacks flags support before Python 2.7, use re.compile()
1862 raw
= re
.subn(re
.compile(r
'^.', re
.MULTILINE
),
1863 indent
+ r
'\g<0>', raw
)
1865 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
1868 def mcgen(code
, **kwds
):
1871 return cgen(code
, **kwds
)
1874 def guardname(filename
):
1875 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
).upper()
1878 def guardstart(name
):
1884 name
=guardname(name
))
1890 #endif /* %(name)s */
1892 name
=guardname(name
))
1895 def gen_enum_lookup(name
, values
, prefix
=None):
1898 const QEnumLookup %(c_name)s_lookup = {
1899 .array = (const char *const[]) {
1901 c_name
=c_name(name
))
1902 for value
in values
:
1903 index
= c_enum_const(name
, value
, prefix
)
1905 [%(index)s] = "%(value)s",
1907 index
=index
, value
=value
)
1911 .size = %(max_index)s
1914 max_index
=c_enum_const(name
, '_MAX', prefix
))
1918 def gen_enum(name
, values
, prefix
=None):
1919 # append automatically generated _MAX value
1920 enum_values
= values
+ ['_MAX']
1924 typedef enum %(c_name)s {
1926 c_name
=c_name(name
))
1929 for value
in enum_values
:
1933 c_enum
=c_enum_const(name
, value
, prefix
),
1940 c_name
=c_name(name
))
1944 #define %(c_name)s_str(val) \\
1945 qapi_enum_lookup(&%(c_name)s_lookup, (val))
1947 extern const QEnumLookup %(c_name)s_lookup;
1949 c_name
=c_name(name
))
1953 def build_params(arg_type
, boxed
, extra
):
1960 ret
+= '%s arg' % arg_type
.c_param_type()
1963 assert not arg_type
.variants
1964 for memb
in arg_type
.members
:
1968 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1969 ret
+= '%s %s' % (memb
.type.c_param_type(),
1977 # Accumulate and write output
1980 class QAPIGen(object):
1986 def preamble_add(self
, text
):
1987 self
._preamble
+= text
1989 def add(self
, text
):
1992 def _top(self
, fname
):
1995 def _bottom(self
, fname
):
1998 def write(self
, output_dir
, fname
):
1999 pathname
= os
.path
.join(output_dir
, fname
)
2000 dir = os
.path
.dirname(pathname
)
2004 except os
.error
as e
:
2005 if e
.errno
!= errno
.EEXIST
:
2007 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2008 f
= os
.fdopen(fd
, 'r+')
2009 text
= (self
._top
(fname
) + self
._preamble
+ self
._body
2010 + self
._bottom
(fname
))
2011 oldtext
= f
.read(len(text
) + 1)
2019 class QAPIGenC(QAPIGen
):
2021 def __init__(self
, blurb
, pydoc
):
2022 QAPIGen
.__init
__(self
)
2024 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2027 def _top(self
, fname
):
2029 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2036 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2037 * See the COPYING.LIB file in the top-level directory.
2041 blurb
=self
._blurb
, copyright
=self
._copyright
)
2043 def _bottom(self
, fname
):
2045 /* Dummy declaration to prevent empty .o file */
2046 char dummy_%(name)s;
2051 class QAPIGenH(QAPIGenC
):
2053 def _top(self
, fname
):
2054 return QAPIGenC
._top
(self
, fname
) + guardstart(fname
)
2056 def _bottom(self
, fname
):
2057 return guardend(fname
)
2060 class QAPIGenDoc(QAPIGen
):
2062 def _top(self
, fname
):
2063 return (QAPIGen
._top
(self
, fname
)
2064 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2067 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2069 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2070 self
._prefix
= prefix
2072 self
._genc
= QAPIGenC(blurb
, pydoc
)
2073 self
._genh
= QAPIGenH(blurb
, pydoc
)
2075 def write(self
, output_dir
):
2076 self
._genc
.write(output_dir
, self
._prefix
+ self
._what
+ '.c')
2077 self
._genh
.write(output_dir
, self
._prefix
+ self
._what
+ '.h')
2080 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2082 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2083 self
._prefix
= prefix
2088 self
._main
_module
= None
2090 def _module_basename(self
, what
, name
):
2092 return re
.sub(r
'-', '-builtin-', what
)
2093 basename
= os
.path
.join(os
.path
.dirname(name
),
2094 self
._prefix
+ what
)
2095 if name
== self
._main
_module
:
2097 return basename
+ '-' + os
.path
.splitext(os
.path
.basename(name
))[0]
2099 def _add_module(self
, name
, blurb
):
2100 if self
._main
_module
is None and name
is not None:
2101 self
._main
_module
= name
2102 genc
= QAPIGenC(blurb
, self
._pydoc
)
2103 genh
= QAPIGenH(blurb
, self
._pydoc
)
2104 self
._module
[name
] = (genc
, genh
)
2105 self
._set
_module
(name
)
2107 def _set_module(self
, name
):
2108 self
._genc
, self
._genh
= self
._module
[name
]
2110 def write(self
, output_dir
, opt_builtins
=False):
2111 for name
in self
._module
:
2112 if name
is None and not opt_builtins
:
2114 basename
= self
._module
_basename
(self
._what
, name
)
2115 (genc
, genh
) = self
._module
[name
]
2116 genc
.write(output_dir
, basename
+ '.c')
2117 genh
.write(output_dir
, basename
+ '.h')
2119 def _begin_module(self
, name
):
2122 def visit_module(self
, name
):
2123 if name
in self
._module
:
2124 self
._set
_module
(name
)
2126 self
._add
_module
(name
, self
._blurb
)
2127 self
._begin
_module
(name
)
2129 def visit_include(self
, name
, info
):
2130 basename
= self
._module
_basename
(self
._what
, name
)
2131 self
._genh
.preamble_add(mcgen('''
2132 #include "%(basename)s.h"