4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2016 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.
20 from ordereddict
import OrderedDict
23 'null': 'QTYPE_QNULL',
24 'str': 'QTYPE_QSTRING',
26 'number': 'QTYPE_QNUM',
27 'bool': 'QTYPE_QBOOL',
29 'int16': 'QTYPE_QNUM',
30 'int32': 'QTYPE_QNUM',
31 'int64': 'QTYPE_QNUM',
32 'uint8': 'QTYPE_QNUM',
33 'uint16': 'QTYPE_QNUM',
34 'uint32': 'QTYPE_QNUM',
35 'uint64': 'QTYPE_QNUM',
37 'any': None, # any QType possible, actually
38 'QType': 'QTYPE_QSTRING',
41 # Are documentation comments required?
44 # Whitelist of commands allowed to return a non-dictionary
45 returns_whitelist
= []
47 # Whitelist of entities allowed to violate case conventions
48 name_case_whitelist
= []
56 # Parsing the schema into expressions
60 def error_path(parent
):
63 res
= ('In file included from %s:%d:\n' % (parent
['file'],
64 parent
['line'])) + res
65 parent
= parent
['parent']
69 class QAPIError(Exception):
70 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
71 Exception.__init
__(self
)
79 loc
= '%s:%d' % (self
.fname
, self
.line
)
80 if self
.col
is not None:
81 loc
+= ':%s' % self
.col
82 return error_path(self
.info
) + '%s: %s' % (loc
, self
.msg
)
85 class QAPIParseError(QAPIError
):
86 def __init__(self
, parser
, msg
):
88 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
90 col
= (col
+ 7) % 8 + 1
93 QAPIError
.__init
__(self
, parser
.fname
, parser
.line
, col
,
94 parser
.incl_info
, msg
)
97 class QAPISemError(QAPIError
):
98 def __init__(self
, info
, msg
):
99 QAPIError
.__init
__(self
, info
['file'], info
['line'], None,
103 class QAPIDoc(object):
104 class Section(object):
105 def __init__(self
, name
=None):
106 # optional section name (argument/member or section name)
108 # the list of lines for this section
111 def append(self
, line
):
112 self
.text
+= line
.rstrip() + '\n'
114 class ArgSection(Section
):
115 def __init__(self
, name
):
116 QAPIDoc
.Section
.__init
__(self
, name
)
119 def connect(self
, member
):
122 def __init__(self
, parser
, info
):
123 # self._parser is used to report errors with QAPIParseError. The
124 # resulting error position depends on the state of the parser.
125 # It happens to be the beginning of the comment. More or less
126 # servicable, but action at a distance.
127 self
._parser
= parser
130 self
.body
= QAPIDoc
.Section()
131 # dict mapping parameter name to ArgSection
132 self
.args
= OrderedDict()
135 # the current section
136 self
._section
= self
.body
138 def has_section(self
, name
):
139 """Return True if we have a section with this name."""
140 for i
in self
.sections
:
145 def append(self
, line
):
146 """Parse a comment line and add it to the documentation."""
149 self
._append
_freeform
(line
)
153 raise QAPIParseError(self
._parser
, "Missing space after #")
156 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
157 # recognized, and get silently treated as ordinary text
159 self
._append
_symbol
_line
(line
)
160 elif not self
.body
.text
and line
.startswith('@'):
161 if not line
.endswith(':'):
162 raise QAPIParseError(self
._parser
, "Line should end with :")
163 self
.symbol
= line
[1:-1]
164 # FIXME invalid names other than the empty string aren't flagged
166 raise QAPIParseError(self
._parser
, "Invalid name")
168 self
._append
_freeform
(line
)
170 def end_comment(self
):
173 def _append_symbol_line(self
, line
):
174 name
= line
.split(' ', 1)[0]
176 if name
.startswith('@') and name
.endswith(':'):
177 line
= line
[len(name
)+1:]
178 self
._start
_args
_section
(name
[1:-1])
179 elif name
in ('Returns:', 'Since:',
180 # those are often singular or plural
182 'Example:', 'Examples:',
184 line
= line
[len(name
)+1:]
185 self
._start
_section
(name
[:-1])
187 self
._append
_freeform
(line
)
189 def _start_args_section(self
, name
):
190 # FIXME invalid names other than the empty string aren't flagged
192 raise QAPIParseError(self
._parser
, "Invalid parameter name")
193 if name
in self
.args
:
194 raise QAPIParseError(self
._parser
,
195 "'%s' parameter name duplicated" % name
)
197 raise QAPIParseError(self
._parser
,
198 "'@%s:' can't follow '%s' section"
199 % (name
, self
.sections
[0].name
))
201 self
._section
= QAPIDoc
.ArgSection(name
)
202 self
.args
[name
] = self
._section
204 def _start_section(self
, name
=None):
205 if name
in ('Returns', 'Since') and self
.has_section(name
):
206 raise QAPIParseError(self
._parser
,
207 "Duplicated '%s' section" % name
)
209 self
._section
= QAPIDoc
.Section(name
)
210 self
.sections
.append(self
._section
)
212 def _end_section(self
):
214 text
= self
._section
.text
= self
._section
.text
.strip()
215 if self
._section
.name
and (not text
or text
.isspace()):
216 raise QAPIParseError(self
._parser
, "Empty doc section '%s'"
217 % self
._section
.name
)
220 def _append_freeform(self
, line
):
221 in_arg
= isinstance(self
._section
, QAPIDoc
.ArgSection
)
222 if (in_arg
and self
._section
.text
.endswith('\n\n')
223 and line
and not line
[0].isspace()):
224 self
._start
_section
()
225 if (in_arg
or not self
._section
.name
226 or not self
._section
.name
.startswith('Example')):
228 match
= re
.match(r
'(@\S+:)', line
)
230 raise QAPIParseError(self
._parser
,
231 "'%s' not allowed in free-form documentation"
233 self
._section
.append(line
)
235 def connect_member(self
, member
):
236 if member
.name
not in self
.args
:
237 # Undocumented TODO outlaw
238 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
239 self
.args
[member
.name
].connect(member
)
241 def check_expr(self
, expr
):
242 if self
.has_section('Returns') and 'command' not in expr
:
243 raise QAPISemError(self
.info
,
244 "'Returns:' is only valid for commands")
247 bogus
= [name
for name
, section
in self
.args
.iteritems()
248 if not section
.member
]
252 "The following documented members are not in "
253 "the declaration: %s" % ", ".join(bogus
))
256 class QAPISchemaParser(object):
258 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
259 abs_fname
= os
.path
.abspath(fp
.name
)
261 previously_included
.append(abs_fname
)
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 self
._include
(include
, info
, os
.path
.dirname(abs_fname
),
294 elif "pragma" in expr
:
295 self
.reject_expr_doc(cur_doc
)
297 raise QAPISemError(info
, "Invalid 'pragma' directive")
298 pragma
= expr
['pragma']
299 if not isinstance(pragma
, dict):
301 info
, "Value of 'pragma' must be a dictionary")
302 for name
, value
in pragma
.iteritems():
303 self
._pragma
(name
, value
, info
)
305 expr_elem
= {'expr': expr
,
308 if not cur_doc
.symbol
:
310 cur_doc
.info
, "Expression documentation required")
311 expr_elem
['doc'] = cur_doc
312 self
.exprs
.append(expr_elem
)
314 self
.reject_expr_doc(cur_doc
)
317 def reject_expr_doc(doc
):
318 if doc
and doc
.symbol
:
321 "Documentation for '%s' is not followed by the definition"
324 def _include(self
, include
, info
, base_dir
, previously_included
):
325 incl_abs_fname
= os
.path
.join(base_dir
, include
)
326 # catch inclusion cycle
329 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
330 raise QAPISemError(info
, "Inclusion loop for %s" % include
)
333 # skip multiple include of the same file
334 if incl_abs_fname
in previously_included
:
337 fobj
= open(incl_abs_fname
, 'r')
339 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, include
))
340 exprs_include
= QAPISchemaParser(fobj
, previously_included
, info
)
341 self
.exprs
.extend(exprs_include
.exprs
)
342 self
.docs
.extend(exprs_include
.docs
)
344 def _pragma(self
, name
, value
, info
):
345 global doc_required
, returns_whitelist
, name_case_whitelist
346 if name
== 'doc-required':
347 if not isinstance(value
, bool):
348 raise QAPISemError(info
,
349 "Pragma 'doc-required' must be boolean")
351 elif name
== 'returns-whitelist':
352 if (not isinstance(value
, list)
353 or any([not isinstance(elt
, str) for elt
in value
])):
354 raise QAPISemError(info
,
355 "Pragma returns-whitelist must be"
356 " a list of strings")
357 returns_whitelist
= value
358 elif name
== 'name-case-whitelist':
359 if (not isinstance(value
, list)
360 or any([not isinstance(elt
, str) for elt
in value
])):
361 raise QAPISemError(info
,
362 "Pragma name-case-whitelist must be"
363 " a list of strings")
364 name_case_whitelist
= value
366 raise QAPISemError(info
, "Unknown pragma '%s'" % name
)
368 def accept(self
, skip_comment
=True):
370 self
.tok
= self
.src
[self
.cursor
]
371 self
.pos
= self
.cursor
376 if self
.src
[self
.cursor
] == '#':
377 # Start of doc comment
379 self
.cursor
= self
.src
.find('\n', self
.cursor
)
381 self
.val
= self
.src
[self
.pos
:self
.cursor
]
383 elif self
.tok
in '{}:,[]':
385 elif self
.tok
== "'":
389 ch
= self
.src
[self
.cursor
]
392 raise QAPIParseError(self
, 'Missing terminating "\'"')
406 for _
in range(0, 4):
407 ch
= self
.src
[self
.cursor
]
409 if ch
not in '0123456789abcdefABCDEF':
410 raise QAPIParseError(self
,
411 '\\u escape needs 4 '
413 value
= (value
<< 4) + int(ch
, 16)
414 # If Python 2 and 3 didn't disagree so much on
415 # how to handle Unicode, then we could allow
416 # Unicode string defaults. But most of QAPI is
417 # ASCII-only, so we aren't losing much for now.
418 if not value
or value
> 0x7f:
419 raise QAPIParseError(self
,
420 'For now, \\u escape '
421 'only supports non-zero '
422 'values up to \\u007f')
427 raise QAPIParseError(self
,
428 "Unknown escape \\%s" % ch
)
437 elif self
.src
.startswith('true', self
.pos
):
441 elif self
.src
.startswith('false', self
.pos
):
445 elif self
.src
.startswith('null', self
.pos
):
449 elif self
.tok
== '\n':
450 if self
.cursor
== len(self
.src
):
454 self
.line_pos
= self
.cursor
455 elif not self
.tok
.isspace():
456 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
458 def get_members(self
):
464 raise QAPIParseError(self
, 'Expected string or "}"')
469 raise QAPIParseError(self
, 'Expected ":"')
472 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
473 expr
[key
] = self
.get_expr(True)
478 raise QAPIParseError(self
, 'Expected "," or "}"')
481 raise QAPIParseError(self
, 'Expected string')
483 def get_values(self
):
488 if self
.tok
not in "{['tfn":
489 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
492 expr
.append(self
.get_expr(True))
497 raise QAPIParseError(self
, 'Expected "," or "]"')
500 def get_expr(self
, nested
):
501 if self
.tok
!= '{' and not nested
:
502 raise QAPIParseError(self
, 'Expected "{"')
505 expr
= self
.get_members()
506 elif self
.tok
== '[':
508 expr
= self
.get_values()
509 elif self
.tok
in "'tfn":
513 raise QAPIParseError(self
, 'Expected "{", "[", string, '
517 def get_doc(self
, info
):
519 raise QAPIParseError(self
, "Junk after '##' at start of "
520 "documentation comment")
522 doc
= QAPIDoc(self
, info
)
524 while self
.tok
== '#':
525 if self
.val
.startswith('##'):
528 raise QAPIParseError(self
, "Junk after '##' at end of "
529 "documentation comment")
537 raise QAPIParseError(self
, "Documentation comment must end with '##'")
541 # Semantic analysis of schema expressions
542 # TODO fold into QAPISchema
543 # TODO catching name collisions in generated code would be nice
547 def find_base_members(base
):
548 if isinstance(base
, dict):
550 base_struct_define
= struct_types
.get(base
)
551 if not base_struct_define
:
553 return base_struct_define
['data']
556 # Return the qtype of an alternate branch, or None on error.
557 def find_alternate_member_qtype(qapi_type
):
558 if qapi_type
in builtin_types
:
559 return builtin_types
[qapi_type
]
560 elif qapi_type
in struct_types
:
562 elif qapi_type
in enum_types
:
563 return 'QTYPE_QSTRING'
564 elif qapi_type
in union_types
:
569 # Return the discriminator enum define if discriminator is specified as an
570 # enum type, otherwise return None.
571 def discriminator_find_enum_define(expr
):
572 base
= expr
.get('base')
573 discriminator
= expr
.get('discriminator')
575 if not (discriminator
and base
):
578 base_members
= find_base_members(base
)
582 discriminator_type
= base_members
.get(discriminator
)
583 if not discriminator_type
:
586 return enum_types
.get(discriminator_type
)
589 # Names must be letters, numbers, -, and _. They must start with letter,
590 # except for downstream extensions which must start with __RFQDN_.
591 # Dots are only valid in the downstream extension prefix.
592 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
593 '[a-zA-Z][a-zA-Z0-9_-]*$')
596 def check_name(info
, source
, name
, allow_optional
=False,
601 if not isinstance(name
, str):
602 raise QAPISemError(info
, "%s requires a string name" % source
)
603 if name
.startswith('*'):
604 membername
= name
[1:]
605 if not allow_optional
:
606 raise QAPISemError(info
, "%s does not allow optional name '%s'"
608 # Enum members can start with a digit, because the generated C
609 # code always prefixes it with the enum name
610 if enum_member
and membername
[0].isdigit():
611 membername
= 'D' + membername
612 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
613 # and 'q_obj_*' implicit type names.
614 if not valid_name
.match(membername
) or \
615 c_name(membername
, False).startswith('q_'):
616 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
619 def add_name(name
, info
, meta
, implicit
=False):
621 check_name(info
, "'%s'" % meta
, name
)
622 # FIXME should reject names that differ only in '_' vs. '.'
623 # vs. '-', because they're liable to clash in generated C.
624 if name
in all_names
:
625 raise QAPISemError(info
, "%s '%s' is already defined"
626 % (all_names
[name
], name
))
627 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
628 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
629 % (meta
, name
, name
[-4:]))
630 all_names
[name
] = meta
633 def check_type(info
, source
, value
, allow_array
=False,
634 allow_dict
=False, allow_optional
=False,
641 # Check if array type for value is okay
642 if isinstance(value
, list):
644 raise QAPISemError(info
, "%s cannot be an array" % source
)
645 if len(value
) != 1 or not isinstance(value
[0], str):
646 raise QAPISemError(info
,
647 "%s: array type must contain single type name" %
651 # Check if type name for value is okay
652 if isinstance(value
, str):
653 if value
not in all_names
:
654 raise QAPISemError(info
, "%s uses unknown type '%s'"
656 if not all_names
[value
] in allow_metas
:
657 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
658 (source
, all_names
[value
], value
))
662 raise QAPISemError(info
, "%s should be a type name" % source
)
664 if not isinstance(value
, OrderedDict
):
665 raise QAPISemError(info
,
666 "%s should be a dictionary or type name" % source
)
668 # value is a dictionary, check that each member is okay
669 for (key
, arg
) in value
.items():
670 check_name(info
, "Member of %s" % source
, key
,
671 allow_optional
=allow_optional
)
672 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
673 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
675 # Todo: allow dictionaries to represent default values of
676 # an optional argument.
677 check_type(info
, "Member '%s' of %s" % (key
, source
), arg
,
679 allow_metas
=['built-in', 'union', 'alternate', 'struct',
683 def check_command(expr
, info
):
684 name
= expr
['command']
685 boxed
= expr
.get('boxed', False)
687 args_meta
= ['struct']
689 args_meta
+= ['union', 'alternate']
690 check_type(info
, "'data' for command '%s'" % name
,
691 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
692 allow_metas
=args_meta
)
693 returns_meta
= ['union', 'struct']
694 if name
in returns_whitelist
:
695 returns_meta
+= ['built-in', 'alternate', 'enum']
696 check_type(info
, "'returns' for command '%s'" % name
,
697 expr
.get('returns'), allow_array
=True,
698 allow_optional
=True, allow_metas
=returns_meta
)
701 def check_event(expr
, info
):
703 boxed
= expr
.get('boxed', False)
707 meta
+= ['union', 'alternate']
708 check_type(info
, "'data' for event '%s'" % name
,
709 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
713 def check_union(expr
, info
):
715 base
= expr
.get('base')
716 discriminator
= expr
.get('discriminator')
717 members
= expr
['data']
719 # Two types of unions, determined by discriminator.
721 # With no discriminator it is a simple union.
722 if discriminator
is None:
724 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
726 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
729 # Else, it's a flat union.
731 # The object must have a string or dictionary 'base'.
732 check_type(info
, "'base' for union '%s'" % name
,
733 base
, allow_dict
=True, allow_optional
=True,
734 allow_metas
=['struct'])
736 raise QAPISemError(info
, "Flat union '%s' must have a base"
738 base_members
= find_base_members(base
)
739 assert base_members
is not None
741 # The value of member 'discriminator' must name a non-optional
742 # member of the base struct.
743 check_name(info
, "Discriminator of flat union '%s'" % name
,
745 discriminator_type
= base_members
.get(discriminator
)
746 if not discriminator_type
:
747 raise QAPISemError(info
,
748 "Discriminator '%s' is not a member of base "
750 % (discriminator
, base
))
751 enum_define
= enum_types
.get(discriminator_type
)
752 allow_metas
= ['struct']
753 # Do not allow string discriminator
755 raise QAPISemError(info
,
756 "Discriminator '%s' must be of enumeration "
757 "type" % discriminator
)
759 # Check every branch; don't allow an empty union
760 if len(members
) == 0:
761 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
762 for (key
, value
) in members
.items():
763 check_name(info
, "Member of union '%s'" % name
, key
)
765 # Each value must name a known type
766 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
767 value
, allow_array
=not base
, allow_metas
=allow_metas
)
769 # If the discriminator names an enum type, then all members
770 # of 'data' must also be members of the enum type.
772 if key
not in enum_define
['data']:
773 raise QAPISemError(info
,
774 "Discriminator value '%s' is not found in "
776 % (key
, enum_define
['enum']))
778 # If discriminator is user-defined, ensure all values are covered
780 for value
in enum_define
['data']:
781 if value
not in members
.keys():
782 raise QAPISemError(info
, "Union '%s' data missing '%s' branch"
786 def check_alternate(expr
, info
):
787 name
= expr
['alternate']
788 members
= expr
['data']
791 # Check every branch; require at least two branches
793 raise QAPISemError(info
,
794 "Alternate '%s' should have at least two branches "
796 for (key
, value
) in members
.items():
797 check_name(info
, "Member of alternate '%s'" % name
, key
)
799 # Ensure alternates have no type conflicts.
800 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
),
802 allow_metas
=['built-in', 'union', 'struct', 'enum'])
803 qtype
= find_alternate_member_qtype(value
)
805 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
806 "type '%s'" % (name
, key
, value
))
807 conflicting
= set([qtype
])
808 if qtype
== 'QTYPE_QSTRING':
809 enum_expr
= enum_types
.get(value
)
811 for v
in enum_expr
['data']:
812 if v
in ['on', 'off']:
813 conflicting
.add('QTYPE_QBOOL')
814 if re
.match(r
'[-+0-9.]', v
): # lazy, could be tightened
815 conflicting
.add('QTYPE_QNUM')
817 conflicting
.add('QTYPE_QNUM')
818 conflicting
.add('QTYPE_QBOOL')
819 for qt
in conflicting
:
821 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
822 "be distinguished from member '%s'"
823 % (name
, key
, types_seen
[qt
]))
827 def check_enum(expr
, info
):
829 members
= expr
.get('data')
830 prefix
= expr
.get('prefix')
832 if not isinstance(members
, list):
833 raise QAPISemError(info
,
834 "Enum '%s' requires an array for 'data'" % name
)
835 if prefix
is not None and not isinstance(prefix
, str):
836 raise QAPISemError(info
,
837 "Enum '%s' requires a string for 'prefix'" % name
)
838 for member
in members
:
839 check_name(info
, "Member of enum '%s'" % name
, member
,
843 def check_struct(expr
, info
):
844 name
= expr
['struct']
845 members
= expr
['data']
847 check_type(info
, "'data' for struct '%s'" % name
, members
,
848 allow_dict
=True, allow_optional
=True)
849 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
850 allow_metas
=['struct'])
853 def check_keys(expr_elem
, meta
, required
, optional
=[]):
854 expr
= expr_elem
['expr']
855 info
= expr_elem
['info']
857 if not isinstance(name
, str):
858 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
859 required
= required
+ [meta
]
860 for (key
, value
) in expr
.items():
861 if key
not in required
and key
not in optional
:
862 raise QAPISemError(info
, "Unknown key '%s' in %s '%s'"
864 if (key
== 'gen' or key
== 'success-response') and value
is not False:
865 raise QAPISemError(info
,
866 "'%s' of %s '%s' should only use false value"
868 if key
== 'boxed' and value
is not True:
869 raise QAPISemError(info
,
870 "'%s' of %s '%s' should only use true value"
874 raise QAPISemError(info
, "Key '%s' is missing from %s '%s'"
878 def check_exprs(exprs
):
881 # Populate name table with names of built-in types
882 for builtin
in builtin_types
.keys():
883 all_names
[builtin
] = 'built-in'
885 # Learn the types and check for valid expression keys
886 for expr_elem
in exprs
:
887 expr
= expr_elem
['expr']
888 info
= expr_elem
['info']
889 doc
= expr_elem
.get('doc')
891 if not doc
and doc_required
:
892 raise QAPISemError(info
,
893 "Expression missing documentation comment")
897 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
898 enum_types
[expr
[meta
]] = expr
899 elif 'union' in expr
:
901 check_keys(expr_elem
, 'union', ['data'],
902 ['base', 'discriminator'])
903 union_types
[expr
[meta
]] = expr
904 elif 'alternate' in expr
:
906 check_keys(expr_elem
, 'alternate', ['data'])
907 elif 'struct' in expr
:
909 check_keys(expr_elem
, 'struct', ['data'], ['base'])
910 struct_types
[expr
[meta
]] = expr
911 elif 'command' in expr
:
913 check_keys(expr_elem
, 'command', [],
914 ['data', 'returns', 'gen', 'success-response', 'boxed'])
915 elif 'event' in expr
:
917 check_keys(expr_elem
, 'event', [], ['data', 'boxed'])
919 raise QAPISemError(expr_elem
['info'],
920 "Expression is missing metatype")
922 add_name(name
, info
, meta
)
923 if doc
and doc
.symbol
!= name
:
924 raise QAPISemError(info
, "Definition of '%s' follows documentation"
925 " for '%s'" % (name
, doc
.symbol
))
927 # Try again for hidden UnionKind enum
928 for expr_elem
in exprs
:
929 expr
= expr_elem
['expr']
930 if 'union' in expr
and not discriminator_find_enum_define(expr
):
931 name
= '%sKind' % expr
['union']
932 elif 'alternate' in expr
:
933 name
= '%sKind' % expr
['alternate']
936 enum_types
[name
] = {'enum': name
}
937 add_name(name
, info
, 'enum', implicit
=True)
939 # Validate that exprs make sense
940 for expr_elem
in exprs
:
941 expr
= expr_elem
['expr']
942 info
= expr_elem
['info']
943 doc
= expr_elem
.get('doc')
946 check_enum(expr
, info
)
947 elif 'union' in expr
:
948 check_union(expr
, info
)
949 elif 'alternate' in expr
:
950 check_alternate(expr
, info
)
951 elif 'struct' in expr
:
952 check_struct(expr
, info
)
953 elif 'command' in expr
:
954 check_command(expr
, info
)
955 elif 'event' in expr
:
956 check_event(expr
, info
)
958 assert False, 'unexpected meta type'
967 # Schema compiler frontend
970 class QAPISchemaEntity(object):
971 def __init__(self
, name
, info
, doc
):
972 assert isinstance(name
, str)
974 # For explicitly defined entities, info points to the (explicit)
975 # definition. For builtins (and their arrays), info is None.
976 # For implicitly defined entities, info points to a place that
977 # triggered the implicit definition (there may be more than one
983 return c_name(self
.name
)
985 def check(self
, schema
):
988 def is_implicit(self
):
991 def visit(self
, visitor
):
995 class QAPISchemaVisitor(object):
996 def visit_begin(self
, schema
):
1002 def visit_needed(self
, entity
):
1003 # Default to visiting everything
1006 def visit_builtin_type(self
, name
, info
, json_type
):
1009 def visit_enum_type(self
, name
, info
, values
, prefix
):
1012 def visit_array_type(self
, name
, info
, element_type
):
1015 def visit_object_type(self
, name
, info
, base
, members
, variants
):
1018 def visit_object_type_flat(self
, name
, info
, members
, variants
):
1021 def visit_alternate_type(self
, name
, info
, variants
):
1024 def visit_command(self
, name
, info
, arg_type
, ret_type
,
1025 gen
, success_response
, boxed
):
1028 def visit_event(self
, name
, info
, arg_type
, boxed
):
1032 class QAPISchemaType(QAPISchemaEntity
):
1033 # Return the C type for common use.
1034 # For the types we commonly box, this is a pointer type.
1038 # Return the C type to be used in a parameter list.
1039 def c_param_type(self
):
1040 return self
.c_type()
1042 # Return the C type to be used where we suppress boxing.
1043 def c_unboxed_type(self
):
1044 return self
.c_type()
1046 def json_type(self
):
1049 def alternate_qtype(self
):
1051 'null': 'QTYPE_QNULL',
1052 'string': 'QTYPE_QSTRING',
1053 'number': 'QTYPE_QNUM',
1054 'int': 'QTYPE_QNUM',
1055 'boolean': 'QTYPE_QBOOL',
1056 'object': 'QTYPE_QDICT'
1058 return json2qtype
.get(self
.json_type())
1061 if self
.is_implicit():
1066 class QAPISchemaBuiltinType(QAPISchemaType
):
1067 def __init__(self
, name
, json_type
, c_type
):
1068 QAPISchemaType
.__init
__(self
, name
, None, None)
1069 assert not c_type
or isinstance(c_type
, str)
1070 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1072 self
._json
_type
_name
= json_type
1073 self
._c
_type
_name
= c_type
1079 return self
._c
_type
_name
1081 def c_param_type(self
):
1082 if self
.name
== 'str':
1083 return 'const ' + self
._c
_type
_name
1084 return self
._c
_type
_name
1086 def json_type(self
):
1087 return self
._json
_type
_name
1090 return self
.json_type()
1092 def visit(self
, visitor
):
1093 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1096 class QAPISchemaEnumType(QAPISchemaType
):
1097 def __init__(self
, name
, info
, doc
, values
, prefix
):
1098 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1100 assert isinstance(v
, QAPISchemaMember
)
1102 assert prefix
is None or isinstance(prefix
, str)
1103 self
.values
= values
1104 self
.prefix
= prefix
1106 def check(self
, schema
):
1108 for v
in self
.values
:
1109 v
.check_clash(self
.info
, seen
)
1111 self
.doc
.connect_member(v
)
1113 def is_implicit(self
):
1114 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1115 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1118 return c_name(self
.name
)
1120 def member_names(self
):
1121 return [v
.name
for v
in self
.values
]
1123 def json_type(self
):
1126 def visit(self
, visitor
):
1127 visitor
.visit_enum_type(self
.name
, self
.info
,
1128 self
.member_names(), self
.prefix
)
1131 class QAPISchemaArrayType(QAPISchemaType
):
1132 def __init__(self
, name
, info
, element_type
):
1133 QAPISchemaType
.__init
__(self
, name
, info
, None)
1134 assert isinstance(element_type
, str)
1135 self
._element
_type
_name
= element_type
1136 self
.element_type
= None
1138 def check(self
, schema
):
1139 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1140 assert self
.element_type
1142 def is_implicit(self
):
1146 return c_name(self
.name
) + pointer_suffix
1148 def json_type(self
):
1152 elt_doc_type
= self
.element_type
.doc_type()
1153 if not elt_doc_type
:
1155 return 'array of ' + elt_doc_type
1157 def visit(self
, visitor
):
1158 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
1161 class QAPISchemaObjectType(QAPISchemaType
):
1162 def __init__(self
, name
, info
, doc
, base
, local_members
, variants
):
1163 # struct has local_members, optional base, and no variants
1164 # flat union has base, variants, and no local_members
1165 # simple union has local_members, variants, and no base
1166 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1167 assert base
is None or isinstance(base
, str)
1168 for m
in local_members
:
1169 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1171 if variants
is not None:
1172 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1173 variants
.set_owner(name
)
1174 self
._base
_name
= base
1176 self
.local_members
= local_members
1177 self
.variants
= variants
1180 def check(self
, schema
):
1181 if self
.members
is False: # check for cycles
1182 raise QAPISemError(self
.info
,
1183 "Object %s contains itself" % self
.name
)
1186 self
.members
= False # mark as being checked
1187 seen
= OrderedDict()
1189 self
.base
= schema
.lookup_type(self
._base
_name
)
1190 assert isinstance(self
.base
, QAPISchemaObjectType
)
1191 self
.base
.check(schema
)
1192 self
.base
.check_clash(self
.info
, seen
)
1193 for m
in self
.local_members
:
1195 m
.check_clash(self
.info
, seen
)
1197 self
.doc
.connect_member(m
)
1198 self
.members
= seen
.values()
1200 self
.variants
.check(schema
, seen
)
1201 assert self
.variants
.tag_member
in self
.members
1202 self
.variants
.check_clash(self
.info
, seen
)
1206 # Check that the members of this type do not cause duplicate JSON members,
1207 # and update seen to track the members seen so far. Report any errors
1208 # on behalf of info, which is not necessarily self.info
1209 def check_clash(self
, info
, seen
):
1210 assert not self
.variants
# not implemented
1211 for m
in self
.members
:
1212 m
.check_clash(info
, seen
)
1214 def is_implicit(self
):
1215 # See QAPISchema._make_implicit_object_type(), as well as
1216 # _def_predefineds()
1217 return self
.name
.startswith('q_')
1220 assert self
.members
is not None
1221 return not self
.members
and not self
.variants
1224 assert self
.name
!= 'q_empty'
1225 return QAPISchemaType
.c_name(self
)
1228 assert not self
.is_implicit()
1229 return c_name(self
.name
) + pointer_suffix
1231 def c_unboxed_type(self
):
1232 return c_name(self
.name
)
1234 def json_type(self
):
1237 def visit(self
, visitor
):
1238 visitor
.visit_object_type(self
.name
, self
.info
,
1239 self
.base
, self
.local_members
, self
.variants
)
1240 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1241 self
.members
, self
.variants
)
1244 class QAPISchemaMember(object):
1247 def __init__(self
, name
):
1248 assert isinstance(name
, str)
1252 def set_owner(self
, name
):
1253 assert not self
.owner
1256 def check_clash(self
, info
, seen
):
1257 cname
= c_name(self
.name
)
1258 if cname
.lower() != cname
and self
.owner
not in name_case_whitelist
:
1259 raise QAPISemError(info
,
1260 "%s should not use uppercase" % self
.describe())
1262 raise QAPISemError(info
, "%s collides with %s" %
1263 (self
.describe(), seen
[cname
].describe()))
1266 def _pretty_owner(self
):
1268 if owner
.startswith('q_obj_'):
1269 # See QAPISchema._make_implicit_object_type() - reverse the
1270 # mapping there to create a nice human-readable description
1272 if owner
.endswith('-arg'):
1273 return '(parameter of %s)' % owner
[:-4]
1274 elif owner
.endswith('-base'):
1275 return '(base of %s)' % owner
[:-5]
1277 assert owner
.endswith('-wrapper')
1278 # Unreachable and not implemented
1280 if owner
.endswith('Kind'):
1281 # See QAPISchema._make_implicit_enum_type()
1282 return '(branch of %s)' % owner
[:-4]
1283 return '(%s of %s)' % (self
.role
, owner
)
1286 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1289 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1290 def __init__(self
, name
, typ
, optional
):
1291 QAPISchemaMember
.__init
__(self
, name
)
1292 assert isinstance(typ
, str)
1293 assert isinstance(optional
, bool)
1294 self
._type
_name
= typ
1296 self
.optional
= optional
1298 def check(self
, schema
):
1300 self
.type = schema
.lookup_type(self
._type
_name
)
1304 class QAPISchemaObjectTypeVariants(object):
1305 def __init__(self
, tag_name
, tag_member
, variants
):
1306 # Flat unions pass tag_name but not tag_member.
1307 # Simple unions and alternates pass tag_member but not tag_name.
1308 # After check(), tag_member is always set, and tag_name remains
1309 # a reliable witness of being used by a flat union.
1310 assert bool(tag_member
) != bool(tag_name
)
1311 assert (isinstance(tag_name
, str) or
1312 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1313 assert len(variants
) > 0
1315 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1316 self
._tag
_name
= tag_name
1317 self
.tag_member
= tag_member
1318 self
.variants
= variants
1320 def set_owner(self
, name
):
1321 for v
in self
.variants
:
1324 def check(self
, schema
, seen
):
1325 if not self
.tag_member
: # flat union
1326 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1327 assert self
._tag
_name
== self
.tag_member
.name
1328 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1329 for v
in self
.variants
:
1331 # Union names must match enum values; alternate names are
1332 # checked separately. Use 'seen' to tell the two apart.
1334 assert v
.name
in self
.tag_member
.type.member_names()
1335 assert isinstance(v
.type, QAPISchemaObjectType
)
1336 v
.type.check(schema
)
1338 def check_clash(self
, info
, seen
):
1339 for v
in self
.variants
:
1340 # Reset seen map for each variant, since qapi names from one
1341 # branch do not affect another branch
1342 assert isinstance(v
.type, QAPISchemaObjectType
)
1343 v
.type.check_clash(info
, dict(seen
))
1346 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1349 def __init__(self
, name
, typ
):
1350 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1353 class QAPISchemaAlternateType(QAPISchemaType
):
1354 def __init__(self
, name
, info
, doc
, variants
):
1355 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1356 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1357 assert variants
.tag_member
1358 variants
.set_owner(name
)
1359 variants
.tag_member
.set_owner(self
.name
)
1360 self
.variants
= variants
1362 def check(self
, schema
):
1363 self
.variants
.tag_member
.check(schema
)
1364 # Not calling self.variants.check_clash(), because there's nothing
1366 self
.variants
.check(schema
, {})
1367 # Alternate branch names have no relation to the tag enum values;
1368 # so we have to check for potential name collisions ourselves.
1370 for v
in self
.variants
.variants
:
1371 v
.check_clash(self
.info
, seen
)
1373 self
.doc
.connect_member(v
)
1378 return c_name(self
.name
) + pointer_suffix
1380 def json_type(self
):
1383 def visit(self
, visitor
):
1384 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1390 class QAPISchemaCommand(QAPISchemaEntity
):
1391 def __init__(self
, name
, info
, doc
, arg_type
, ret_type
,
1392 gen
, success_response
, boxed
):
1393 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1394 assert not arg_type
or isinstance(arg_type
, str)
1395 assert not ret_type
or isinstance(ret_type
, str)
1396 self
._arg
_type
_name
= arg_type
1397 self
.arg_type
= None
1398 self
._ret
_type
_name
= ret_type
1399 self
.ret_type
= None
1401 self
.success_response
= success_response
1404 def check(self
, schema
):
1405 if self
._arg
_type
_name
:
1406 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1407 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1408 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1409 self
.arg_type
.check(schema
)
1411 if self
.arg_type
.is_empty():
1412 raise QAPISemError(self
.info
,
1413 "Cannot use 'boxed' with empty type")
1415 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1416 assert not self
.arg_type
.variants
1418 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1419 if self
._ret
_type
_name
:
1420 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1421 assert isinstance(self
.ret_type
, QAPISchemaType
)
1423 def visit(self
, visitor
):
1424 visitor
.visit_command(self
.name
, self
.info
,
1425 self
.arg_type
, self
.ret_type
,
1426 self
.gen
, self
.success_response
, self
.boxed
)
1429 class QAPISchemaEvent(QAPISchemaEntity
):
1430 def __init__(self
, name
, info
, doc
, arg_type
, boxed
):
1431 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1432 assert not arg_type
or isinstance(arg_type
, str)
1433 self
._arg
_type
_name
= arg_type
1434 self
.arg_type
= None
1437 def check(self
, schema
):
1438 if self
._arg
_type
_name
:
1439 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1440 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1441 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1442 self
.arg_type
.check(schema
)
1444 if self
.arg_type
.is_empty():
1445 raise QAPISemError(self
.info
,
1446 "Cannot use 'boxed' with empty type")
1448 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1449 assert not self
.arg_type
.variants
1451 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1453 def visit(self
, visitor
):
1454 visitor
.visit_event(self
.name
, self
.info
, self
.arg_type
, self
.boxed
)
1457 class QAPISchema(object):
1458 def __init__(self
, fname
):
1460 parser
= QAPISchemaParser(open(fname
, 'r'))
1461 self
.exprs
= check_exprs(parser
.exprs
)
1462 self
.docs
= parser
.docs
1463 self
._entity
_dict
= {}
1464 self
._predefining
= True
1465 self
._def
_predefineds
()
1466 self
._predefining
= False
1469 except QAPIError
as err
:
1470 print >>sys
.stderr
, err
1473 def _def_entity(self
, ent
):
1474 # Only the predefined types are allowed to not have info
1475 assert ent
.info
or self
._predefining
1476 assert ent
.name
not in self
._entity
_dict
1477 self
._entity
_dict
[ent
.name
] = ent
1479 def lookup_entity(self
, name
, typ
=None):
1480 ent
= self
._entity
_dict
.get(name
)
1481 if typ
and not isinstance(ent
, typ
):
1485 def lookup_type(self
, name
):
1486 return self
.lookup_entity(name
, QAPISchemaType
)
1488 def _def_builtin_type(self
, name
, json_type
, c_type
):
1489 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1490 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1491 # qapi-types.h from a single .c, all arrays of builtins must be
1492 # declared in the first file whether or not they are used. Nicer
1493 # would be to use lazy instantiation, while figuring out how to
1494 # avoid compilation issues with multiple qapi-types.h.
1495 self
._make
_array
_type
(name
, None)
1497 def _def_predefineds(self
):
1498 for t
in [('str', 'string', 'char' + pointer_suffix
),
1499 ('number', 'number', 'double'),
1500 ('int', 'int', 'int64_t'),
1501 ('int8', 'int', 'int8_t'),
1502 ('int16', 'int', 'int16_t'),
1503 ('int32', 'int', 'int32_t'),
1504 ('int64', 'int', 'int64_t'),
1505 ('uint8', 'int', 'uint8_t'),
1506 ('uint16', 'int', 'uint16_t'),
1507 ('uint32', 'int', 'uint32_t'),
1508 ('uint64', 'int', 'uint64_t'),
1509 ('size', 'int', 'uint64_t'),
1510 ('bool', 'boolean', 'bool'),
1511 ('any', 'value', 'QObject' + pointer_suffix
),
1512 ('null', 'null', 'QNull' + pointer_suffix
)]:
1513 self
._def
_builtin
_type
(*t
)
1514 self
.the_empty_object_type
= QAPISchemaObjectType(
1515 'q_empty', None, None, None, [], None)
1516 self
._def
_entity
(self
.the_empty_object_type
)
1517 qtype_values
= self
._make
_enum
_members
(['none', 'qnull', 'qnum',
1518 'qstring', 'qdict', 'qlist',
1520 self
._def
_entity
(QAPISchemaEnumType('QType', None, None,
1521 qtype_values
, 'QTYPE'))
1523 def _make_enum_members(self
, values
):
1524 return [QAPISchemaMember(v
) for v
in values
]
1526 def _make_implicit_enum_type(self
, name
, info
, values
):
1527 # See also QAPISchemaObjectTypeMember._pretty_owner()
1528 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1529 self
._def
_entity
(QAPISchemaEnumType(
1530 name
, info
, None, self
._make
_enum
_members
(values
), None))
1533 def _make_array_type(self
, element_type
, info
):
1534 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1535 if not self
.lookup_type(name
):
1536 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1539 def _make_implicit_object_type(self
, name
, info
, doc
, role
, members
):
1542 # See also QAPISchemaObjectTypeMember._pretty_owner()
1543 name
= 'q_obj_%s-%s' % (name
, role
)
1544 if not self
.lookup_entity(name
, QAPISchemaObjectType
):
1545 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, None,
1549 def _def_enum_type(self
, expr
, info
, doc
):
1552 prefix
= expr
.get('prefix')
1553 self
._def
_entity
(QAPISchemaEnumType(
1554 name
, info
, doc
, self
._make
_enum
_members
(data
), prefix
))
1556 def _make_member(self
, name
, typ
, info
):
1558 if name
.startswith('*'):
1561 if isinstance(typ
, list):
1562 assert len(typ
) == 1
1563 typ
= self
._make
_array
_type
(typ
[0], info
)
1564 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1566 def _make_members(self
, data
, info
):
1567 return [self
._make
_member
(key
, value
, info
)
1568 for (key
, value
) in data
.iteritems()]
1570 def _def_struct_type(self
, expr
, info
, doc
):
1571 name
= expr
['struct']
1572 base
= expr
.get('base')
1574 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, base
,
1575 self
._make
_members
(data
, info
),
1578 def _make_variant(self
, case
, typ
):
1579 return QAPISchemaObjectTypeVariant(case
, typ
)
1581 def _make_simple_variant(self
, case
, typ
, info
):
1582 if isinstance(typ
, list):
1583 assert len(typ
) == 1
1584 typ
= self
._make
_array
_type
(typ
[0], info
)
1585 typ
= self
._make
_implicit
_object
_type
(
1586 typ
, info
, None, 'wrapper', [self
._make
_member
('data', typ
, info
)])
1587 return QAPISchemaObjectTypeVariant(case
, typ
)
1589 def _def_union_type(self
, expr
, info
, doc
):
1590 name
= expr
['union']
1592 base
= expr
.get('base')
1593 tag_name
= expr
.get('discriminator')
1595 if isinstance(base
, dict):
1596 base
= (self
._make
_implicit
_object
_type
(
1597 name
, info
, doc
, 'base', self
._make
_members
(base
, info
)))
1599 variants
= [self
._make
_variant
(key
, value
)
1600 for (key
, value
) in data
.iteritems()]
1603 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1604 for (key
, value
) in data
.iteritems()]
1605 typ
= self
._make
_implicit
_enum
_type
(name
, info
,
1606 [v
.name
for v
in variants
])
1607 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1608 members
= [tag_member
]
1610 QAPISchemaObjectType(name
, info
, doc
, base
, members
,
1611 QAPISchemaObjectTypeVariants(tag_name
,
1615 def _def_alternate_type(self
, expr
, info
, doc
):
1616 name
= expr
['alternate']
1618 variants
= [self
._make
_variant
(key
, value
)
1619 for (key
, value
) in data
.iteritems()]
1620 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1622 QAPISchemaAlternateType(name
, info
, doc
,
1623 QAPISchemaObjectTypeVariants(None,
1627 def _def_command(self
, expr
, info
, doc
):
1628 name
= expr
['command']
1629 data
= expr
.get('data')
1630 rets
= expr
.get('returns')
1631 gen
= expr
.get('gen', True)
1632 success_response
= expr
.get('success-response', True)
1633 boxed
= expr
.get('boxed', False)
1634 if isinstance(data
, OrderedDict
):
1635 data
= self
._make
_implicit
_object
_type
(
1636 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1637 if isinstance(rets
, list):
1638 assert len(rets
) == 1
1639 rets
= self
._make
_array
_type
(rets
[0], info
)
1640 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, data
, rets
,
1641 gen
, success_response
, boxed
))
1643 def _def_event(self
, expr
, info
, doc
):
1644 name
= expr
['event']
1645 data
= expr
.get('data')
1646 boxed
= expr
.get('boxed', False)
1647 if isinstance(data
, OrderedDict
):
1648 data
= self
._make
_implicit
_object
_type
(
1649 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1650 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, data
, boxed
))
1652 def _def_exprs(self
):
1653 for expr_elem
in self
.exprs
:
1654 expr
= expr_elem
['expr']
1655 info
= expr_elem
['info']
1656 doc
= expr_elem
.get('doc')
1658 self
._def
_enum
_type
(expr
, info
, doc
)
1659 elif 'struct' in expr
:
1660 self
._def
_struct
_type
(expr
, info
, doc
)
1661 elif 'union' in expr
:
1662 self
._def
_union
_type
(expr
, info
, doc
)
1663 elif 'alternate' in expr
:
1664 self
._def
_alternate
_type
(expr
, info
, doc
)
1665 elif 'command' in expr
:
1666 self
._def
_command
(expr
, info
, doc
)
1667 elif 'event' in expr
:
1668 self
._def
_event
(expr
, info
, doc
)
1673 for ent
in self
._entity
_dict
.values():
1676 def visit(self
, visitor
):
1677 visitor
.visit_begin(self
)
1678 for (name
, entity
) in sorted(self
._entity
_dict
.items()):
1679 if visitor
.visit_needed(entity
):
1680 entity
.visit(visitor
)
1685 # Code generation helpers
1688 def camel_case(name
):
1692 if ch
in ['_', '-']:
1695 new_name
+= ch
.upper()
1698 new_name
+= ch
.lower()
1702 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1703 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1704 # ENUM24_Name -> ENUM24_NAME
1705 def camel_to_upper(value
):
1706 c_fun_str
= c_name(value
, False)
1714 # When c is upper and no '_' appears before, do more checks
1715 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
1716 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1718 elif c_fun_str
[i
- 1].isdigit():
1721 return new_name
.lstrip('_').upper()
1724 def c_enum_const(type_name
, const_name
, prefix
=None):
1725 if prefix
is not None:
1727 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1729 c_name_trans
= string
.maketrans('.-', '__')
1732 # Map @name to a valid C identifier.
1733 # If @protect, avoid returning certain ticklish identifiers (like
1734 # C keywords) by prepending 'q_'.
1736 # Used for converting 'name' from a 'name':'type' qapi definition
1737 # into a generated struct member, as well as converting type names
1738 # into substrings of a generated C function name.
1739 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1740 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1741 def c_name(name
, protect
=True):
1742 # ANSI X3J11/88-090, 3.1.1
1743 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1744 'default', 'do', 'double', 'else', 'enum', 'extern',
1745 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1746 'return', 'short', 'signed', 'sizeof', 'static',
1747 'struct', 'switch', 'typedef', 'union', 'unsigned',
1748 'void', 'volatile', 'while'])
1749 # ISO/IEC 9899:1999, 6.4.1
1750 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1751 # ISO/IEC 9899:2011, 6.4.1
1752 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1753 '_Noreturn', '_Static_assert', '_Thread_local'])
1754 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1756 gcc_words
= set(['asm', 'typeof'])
1757 # C++ ISO/IEC 14882:2003 2.11
1758 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1759 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1760 'namespace', 'new', 'operator', 'private', 'protected',
1761 'public', 'reinterpret_cast', 'static_cast', 'template',
1762 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1763 'using', 'virtual', 'wchar_t',
1764 # alternative representations
1765 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1766 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1767 # namespace pollution:
1768 polluted_words
= set(['unix', 'errno', 'mips', 'sparc'])
1769 name
= name
.translate(c_name_trans
)
1770 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1771 | cpp_words | polluted_words
):
1775 eatspace
= '\033EATSPACE.'
1776 pointer_suffix
= ' *' + eatspace
1779 def genindent(count
):
1781 for _
in range(count
):
1788 def push_indent(indent_amount
=4):
1790 indent_level
+= indent_amount
1793 def pop_indent(indent_amount
=4):
1795 indent_level
-= indent_amount
1798 # Generate @code with @kwds interpolated.
1799 # Obey indent_level, and strip eatspace.
1800 def cgen(code
, **kwds
):
1803 indent
= genindent(indent_level
)
1804 # re.subn() lacks flags support before Python 2.7, use re.compile()
1805 raw
= re
.subn(re
.compile(r
'^.', re
.MULTILINE
),
1806 indent
+ r
'\g<0>', raw
)
1808 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
1811 def mcgen(code
, **kwds
):
1814 return cgen(code
, **kwds
)
1817 def guardname(filename
):
1818 return c_name(filename
, protect
=False).upper()
1821 def guardstart(name
):
1828 name
=guardname(name
))
1834 #endif /* %(name)s */
1837 name
=guardname(name
))
1840 def gen_enum_lookup(name
, values
, prefix
=None):
1843 const QEnumLookup %(c_name)s_lookup = {
1844 .array = (const char *const[]) {
1846 c_name
=c_name(name
))
1847 for value
in values
:
1848 index
= c_enum_const(name
, value
, prefix
)
1850 [%(index)s] = "%(value)s",
1852 index
=index
, value
=value
)
1856 .size = %(max_index)s
1859 max_index
=c_enum_const(name
, '_MAX', prefix
))
1863 def gen_enum(name
, values
, prefix
=None):
1864 # append automatically generated _MAX value
1865 enum_values
= values
+ ['_MAX']
1869 typedef enum %(c_name)s {
1871 c_name
=c_name(name
))
1874 for value
in enum_values
:
1878 c_enum
=c_enum_const(name
, value
, prefix
),
1885 c_name
=c_name(name
))
1889 #define %(c_name)s_str(val) \\
1890 qapi_enum_lookup(&%(c_name)s_lookup, (val))
1892 extern const QEnumLookup %(c_name)s_lookup;
1894 c_name
=c_name(name
))
1898 def build_params(arg_type
, boxed
, extra
):
1905 ret
+= '%s arg' % arg_type
.c_param_type()
1908 assert not arg_type
.variants
1909 for memb
in arg_type
.members
:
1913 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1914 ret
+= '%s %s' % (memb
.type.c_param_type(),
1922 # Common command line parsing
1926 def parse_command_line(extra_options
='', extra_long_options
=[]):
1929 opts
, args
= getopt
.gnu_getopt(sys
.argv
[1:],
1930 'chp:o:' + extra_options
,
1931 ['source', 'header', 'prefix=',
1932 'output-dir='] + extra_long_options
)
1933 except getopt
.GetoptError
as err
:
1934 print >>sys
.stderr
, "%s: %s" % (sys
.argv
[0], str(err
))
1945 if o
in ('-p', '--prefix'):
1946 match
= re
.match(r
'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a
)
1947 if match
.end() != len(a
):
1948 print >>sys
.stderr
, \
1949 "%s: 'funny character '%s' in argument of --prefix" \
1950 % (sys
.argv
[0], a
[match
.end()])
1953 elif o
in ('-o', '--output-dir'):
1954 output_dir
= a
+ '/'
1955 elif o
in ('-c', '--source'):
1957 elif o
in ('-h', '--header'):
1960 extra_opts
.append(oa
)
1962 if not do_c
and not do_h
:
1967 print >>sys
.stderr
, "%s: need exactly one argument" % sys
.argv
[0]
1971 return (fname
, output_dir
, do_c
, do_h
, prefix
, extra_opts
)
1974 # Generate output files with boilerplate
1978 def open_output(output_dir
, do_c
, do_h
, prefix
, c_file
, h_file
,
1979 c_comment
, h_comment
):
1980 guard
= guardname(prefix
+ h_file
)
1981 c_file
= output_dir
+ prefix
+ c_file
1982 h_file
= output_dir
+ prefix
+ h_file
1986 os
.makedirs(output_dir
)
1987 except os
.error
as e
:
1988 if e
.errno
!= errno
.EEXIST
:
1991 def maybe_open(really
, name
, opt
):
1993 return open(name
, opt
)
1996 return StringIO
.StringIO()
1998 fdef
= maybe_open(do_c
, c_file
, 'w')
1999 fdecl
= maybe_open(do_h
, h_file
, 'w')
2001 fdef
.write(mcgen('''
2002 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2007 fdecl
.write(mcgen('''
2008 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2014 comment
=h_comment
, guard
=guard
))
2016 return (fdef
, fdecl
)
2019 def close_output(fdef
, fdecl
):