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 'str': 'QTYPE_QSTRING',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
36 'any': None, # any QType possible, actually
37 'QType': 'QTYPE_QSTRING',
40 # Are documentation comments required?
43 # Whitelist of commands allowed to return a non-dictionary
44 returns_whitelist
= []
46 # Whitelist of entities allowed to violate case conventions
47 name_case_whitelist
= []
55 # Parsing the schema into expressions
59 def error_path(parent
):
62 res
= ('In file included from %s:%d:\n' % (parent
['file'],
63 parent
['line'])) + res
64 parent
= parent
['parent']
68 class QAPIError(Exception):
69 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
70 Exception.__init
__(self
)
78 loc
= '%s:%d' % (self
.fname
, self
.line
)
79 if self
.col
is not None:
80 loc
+= ':%s' % self
.col
81 return error_path(self
.info
) + '%s: %s' % (loc
, self
.msg
)
84 class QAPIParseError(QAPIError
):
85 def __init__(self
, parser
, msg
):
87 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
89 col
= (col
+ 7) % 8 + 1
92 QAPIError
.__init
__(self
, parser
.fname
, parser
.line
, col
,
93 parser
.incl_info
, msg
)
96 class QAPISemError(QAPIError
):
97 def __init__(self
, info
, msg
):
98 QAPIError
.__init
__(self
, info
['file'], info
['line'], None,
102 class QAPIDoc(object):
103 class Section(object):
104 def __init__(self
, name
=None):
105 # optional section name (argument/member or section name)
107 # the list of lines for this section
110 def append(self
, line
):
111 self
.content
.append(line
)
114 return '\n'.join(self
.content
).strip()
116 class ArgSection(Section
):
117 def __init__(self
, name
):
118 QAPIDoc
.Section
.__init
__(self
, name
)
121 def connect(self
, member
):
124 def __init__(self
, parser
, info
):
125 # self.parser is used to report errors with QAPIParseError. The
126 # resulting error position depends on the state of the parser.
127 # It happens to be the beginning of the comment. More or less
128 # servicable, but action at a distance.
132 self
.body
= QAPIDoc
.Section()
133 # dict mapping parameter name to ArgSection
134 self
.args
= OrderedDict()
137 # the current section
138 self
.section
= self
.body
140 def has_section(self
, name
):
141 """Return True if we have a section with this name."""
142 for i
in self
.sections
:
147 def append(self
, line
):
148 """Parse a comment line and add it to the documentation."""
151 self
._append
_freeform
(line
)
155 raise QAPIParseError(self
.parser
, "Missing space after #")
158 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
159 # recognized, and get silently treated as ordinary text
161 self
._append
_symbol
_line
(line
)
162 elif not self
.body
.content
and line
.startswith('@'):
163 if not line
.endswith(':'):
164 raise QAPIParseError(self
.parser
, "Line should end with :")
165 self
.symbol
= line
[1:-1]
166 # FIXME invalid names other than the empty string aren't flagged
168 raise QAPIParseError(self
.parser
, "Invalid name")
170 self
._append
_freeform
(line
)
172 def end_comment(self
):
175 def _append_symbol_line(self
, line
):
176 name
= line
.split(' ', 1)[0]
178 if name
.startswith('@') and name
.endswith(':'):
179 line
= line
[len(name
)+1:]
180 self
._start
_args
_section
(name
[1:-1])
181 elif name
in ('Returns:', 'Since:',
182 # those are often singular or plural
184 'Example:', 'Examples:',
186 line
= line
[len(name
)+1:]
187 self
._start
_section
(name
[:-1])
189 self
._append
_freeform
(line
)
191 def _start_args_section(self
, name
):
192 # FIXME invalid names other than the empty string aren't flagged
194 raise QAPIParseError(self
.parser
, "Invalid parameter name")
195 if name
in self
.args
:
196 raise QAPIParseError(self
.parser
,
197 "'%s' parameter name duplicated" % name
)
199 raise QAPIParseError(self
.parser
,
200 "'@%s:' can't follow '%s' section"
201 % (name
, self
.sections
[0].name
))
203 self
.section
= QAPIDoc
.ArgSection(name
)
204 self
.args
[name
] = self
.section
206 def _start_section(self
, name
=''):
207 if name
in ('Returns', 'Since') and self
.has_section(name
):
208 raise QAPIParseError(self
.parser
,
209 "Duplicated '%s' section" % name
)
211 self
.section
= QAPIDoc
.Section(name
)
212 self
.sections
.append(self
.section
)
214 def _end_section(self
):
216 contents
= str(self
.section
)
217 if self
.section
.name
and (not contents
or contents
.isspace()):
218 raise QAPIParseError(self
.parser
, "Empty doc section '%s'"
222 def _append_freeform(self
, line
):
223 in_arg
= isinstance(self
.section
, QAPIDoc
.ArgSection
)
224 if (in_arg
and self
.section
.content
225 and not self
.section
.content
[-1]
226 and line
and not line
[0].isspace()):
227 self
._start
_section
()
228 if (in_arg
or not self
.section
.name
229 or not self
.section
.name
.startswith('Example')):
231 match
= re
.match(r
'(@\S+:)', line
)
233 raise QAPIParseError(self
.parser
,
234 "'%s' not allowed in free-form documentation"
236 # TODO Drop this once the dust has settled
237 if (isinstance(self
.section
, QAPIDoc
.ArgSection
)
238 and '#optional' in line
):
239 raise QAPISemError(self
.info
, "Please drop the #optional tag")
240 self
.section
.append(line
)
242 def connect_member(self
, member
):
243 if member
.name
not in self
.args
:
244 # Undocumented TODO outlaw
245 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
246 self
.args
[member
.name
].connect(member
)
248 def check_expr(self
, expr
):
249 if self
.has_section('Returns') and 'command' not in expr
:
250 raise QAPISemError(self
.info
,
251 "'Returns:' is only valid for commands")
254 bogus
= [name
for name
, section
in self
.args
.iteritems()
255 if not section
.member
]
259 "The following documented members are not in "
260 "the declaration: %s" % ", ".join(bogus
))
263 class QAPISchemaParser(object):
265 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
266 abs_fname
= os
.path
.abspath(fp
.name
)
269 previously_included
.append(abs_fname
)
270 self
.incl_info
= incl_info
272 if self
.src
== '' or self
.src
[-1] != '\n':
282 while self
.tok
is not None:
283 info
= {'file': fname
, 'line': self
.line
,
284 'parent': self
.incl_info
}
286 self
.reject_expr_doc()
287 self
.cur_doc
= self
.get_doc(info
)
288 self
.docs
.append(self
.cur_doc
)
291 expr
= self
.get_expr(False)
292 if 'include' in expr
:
293 self
.reject_expr_doc()
295 raise QAPISemError(info
, "Invalid 'include' directive")
296 include
= expr
['include']
297 if not isinstance(include
, str):
298 raise QAPISemError(info
,
299 "Value of 'include' must be a string")
300 self
._include
(include
, info
, os
.path
.dirname(abs_fname
),
302 elif "pragma" in expr
:
303 self
.reject_expr_doc()
305 raise QAPISemError(info
, "Invalid 'pragma' directive")
306 pragma
= expr
['pragma']
307 if not isinstance(pragma
, dict):
309 info
, "Value of 'pragma' must be a dictionary")
310 for name
, value
in pragma
.iteritems():
311 self
._pragma
(name
, value
, info
)
313 expr_elem
= {'expr': expr
,
316 if not self
.cur_doc
.symbol
:
319 "Expression documentation required")
320 expr_elem
['doc'] = self
.cur_doc
321 self
.exprs
.append(expr_elem
)
323 self
.reject_expr_doc()
325 def reject_expr_doc(self
):
326 if self
.cur_doc
and self
.cur_doc
.symbol
:
329 "Documentation for '%s' is not followed by the definition"
330 % self
.cur_doc
.symbol
)
332 def _include(self
, include
, info
, base_dir
, previously_included
):
333 incl_abs_fname
= os
.path
.join(base_dir
, include
)
334 # catch inclusion cycle
337 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
338 raise QAPISemError(info
, "Inclusion loop for %s" % include
)
341 # skip multiple include of the same file
342 if incl_abs_fname
in previously_included
:
345 fobj
= open(incl_abs_fname
, 'r')
347 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, include
))
348 exprs_include
= QAPISchemaParser(fobj
, previously_included
, info
)
349 self
.exprs
.extend(exprs_include
.exprs
)
350 self
.docs
.extend(exprs_include
.docs
)
352 def _pragma(self
, name
, value
, info
):
353 global doc_required
, returns_whitelist
, name_case_whitelist
354 if name
== 'doc-required':
355 if not isinstance(value
, bool):
356 raise QAPISemError(info
,
357 "Pragma 'doc-required' must be boolean")
359 elif name
== 'returns-whitelist':
360 if (not isinstance(value
, list)
361 or any([not isinstance(elt
, str) for elt
in value
])):
362 raise QAPISemError(info
,
363 "Pragma returns-whitelist must be"
364 " a list of strings")
365 returns_whitelist
= value
366 elif name
== 'name-case-whitelist':
367 if (not isinstance(value
, list)
368 or any([not isinstance(elt
, str) for elt
in value
])):
369 raise QAPISemError(info
,
370 "Pragma name-case-whitelist must be"
371 " a list of strings")
372 name_case_whitelist
= value
374 raise QAPISemError(info
, "Unknown pragma '%s'" % name
)
376 def accept(self
, skip_comment
=True):
378 self
.tok
= self
.src
[self
.cursor
]
379 self
.pos
= self
.cursor
384 if self
.src
[self
.cursor
] == '#':
385 # Start of doc comment
387 self
.cursor
= self
.src
.find('\n', self
.cursor
)
389 self
.val
= self
.src
[self
.pos
:self
.cursor
]
391 elif self
.tok
in '{}:,[]':
393 elif self
.tok
== "'":
397 ch
= self
.src
[self
.cursor
]
400 raise QAPIParseError(self
, 'Missing terminating "\'"')
414 for _
in range(0, 4):
415 ch
= self
.src
[self
.cursor
]
417 if ch
not in '0123456789abcdefABCDEF':
418 raise QAPIParseError(self
,
419 '\\u escape needs 4 '
421 value
= (value
<< 4) + int(ch
, 16)
422 # If Python 2 and 3 didn't disagree so much on
423 # how to handle Unicode, then we could allow
424 # Unicode string defaults. But most of QAPI is
425 # ASCII-only, so we aren't losing much for now.
426 if not value
or value
> 0x7f:
427 raise QAPIParseError(self
,
428 'For now, \\u escape '
429 'only supports non-zero '
430 'values up to \\u007f')
435 raise QAPIParseError(self
,
436 "Unknown escape \\%s" % ch
)
445 elif self
.src
.startswith('true', self
.pos
):
449 elif self
.src
.startswith('false', self
.pos
):
453 elif self
.src
.startswith('null', self
.pos
):
457 elif self
.tok
== '\n':
458 if self
.cursor
== len(self
.src
):
462 self
.line_pos
= self
.cursor
463 elif not self
.tok
.isspace():
464 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
466 def get_members(self
):
472 raise QAPIParseError(self
, 'Expected string or "}"')
477 raise QAPIParseError(self
, 'Expected ":"')
480 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
481 expr
[key
] = self
.get_expr(True)
486 raise QAPIParseError(self
, 'Expected "," or "}"')
489 raise QAPIParseError(self
, 'Expected string')
491 def get_values(self
):
496 if self
.tok
not in "{['tfn":
497 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
500 expr
.append(self
.get_expr(True))
505 raise QAPIParseError(self
, 'Expected "," or "]"')
508 def get_expr(self
, nested
):
509 if self
.tok
!= '{' and not nested
:
510 raise QAPIParseError(self
, 'Expected "{"')
513 expr
= self
.get_members()
514 elif self
.tok
== '[':
516 expr
= self
.get_values()
517 elif self
.tok
in "'tfn":
521 raise QAPIParseError(self
, 'Expected "{", "[", string, '
525 def get_doc(self
, info
):
527 raise QAPIParseError(self
, "Junk after '##' at start of "
528 "documentation comment")
530 doc
= QAPIDoc(self
, info
)
532 while self
.tok
== '#':
533 if self
.val
.startswith('##'):
536 raise QAPIParseError(self
, "Junk after '##' at end of "
537 "documentation comment")
545 raise QAPIParseError(self
, "Documentation comment must end with '##'")
549 # Semantic analysis of schema expressions
550 # TODO fold into QAPISchema
551 # TODO catching name collisions in generated code would be nice
555 def find_base_members(base
):
556 if isinstance(base
, dict):
558 base_struct_define
= struct_types
.get(base
)
559 if not base_struct_define
:
561 return base_struct_define
['data']
564 # Return the qtype of an alternate branch, or None on error.
565 def find_alternate_member_qtype(qapi_type
):
566 if qapi_type
in builtin_types
:
567 return builtin_types
[qapi_type
]
568 elif qapi_type
in struct_types
:
570 elif qapi_type
in enum_types
:
571 return 'QTYPE_QSTRING'
572 elif qapi_type
in union_types
:
577 # Return the discriminator enum define if discriminator is specified as an
578 # enum type, otherwise return None.
579 def discriminator_find_enum_define(expr
):
580 base
= expr
.get('base')
581 discriminator
= expr
.get('discriminator')
583 if not (discriminator
and base
):
586 base_members
= find_base_members(base
)
590 discriminator_type
= base_members
.get(discriminator
)
591 if not discriminator_type
:
594 return enum_types
.get(discriminator_type
)
597 # Names must be letters, numbers, -, and _. They must start with letter,
598 # except for downstream extensions which must start with __RFQDN_.
599 # Dots are only valid in the downstream extension prefix.
600 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
601 '[a-zA-Z][a-zA-Z0-9_-]*$')
604 def check_name(info
, source
, name
, allow_optional
=False,
609 if not isinstance(name
, str):
610 raise QAPISemError(info
, "%s requires a string name" % source
)
611 if name
.startswith('*'):
612 membername
= name
[1:]
613 if not allow_optional
:
614 raise QAPISemError(info
, "%s does not allow optional name '%s'"
616 # Enum members can start with a digit, because the generated C
617 # code always prefixes it with the enum name
618 if enum_member
and membername
[0].isdigit():
619 membername
= 'D' + membername
620 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
621 # and 'q_obj_*' implicit type names.
622 if not valid_name
.match(membername
) or \
623 c_name(membername
, False).startswith('q_'):
624 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
627 def add_name(name
, info
, meta
, implicit
=False):
629 check_name(info
, "'%s'" % meta
, name
)
630 # FIXME should reject names that differ only in '_' vs. '.'
631 # vs. '-', because they're liable to clash in generated C.
632 if name
in all_names
:
633 raise QAPISemError(info
, "%s '%s' is already defined"
634 % (all_names
[name
], name
))
635 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
636 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
637 % (meta
, name
, name
[-4:]))
638 all_names
[name
] = meta
641 def check_type(info
, source
, value
, allow_array
=False,
642 allow_dict
=False, allow_optional
=False,
649 # Check if array type for value is okay
650 if isinstance(value
, list):
652 raise QAPISemError(info
, "%s cannot be an array" % source
)
653 if len(value
) != 1 or not isinstance(value
[0], str):
654 raise QAPISemError(info
,
655 "%s: array type must contain single type name" %
659 # Check if type name for value is okay
660 if isinstance(value
, str):
661 if value
not in all_names
:
662 raise QAPISemError(info
, "%s uses unknown type '%s'"
664 if not all_names
[value
] in allow_metas
:
665 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
666 (source
, all_names
[value
], value
))
670 raise QAPISemError(info
, "%s should be a type name" % source
)
672 if not isinstance(value
, OrderedDict
):
673 raise QAPISemError(info
,
674 "%s should be a dictionary or type name" % source
)
676 # value is a dictionary, check that each member is okay
677 for (key
, arg
) in value
.items():
678 check_name(info
, "Member of %s" % source
, key
,
679 allow_optional
=allow_optional
)
680 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
681 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
683 # Todo: allow dictionaries to represent default values of
684 # an optional argument.
685 check_type(info
, "Member '%s' of %s" % (key
, source
), arg
,
687 allow_metas
=['built-in', 'union', 'alternate', 'struct',
691 def check_command(expr
, info
):
692 name
= expr
['command']
693 boxed
= expr
.get('boxed', False)
695 args_meta
= ['struct']
697 args_meta
+= ['union', 'alternate']
698 check_type(info
, "'data' for command '%s'" % name
,
699 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
700 allow_metas
=args_meta
)
701 returns_meta
= ['union', 'struct']
702 if name
in returns_whitelist
:
703 returns_meta
+= ['built-in', 'alternate', 'enum']
704 check_type(info
, "'returns' for command '%s'" % name
,
705 expr
.get('returns'), allow_array
=True,
706 allow_optional
=True, allow_metas
=returns_meta
)
709 def check_event(expr
, info
):
711 boxed
= expr
.get('boxed', False)
715 meta
+= ['union', 'alternate']
716 check_type(info
, "'data' for event '%s'" % name
,
717 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
721 def check_union(expr
, info
):
723 base
= expr
.get('base')
724 discriminator
= expr
.get('discriminator')
725 members
= expr
['data']
727 # Two types of unions, determined by discriminator.
729 # With no discriminator it is a simple union.
730 if discriminator
is None:
732 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
734 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
737 # Else, it's a flat union.
739 # The object must have a string or dictionary 'base'.
740 check_type(info
, "'base' for union '%s'" % name
,
741 base
, allow_dict
=True, allow_optional
=True,
742 allow_metas
=['struct'])
744 raise QAPISemError(info
, "Flat union '%s' must have a base"
746 base_members
= find_base_members(base
)
747 assert base_members
is not None
749 # The value of member 'discriminator' must name a non-optional
750 # member of the base struct.
751 check_name(info
, "Discriminator of flat union '%s'" % name
,
753 discriminator_type
= base_members
.get(discriminator
)
754 if not discriminator_type
:
755 raise QAPISemError(info
,
756 "Discriminator '%s' is not a member of base "
758 % (discriminator
, base
))
759 enum_define
= enum_types
.get(discriminator_type
)
760 allow_metas
= ['struct']
761 # Do not allow string discriminator
763 raise QAPISemError(info
,
764 "Discriminator '%s' must be of enumeration "
765 "type" % discriminator
)
767 # Check every branch; don't allow an empty union
768 if len(members
) == 0:
769 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
770 for (key
, value
) in members
.items():
771 check_name(info
, "Member of union '%s'" % name
, key
)
773 # Each value must name a known type
774 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
775 value
, allow_array
=not base
, allow_metas
=allow_metas
)
777 # If the discriminator names an enum type, then all members
778 # of 'data' must also be members of the enum type.
780 if key
not in enum_define
['data']:
781 raise QAPISemError(info
,
782 "Discriminator value '%s' is not found in "
784 % (key
, enum_define
['enum']))
786 # If discriminator is user-defined, ensure all values are covered
788 for value
in enum_define
['data']:
789 if value
not in members
.keys():
790 raise QAPISemError(info
, "Union '%s' data missing '%s' branch"
794 def check_alternate(expr
, info
):
795 name
= expr
['alternate']
796 members
= expr
['data']
799 # Check every branch; require at least two branches
801 raise QAPISemError(info
,
802 "Alternate '%s' should have at least two branches "
804 for (key
, value
) in members
.items():
805 check_name(info
, "Member of alternate '%s'" % name
, key
)
807 # Ensure alternates have no type conflicts.
808 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
),
810 allow_metas
=['built-in', 'union', 'struct', 'enum'])
811 qtype
= find_alternate_member_qtype(value
)
813 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
814 "type '%s'" % (name
, key
, value
))
815 if qtype
in types_seen
:
816 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
817 "be distinguished from member '%s'"
818 % (name
, key
, types_seen
[qtype
]))
819 types_seen
[qtype
] = key
822 def check_enum(expr
, info
):
824 members
= expr
.get('data')
825 prefix
= expr
.get('prefix')
827 if not isinstance(members
, list):
828 raise QAPISemError(info
,
829 "Enum '%s' requires an array for 'data'" % name
)
830 if prefix
is not None and not isinstance(prefix
, str):
831 raise QAPISemError(info
,
832 "Enum '%s' requires a string for 'prefix'" % name
)
833 for member
in members
:
834 check_name(info
, "Member of enum '%s'" % name
, member
,
838 def check_struct(expr
, info
):
839 name
= expr
['struct']
840 members
= expr
['data']
842 check_type(info
, "'data' for struct '%s'" % name
, members
,
843 allow_dict
=True, allow_optional
=True)
844 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
845 allow_metas
=['struct'])
848 def check_keys(expr_elem
, meta
, required
, optional
=[]):
849 expr
= expr_elem
['expr']
850 info
= expr_elem
['info']
852 if not isinstance(name
, str):
853 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
854 required
= required
+ [meta
]
855 for (key
, value
) in expr
.items():
856 if key
not in required
and key
not in optional
:
857 raise QAPISemError(info
, "Unknown key '%s' in %s '%s'"
859 if (key
== 'gen' or key
== 'success-response') and value
is not False:
860 raise QAPISemError(info
,
861 "'%s' of %s '%s' should only use false value"
863 if key
== 'boxed' and value
is not True:
864 raise QAPISemError(info
,
865 "'%s' of %s '%s' should only use true value"
869 raise QAPISemError(info
, "Key '%s' is missing from %s '%s'"
873 def check_exprs(exprs
):
876 # Populate name table with names of built-in types
877 for builtin
in builtin_types
.keys():
878 all_names
[builtin
] = 'built-in'
880 # Learn the types and check for valid expression keys
881 for expr_elem
in exprs
:
882 expr
= expr_elem
['expr']
883 info
= expr_elem
['info']
884 doc
= expr_elem
.get('doc')
886 if not doc
and doc_required
:
887 raise QAPISemError(info
,
888 "Expression missing documentation comment")
892 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
893 enum_types
[expr
[meta
]] = expr
894 elif 'union' in expr
:
896 check_keys(expr_elem
, 'union', ['data'],
897 ['base', 'discriminator'])
898 union_types
[expr
[meta
]] = expr
899 elif 'alternate' in expr
:
901 check_keys(expr_elem
, 'alternate', ['data'])
902 elif 'struct' in expr
:
904 check_keys(expr_elem
, 'struct', ['data'], ['base'])
905 struct_types
[expr
[meta
]] = expr
906 elif 'command' in expr
:
908 check_keys(expr_elem
, 'command', [],
909 ['data', 'returns', 'gen', 'success-response', 'boxed'])
910 elif 'event' in expr
:
912 check_keys(expr_elem
, 'event', [], ['data', 'boxed'])
914 raise QAPISemError(expr_elem
['info'],
915 "Expression is missing metatype")
917 add_name(name
, info
, meta
)
918 if doc
and doc
.symbol
!= name
:
919 raise QAPISemError(info
, "Definition of '%s' follows documentation"
920 " for '%s'" % (name
, doc
.symbol
))
922 # Try again for hidden UnionKind enum
923 for expr_elem
in exprs
:
924 expr
= expr_elem
['expr']
925 if 'union' in expr
and not discriminator_find_enum_define(expr
):
926 name
= '%sKind' % expr
['union']
927 elif 'alternate' in expr
:
928 name
= '%sKind' % expr
['alternate']
931 enum_types
[name
] = {'enum': name
}
932 add_name(name
, info
, 'enum', implicit
=True)
934 # Validate that exprs make sense
935 for expr_elem
in exprs
:
936 expr
= expr_elem
['expr']
937 info
= expr_elem
['info']
938 doc
= expr_elem
.get('doc')
941 check_enum(expr
, info
)
942 elif 'union' in expr
:
943 check_union(expr
, info
)
944 elif 'alternate' in expr
:
945 check_alternate(expr
, info
)
946 elif 'struct' in expr
:
947 check_struct(expr
, info
)
948 elif 'command' in expr
:
949 check_command(expr
, info
)
950 elif 'event' in expr
:
951 check_event(expr
, info
)
953 assert False, 'unexpected meta type'
962 # Schema compiler frontend
965 class QAPISchemaEntity(object):
966 def __init__(self
, name
, info
, doc
):
967 assert isinstance(name
, str)
969 # For explicitly defined entities, info points to the (explicit)
970 # definition. For builtins (and their arrays), info is None.
971 # For implicitly defined entities, info points to a place that
972 # triggered the implicit definition (there may be more than one
978 return c_name(self
.name
)
980 def check(self
, schema
):
983 def is_implicit(self
):
986 def visit(self
, visitor
):
990 class QAPISchemaVisitor(object):
991 def visit_begin(self
, schema
):
997 def visit_needed(self
, entity
):
998 # Default to visiting everything
1001 def visit_builtin_type(self
, name
, info
, json_type
):
1004 def visit_enum_type(self
, name
, info
, values
, prefix
):
1007 def visit_array_type(self
, name
, info
, element_type
):
1010 def visit_object_type(self
, name
, info
, base
, members
, variants
):
1013 def visit_object_type_flat(self
, name
, info
, members
, variants
):
1016 def visit_alternate_type(self
, name
, info
, variants
):
1019 def visit_command(self
, name
, info
, arg_type
, ret_type
,
1020 gen
, success_response
, boxed
):
1023 def visit_event(self
, name
, info
, arg_type
, boxed
):
1027 class QAPISchemaType(QAPISchemaEntity
):
1028 # Return the C type for common use.
1029 # For the types we commonly box, this is a pointer type.
1033 # Return the C type to be used in a parameter list.
1034 def c_param_type(self
):
1035 return self
.c_type()
1037 # Return the C type to be used where we suppress boxing.
1038 def c_unboxed_type(self
):
1039 return self
.c_type()
1041 def json_type(self
):
1044 def alternate_qtype(self
):
1046 'string': 'QTYPE_QSTRING',
1047 'number': 'QTYPE_QFLOAT',
1048 'int': 'QTYPE_QINT',
1049 'boolean': 'QTYPE_QBOOL',
1050 'object': 'QTYPE_QDICT'
1052 return json2qtype
.get(self
.json_type())
1055 if self
.is_implicit():
1060 class QAPISchemaBuiltinType(QAPISchemaType
):
1061 def __init__(self
, name
, json_type
, c_type
):
1062 QAPISchemaType
.__init
__(self
, name
, None, None)
1063 assert not c_type
or isinstance(c_type
, str)
1064 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1066 self
._json
_type
_name
= json_type
1067 self
._c
_type
_name
= c_type
1073 return self
._c
_type
_name
1075 def c_param_type(self
):
1076 if self
.name
== 'str':
1077 return 'const ' + self
._c
_type
_name
1078 return self
._c
_type
_name
1080 def json_type(self
):
1081 return self
._json
_type
_name
1084 return self
.json_type()
1086 def visit(self
, visitor
):
1087 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1090 class QAPISchemaEnumType(QAPISchemaType
):
1091 def __init__(self
, name
, info
, doc
, values
, prefix
):
1092 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1094 assert isinstance(v
, QAPISchemaMember
)
1096 assert prefix
is None or isinstance(prefix
, str)
1097 self
.values
= values
1098 self
.prefix
= prefix
1100 def check(self
, schema
):
1102 for v
in self
.values
:
1103 v
.check_clash(self
.info
, seen
)
1105 self
.doc
.connect_member(v
)
1107 def is_implicit(self
):
1108 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1109 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1112 return c_name(self
.name
)
1114 def member_names(self
):
1115 return [v
.name
for v
in self
.values
]
1117 def json_type(self
):
1120 def visit(self
, visitor
):
1121 visitor
.visit_enum_type(self
.name
, self
.info
,
1122 self
.member_names(), self
.prefix
)
1125 class QAPISchemaArrayType(QAPISchemaType
):
1126 def __init__(self
, name
, info
, element_type
):
1127 QAPISchemaType
.__init
__(self
, name
, info
, None)
1128 assert isinstance(element_type
, str)
1129 self
._element
_type
_name
= element_type
1130 self
.element_type
= None
1132 def check(self
, schema
):
1133 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1134 assert self
.element_type
1136 def is_implicit(self
):
1140 return c_name(self
.name
) + pointer_suffix
1142 def json_type(self
):
1146 elt_doc_type
= self
.element_type
.doc_type()
1147 if not elt_doc_type
:
1149 return 'array of ' + elt_doc_type
1151 def visit(self
, visitor
):
1152 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
1155 class QAPISchemaObjectType(QAPISchemaType
):
1156 def __init__(self
, name
, info
, doc
, base
, local_members
, variants
):
1157 # struct has local_members, optional base, and no variants
1158 # flat union has base, variants, and no local_members
1159 # simple union has local_members, variants, and no base
1160 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1161 assert base
is None or isinstance(base
, str)
1162 for m
in local_members
:
1163 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1165 if variants
is not None:
1166 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1167 variants
.set_owner(name
)
1168 self
._base
_name
= base
1170 self
.local_members
= local_members
1171 self
.variants
= variants
1174 def check(self
, schema
):
1175 if self
.members
is False: # check for cycles
1176 raise QAPISemError(self
.info
,
1177 "Object %s contains itself" % self
.name
)
1180 self
.members
= False # mark as being checked
1181 seen
= OrderedDict()
1183 self
.base
= schema
.lookup_type(self
._base
_name
)
1184 assert isinstance(self
.base
, QAPISchemaObjectType
)
1185 self
.base
.check(schema
)
1186 self
.base
.check_clash(self
.info
, seen
)
1187 for m
in self
.local_members
:
1189 m
.check_clash(self
.info
, seen
)
1191 self
.doc
.connect_member(m
)
1192 self
.members
= seen
.values()
1194 self
.variants
.check(schema
, seen
)
1195 assert self
.variants
.tag_member
in self
.members
1196 self
.variants
.check_clash(self
.info
, seen
)
1200 # Check that the members of this type do not cause duplicate JSON members,
1201 # and update seen to track the members seen so far. Report any errors
1202 # on behalf of info, which is not necessarily self.info
1203 def check_clash(self
, info
, seen
):
1204 assert not self
.variants
# not implemented
1205 for m
in self
.members
:
1206 m
.check_clash(info
, seen
)
1208 def is_implicit(self
):
1209 # See QAPISchema._make_implicit_object_type(), as well as
1210 # _def_predefineds()
1211 return self
.name
.startswith('q_')
1214 assert self
.members
is not None
1215 return not self
.members
and not self
.variants
1218 assert self
.name
!= 'q_empty'
1219 return QAPISchemaType
.c_name(self
)
1222 assert not self
.is_implicit()
1223 return c_name(self
.name
) + pointer_suffix
1225 def c_unboxed_type(self
):
1226 return c_name(self
.name
)
1228 def json_type(self
):
1231 def visit(self
, visitor
):
1232 visitor
.visit_object_type(self
.name
, self
.info
,
1233 self
.base
, self
.local_members
, self
.variants
)
1234 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1235 self
.members
, self
.variants
)
1238 class QAPISchemaMember(object):
1241 def __init__(self
, name
):
1242 assert isinstance(name
, str)
1246 def set_owner(self
, name
):
1247 assert not self
.owner
1250 def check_clash(self
, info
, seen
):
1251 cname
= c_name(self
.name
)
1252 if cname
.lower() != cname
and self
.owner
not in name_case_whitelist
:
1253 raise QAPISemError(info
,
1254 "%s should not use uppercase" % self
.describe())
1256 raise QAPISemError(info
, "%s collides with %s" %
1257 (self
.describe(), seen
[cname
].describe()))
1260 def _pretty_owner(self
):
1262 if owner
.startswith('q_obj_'):
1263 # See QAPISchema._make_implicit_object_type() - reverse the
1264 # mapping there to create a nice human-readable description
1266 if owner
.endswith('-arg'):
1267 return '(parameter of %s)' % owner
[:-4]
1268 elif owner
.endswith('-base'):
1269 return '(base of %s)' % owner
[:-5]
1271 assert owner
.endswith('-wrapper')
1272 # Unreachable and not implemented
1274 if owner
.endswith('Kind'):
1275 # See QAPISchema._make_implicit_enum_type()
1276 return '(branch of %s)' % owner
[:-4]
1277 return '(%s of %s)' % (self
.role
, owner
)
1280 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1283 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1284 def __init__(self
, name
, typ
, optional
):
1285 QAPISchemaMember
.__init
__(self
, name
)
1286 assert isinstance(typ
, str)
1287 assert isinstance(optional
, bool)
1288 self
._type
_name
= typ
1290 self
.optional
= optional
1292 def check(self
, schema
):
1294 self
.type = schema
.lookup_type(self
._type
_name
)
1298 class QAPISchemaObjectTypeVariants(object):
1299 def __init__(self
, tag_name
, tag_member
, variants
):
1300 # Flat unions pass tag_name but not tag_member.
1301 # Simple unions and alternates pass tag_member but not tag_name.
1302 # After check(), tag_member is always set, and tag_name remains
1303 # a reliable witness of being used by a flat union.
1304 assert bool(tag_member
) != bool(tag_name
)
1305 assert (isinstance(tag_name
, str) or
1306 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1307 assert len(variants
) > 0
1309 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1310 self
._tag
_name
= tag_name
1311 self
.tag_member
= tag_member
1312 self
.variants
= variants
1314 def set_owner(self
, name
):
1315 for v
in self
.variants
:
1318 def check(self
, schema
, seen
):
1319 if not self
.tag_member
: # flat union
1320 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1321 assert self
._tag
_name
== self
.tag_member
.name
1322 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1323 for v
in self
.variants
:
1325 # Union names must match enum values; alternate names are
1326 # checked separately. Use 'seen' to tell the two apart.
1328 assert v
.name
in self
.tag_member
.type.member_names()
1329 assert isinstance(v
.type, QAPISchemaObjectType
)
1330 v
.type.check(schema
)
1332 def check_clash(self
, info
, seen
):
1333 for v
in self
.variants
:
1334 # Reset seen map for each variant, since qapi names from one
1335 # branch do not affect another branch
1336 assert isinstance(v
.type, QAPISchemaObjectType
)
1337 v
.type.check_clash(info
, dict(seen
))
1340 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1343 def __init__(self
, name
, typ
):
1344 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1347 class QAPISchemaAlternateType(QAPISchemaType
):
1348 def __init__(self
, name
, info
, doc
, variants
):
1349 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1350 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1351 assert variants
.tag_member
1352 variants
.set_owner(name
)
1353 variants
.tag_member
.set_owner(self
.name
)
1354 self
.variants
= variants
1356 def check(self
, schema
):
1357 self
.variants
.tag_member
.check(schema
)
1358 # Not calling self.variants.check_clash(), because there's nothing
1360 self
.variants
.check(schema
, {})
1361 # Alternate branch names have no relation to the tag enum values;
1362 # so we have to check for potential name collisions ourselves.
1364 for v
in self
.variants
.variants
:
1365 v
.check_clash(self
.info
, seen
)
1367 self
.doc
.connect_member(v
)
1372 return c_name(self
.name
) + pointer_suffix
1374 def json_type(self
):
1377 def visit(self
, visitor
):
1378 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1384 class QAPISchemaCommand(QAPISchemaEntity
):
1385 def __init__(self
, name
, info
, doc
, arg_type
, ret_type
,
1386 gen
, success_response
, boxed
):
1387 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1388 assert not arg_type
or isinstance(arg_type
, str)
1389 assert not ret_type
or isinstance(ret_type
, str)
1390 self
._arg
_type
_name
= arg_type
1391 self
.arg_type
= None
1392 self
._ret
_type
_name
= ret_type
1393 self
.ret_type
= None
1395 self
.success_response
= success_response
1398 def check(self
, schema
):
1399 if self
._arg
_type
_name
:
1400 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1401 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1402 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1403 self
.arg_type
.check(schema
)
1405 if self
.arg_type
.is_empty():
1406 raise QAPISemError(self
.info
,
1407 "Cannot use 'boxed' with empty type")
1409 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1410 assert not self
.arg_type
.variants
1412 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1413 if self
._ret
_type
_name
:
1414 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1415 assert isinstance(self
.ret_type
, QAPISchemaType
)
1417 def visit(self
, visitor
):
1418 visitor
.visit_command(self
.name
, self
.info
,
1419 self
.arg_type
, self
.ret_type
,
1420 self
.gen
, self
.success_response
, self
.boxed
)
1423 class QAPISchemaEvent(QAPISchemaEntity
):
1424 def __init__(self
, name
, info
, doc
, arg_type
, boxed
):
1425 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1426 assert not arg_type
or isinstance(arg_type
, str)
1427 self
._arg
_type
_name
= arg_type
1428 self
.arg_type
= None
1431 def check(self
, schema
):
1432 if self
._arg
_type
_name
:
1433 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1434 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1435 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1436 self
.arg_type
.check(schema
)
1438 if self
.arg_type
.is_empty():
1439 raise QAPISemError(self
.info
,
1440 "Cannot use 'boxed' with empty type")
1442 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1443 assert not self
.arg_type
.variants
1445 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1447 def visit(self
, visitor
):
1448 visitor
.visit_event(self
.name
, self
.info
, self
.arg_type
, self
.boxed
)
1451 class QAPISchema(object):
1452 def __init__(self
, fname
):
1454 parser
= QAPISchemaParser(open(fname
, 'r'))
1455 self
.exprs
= check_exprs(parser
.exprs
)
1456 self
.docs
= parser
.docs
1457 self
._entity
_dict
= {}
1458 self
._predefining
= True
1459 self
._def
_predefineds
()
1460 self
._predefining
= False
1463 except QAPIError
as err
:
1464 print >>sys
.stderr
, err
1467 def _def_entity(self
, ent
):
1468 # Only the predefined types are allowed to not have info
1469 assert ent
.info
or self
._predefining
1470 assert ent
.name
not in self
._entity
_dict
1471 self
._entity
_dict
[ent
.name
] = ent
1473 def lookup_entity(self
, name
, typ
=None):
1474 ent
= self
._entity
_dict
.get(name
)
1475 if typ
and not isinstance(ent
, typ
):
1479 def lookup_type(self
, name
):
1480 return self
.lookup_entity(name
, QAPISchemaType
)
1482 def _def_builtin_type(self
, name
, json_type
, c_type
):
1483 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1484 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1485 # qapi-types.h from a single .c, all arrays of builtins must be
1486 # declared in the first file whether or not they are used. Nicer
1487 # would be to use lazy instantiation, while figuring out how to
1488 # avoid compilation issues with multiple qapi-types.h.
1489 self
._make
_array
_type
(name
, None)
1491 def _def_predefineds(self
):
1492 for t
in [('str', 'string', 'char' + pointer_suffix
),
1493 ('number', 'number', 'double'),
1494 ('int', 'int', 'int64_t'),
1495 ('int8', 'int', 'int8_t'),
1496 ('int16', 'int', 'int16_t'),
1497 ('int32', 'int', 'int32_t'),
1498 ('int64', 'int', 'int64_t'),
1499 ('uint8', 'int', 'uint8_t'),
1500 ('uint16', 'int', 'uint16_t'),
1501 ('uint32', 'int', 'uint32_t'),
1502 ('uint64', 'int', 'uint64_t'),
1503 ('size', 'int', 'uint64_t'),
1504 ('bool', 'boolean', 'bool'),
1505 ('any', 'value', 'QObject' + pointer_suffix
)]:
1506 self
._def
_builtin
_type
(*t
)
1507 self
.the_empty_object_type
= QAPISchemaObjectType(
1508 'q_empty', None, None, None, [], None)
1509 self
._def
_entity
(self
.the_empty_object_type
)
1510 qtype_values
= self
._make
_enum
_members
(['none', 'qnull', 'qint',
1511 'qstring', 'qdict', 'qlist',
1513 self
._def
_entity
(QAPISchemaEnumType('QType', None, None,
1514 qtype_values
, 'QTYPE'))
1516 def _make_enum_members(self
, values
):
1517 return [QAPISchemaMember(v
) for v
in values
]
1519 def _make_implicit_enum_type(self
, name
, info
, values
):
1520 # See also QAPISchemaObjectTypeMember._pretty_owner()
1521 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1522 self
._def
_entity
(QAPISchemaEnumType(
1523 name
, info
, None, self
._make
_enum
_members
(values
), None))
1526 def _make_array_type(self
, element_type
, info
):
1527 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1528 if not self
.lookup_type(name
):
1529 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1532 def _make_implicit_object_type(self
, name
, info
, doc
, role
, members
):
1535 # See also QAPISchemaObjectTypeMember._pretty_owner()
1536 name
= 'q_obj_%s-%s' % (name
, role
)
1537 if not self
.lookup_entity(name
, QAPISchemaObjectType
):
1538 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, None,
1542 def _def_enum_type(self
, expr
, info
, doc
):
1545 prefix
= expr
.get('prefix')
1546 self
._def
_entity
(QAPISchemaEnumType(
1547 name
, info
, doc
, self
._make
_enum
_members
(data
), prefix
))
1549 def _make_member(self
, name
, typ
, info
):
1551 if name
.startswith('*'):
1554 if isinstance(typ
, list):
1555 assert len(typ
) == 1
1556 typ
= self
._make
_array
_type
(typ
[0], info
)
1557 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1559 def _make_members(self
, data
, info
):
1560 return [self
._make
_member
(key
, value
, info
)
1561 for (key
, value
) in data
.iteritems()]
1563 def _def_struct_type(self
, expr
, info
, doc
):
1564 name
= expr
['struct']
1565 base
= expr
.get('base')
1567 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, base
,
1568 self
._make
_members
(data
, info
),
1571 def _make_variant(self
, case
, typ
):
1572 return QAPISchemaObjectTypeVariant(case
, typ
)
1574 def _make_simple_variant(self
, case
, typ
, info
):
1575 if isinstance(typ
, list):
1576 assert len(typ
) == 1
1577 typ
= self
._make
_array
_type
(typ
[0], info
)
1578 typ
= self
._make
_implicit
_object
_type
(
1579 typ
, info
, None, 'wrapper', [self
._make
_member
('data', typ
, info
)])
1580 return QAPISchemaObjectTypeVariant(case
, typ
)
1582 def _def_union_type(self
, expr
, info
, doc
):
1583 name
= expr
['union']
1585 base
= expr
.get('base')
1586 tag_name
= expr
.get('discriminator')
1588 if isinstance(base
, dict):
1589 base
= (self
._make
_implicit
_object
_type
(
1590 name
, info
, doc
, 'base', self
._make
_members
(base
, info
)))
1592 variants
= [self
._make
_variant
(key
, value
)
1593 for (key
, value
) in data
.iteritems()]
1596 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1597 for (key
, value
) in data
.iteritems()]
1598 typ
= self
._make
_implicit
_enum
_type
(name
, info
,
1599 [v
.name
for v
in variants
])
1600 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1601 members
= [tag_member
]
1603 QAPISchemaObjectType(name
, info
, doc
, base
, members
,
1604 QAPISchemaObjectTypeVariants(tag_name
,
1608 def _def_alternate_type(self
, expr
, info
, doc
):
1609 name
= expr
['alternate']
1611 variants
= [self
._make
_variant
(key
, value
)
1612 for (key
, value
) in data
.iteritems()]
1613 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1615 QAPISchemaAlternateType(name
, info
, doc
,
1616 QAPISchemaObjectTypeVariants(None,
1620 def _def_command(self
, expr
, info
, doc
):
1621 name
= expr
['command']
1622 data
= expr
.get('data')
1623 rets
= expr
.get('returns')
1624 gen
= expr
.get('gen', True)
1625 success_response
= expr
.get('success-response', True)
1626 boxed
= expr
.get('boxed', False)
1627 if isinstance(data
, OrderedDict
):
1628 data
= self
._make
_implicit
_object
_type
(
1629 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1630 if isinstance(rets
, list):
1631 assert len(rets
) == 1
1632 rets
= self
._make
_array
_type
(rets
[0], info
)
1633 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, data
, rets
,
1634 gen
, success_response
, boxed
))
1636 def _def_event(self
, expr
, info
, doc
):
1637 name
= expr
['event']
1638 data
= expr
.get('data')
1639 boxed
= expr
.get('boxed', False)
1640 if isinstance(data
, OrderedDict
):
1641 data
= self
._make
_implicit
_object
_type
(
1642 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1643 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, data
, boxed
))
1645 def _def_exprs(self
):
1646 for expr_elem
in self
.exprs
:
1647 expr
= expr_elem
['expr']
1648 info
= expr_elem
['info']
1649 doc
= expr_elem
.get('doc')
1651 self
._def
_enum
_type
(expr
, info
, doc
)
1652 elif 'struct' in expr
:
1653 self
._def
_struct
_type
(expr
, info
, doc
)
1654 elif 'union' in expr
:
1655 self
._def
_union
_type
(expr
, info
, doc
)
1656 elif 'alternate' in expr
:
1657 self
._def
_alternate
_type
(expr
, info
, doc
)
1658 elif 'command' in expr
:
1659 self
._def
_command
(expr
, info
, doc
)
1660 elif 'event' in expr
:
1661 self
._def
_event
(expr
, info
, doc
)
1666 for ent
in self
._entity
_dict
.values():
1669 def visit(self
, visitor
):
1670 visitor
.visit_begin(self
)
1671 for (name
, entity
) in sorted(self
._entity
_dict
.items()):
1672 if visitor
.visit_needed(entity
):
1673 entity
.visit(visitor
)
1678 # Code generation helpers
1681 def camel_case(name
):
1685 if ch
in ['_', '-']:
1688 new_name
+= ch
.upper()
1691 new_name
+= ch
.lower()
1695 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1696 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1697 # ENUM24_Name -> ENUM24_NAME
1698 def camel_to_upper(value
):
1699 c_fun_str
= c_name(value
, False)
1707 # When c is upper and no '_' appears before, do more checks
1708 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
1709 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1711 elif c_fun_str
[i
- 1].isdigit():
1714 return new_name
.lstrip('_').upper()
1717 def c_enum_const(type_name
, const_name
, prefix
=None):
1718 if prefix
is not None:
1720 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1722 c_name_trans
= string
.maketrans('.-', '__')
1725 # Map @name to a valid C identifier.
1726 # If @protect, avoid returning certain ticklish identifiers (like
1727 # C keywords) by prepending 'q_'.
1729 # Used for converting 'name' from a 'name':'type' qapi definition
1730 # into a generated struct member, as well as converting type names
1731 # into substrings of a generated C function name.
1732 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1733 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1734 def c_name(name
, protect
=True):
1735 # ANSI X3J11/88-090, 3.1.1
1736 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1737 'default', 'do', 'double', 'else', 'enum', 'extern',
1738 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1739 'return', 'short', 'signed', 'sizeof', 'static',
1740 'struct', 'switch', 'typedef', 'union', 'unsigned',
1741 'void', 'volatile', 'while'])
1742 # ISO/IEC 9899:1999, 6.4.1
1743 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1744 # ISO/IEC 9899:2011, 6.4.1
1745 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1746 '_Noreturn', '_Static_assert', '_Thread_local'])
1747 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1749 gcc_words
= set(['asm', 'typeof'])
1750 # C++ ISO/IEC 14882:2003 2.11
1751 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1752 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1753 'namespace', 'new', 'operator', 'private', 'protected',
1754 'public', 'reinterpret_cast', 'static_cast', 'template',
1755 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1756 'using', 'virtual', 'wchar_t',
1757 # alternative representations
1758 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1759 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1760 # namespace pollution:
1761 polluted_words
= set(['unix', 'errno', 'mips', 'sparc'])
1762 name
= name
.translate(c_name_trans
)
1763 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1764 | cpp_words | polluted_words
):
1768 eatspace
= '\033EATSPACE.'
1769 pointer_suffix
= ' *' + eatspace
1772 def genindent(count
):
1774 for _
in range(count
):
1781 def push_indent(indent_amount
=4):
1783 indent_level
+= indent_amount
1786 def pop_indent(indent_amount
=4):
1788 indent_level
-= indent_amount
1791 # Generate @code with @kwds interpolated.
1792 # Obey indent_level, and strip eatspace.
1793 def cgen(code
, **kwds
):
1796 indent
= genindent(indent_level
)
1797 # re.subn() lacks flags support before Python 2.7, use re.compile()
1798 raw
= re
.subn(re
.compile(r
'^.', re
.MULTILINE
),
1799 indent
+ r
'\g<0>', raw
)
1801 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
1804 def mcgen(code
, **kwds
):
1807 return cgen(code
, **kwds
)
1810 def guardname(filename
):
1811 return c_name(filename
, protect
=False).upper()
1814 def guardstart(name
):
1821 name
=guardname(name
))
1827 #endif /* %(name)s */
1830 name
=guardname(name
))
1833 def gen_enum_lookup(name
, values
, prefix
=None):
1836 const char *const %(c_name)s_lookup[] = {
1838 c_name
=c_name(name
))
1839 for value
in values
:
1840 index
= c_enum_const(name
, value
, prefix
)
1842 [%(index)s] = "%(value)s",
1844 index
=index
, value
=value
)
1846 max_index
= c_enum_const(name
, '_MAX', prefix
)
1848 [%(max_index)s] = NULL,
1851 max_index
=max_index
)
1855 def gen_enum(name
, values
, prefix
=None):
1856 # append automatically generated _MAX value
1857 enum_values
= values
+ ['_MAX']
1861 typedef enum %(c_name)s {
1863 c_name
=c_name(name
))
1866 for value
in enum_values
:
1870 c_enum
=c_enum_const(name
, value
, prefix
),
1877 c_name
=c_name(name
))
1881 extern const char *const %(c_name)s_lookup[];
1883 c_name
=c_name(name
))
1887 def gen_params(arg_type
, boxed
, extra
):
1894 ret
+= '%s arg' % arg_type
.c_param_type()
1897 assert not arg_type
.variants
1898 for memb
in arg_type
.members
:
1902 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1903 ret
+= '%s %s' % (memb
.type.c_param_type(),
1911 # Common command line parsing
1915 def parse_command_line(extra_options
='', extra_long_options
=[]):
1918 opts
, args
= getopt
.gnu_getopt(sys
.argv
[1:],
1919 'chp:o:' + extra_options
,
1920 ['source', 'header', 'prefix=',
1921 'output-dir='] + extra_long_options
)
1922 except getopt
.GetoptError
as err
:
1923 print >>sys
.stderr
, "%s: %s" % (sys
.argv
[0], str(err
))
1934 if o
in ('-p', '--prefix'):
1935 match
= re
.match(r
'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a
)
1936 if match
.end() != len(a
):
1937 print >>sys
.stderr
, \
1938 "%s: 'funny character '%s' in argument of --prefix" \
1939 % (sys
.argv
[0], a
[match
.end()])
1942 elif o
in ('-o', '--output-dir'):
1943 output_dir
= a
+ '/'
1944 elif o
in ('-c', '--source'):
1946 elif o
in ('-h', '--header'):
1949 extra_opts
.append(oa
)
1951 if not do_c
and not do_h
:
1956 print >>sys
.stderr
, "%s: need exactly one argument" % sys
.argv
[0]
1960 return (fname
, output_dir
, do_c
, do_h
, prefix
, extra_opts
)
1963 # Generate output files with boilerplate
1967 def open_output(output_dir
, do_c
, do_h
, prefix
, c_file
, h_file
,
1968 c_comment
, h_comment
):
1969 guard
= guardname(prefix
+ h_file
)
1970 c_file
= output_dir
+ prefix
+ c_file
1971 h_file
= output_dir
+ prefix
+ h_file
1975 os
.makedirs(output_dir
)
1976 except os
.error
as e
:
1977 if e
.errno
!= errno
.EEXIST
:
1980 def maybe_open(really
, name
, opt
):
1982 return open(name
, opt
)
1985 return StringIO
.StringIO()
1987 fdef
= maybe_open(do_c
, c_file
, 'w')
1988 fdecl
= maybe_open(do_h
, h_file
, 'w')
1990 fdef
.write(mcgen('''
1991 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1996 fdecl
.write(mcgen('''
1997 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2003 comment
=h_comment
, guard
=guard
))
2005 return (fdef
, fdecl
)
2008 def close_output(fdef
, fdecl
):