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
15 from contextlib
import contextmanager
21 from collections
import OrderedDict
24 'null': 'QTYPE_QNULL',
25 'str': 'QTYPE_QSTRING',
27 'number': 'QTYPE_QNUM',
28 'bool': 'QTYPE_QBOOL',
30 'int16': 'QTYPE_QNUM',
31 'int32': 'QTYPE_QNUM',
32 'int64': 'QTYPE_QNUM',
33 'uint8': 'QTYPE_QNUM',
34 'uint16': 'QTYPE_QNUM',
35 'uint32': 'QTYPE_QNUM',
36 'uint64': 'QTYPE_QNUM',
38 'any': None, # any QType possible, actually
39 'QType': 'QTYPE_QSTRING',
42 # Are documentation comments required?
45 # Whitelist of commands allowed to return a non-dictionary
46 returns_whitelist
= []
48 # Whitelist of entities allowed to violate case conventions
49 name_case_whitelist
= []
57 # Parsing the schema into expressions
61 def error_path(parent
):
64 res
= ('In file included from %s:%d:\n' % (parent
['file'],
65 parent
['line'])) + res
66 parent
= parent
['parent']
70 class QAPIError(Exception):
71 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
72 Exception.__init
__(self
)
80 loc
= '%s:%d' % (self
.fname
, self
.line
)
81 if self
.col
is not None:
82 loc
+= ':%s' % self
.col
83 return error_path(self
.info
) + '%s: %s' % (loc
, self
.msg
)
86 class QAPIParseError(QAPIError
):
87 def __init__(self
, parser
, msg
):
89 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
91 col
= (col
+ 7) % 8 + 1
94 QAPIError
.__init
__(self
, parser
.fname
, parser
.line
, col
,
95 parser
.incl_info
, msg
)
98 class QAPISemError(QAPIError
):
99 def __init__(self
, info
, msg
):
100 QAPIError
.__init
__(self
, info
['file'], info
['line'], None,
104 class QAPIDoc(object):
105 class Section(object):
106 def __init__(self
, name
=None):
107 # optional section name (argument/member or section name)
109 # the list of lines for this section
112 def append(self
, line
):
113 self
.text
+= line
.rstrip() + '\n'
115 class ArgSection(Section
):
116 def __init__(self
, name
):
117 QAPIDoc
.Section
.__init
__(self
, name
)
120 def connect(self
, member
):
123 def __init__(self
, parser
, info
):
124 # self._parser is used to report errors with QAPIParseError. The
125 # resulting error position depends on the state of the parser.
126 # It happens to be the beginning of the comment. More or less
127 # servicable, but action at a distance.
128 self
._parser
= parser
131 self
.body
= QAPIDoc
.Section()
132 # dict mapping parameter name to ArgSection
133 self
.args
= OrderedDict()
136 # the current section
137 self
._section
= self
.body
139 def has_section(self
, name
):
140 """Return True if we have a section with this name."""
141 for i
in self
.sections
:
146 def append(self
, line
):
147 """Parse a comment line and add it to the documentation."""
150 self
._append
_freeform
(line
)
154 raise QAPIParseError(self
._parser
, "Missing space after #")
157 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
158 # recognized, and get silently treated as ordinary text
160 self
._append
_symbol
_line
(line
)
161 elif not self
.body
.text
and line
.startswith('@'):
162 if not line
.endswith(':'):
163 raise QAPIParseError(self
._parser
, "Line should end with :")
164 self
.symbol
= line
[1:-1]
165 # FIXME invalid names other than the empty string aren't flagged
167 raise QAPIParseError(self
._parser
, "Invalid name")
169 self
._append
_freeform
(line
)
171 def end_comment(self
):
174 def _append_symbol_line(self
, line
):
175 name
= line
.split(' ', 1)[0]
177 if name
.startswith('@') and name
.endswith(':'):
178 line
= line
[len(name
)+1:]
179 self
._start
_args
_section
(name
[1:-1])
180 elif name
in ('Returns:', 'Since:',
181 # those are often singular or plural
183 'Example:', 'Examples:',
185 line
= line
[len(name
)+1:]
186 self
._start
_section
(name
[:-1])
188 self
._append
_freeform
(line
)
190 def _start_args_section(self
, name
):
191 # FIXME invalid names other than the empty string aren't flagged
193 raise QAPIParseError(self
._parser
, "Invalid parameter name")
194 if name
in self
.args
:
195 raise QAPIParseError(self
._parser
,
196 "'%s' parameter name duplicated" % name
)
198 raise QAPIParseError(self
._parser
,
199 "'@%s:' can't follow '%s' section"
200 % (name
, self
.sections
[0].name
))
202 self
._section
= QAPIDoc
.ArgSection(name
)
203 self
.args
[name
] = self
._section
205 def _start_section(self
, name
=None):
206 if name
in ('Returns', 'Since') and self
.has_section(name
):
207 raise QAPIParseError(self
._parser
,
208 "Duplicated '%s' section" % name
)
210 self
._section
= QAPIDoc
.Section(name
)
211 self
.sections
.append(self
._section
)
213 def _end_section(self
):
215 text
= self
._section
.text
= self
._section
.text
.strip()
216 if self
._section
.name
and (not text
or text
.isspace()):
217 raise QAPIParseError(self
._parser
, "Empty doc section '%s'"
218 % self
._section
.name
)
221 def _append_freeform(self
, line
):
222 in_arg
= isinstance(self
._section
, QAPIDoc
.ArgSection
)
223 if (in_arg
and self
._section
.text
.endswith('\n\n')
224 and line
and not line
[0].isspace()):
225 self
._start
_section
()
226 if (in_arg
or not self
._section
.name
227 or not self
._section
.name
.startswith('Example')):
229 match
= re
.match(r
'(@\S+:)', line
)
231 raise QAPIParseError(self
._parser
,
232 "'%s' not allowed in free-form documentation"
234 self
._section
.append(line
)
236 def connect_member(self
, member
):
237 if member
.name
not in self
.args
:
238 # Undocumented TODO outlaw
239 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
240 self
.args
[member
.name
].connect(member
)
242 def check_expr(self
, expr
):
243 if self
.has_section('Returns') and 'command' not in expr
:
244 raise QAPISemError(self
.info
,
245 "'Returns:' is only valid for commands")
248 bogus
= [name
for name
, section
in self
.args
.items()
249 if not section
.member
]
253 "The following documented members are not in "
254 "the declaration: %s" % ", ".join(bogus
))
257 class QAPISchemaParser(object):
259 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
261 previously_included
.append(os
.path
.abspath(fp
.name
))
262 self
.incl_info
= incl_info
264 if self
.src
== '' or self
.src
[-1] != '\n':
274 while self
.tok
is not None:
275 info
= {'file': self
.fname
, 'line': self
.line
,
276 'parent': self
.incl_info
}
278 self
.reject_expr_doc(cur_doc
)
279 cur_doc
= self
.get_doc(info
)
280 self
.docs
.append(cur_doc
)
283 expr
= self
.get_expr(False)
284 if 'include' in expr
:
285 self
.reject_expr_doc(cur_doc
)
287 raise QAPISemError(info
, "Invalid 'include' directive")
288 include
= expr
['include']
289 if not isinstance(include
, str):
290 raise QAPISemError(info
,
291 "Value of 'include' must be a string")
292 incl_fname
= os
.path
.join(os
.path
.dirname(self
.fname
),
294 self
.exprs
.append({'expr': {'include': incl_fname
},
296 exprs_include
= self
._include
(include
, info
, incl_fname
,
299 self
.exprs
.extend(exprs_include
.exprs
)
300 self
.docs
.extend(exprs_include
.docs
)
301 elif "pragma" in expr
:
302 self
.reject_expr_doc(cur_doc
)
304 raise QAPISemError(info
, "Invalid 'pragma' directive")
305 pragma
= expr
['pragma']
306 if not isinstance(pragma
, dict):
308 info
, "Value of 'pragma' must be a dictionary")
309 for name
, value
in pragma
.items():
310 self
._pragma
(name
, value
, info
)
312 expr_elem
= {'expr': expr
,
315 if not cur_doc
.symbol
:
317 cur_doc
.info
, "Expression documentation required")
318 expr_elem
['doc'] = cur_doc
319 self
.exprs
.append(expr_elem
)
321 self
.reject_expr_doc(cur_doc
)
324 def reject_expr_doc(doc
):
325 if doc
and doc
.symbol
:
328 "Documentation for '%s' is not followed by the definition"
331 def _include(self
, include
, info
, incl_fname
, previously_included
):
332 incl_abs_fname
= os
.path
.abspath(incl_fname
)
333 # catch inclusion cycle
336 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
337 raise QAPISemError(info
, "Inclusion loop for %s" % include
)
340 # skip multiple include of the same file
341 if incl_abs_fname
in previously_included
:
345 if sys
.version_info
[0] >= 3:
346 fobj
= open(incl_fname
, 'r', encoding
='utf-8')
348 fobj
= open(incl_fname
, 'r')
350 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, incl_fname
))
351 return QAPISchemaParser(fobj
, previously_included
, info
)
353 def _pragma(self
, name
, value
, info
):
354 global doc_required
, returns_whitelist
, name_case_whitelist
355 if name
== 'doc-required':
356 if not isinstance(value
, bool):
357 raise QAPISemError(info
,
358 "Pragma 'doc-required' must be boolean")
360 elif name
== 'returns-whitelist':
361 if (not isinstance(value
, list)
362 or any([not isinstance(elt
, str) for elt
in value
])):
363 raise QAPISemError(info
,
364 "Pragma returns-whitelist must be"
365 " a list of strings")
366 returns_whitelist
= value
367 elif name
== 'name-case-whitelist':
368 if (not isinstance(value
, list)
369 or any([not isinstance(elt
, str) for elt
in value
])):
370 raise QAPISemError(info
,
371 "Pragma name-case-whitelist must be"
372 " a list of strings")
373 name_case_whitelist
= value
375 raise QAPISemError(info
, "Unknown pragma '%s'" % name
)
377 def accept(self
, skip_comment
=True):
379 self
.tok
= self
.src
[self
.cursor
]
380 self
.pos
= self
.cursor
385 if self
.src
[self
.cursor
] == '#':
386 # Start of doc comment
388 self
.cursor
= self
.src
.find('\n', self
.cursor
)
390 self
.val
= self
.src
[self
.pos
:self
.cursor
]
392 elif self
.tok
in '{}:,[]':
394 elif self
.tok
== "'":
398 ch
= self
.src
[self
.cursor
]
401 raise QAPIParseError(self
, 'Missing terminating "\'"')
415 for _
in range(0, 4):
416 ch
= self
.src
[self
.cursor
]
418 if ch
not in '0123456789abcdefABCDEF':
419 raise QAPIParseError(self
,
420 '\\u escape needs 4 '
422 value
= (value
<< 4) + int(ch
, 16)
423 # If Python 2 and 3 didn't disagree so much on
424 # how to handle Unicode, then we could allow
425 # Unicode string defaults. But most of QAPI is
426 # ASCII-only, so we aren't losing much for now.
427 if not value
or value
> 0x7f:
428 raise QAPIParseError(self
,
429 'For now, \\u escape '
430 'only supports non-zero '
431 'values up to \\u007f')
436 raise QAPIParseError(self
,
437 "Unknown escape \\%s" % ch
)
446 elif self
.src
.startswith('true', self
.pos
):
450 elif self
.src
.startswith('false', self
.pos
):
454 elif self
.src
.startswith('null', self
.pos
):
458 elif self
.tok
== '\n':
459 if self
.cursor
== len(self
.src
):
463 self
.line_pos
= self
.cursor
464 elif not self
.tok
.isspace():
465 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
467 def get_members(self
):
473 raise QAPIParseError(self
, 'Expected string or "}"')
478 raise QAPIParseError(self
, 'Expected ":"')
481 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
482 expr
[key
] = self
.get_expr(True)
487 raise QAPIParseError(self
, 'Expected "," or "}"')
490 raise QAPIParseError(self
, 'Expected string')
492 def get_values(self
):
497 if self
.tok
not in "{['tfn":
498 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
501 expr
.append(self
.get_expr(True))
506 raise QAPIParseError(self
, 'Expected "," or "]"')
509 def get_expr(self
, nested
):
510 if self
.tok
!= '{' and not nested
:
511 raise QAPIParseError(self
, 'Expected "{"')
514 expr
= self
.get_members()
515 elif self
.tok
== '[':
517 expr
= self
.get_values()
518 elif self
.tok
in "'tfn":
522 raise QAPIParseError(self
, 'Expected "{", "[", string, '
526 def get_doc(self
, info
):
528 raise QAPIParseError(self
, "Junk after '##' at start of "
529 "documentation comment")
531 doc
= QAPIDoc(self
, info
)
533 while self
.tok
== '#':
534 if self
.val
.startswith('##'):
537 raise QAPIParseError(self
, "Junk after '##' at end of "
538 "documentation comment")
546 raise QAPIParseError(self
, "Documentation comment must end with '##'")
550 # Semantic analysis of schema expressions
551 # TODO fold into QAPISchema
552 # TODO catching name collisions in generated code would be nice
556 def find_base_members(base
):
557 if isinstance(base
, dict):
559 base_struct_define
= struct_types
.get(base
)
560 if not base_struct_define
:
562 return base_struct_define
['data']
565 # Return the qtype of an alternate branch, or None on error.
566 def find_alternate_member_qtype(qapi_type
):
567 if qapi_type
in builtin_types
:
568 return builtin_types
[qapi_type
]
569 elif qapi_type
in struct_types
:
571 elif qapi_type
in enum_types
:
572 return 'QTYPE_QSTRING'
573 elif qapi_type
in union_types
:
578 # Return the discriminator enum define if discriminator is specified as an
579 # enum type, otherwise return None.
580 def discriminator_find_enum_define(expr
):
581 base
= expr
.get('base')
582 discriminator
= expr
.get('discriminator')
584 if not (discriminator
and base
):
587 base_members
= find_base_members(base
)
591 discriminator_type
= base_members
.get(discriminator
)
592 if not discriminator_type
:
595 return enum_types
.get(discriminator_type
)
598 # Names must be letters, numbers, -, and _. They must start with letter,
599 # except for downstream extensions which must start with __RFQDN_.
600 # Dots are only valid in the downstream extension prefix.
601 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
602 '[a-zA-Z][a-zA-Z0-9_-]*$')
605 def check_name(info
, source
, name
, allow_optional
=False,
610 if not isinstance(name
, str):
611 raise QAPISemError(info
, "%s requires a string name" % source
)
612 if name
.startswith('*'):
613 membername
= name
[1:]
614 if not allow_optional
:
615 raise QAPISemError(info
, "%s does not allow optional name '%s'"
617 # Enum members can start with a digit, because the generated C
618 # code always prefixes it with the enum name
619 if enum_member
and membername
[0].isdigit():
620 membername
= 'D' + membername
621 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
622 # and 'q_obj_*' implicit type names.
623 if not valid_name
.match(membername
) or \
624 c_name(membername
, False).startswith('q_'):
625 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
628 def add_name(name
, info
, meta
, implicit
=False):
630 check_name(info
, "'%s'" % meta
, name
)
631 # FIXME should reject names that differ only in '_' vs. '.'
632 # vs. '-', because they're liable to clash in generated C.
633 if name
in all_names
:
634 raise QAPISemError(info
, "%s '%s' is already defined"
635 % (all_names
[name
], name
))
636 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
637 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
638 % (meta
, name
, name
[-4:]))
639 all_names
[name
] = meta
642 def check_if(expr
, info
):
644 def check_if_str(ifcond
, info
):
645 if not isinstance(ifcond
, str):
647 info
, "'if' condition must be a string or a list of strings")
649 raise QAPISemError(info
, "'if' condition '' makes no sense")
651 ifcond
= expr
.get('if')
654 if isinstance(ifcond
, list):
656 raise QAPISemError(info
, "'if' condition [] is useless")
658 check_if_str(elt
, info
)
660 check_if_str(ifcond
, info
)
663 def check_type(info
, source
, value
, allow_array
=False,
664 allow_dict
=False, allow_optional
=False,
671 # Check if array type for value is okay
672 if isinstance(value
, list):
674 raise QAPISemError(info
, "%s cannot be an array" % source
)
675 if len(value
) != 1 or not isinstance(value
[0], str):
676 raise QAPISemError(info
,
677 "%s: array type must contain single type name" %
681 # Check if type name for value is okay
682 if isinstance(value
, str):
683 if value
not in all_names
:
684 raise QAPISemError(info
, "%s uses unknown type '%s'"
686 if not all_names
[value
] in allow_metas
:
687 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
688 (source
, all_names
[value
], value
))
692 raise QAPISemError(info
, "%s should be a type name" % source
)
694 if not isinstance(value
, OrderedDict
):
695 raise QAPISemError(info
,
696 "%s should be a dictionary or type name" % source
)
698 # value is a dictionary, check that each member is okay
699 for (key
, arg
) in value
.items():
700 check_name(info
, "Member of %s" % source
, key
,
701 allow_optional
=allow_optional
)
702 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
703 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
705 # Todo: allow dictionaries to represent default values of
706 # an optional argument.
707 check_type(info
, "Member '%s' of %s" % (key
, source
), arg
,
709 allow_metas
=['built-in', 'union', 'alternate', 'struct',
713 def check_command(expr
, info
):
714 name
= expr
['command']
715 boxed
= expr
.get('boxed', False)
717 args_meta
= ['struct']
719 args_meta
+= ['union', 'alternate']
720 check_type(info
, "'data' for command '%s'" % name
,
721 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
722 allow_metas
=args_meta
)
723 returns_meta
= ['union', 'struct']
724 if name
in returns_whitelist
:
725 returns_meta
+= ['built-in', 'alternate', 'enum']
726 check_type(info
, "'returns' for command '%s'" % name
,
727 expr
.get('returns'), allow_array
=True,
728 allow_optional
=True, allow_metas
=returns_meta
)
731 def check_event(expr
, info
):
733 boxed
= expr
.get('boxed', False)
737 meta
+= ['union', 'alternate']
738 check_type(info
, "'data' for event '%s'" % name
,
739 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
743 def enum_get_names(expr
):
744 return [e
['name'] for e
in expr
['data']]
747 def check_union(expr
, info
):
749 base
= expr
.get('base')
750 discriminator
= expr
.get('discriminator')
751 members
= expr
['data']
753 # Two types of unions, determined by discriminator.
755 # With no discriminator it is a simple union.
756 if discriminator
is None:
758 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
760 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
763 # Else, it's a flat union.
765 # The object must have a string or dictionary 'base'.
766 check_type(info
, "'base' for union '%s'" % name
,
767 base
, allow_dict
=True, allow_optional
=True,
768 allow_metas
=['struct'])
770 raise QAPISemError(info
, "Flat union '%s' must have a base"
772 base_members
= find_base_members(base
)
773 assert base_members
is not None
775 # The value of member 'discriminator' must name a non-optional
776 # member of the base struct.
777 check_name(info
, "Discriminator of flat union '%s'" % name
,
779 discriminator_type
= base_members
.get(discriminator
)
780 if not discriminator_type
:
781 raise QAPISemError(info
,
782 "Discriminator '%s' is not a member of base "
784 % (discriminator
, base
))
785 enum_define
= enum_types
.get(discriminator_type
)
786 allow_metas
= ['struct']
787 # Do not allow string discriminator
789 raise QAPISemError(info
,
790 "Discriminator '%s' must be of enumeration "
791 "type" % discriminator
)
793 # Check every branch; don't allow an empty union
794 if len(members
) == 0:
795 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
796 for (key
, value
) in members
.items():
797 check_name(info
, "Member of union '%s'" % name
, key
)
799 # Each value must name a known type
800 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
801 value
, allow_array
=not base
, allow_metas
=allow_metas
)
803 # If the discriminator names an enum type, then all members
804 # of 'data' must also be members of the enum type.
806 if key
not in enum_get_names(enum_define
):
807 raise QAPISemError(info
,
808 "Discriminator value '%s' is not found in "
810 % (key
, enum_define
['enum']))
813 def check_alternate(expr
, info
):
814 name
= expr
['alternate']
815 members
= expr
['data']
818 # Check every branch; require at least two branches
820 raise QAPISemError(info
,
821 "Alternate '%s' should have at least two branches "
823 for (key
, value
) in members
.items():
824 check_name(info
, "Member of alternate '%s'" % name
, key
)
826 # Ensure alternates have no type conflicts.
827 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
),
829 allow_metas
=['built-in', 'union', 'struct', 'enum'])
830 qtype
= find_alternate_member_qtype(value
)
832 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
833 "type '%s'" % (name
, key
, value
))
834 conflicting
= set([qtype
])
835 if qtype
== 'QTYPE_QSTRING':
836 enum_expr
= enum_types
.get(value
)
838 for v
in enum_get_names(enum_expr
):
839 if v
in ['on', 'off']:
840 conflicting
.add('QTYPE_QBOOL')
841 if re
.match(r
'[-+0-9.]', v
): # lazy, could be tightened
842 conflicting
.add('QTYPE_QNUM')
844 conflicting
.add('QTYPE_QNUM')
845 conflicting
.add('QTYPE_QBOOL')
846 for qt
in conflicting
:
848 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
849 "be distinguished from member '%s'"
850 % (name
, key
, types_seen
[qt
]))
854 def normalize_enum(expr
):
855 if isinstance(expr
['data'], list):
856 expr
['data'] = [m
if isinstance(m
, dict) else {'name': m
}
857 for m
in expr
['data']]
860 def check_enum(expr
, info
):
862 members
= expr
['data']
863 prefix
= expr
.get('prefix')
865 if not isinstance(members
, list):
866 raise QAPISemError(info
,
867 "Enum '%s' requires an array for 'data'" % name
)
868 if prefix
is not None and not isinstance(prefix
, str):
869 raise QAPISemError(info
,
870 "Enum '%s' requires a string for 'prefix'" % name
)
872 for member
in members
:
873 source
= "dictionary member of enum '%s'" % name
874 check_known_keys(info
, source
, member
, ['name'], ['if'])
875 check_if(member
, info
)
876 check_name(info
, "Member of enum '%s'" % name
, member
['name'],
880 def check_struct(expr
, info
):
881 name
= expr
['struct']
882 members
= expr
['data']
884 check_type(info
, "'data' for struct '%s'" % name
, members
,
885 allow_dict
=True, allow_optional
=True)
886 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
887 allow_metas
=['struct'])
890 def check_known_keys(info
, source
, keys
, required
, optional
):
893 return ', '.join("'" + e
+ "'" for e
in sorted(elems
))
895 missing
= set(required
) - set(keys
)
897 raise QAPISemError(info
, "Key%s %s %s missing from %s"
898 % ('s' if len(missing
) > 1 else '', pprint(missing
),
899 'are' if len(missing
) > 1 else 'is', source
))
900 allowed
= set(required
+ optional
)
901 unknown
= set(keys
) - allowed
903 raise QAPISemError(info
, "Unknown key%s %s in %s\nValid keys are %s."
904 % ('s' if len(unknown
) > 1 else '', pprint(unknown
),
905 source
, pprint(allowed
)))
908 def check_keys(expr_elem
, meta
, required
, optional
=[]):
909 expr
= expr_elem
['expr']
910 info
= expr_elem
['info']
912 if not isinstance(name
, str):
913 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
914 required
= required
+ [meta
]
915 source
= "%s '%s'" % (meta
, name
)
916 check_known_keys(info
, source
, expr
.keys(), required
, optional
)
917 for (key
, value
) in expr
.items():
918 if key
in ['gen', 'success-response'] and value
is not False:
919 raise QAPISemError(info
,
920 "'%s' of %s '%s' should only use false value"
922 if (key
in ['boxed', 'allow-oob', 'allow-preconfig']
923 and value
is not True):
924 raise QAPISemError(info
,
925 "'%s' of %s '%s' should only use true value"
931 def check_exprs(exprs
):
934 # Populate name table with names of built-in types
935 for builtin
in builtin_types
.keys():
936 all_names
[builtin
] = 'built-in'
938 # Learn the types and check for valid expression keys
939 for expr_elem
in exprs
:
940 expr
= expr_elem
['expr']
941 info
= expr_elem
['info']
942 doc
= expr_elem
.get('doc')
944 if 'include' in expr
:
947 if not doc
and doc_required
:
948 raise QAPISemError(info
,
949 "Expression missing documentation comment")
953 check_keys(expr_elem
, 'enum', ['data'], ['if', 'prefix'])
955 enum_types
[expr
[meta
]] = expr
956 elif 'union' in expr
:
958 check_keys(expr_elem
, 'union', ['data'],
959 ['base', 'discriminator', 'if'])
960 union_types
[expr
[meta
]] = expr
961 elif 'alternate' in expr
:
963 check_keys(expr_elem
, 'alternate', ['data'], ['if'])
964 elif 'struct' in expr
:
966 check_keys(expr_elem
, 'struct', ['data'], ['base', 'if'])
967 struct_types
[expr
[meta
]] = expr
968 elif 'command' in expr
:
970 check_keys(expr_elem
, 'command', [],
971 ['data', 'returns', 'gen', 'success-response',
972 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
973 elif 'event' in expr
:
975 check_keys(expr_elem
, 'event', [], ['data', 'boxed', 'if'])
977 raise QAPISemError(expr_elem
['info'],
978 "Expression is missing metatype")
980 add_name(name
, info
, meta
)
981 if doc
and doc
.symbol
!= name
:
982 raise QAPISemError(info
, "Definition of '%s' follows documentation"
983 " for '%s'" % (name
, doc
.symbol
))
985 # Try again for hidden UnionKind enum
986 for expr_elem
in exprs
:
987 expr
= expr_elem
['expr']
989 if 'include' in expr
:
991 if 'union' in expr
and not discriminator_find_enum_define(expr
):
992 name
= '%sKind' % expr
['union']
993 elif 'alternate' in expr
:
994 name
= '%sKind' % expr
['alternate']
997 enum_types
[name
] = {'enum': name
}
998 add_name(name
, info
, 'enum', implicit
=True)
1000 # Validate that exprs make sense
1001 for expr_elem
in exprs
:
1002 expr
= expr_elem
['expr']
1003 info
= expr_elem
['info']
1004 doc
= expr_elem
.get('doc')
1006 if 'include' in expr
:
1009 check_enum(expr
, info
)
1010 elif 'union' in expr
:
1011 check_union(expr
, info
)
1012 elif 'alternate' in expr
:
1013 check_alternate(expr
, info
)
1014 elif 'struct' in expr
:
1015 check_struct(expr
, info
)
1016 elif 'command' in expr
:
1017 check_command(expr
, info
)
1018 elif 'event' in expr
:
1019 check_event(expr
, info
)
1021 assert False, 'unexpected meta type'
1024 doc
.check_expr(expr
)
1030 # Schema compiler frontend
1033 def listify_cond(ifcond
):
1036 if not isinstance(ifcond
, list):
1041 class QAPISchemaEntity(object):
1042 def __init__(self
, name
, info
, doc
, ifcond
=None):
1043 assert name
is None or isinstance(name
, str)
1046 # For explicitly defined entities, info points to the (explicit)
1047 # definition. For builtins (and their arrays), info is None.
1048 # For implicitly defined entities, info points to a place that
1049 # triggered the implicit definition (there may be more than one
1053 self
._ifcond
= ifcond
# self.ifcond is set only after .check()
1056 return c_name(self
.name
)
1058 def check(self
, schema
):
1059 if isinstance(self
._ifcond
, QAPISchemaType
):
1060 # inherit the condition from a type
1063 self
.ifcond
= typ
.ifcond
1065 self
.ifcond
= listify_cond(self
._ifcond
)
1067 def is_implicit(self
):
1068 return not self
.info
1070 def visit(self
, visitor
):
1074 class QAPISchemaVisitor(object):
1075 def visit_begin(self
, schema
):
1078 def visit_end(self
):
1081 def visit_module(self
, fname
):
1084 def visit_needed(self
, entity
):
1085 # Default to visiting everything
1088 def visit_include(self
, fname
, info
):
1091 def visit_builtin_type(self
, name
, info
, json_type
):
1094 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
1097 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
1100 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
):
1103 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
):
1106 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
1109 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
1110 success_response
, boxed
, allow_oob
, allow_preconfig
):
1113 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
1117 class QAPISchemaInclude(QAPISchemaEntity
):
1119 def __init__(self
, fname
, info
):
1120 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1123 def visit(self
, visitor
):
1124 visitor
.visit_include(self
.fname
, self
.info
)
1127 class QAPISchemaType(QAPISchemaEntity
):
1128 # Return the C type for common use.
1129 # For the types we commonly box, this is a pointer type.
1133 # Return the C type to be used in a parameter list.
1134 def c_param_type(self
):
1135 return self
.c_type()
1137 # Return the C type to be used where we suppress boxing.
1138 def c_unboxed_type(self
):
1139 return self
.c_type()
1141 def json_type(self
):
1144 def alternate_qtype(self
):
1146 'null': 'QTYPE_QNULL',
1147 'string': 'QTYPE_QSTRING',
1148 'number': 'QTYPE_QNUM',
1149 'int': 'QTYPE_QNUM',
1150 'boolean': 'QTYPE_QBOOL',
1151 'object': 'QTYPE_QDICT'
1153 return json2qtype
.get(self
.json_type())
1156 if self
.is_implicit():
1161 class QAPISchemaBuiltinType(QAPISchemaType
):
1162 def __init__(self
, name
, json_type
, c_type
):
1163 QAPISchemaType
.__init
__(self
, name
, None, None)
1164 assert not c_type
or isinstance(c_type
, str)
1165 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1167 self
._json
_type
_name
= json_type
1168 self
._c
_type
_name
= c_type
1174 return self
._c
_type
_name
1176 def c_param_type(self
):
1177 if self
.name
== 'str':
1178 return 'const ' + self
._c
_type
_name
1179 return self
._c
_type
_name
1181 def json_type(self
):
1182 return self
._json
_type
_name
1185 return self
.json_type()
1187 def visit(self
, visitor
):
1188 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1191 class QAPISchemaEnumType(QAPISchemaType
):
1192 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
1193 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1195 assert isinstance(m
, QAPISchemaMember
)
1197 assert prefix
is None or isinstance(prefix
, str)
1198 self
.members
= members
1199 self
.prefix
= prefix
1201 def check(self
, schema
):
1202 QAPISchemaType
.check(self
, schema
)
1204 for m
in self
.members
:
1205 m
.check_clash(self
.info
, seen
)
1207 self
.doc
.connect_member(m
)
1209 def is_implicit(self
):
1210 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1211 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1214 return c_name(self
.name
)
1216 def member_names(self
):
1217 return [m
.name
for m
in self
.members
]
1219 def json_type(self
):
1222 def visit(self
, visitor
):
1223 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
1224 self
.members
, self
.prefix
)
1227 class QAPISchemaArrayType(QAPISchemaType
):
1228 def __init__(self
, name
, info
, element_type
):
1229 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
1230 assert isinstance(element_type
, str)
1231 self
._element
_type
_name
= element_type
1232 self
.element_type
= None
1234 def check(self
, schema
):
1235 QAPISchemaType
.check(self
, schema
)
1236 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1237 assert self
.element_type
1238 self
.element_type
.check(schema
)
1239 self
.ifcond
= self
.element_type
.ifcond
1241 def is_implicit(self
):
1245 return c_name(self
.name
) + pointer_suffix
1247 def json_type(self
):
1251 elt_doc_type
= self
.element_type
.doc_type()
1252 if not elt_doc_type
:
1254 return 'array of ' + elt_doc_type
1256 def visit(self
, visitor
):
1257 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
1261 class QAPISchemaObjectType(QAPISchemaType
):
1262 def __init__(self
, name
, info
, doc
, ifcond
,
1263 base
, local_members
, variants
):
1264 # struct has local_members, optional base, and no variants
1265 # flat union has base, variants, and no local_members
1266 # simple union has local_members, variants, and no base
1267 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1268 assert base
is None or isinstance(base
, str)
1269 for m
in local_members
:
1270 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1272 if variants
is not None:
1273 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1274 variants
.set_owner(name
)
1275 self
._base
_name
= base
1277 self
.local_members
= local_members
1278 self
.variants
= variants
1281 def check(self
, schema
):
1282 QAPISchemaType
.check(self
, schema
)
1283 if self
.members
is False: # check for cycles
1284 raise QAPISemError(self
.info
,
1285 "Object %s contains itself" % self
.name
)
1288 self
.members
= False # mark as being checked
1289 seen
= OrderedDict()
1291 self
.base
= schema
.lookup_type(self
._base
_name
)
1292 assert isinstance(self
.base
, QAPISchemaObjectType
)
1293 self
.base
.check(schema
)
1294 self
.base
.check_clash(self
.info
, seen
)
1295 for m
in self
.local_members
:
1297 m
.check_clash(self
.info
, seen
)
1299 self
.doc
.connect_member(m
)
1300 self
.members
= seen
.values()
1302 self
.variants
.check(schema
, seen
)
1303 assert self
.variants
.tag_member
in self
.members
1304 self
.variants
.check_clash(self
.info
, seen
)
1308 # Check that the members of this type do not cause duplicate JSON members,
1309 # and update seen to track the members seen so far. Report any errors
1310 # on behalf of info, which is not necessarily self.info
1311 def check_clash(self
, info
, seen
):
1312 assert not self
.variants
# not implemented
1313 for m
in self
.members
:
1314 m
.check_clash(info
, seen
)
1316 def is_implicit(self
):
1317 # See QAPISchema._make_implicit_object_type(), as well as
1318 # _def_predefineds()
1319 return self
.name
.startswith('q_')
1322 assert self
.members
is not None
1323 return not self
.members
and not self
.variants
1326 assert self
.name
!= 'q_empty'
1327 return QAPISchemaType
.c_name(self
)
1330 assert not self
.is_implicit()
1331 return c_name(self
.name
) + pointer_suffix
1333 def c_unboxed_type(self
):
1334 return c_name(self
.name
)
1336 def json_type(self
):
1339 def visit(self
, visitor
):
1340 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
1341 self
.base
, self
.local_members
, self
.variants
)
1342 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
1343 self
.members
, self
.variants
)
1346 class QAPISchemaMember(object):
1349 def __init__(self
, name
, ifcond
=None):
1350 assert isinstance(name
, str)
1352 self
.ifcond
= listify_cond(ifcond
)
1355 def set_owner(self
, name
):
1356 assert not self
.owner
1359 def check_clash(self
, info
, seen
):
1360 cname
= c_name(self
.name
)
1361 if cname
.lower() != cname
and self
.owner
not in name_case_whitelist
:
1362 raise QAPISemError(info
,
1363 "%s should not use uppercase" % self
.describe())
1365 raise QAPISemError(info
, "%s collides with %s" %
1366 (self
.describe(), seen
[cname
].describe()))
1369 def _pretty_owner(self
):
1371 if owner
.startswith('q_obj_'):
1372 # See QAPISchema._make_implicit_object_type() - reverse the
1373 # mapping there to create a nice human-readable description
1375 if owner
.endswith('-arg'):
1376 return '(parameter of %s)' % owner
[:-4]
1377 elif owner
.endswith('-base'):
1378 return '(base of %s)' % owner
[:-5]
1380 assert owner
.endswith('-wrapper')
1381 # Unreachable and not implemented
1383 if owner
.endswith('Kind'):
1384 # See QAPISchema._make_implicit_enum_type()
1385 return '(branch of %s)' % owner
[:-4]
1386 return '(%s of %s)' % (self
.role
, owner
)
1389 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1392 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1393 def __init__(self
, name
, typ
, optional
):
1394 QAPISchemaMember
.__init
__(self
, name
)
1395 assert isinstance(typ
, str)
1396 assert isinstance(optional
, bool)
1397 self
._type
_name
= typ
1399 self
.optional
= optional
1401 def check(self
, schema
):
1403 self
.type = schema
.lookup_type(self
._type
_name
)
1407 class QAPISchemaObjectTypeVariants(object):
1408 def __init__(self
, tag_name
, tag_member
, variants
):
1409 # Flat unions pass tag_name but not tag_member.
1410 # Simple unions and alternates pass tag_member but not tag_name.
1411 # After check(), tag_member is always set, and tag_name remains
1412 # a reliable witness of being used by a flat union.
1413 assert bool(tag_member
) != bool(tag_name
)
1414 assert (isinstance(tag_name
, str) or
1415 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1416 assert len(variants
) > 0
1418 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1419 self
._tag
_name
= tag_name
1420 self
.tag_member
= tag_member
1421 self
.variants
= variants
1423 def set_owner(self
, name
):
1424 for v
in self
.variants
:
1427 def check(self
, schema
, seen
):
1428 if not self
.tag_member
: # flat union
1429 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1430 assert self
._tag
_name
== self
.tag_member
.name
1431 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1432 if self
._tag
_name
: # flat union
1433 # branches that are not explicitly covered get an empty type
1434 cases
= set([v
.name
for v
in self
.variants
])
1435 for m
in self
.tag_member
.type.members
:
1436 if m
.name
not in cases
:
1437 v
= QAPISchemaObjectTypeVariant(m
.name
, 'q_empty')
1438 v
.set_owner(self
.tag_member
.owner
)
1439 self
.variants
.append(v
)
1440 for v
in self
.variants
:
1442 # Union names must match enum values; alternate names are
1443 # checked separately. Use 'seen' to tell the two apart.
1445 assert v
.name
in self
.tag_member
.type.member_names()
1446 assert isinstance(v
.type, QAPISchemaObjectType
)
1447 v
.type.check(schema
)
1449 def check_clash(self
, info
, seen
):
1450 for v
in self
.variants
:
1451 # Reset seen map for each variant, since qapi names from one
1452 # branch do not affect another branch
1453 assert isinstance(v
.type, QAPISchemaObjectType
)
1454 v
.type.check_clash(info
, dict(seen
))
1457 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1460 def __init__(self
, name
, typ
):
1461 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1464 class QAPISchemaAlternateType(QAPISchemaType
):
1465 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
1466 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1467 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1468 assert variants
.tag_member
1469 variants
.set_owner(name
)
1470 variants
.tag_member
.set_owner(self
.name
)
1471 self
.variants
= variants
1473 def check(self
, schema
):
1474 QAPISchemaType
.check(self
, schema
)
1475 self
.variants
.tag_member
.check(schema
)
1476 # Not calling self.variants.check_clash(), because there's nothing
1478 self
.variants
.check(schema
, {})
1479 # Alternate branch names have no relation to the tag enum values;
1480 # so we have to check for potential name collisions ourselves.
1482 for v
in self
.variants
.variants
:
1483 v
.check_clash(self
.info
, seen
)
1485 self
.doc
.connect_member(v
)
1490 return c_name(self
.name
) + pointer_suffix
1492 def json_type(self
):
1495 def visit(self
, visitor
):
1496 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
1503 class QAPISchemaCommand(QAPISchemaEntity
):
1504 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
1505 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
1506 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1507 assert not arg_type
or isinstance(arg_type
, str)
1508 assert not ret_type
or isinstance(ret_type
, str)
1509 self
._arg
_type
_name
= arg_type
1510 self
.arg_type
= None
1511 self
._ret
_type
_name
= ret_type
1512 self
.ret_type
= None
1514 self
.success_response
= success_response
1516 self
.allow_oob
= allow_oob
1517 self
.allow_preconfig
= allow_preconfig
1519 def check(self
, schema
):
1520 QAPISchemaEntity
.check(self
, schema
)
1521 if self
._arg
_type
_name
:
1522 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1523 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1524 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1525 self
.arg_type
.check(schema
)
1527 if self
.arg_type
.is_empty():
1528 raise QAPISemError(self
.info
,
1529 "Cannot use 'boxed' with empty type")
1531 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1532 assert not self
.arg_type
.variants
1534 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1535 if self
._ret
_type
_name
:
1536 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1537 assert isinstance(self
.ret_type
, QAPISchemaType
)
1539 def visit(self
, visitor
):
1540 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
1541 self
.arg_type
, self
.ret_type
,
1542 self
.gen
, self
.success_response
,
1543 self
.boxed
, self
.allow_oob
,
1544 self
.allow_preconfig
)
1547 class QAPISchemaEvent(QAPISchemaEntity
):
1548 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
1549 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1550 assert not arg_type
or isinstance(arg_type
, str)
1551 self
._arg
_type
_name
= arg_type
1552 self
.arg_type
= None
1555 def check(self
, schema
):
1556 QAPISchemaEntity
.check(self
, schema
)
1557 if self
._arg
_type
_name
:
1558 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1559 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1560 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1561 self
.arg_type
.check(schema
)
1563 if self
.arg_type
.is_empty():
1564 raise QAPISemError(self
.info
,
1565 "Cannot use 'boxed' with empty type")
1567 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1568 assert not self
.arg_type
.variants
1570 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1572 def visit(self
, visitor
):
1573 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
1574 self
.arg_type
, self
.boxed
)
1577 class QAPISchema(object):
1578 def __init__(self
, fname
):
1580 if sys
.version_info
[0] >= 3:
1581 f
= open(fname
, 'r', encoding
='utf-8')
1583 f
= open(fname
, 'r')
1584 parser
= QAPISchemaParser(f
)
1585 exprs
= check_exprs(parser
.exprs
)
1586 self
.docs
= parser
.docs
1587 self
._entity
_list
= []
1588 self
._entity
_dict
= {}
1589 self
._predefining
= True
1590 self
._def
_predefineds
()
1591 self
._predefining
= False
1592 self
._def
_exprs
(exprs
)
1595 def _def_entity(self
, ent
):
1596 # Only the predefined types are allowed to not have info
1597 assert ent
.info
or self
._predefining
1598 assert ent
.name
is None or ent
.name
not in self
._entity
_dict
1599 self
._entity
_list
.append(ent
)
1600 if ent
.name
is not None:
1601 self
._entity
_dict
[ent
.name
] = ent
1603 ent
.module
= os
.path
.relpath(ent
.info
['file'],
1604 os
.path
.dirname(self
._fname
))
1606 def lookup_entity(self
, name
, typ
=None):
1607 ent
= self
._entity
_dict
.get(name
)
1608 if typ
and not isinstance(ent
, typ
):
1612 def lookup_type(self
, name
):
1613 return self
.lookup_entity(name
, QAPISchemaType
)
1615 def _def_include(self
, expr
, info
, doc
):
1616 include
= expr
['include']
1619 while main_info
['parent']:
1620 main_info
= main_info
['parent']
1621 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
['file']))
1622 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
1624 def _def_builtin_type(self
, name
, json_type
, c_type
):
1625 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1626 # Instantiating only the arrays that are actually used would
1627 # be nice, but we can't as long as their generated code
1628 # (qapi-builtin-types.[ch]) may be shared by some other
1630 self
._make
_array
_type
(name
, None)
1632 def _def_predefineds(self
):
1633 for t
in [('str', 'string', 'char' + pointer_suffix
),
1634 ('number', 'number', 'double'),
1635 ('int', 'int', 'int64_t'),
1636 ('int8', 'int', 'int8_t'),
1637 ('int16', 'int', 'int16_t'),
1638 ('int32', 'int', 'int32_t'),
1639 ('int64', 'int', 'int64_t'),
1640 ('uint8', 'int', 'uint8_t'),
1641 ('uint16', 'int', 'uint16_t'),
1642 ('uint32', 'int', 'uint32_t'),
1643 ('uint64', 'int', 'uint64_t'),
1644 ('size', 'int', 'uint64_t'),
1645 ('bool', 'boolean', 'bool'),
1646 ('any', 'value', 'QObject' + pointer_suffix
),
1647 ('null', 'null', 'QNull' + pointer_suffix
)]:
1648 self
._def
_builtin
_type
(*t
)
1649 self
.the_empty_object_type
= QAPISchemaObjectType(
1650 'q_empty', None, None, None, None, [], None)
1651 self
._def
_entity
(self
.the_empty_object_type
)
1653 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1655 qtype_values
= self
._make
_enum
_members
([{'name': n
} for n
in qtypes
])
1657 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
1658 qtype_values
, 'QTYPE'))
1660 def _make_enum_members(self
, values
):
1661 return [QAPISchemaMember(v
['name'], v
.get('if')) for v
in values
]
1663 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
1664 # See also QAPISchemaObjectTypeMember._pretty_owner()
1665 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1666 self
._def
_entity
(QAPISchemaEnumType(
1667 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
), None))
1670 def _make_array_type(self
, element_type
, info
):
1671 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1672 if not self
.lookup_type(name
):
1673 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1676 def _make_implicit_object_type(self
, name
, info
, doc
, ifcond
,
1680 # See also QAPISchemaObjectTypeMember._pretty_owner()
1681 name
= 'q_obj_%s-%s' % (name
, role
)
1682 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1684 # The implicit object type has multiple users. This can
1685 # happen only for simple unions' implicit wrapper types.
1686 # Its ifcond should be the disjunction of its user's
1687 # ifconds. Not implemented. Instead, we always pass the
1688 # wrapped type's ifcond, which is trivially the same for all
1689 # users. It's also necessary for the wrapper to compile.
1690 # But it's not tight: the disjunction need not imply it. We
1691 # may end up compiling useless wrapper types.
1692 # TODO kill simple unions or implement the disjunction
1693 assert ifcond
== typ
._ifcond
# pylint: disable=protected-access
1695 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
,
1696 None, members
, None))
1699 def _def_enum_type(self
, expr
, info
, doc
):
1702 prefix
= expr
.get('prefix')
1703 ifcond
= expr
.get('if')
1704 self
._def
_entity
(QAPISchemaEnumType(
1705 name
, info
, doc
, ifcond
,
1706 self
._make
_enum
_members
(data
), prefix
))
1708 def _make_member(self
, name
, typ
, info
):
1710 if name
.startswith('*'):
1713 if isinstance(typ
, list):
1714 assert len(typ
) == 1
1715 typ
= self
._make
_array
_type
(typ
[0], info
)
1716 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1718 def _make_members(self
, data
, info
):
1719 return [self
._make
_member
(key
, value
, info
)
1720 for (key
, value
) in data
.items()]
1722 def _def_struct_type(self
, expr
, info
, doc
):
1723 name
= expr
['struct']
1724 base
= expr
.get('base')
1726 ifcond
= expr
.get('if')
1727 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
,
1728 self
._make
_members
(data
, info
),
1731 def _make_variant(self
, case
, typ
):
1732 return QAPISchemaObjectTypeVariant(case
, typ
)
1734 def _make_simple_variant(self
, case
, typ
, info
):
1735 if isinstance(typ
, list):
1736 assert len(typ
) == 1
1737 typ
= self
._make
_array
_type
(typ
[0], info
)
1738 typ
= self
._make
_implicit
_object
_type
(
1739 typ
, info
, None, self
.lookup_type(typ
),
1740 'wrapper', [self
._make
_member
('data', typ
, info
)])
1741 return QAPISchemaObjectTypeVariant(case
, typ
)
1743 def _def_union_type(self
, expr
, info
, doc
):
1744 name
= expr
['union']
1746 base
= expr
.get('base')
1747 ifcond
= expr
.get('if')
1748 tag_name
= expr
.get('discriminator')
1750 if isinstance(base
, dict):
1751 base
= self
._make
_implicit
_object
_type
(
1752 name
, info
, doc
, ifcond
,
1753 'base', self
._make
_members
(base
, info
))
1755 variants
= [self
._make
_variant
(key
, value
)
1756 for (key
, value
) in data
.items()]
1759 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1760 for (key
, value
) in data
.items()]
1761 enum
= [{'name': v
.name
} for v
in variants
]
1762 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1763 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1764 members
= [tag_member
]
1766 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
1767 QAPISchemaObjectTypeVariants(tag_name
,
1771 def _def_alternate_type(self
, expr
, info
, doc
):
1772 name
= expr
['alternate']
1774 ifcond
= expr
.get('if')
1775 variants
= [self
._make
_variant
(key
, value
)
1776 for (key
, value
) in data
.items()]
1777 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1779 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
1780 QAPISchemaObjectTypeVariants(None,
1784 def _def_command(self
, expr
, info
, doc
):
1785 name
= expr
['command']
1786 data
= expr
.get('data')
1787 rets
= expr
.get('returns')
1788 gen
= expr
.get('gen', True)
1789 success_response
= expr
.get('success-response', True)
1790 boxed
= expr
.get('boxed', False)
1791 allow_oob
= expr
.get('allow-oob', False)
1792 allow_preconfig
= expr
.get('allow-preconfig', False)
1793 ifcond
= expr
.get('if')
1794 if isinstance(data
, OrderedDict
):
1795 data
= self
._make
_implicit
_object
_type
(
1796 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1797 if isinstance(rets
, list):
1798 assert len(rets
) == 1
1799 rets
= self
._make
_array
_type
(rets
[0], info
)
1800 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
1801 gen
, success_response
,
1802 boxed
, allow_oob
, allow_preconfig
))
1804 def _def_event(self
, expr
, info
, doc
):
1805 name
= expr
['event']
1806 data
= expr
.get('data')
1807 boxed
= expr
.get('boxed', False)
1808 ifcond
= expr
.get('if')
1809 if isinstance(data
, OrderedDict
):
1810 data
= self
._make
_implicit
_object
_type
(
1811 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1812 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
1814 def _def_exprs(self
, exprs
):
1815 for expr_elem
in exprs
:
1816 expr
= expr_elem
['expr']
1817 info
= expr_elem
['info']
1818 doc
= expr_elem
.get('doc')
1820 self
._def
_enum
_type
(expr
, info
, doc
)
1821 elif 'struct' in expr
:
1822 self
._def
_struct
_type
(expr
, info
, doc
)
1823 elif 'union' in expr
:
1824 self
._def
_union
_type
(expr
, info
, doc
)
1825 elif 'alternate' in expr
:
1826 self
._def
_alternate
_type
(expr
, info
, doc
)
1827 elif 'command' in expr
:
1828 self
._def
_command
(expr
, info
, doc
)
1829 elif 'event' in expr
:
1830 self
._def
_event
(expr
, info
, doc
)
1831 elif 'include' in expr
:
1832 self
._def
_include
(expr
, info
, doc
)
1837 for ent
in self
._entity
_list
:
1840 def visit(self
, visitor
):
1841 visitor
.visit_begin(self
)
1843 for entity
in self
._entity
_list
:
1844 if visitor
.visit_needed(entity
):
1845 if entity
.module
!= module
:
1846 module
= entity
.module
1847 visitor
.visit_module(module
)
1848 entity
.visit(visitor
)
1853 # Code generation helpers
1856 def camel_case(name
):
1860 if ch
in ['_', '-']:
1863 new_name
+= ch
.upper()
1866 new_name
+= ch
.lower()
1870 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1871 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1872 # ENUM24_Name -> ENUM24_NAME
1873 def camel_to_upper(value
):
1874 c_fun_str
= c_name(value
, False)
1879 length
= len(c_fun_str
)
1880 for i
in range(length
):
1882 # When c is upper and no '_' appears before, do more checks
1883 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
1884 if i
< length
- 1 and c_fun_str
[i
+ 1].islower():
1886 elif c_fun_str
[i
- 1].isdigit():
1889 return new_name
.lstrip('_').upper()
1892 def c_enum_const(type_name
, const_name
, prefix
=None):
1893 if prefix
is not None:
1895 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1898 if hasattr(str, 'maketrans'):
1899 c_name_trans
= str.maketrans('.-', '__')
1901 c_name_trans
= string
.maketrans('.-', '__')
1904 # Map @name to a valid C identifier.
1905 # If @protect, avoid returning certain ticklish identifiers (like
1906 # C keywords) by prepending 'q_'.
1908 # Used for converting 'name' from a 'name':'type' qapi definition
1909 # into a generated struct member, as well as converting type names
1910 # into substrings of a generated C function name.
1911 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1912 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1913 def c_name(name
, protect
=True):
1914 # ANSI X3J11/88-090, 3.1.1
1915 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1916 'default', 'do', 'double', 'else', 'enum', 'extern',
1917 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1918 'return', 'short', 'signed', 'sizeof', 'static',
1919 'struct', 'switch', 'typedef', 'union', 'unsigned',
1920 'void', 'volatile', 'while'])
1921 # ISO/IEC 9899:1999, 6.4.1
1922 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1923 # ISO/IEC 9899:2011, 6.4.1
1924 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1925 '_Noreturn', '_Static_assert', '_Thread_local'])
1926 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1928 gcc_words
= set(['asm', 'typeof'])
1929 # C++ ISO/IEC 14882:2003 2.11
1930 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1931 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1932 'namespace', 'new', 'operator', 'private', 'protected',
1933 'public', 'reinterpret_cast', 'static_cast', 'template',
1934 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1935 'using', 'virtual', 'wchar_t',
1936 # alternative representations
1937 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1938 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1939 # namespace pollution:
1940 polluted_words
= set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1941 name
= name
.translate(c_name_trans
)
1942 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1943 | cpp_words | polluted_words
):
1948 eatspace
= '\033EATSPACE.'
1949 pointer_suffix
= ' *' + eatspace
1952 def genindent(count
):
1954 for _
in range(count
):
1962 def push_indent(indent_amount
=4):
1964 indent_level
+= indent_amount
1967 def pop_indent(indent_amount
=4):
1969 indent_level
-= indent_amount
1972 # Generate @code with @kwds interpolated.
1973 # Obey indent_level, and strip eatspace.
1974 def cgen(code
, **kwds
):
1977 indent
= genindent(indent_level
)
1978 # re.subn() lacks flags support before Python 2.7, use re.compile()
1979 raw
= re
.subn(re
.compile(r
'^(?!(#|$))', re
.MULTILINE
),
1982 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
1985 def mcgen(code
, **kwds
):
1988 return cgen(code
, **kwds
)
1991 def guardname(filename
):
1992 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
).upper()
1995 def guardstart(name
):
2001 name
=guardname(name
))
2007 #endif /* %(name)s */
2009 name
=guardname(name
))
2021 def gen_endif(ifcond
):
2023 for ifc
in reversed(ifcond
):
2025 #endif /* %(cond)s */
2030 def _wrap_ifcond(ifcond
, before
, after
):
2032 return after
# suppress empty #if ... #endif
2034 assert after
.startswith(before
)
2036 added
= after
[len(before
):]
2037 if added
[0] == '\n':
2040 out
+= gen_if(ifcond
)
2042 out
+= gen_endif(ifcond
)
2046 def gen_enum_lookup(name
, members
, prefix
=None):
2049 const QEnumLookup %(c_name)s_lookup = {
2050 .array = (const char *const[]) {
2052 c_name
=c_name(name
))
2054 index
= c_enum_const(name
, m
.name
, prefix
)
2056 [%(index)s] = "%(name)s",
2058 index
=index
, name
=m
.name
)
2062 .size = %(max_index)s
2065 max_index
=c_enum_const(name
, '_MAX', prefix
))
2069 def gen_enum(name
, members
, prefix
=None):
2070 # append automatically generated _MAX value
2071 enum_members
= members
+ [QAPISchemaMember('_MAX')]
2075 typedef enum %(c_name)s {
2077 c_name
=c_name(name
))
2079 for m
in enum_members
:
2083 c_enum
=c_enum_const(name
, m
.name
, prefix
))
2088 c_name
=c_name(name
))
2092 #define %(c_name)s_str(val) \\
2093 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2095 extern const QEnumLookup %(c_name)s_lookup;
2097 c_name
=c_name(name
))
2101 def build_params(arg_type
, boxed
, extra
=None):
2106 ret
+= '%s arg' % arg_type
.c_param_type()
2109 assert not arg_type
.variants
2110 for memb
in arg_type
.members
:
2114 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
2115 ret
+= '%s %s' % (memb
.type.c_param_type(),
2119 return ret
if ret
else 'void'
2123 # Accumulate and write output
2126 class QAPIGen(object):
2132 def preamble_add(self
, text
):
2133 self
._preamble
+= text
2135 def add(self
, text
):
2138 def get_content(self
, fname
=None):
2139 return (self
._top
(fname
) + self
._preamble
+ self
._body
2140 + self
._bottom
(fname
))
2142 def _top(self
, fname
):
2145 def _bottom(self
, fname
):
2148 def write(self
, output_dir
, fname
):
2149 pathname
= os
.path
.join(output_dir
, fname
)
2150 dir = os
.path
.dirname(pathname
)
2154 except os
.error
as e
:
2155 if e
.errno
!= errno
.EEXIST
:
2157 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2158 if sys
.version_info
[0] >= 3:
2159 f
= open(fd
, 'r+', encoding
='utf-8')
2161 f
= os
.fdopen(fd
, 'r+')
2162 text
= self
.get_content(fname
)
2163 oldtext
= f
.read(len(text
) + 1)
2172 def ifcontext(ifcond
, *args
):
2173 """A 'with' statement context manager to wrap with start_if()/end_if()
2175 *args: any number of QAPIGenCCode
2179 with ifcontext(ifcond, self._genh, self._genc):
2180 modify self._genh and self._genc ...
2182 Is equivalent to calling::
2184 self._genh.start_if(ifcond)
2185 self._genc.start_if(ifcond)
2186 modify self._genh and self._genc ...
2191 arg
.start_if(ifcond
)
2197 class QAPIGenCCode(QAPIGen
):
2200 QAPIGen
.__init
__(self
)
2201 self
._start
_if
= None
2203 def start_if(self
, ifcond
):
2204 assert self
._start
_if
is None
2205 self
._start
_if
= (ifcond
, self
._body
, self
._preamble
)
2208 assert self
._start
_if
2210 self
._start
_if
= None
2212 def _wrap_ifcond(self
):
2213 self
._body
= _wrap_ifcond(self
._start
_if
[0],
2214 self
._start
_if
[1], self
._body
)
2215 self
._preamble
= _wrap_ifcond(self
._start
_if
[0],
2216 self
._start
_if
[2], self
._preamble
)
2218 def get_content(self
, fname
=None):
2219 assert self
._start
_if
is None
2220 return QAPIGen
.get_content(self
, fname
)
2223 class QAPIGenC(QAPIGenCCode
):
2225 def __init__(self
, blurb
, pydoc
):
2226 QAPIGenCCode
.__init
__(self
)
2228 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2231 def _top(self
, fname
):
2233 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2240 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2241 * See the COPYING.LIB file in the top-level directory.
2245 blurb
=self
._blurb
, copyright
=self
._copyright
)
2247 def _bottom(self
, fname
):
2250 /* Dummy declaration to prevent empty .o file */
2251 char dummy_%(name)s;
2256 class QAPIGenH(QAPIGenC
):
2258 def _top(self
, fname
):
2259 return QAPIGenC
._top
(self
, fname
) + guardstart(fname
)
2261 def _bottom(self
, fname
):
2262 return guardend(fname
)
2265 class QAPIGenDoc(QAPIGen
):
2267 def _top(self
, fname
):
2268 return (QAPIGen
._top
(self
, fname
)
2269 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2272 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2274 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2275 self
._prefix
= prefix
2277 self
._genc
= QAPIGenC(blurb
, pydoc
)
2278 self
._genh
= QAPIGenH(blurb
, pydoc
)
2280 def write(self
, output_dir
):
2281 self
._genc
.write(output_dir
, self
._prefix
+ self
._what
+ '.c')
2282 self
._genh
.write(output_dir
, self
._prefix
+ self
._what
+ '.h')
2285 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2287 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2288 self
._prefix
= prefix
2293 self
._main
_module
= None
2295 def _module_basename(self
, what
, name
):
2297 return re
.sub(r
'-', '-builtin-', what
)
2298 basename
= os
.path
.join(os
.path
.dirname(name
),
2299 self
._prefix
+ what
)
2300 if name
== self
._main
_module
:
2302 return basename
+ '-' + os
.path
.splitext(os
.path
.basename(name
))[0]
2304 def _add_module(self
, name
, blurb
):
2305 if self
._main
_module
is None and name
is not None:
2306 self
._main
_module
= name
2307 genc
= QAPIGenC(blurb
, self
._pydoc
)
2308 genh
= QAPIGenH(blurb
, self
._pydoc
)
2309 self
._module
[name
] = (genc
, genh
)
2310 self
._set
_module
(name
)
2312 def _set_module(self
, name
):
2313 self
._genc
, self
._genh
= self
._module
[name
]
2315 def write(self
, output_dir
, opt_builtins
=False):
2316 for name
in self
._module
:
2317 if name
is None and not opt_builtins
:
2319 basename
= self
._module
_basename
(self
._what
, name
)
2320 (genc
, genh
) = self
._module
[name
]
2321 genc
.write(output_dir
, basename
+ '.c')
2322 genh
.write(output_dir
, basename
+ '.h')
2324 def _begin_module(self
, name
):
2327 def visit_module(self
, name
):
2328 if name
in self
._module
:
2329 self
._set
_module
(name
)
2331 self
._add
_module
(name
, self
._blurb
)
2332 self
._begin
_module
(name
)
2334 def visit_include(self
, name
, info
):
2335 basename
= self
._module
_basename
(self
._what
, name
)
2336 self
._genh
.preamble_add(mcgen('''
2337 #include "%(basename)s.h"