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.
15 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
46 'human-monitor-command',
48 'query-migrate-cache-size',
55 'guest-fsfreeze-freeze',
56 'guest-fsfreeze-freeze-list',
57 'guest-fsfreeze-status',
58 'guest-fsfreeze-thaw',
62 'guest-sync-delimited',
65 # Whitelist of entities allowed to violate case conventions
68 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
69 'CpuInfoMIPS', # PC, visible through query-cpu
70 'CpuInfoTricore', # PC, visible through query-cpu
71 'QapiErrorClass', # all members, visible through errors
72 'UuidInfo', # UUID, visible through query-uuid
73 'X86CPURegister32', # all members, visible indirectly through qom-get
74 'q_obj_CpuInfo-base', # CPU, visible through query-cpu
84 # Parsing the schema into expressions
88 def error_path(parent
):
91 res
= ("In file included from %s:%d:\n" % (parent
['file'],
92 parent
['line'])) + res
93 parent
= parent
['parent']
97 class QAPIError(Exception):
98 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
99 Exception.__init
__(self
)
103 self
.info
= incl_info
107 loc
= "%s:%d" % (self
.fname
, self
.line
)
108 if self
.col
is not None:
109 loc
+= ":%s" % self
.col
110 return error_path(self
.info
) + "%s: %s" % (loc
, self
.msg
)
113 class QAPIParseError(QAPIError
):
114 def __init__(self
, parser
, msg
):
116 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
118 col
= (col
+ 7) % 8 + 1
121 QAPIError
.__init
__(self
, parser
.fname
, parser
.line
, col
,
122 parser
.incl_info
, msg
)
125 class QAPISemError(QAPIError
):
126 def __init__(self
, info
, msg
):
127 QAPIError
.__init
__(self
, info
['file'], info
['line'], None,
131 class QAPIDoc(object):
132 class Section(object):
133 def __init__(self
, name
=None):
134 # optional section name (argument/member or section name)
136 # the list of lines for this section
139 def append(self
, line
):
140 self
.content
.append(line
)
143 return "\n".join(self
.content
).strip()
145 class ArgSection(Section
):
148 def __init__(self
, parser
, info
):
149 # self.parser is used to report errors with QAPIParseError. The
150 # resulting error position depends on the state of the parser.
151 # It happens to be the beginning of the comment. More or less
152 # servicable, but action at a distance.
156 self
.body
= QAPIDoc
.Section()
157 # dict mapping parameter name to ArgSection
158 self
.args
= OrderedDict()
161 # the current section
162 self
.section
= self
.body
163 # associated expression (to be set by expression parser)
166 def has_section(self
, name
):
167 """Return True if we have a section with this name."""
168 for i
in self
.sections
:
173 def append(self
, line
):
174 """Parse a comment line and add it to the documentation."""
177 self
._append
_freeform
(line
)
181 raise QAPIParseError(self
.parser
, "Missing space after #")
184 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
185 # recognized, and get silently treated as ordinary text
187 self
._append
_symbol
_line
(line
)
188 elif not self
.body
.content
and line
.startswith("@"):
189 if not line
.endswith(":"):
190 raise QAPIParseError(self
.parser
, "Line should end with :")
191 self
.symbol
= line
[1:-1]
192 # FIXME invalid names other than the empty string aren't flagged
194 raise QAPIParseError(self
.parser
, "Invalid name")
196 self
._append
_freeform
(line
)
198 def _append_symbol_line(self
, line
):
199 name
= line
.split(' ', 1)[0]
201 if name
.startswith("@") and name
.endswith(":"):
202 line
= line
[len(name
)+1:]
203 self
._start
_args
_section
(name
[1:-1])
204 elif name
in ("Returns:", "Since:",
205 # those are often singular or plural
207 "Example:", "Examples:",
209 line
= line
[len(name
)+1:]
210 self
._start
_section
(name
[:-1])
212 self
._append
_freeform
(line
)
214 def _start_args_section(self
, name
):
215 # FIXME invalid names other than the empty string aren't flagged
217 raise QAPIParseError(self
.parser
, "Invalid parameter name")
218 if name
in self
.args
:
219 raise QAPIParseError(self
.parser
,
220 "'%s' parameter name duplicated" % name
)
222 raise QAPIParseError(self
.parser
,
223 "'@%s:' can't follow '%s' section"
224 % (name
, self
.sections
[0].name
))
225 self
.section
= QAPIDoc
.ArgSection(name
)
226 self
.args
[name
] = self
.section
228 def _start_section(self
, name
=""):
229 if name
in ("Returns", "Since") and self
.has_section(name
):
230 raise QAPIParseError(self
.parser
,
231 "Duplicated '%s' section" % name
)
232 self
.section
= QAPIDoc
.Section(name
)
233 self
.sections
.append(self
.section
)
235 def _append_freeform(self
, line
):
236 in_arg
= isinstance(self
.section
, QAPIDoc
.ArgSection
)
237 if (in_arg
and self
.section
.content
238 and not self
.section
.content
[-1]
239 and line
and not line
[0].isspace()):
240 self
._start
_section
()
241 if (in_arg
or not self
.section
.name
242 or not self
.section
.name
.startswith("Example")):
244 self
.section
.append(line
)
247 class QAPISchemaParser(object):
249 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
250 abs_fname
= os
.path
.abspath(fp
.name
)
253 previously_included
.append(abs_fname
)
254 self
.incl_info
= incl_info
256 if self
.src
== '' or self
.src
[-1] != '\n':
265 while self
.tok
is not None:
266 info
= {'file': fname
, 'line': self
.line
,
267 'parent': self
.incl_info
}
269 doc
= self
.get_doc(info
)
270 self
.docs
.append(doc
)
273 expr
= self
.get_expr(False)
274 if 'include' in expr
:
276 raise QAPISemError(info
, "Invalid 'include' directive")
277 include
= expr
["include"]
278 if not isinstance(include
, str):
279 raise QAPISemError(info
,
280 "Value of 'include' must be a string")
281 self
._include
(include
, info
, os
.path
.dirname(abs_fname
),
283 elif "pragma" in expr
:
285 raise QAPISemError(info
, "Invalid 'pragma' directive")
286 pragma
= expr
['pragma']
287 if not isinstance(pragma
, dict):
289 info
, "Value of 'pragma' must be a dictionary")
290 for name
, value
in pragma
.iteritems():
291 self
._pragma
(name
, value
, info
)
293 expr_elem
= {'expr': expr
,
296 and self
.docs
[-1].info
['file'] == fname
297 and not self
.docs
[-1].expr
):
298 self
.docs
[-1].expr
= expr
299 expr_elem
['doc'] = self
.docs
[-1]
301 self
.exprs
.append(expr_elem
)
303 def _include(self
, include
, info
, base_dir
, previously_included
):
304 incl_abs_fname
= os
.path
.join(base_dir
, include
)
305 # catch inclusion cycle
308 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
309 raise QAPISemError(info
, "Inclusion loop for %s" % include
)
312 # skip multiple include of the same file
313 if incl_abs_fname
in previously_included
:
316 fobj
= open(incl_abs_fname
, 'r')
318 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, include
))
319 exprs_include
= QAPISchemaParser(fobj
, previously_included
, info
)
320 self
.exprs
.extend(exprs_include
.exprs
)
321 self
.docs
.extend(exprs_include
.docs
)
323 def _pragma(self
, name
, value
, info
):
325 if name
== 'doc-required':
326 if not isinstance(value
, bool):
327 raise QAPISemError(info
,
328 "Pragma 'doc-required' must be boolean")
331 raise QAPISemError(info
, "Unknown pragma '%s'" % name
)
333 def accept(self
, skip_comment
=True):
335 self
.tok
= self
.src
[self
.cursor
]
336 self
.pos
= self
.cursor
341 if self
.src
[self
.cursor
] == '#':
342 # Start of doc comment
344 self
.cursor
= self
.src
.find('\n', self
.cursor
)
346 self
.val
= self
.src
[self
.pos
:self
.cursor
]
348 elif self
.tok
in "{}:,[]":
350 elif self
.tok
== "'":
354 ch
= self
.src
[self
.cursor
]
357 raise QAPIParseError(self
, 'Missing terminating "\'"')
371 for _
in range(0, 4):
372 ch
= self
.src
[self
.cursor
]
374 if ch
not in "0123456789abcdefABCDEF":
375 raise QAPIParseError(self
,
376 '\\u escape needs 4 '
378 value
= (value
<< 4) + int(ch
, 16)
379 # If Python 2 and 3 didn't disagree so much on
380 # how to handle Unicode, then we could allow
381 # Unicode string defaults. But most of QAPI is
382 # ASCII-only, so we aren't losing much for now.
383 if not value
or value
> 0x7f:
384 raise QAPIParseError(self
,
385 'For now, \\u escape '
386 'only supports non-zero '
387 'values up to \\u007f')
392 raise QAPIParseError(self
,
393 "Unknown escape \\%s" % ch
)
402 elif self
.src
.startswith("true", self
.pos
):
406 elif self
.src
.startswith("false", self
.pos
):
410 elif self
.src
.startswith("null", self
.pos
):
414 elif self
.tok
== '\n':
415 if self
.cursor
== len(self
.src
):
419 self
.line_pos
= self
.cursor
420 elif not self
.tok
.isspace():
421 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
423 def get_members(self
):
429 raise QAPIParseError(self
, 'Expected string or "}"')
434 raise QAPIParseError(self
, 'Expected ":"')
437 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
438 expr
[key
] = self
.get_expr(True)
443 raise QAPIParseError(self
, 'Expected "," or "}"')
446 raise QAPIParseError(self
, 'Expected string')
448 def get_values(self
):
453 if self
.tok
not in "{['tfn":
454 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
457 expr
.append(self
.get_expr(True))
462 raise QAPIParseError(self
, 'Expected "," or "]"')
465 def get_expr(self
, nested
):
466 if self
.tok
!= '{' and not nested
:
467 raise QAPIParseError(self
, 'Expected "{"')
470 expr
= self
.get_members()
471 elif self
.tok
== '[':
473 expr
= self
.get_values()
474 elif self
.tok
in "'tfn":
478 raise QAPIParseError(self
, 'Expected "{", "[" or string')
481 def get_doc(self
, info
):
483 raise QAPIParseError(self
, "Junk after '##' at start of "
484 "documentation comment")
486 doc
= QAPIDoc(self
, info
)
488 while self
.tok
== '#':
489 if self
.val
.startswith('##'):
492 raise QAPIParseError(self
, "Junk after '##' at end of "
493 "documentation comment")
500 raise QAPIParseError(self
, "Documentation comment must end with '##'")
504 # Semantic analysis of schema expressions
505 # TODO fold into QAPISchema
506 # TODO catching name collisions in generated code would be nice
510 def find_base_members(base
):
511 if isinstance(base
, dict):
513 base_struct_define
= find_struct(base
)
514 if not base_struct_define
:
516 return base_struct_define
['data']
519 # Return the qtype of an alternate branch, or None on error.
520 def find_alternate_member_qtype(qapi_type
):
521 if qapi_type
in builtin_types
:
522 return builtin_types
[qapi_type
]
523 elif find_struct(qapi_type
):
525 elif find_enum(qapi_type
):
526 return "QTYPE_QSTRING"
527 elif find_union(qapi_type
):
532 # Return the discriminator enum define if discriminator is specified as an
533 # enum type, otherwise return None.
534 def discriminator_find_enum_define(expr
):
535 base
= expr
.get('base')
536 discriminator
= expr
.get('discriminator')
538 if not (discriminator
and base
):
541 base_members
= find_base_members(base
)
545 discriminator_type
= base_members
.get(discriminator
)
546 if not discriminator_type
:
549 return find_enum(discriminator_type
)
552 # Names must be letters, numbers, -, and _. They must start with letter,
553 # except for downstream extensions which must start with __RFQDN_.
554 # Dots are only valid in the downstream extension prefix.
555 valid_name
= re
.compile('^(__[a-zA-Z0-9.-]+_)?'
556 '[a-zA-Z][a-zA-Z0-9_-]*$')
559 def check_name(info
, source
, name
, allow_optional
=False,
564 if not isinstance(name
, str):
565 raise QAPISemError(info
, "%s requires a string name" % source
)
566 if name
.startswith('*'):
567 membername
= name
[1:]
568 if not allow_optional
:
569 raise QAPISemError(info
, "%s does not allow optional name '%s'"
571 # Enum members can start with a digit, because the generated C
572 # code always prefixes it with the enum name
573 if enum_member
and membername
[0].isdigit():
574 membername
= 'D' + membername
575 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
576 # and 'q_obj_*' implicit type names.
577 if not valid_name
.match(membername
) or \
578 c_name(membername
, False).startswith('q_'):
579 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
582 def add_name(name
, info
, meta
, implicit
=False):
584 check_name(info
, "'%s'" % meta
, name
)
585 # FIXME should reject names that differ only in '_' vs. '.'
586 # vs. '-', because they're liable to clash in generated C.
587 if name
in all_names
:
588 raise QAPISemError(info
, "%s '%s' is already defined"
589 % (all_names
[name
], name
))
590 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
591 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
592 % (meta
, name
, name
[-4:]))
593 all_names
[name
] = meta
596 def add_struct(definition
, info
):
598 name
= definition
['struct']
599 add_name(name
, info
, 'struct')
600 struct_types
.append(definition
)
603 def find_struct(name
):
605 for struct
in struct_types
:
606 if struct
['struct'] == name
:
611 def add_union(definition
, info
):
613 name
= definition
['union']
614 add_name(name
, info
, 'union')
615 union_types
.append(definition
)
618 def find_union(name
):
620 for union
in union_types
:
621 if union
['union'] == name
:
626 def add_enum(name
, info
, enum_values
=None, implicit
=False):
628 add_name(name
, info
, 'enum', implicit
)
629 enum_types
.append({"enum_name": name
, "enum_values": enum_values
})
634 for enum
in enum_types
:
635 if enum
['enum_name'] == name
:
641 return find_enum(name
) is not None
644 def check_type(info
, source
, value
, allow_array
=False,
645 allow_dict
=False, allow_optional
=False,
652 # Check if array type for value is okay
653 if isinstance(value
, list):
655 raise QAPISemError(info
, "%s cannot be an array" % source
)
656 if len(value
) != 1 or not isinstance(value
[0], str):
657 raise QAPISemError(info
,
658 "%s: array type must contain single type name" %
662 # Check if type name for value is okay
663 if isinstance(value
, str):
664 if value
not in all_names
:
665 raise QAPISemError(info
, "%s uses unknown type '%s'"
667 if not all_names
[value
] in allow_metas
:
668 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
669 (source
, all_names
[value
], value
))
673 raise QAPISemError(info
, "%s should be a type name" % source
)
675 if not isinstance(value
, OrderedDict
):
676 raise QAPISemError(info
,
677 "%s should be a dictionary or type name" % source
)
679 # value is a dictionary, check that each member is okay
680 for (key
, arg
) in value
.items():
681 check_name(info
, "Member of %s" % source
, key
,
682 allow_optional
=allow_optional
)
683 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
684 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
686 # Todo: allow dictionaries to represent default values of
687 # an optional argument.
688 check_type(info
, "Member '%s' of %s" % (key
, source
), arg
,
690 allow_metas
=['built-in', 'union', 'alternate', 'struct',
694 def check_command(expr
, info
):
695 name
= expr
['command']
696 boxed
= expr
.get('boxed', False)
698 args_meta
= ['struct']
700 args_meta
+= ['union', 'alternate']
701 check_type(info
, "'data' for command '%s'" % name
,
702 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
703 allow_metas
=args_meta
)
704 returns_meta
= ['union', 'struct']
705 if name
in returns_whitelist
:
706 returns_meta
+= ['built-in', 'alternate', 'enum']
707 check_type(info
, "'returns' for command '%s'" % name
,
708 expr
.get('returns'), allow_array
=True,
709 allow_optional
=True, allow_metas
=returns_meta
)
712 def check_event(expr
, info
):
715 boxed
= expr
.get('boxed', False)
719 meta
+= ['union', 'alternate']
721 check_type(info
, "'data' for event '%s'" % name
,
722 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
726 def check_union(expr
, info
):
728 base
= expr
.get('base')
729 discriminator
= expr
.get('discriminator')
730 members
= expr
['data']
732 # Two types of unions, determined by discriminator.
734 # With no discriminator it is a simple union.
735 if discriminator
is None:
737 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
739 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
742 # Else, it's a flat union.
744 # The object must have a string or dictionary 'base'.
745 check_type(info
, "'base' for union '%s'" % name
,
746 base
, allow_dict
=True, allow_optional
=True,
747 allow_metas
=['struct'])
749 raise QAPISemError(info
, "Flat union '%s' must have a base"
751 base_members
= find_base_members(base
)
754 # The value of member 'discriminator' must name a non-optional
755 # member of the base struct.
756 check_name(info
, "Discriminator of flat union '%s'" % name
,
758 discriminator_type
= base_members
.get(discriminator
)
759 if not discriminator_type
:
760 raise QAPISemError(info
,
761 "Discriminator '%s' is not a member of base "
763 % (discriminator
, base
))
764 enum_define
= find_enum(discriminator_type
)
765 allow_metas
= ['struct']
766 # Do not allow string discriminator
768 raise QAPISemError(info
,
769 "Discriminator '%s' must be of enumeration "
770 "type" % discriminator
)
772 # Check every branch; don't allow an empty union
773 if len(members
) == 0:
774 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
775 for (key
, value
) in members
.items():
776 check_name(info
, "Member of union '%s'" % name
, key
)
778 # Each value must name a known type
779 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
780 value
, allow_array
=not base
, allow_metas
=allow_metas
)
782 # If the discriminator names an enum type, then all members
783 # of 'data' must also be members of the enum type.
785 if key
not in enum_define
['enum_values']:
786 raise QAPISemError(info
,
787 "Discriminator value '%s' is not found in "
789 % (key
, enum_define
["enum_name"]))
791 # If discriminator is user-defined, ensure all values are covered
793 for value
in enum_define
['enum_values']:
794 if value
not in members
.keys():
795 raise QAPISemError(info
, "Union '%s' data missing '%s' branch"
799 def check_alternate(expr
, info
):
800 name
= expr
['alternate']
801 members
= expr
['data']
804 # Check every branch; require at least two branches
806 raise QAPISemError(info
,
807 "Alternate '%s' should have at least two branches "
809 for (key
, value
) in members
.items():
810 check_name(info
, "Member of alternate '%s'" % name
, key
)
812 # Ensure alternates have no type conflicts.
813 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
),
815 allow_metas
=['built-in', 'union', 'struct', 'enum'])
816 qtype
= find_alternate_member_qtype(value
)
818 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
819 "type '%s'" % (name
, key
, value
))
820 if qtype
in types_seen
:
821 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
822 "be distinguished from member '%s'"
823 % (name
, key
, types_seen
[qtype
]))
824 types_seen
[qtype
] = key
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 # Learn the types and check for valid expression keys
882 for builtin
in builtin_types
.keys():
883 all_names
[builtin
] = 'built-in'
884 for expr_elem
in exprs
:
885 expr
= expr_elem
['expr']
886 info
= expr_elem
['info']
888 if 'doc' not in expr_elem
and doc_required
:
889 raise QAPISemError(info
,
890 "Expression missing documentation comment")
893 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
894 add_enum(expr
['enum'], info
, expr
['data'])
895 elif 'union' in expr
:
896 check_keys(expr_elem
, 'union', ['data'],
897 ['base', 'discriminator'])
898 add_union(expr
, info
)
899 elif 'alternate' in expr
:
900 check_keys(expr_elem
, 'alternate', ['data'])
901 add_name(expr
['alternate'], info
, 'alternate')
902 elif 'struct' in expr
:
903 check_keys(expr_elem
, 'struct', ['data'], ['base'])
904 add_struct(expr
, info
)
905 elif 'command' in expr
:
906 check_keys(expr_elem
, 'command', [],
907 ['data', 'returns', 'gen', 'success-response', 'boxed'])
908 add_name(expr
['command'], info
, 'command')
909 elif 'event' in expr
:
910 check_keys(expr_elem
, 'event', [], ['data', 'boxed'])
911 add_name(expr
['event'], info
, 'event')
913 raise QAPISemError(expr_elem
['info'],
914 "Expression is missing metatype")
916 # Try again for hidden UnionKind enum
917 for expr_elem
in exprs
:
918 expr
= expr_elem
['expr']
920 if not discriminator_find_enum_define(expr
):
921 add_enum('%sKind' % expr
['union'], expr_elem
['info'],
923 elif 'alternate' in expr
:
924 add_enum('%sKind' % expr
['alternate'], expr_elem
['info'],
927 # Validate that exprs make sense
928 for expr_elem
in exprs
:
929 expr
= expr_elem
['expr']
930 info
= expr_elem
['info']
933 check_enum(expr
, info
)
934 elif 'union' in expr
:
935 check_union(expr
, info
)
936 elif 'alternate' in expr
:
937 check_alternate(expr
, info
)
938 elif 'struct' in expr
:
939 check_struct(expr
, info
)
940 elif 'command' in expr
:
941 check_command(expr
, info
)
942 elif 'event' in expr
:
943 check_event(expr
, info
)
945 assert False, 'unexpected meta type'
950 def check_freeform_doc(doc
):
952 raise QAPISemError(doc
.info
,
953 "Documention for '%s' is not followed"
954 " by the definition" % doc
.symbol
)
957 if re
.search(r
'@\S+:', body
, re
.MULTILINE
):
958 raise QAPISemError(doc
.info
,
959 "Free-form documentation block must not contain"
963 def check_definition_doc(doc
, expr
, info
):
964 for i
in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
970 if doc
.symbol
!= name
:
971 raise QAPISemError(info
, "Definition of '%s' follows documentation"
972 " for '%s'" % (name
, doc
.symbol
))
973 if doc
.has_section('Returns') and 'command' not in expr
:
974 raise QAPISemError(info
, "'Returns:' is only valid for commands")
977 args
= expr
.get('base', [])
979 args
= expr
.get('data', [])
980 if isinstance(args
, str):
982 if isinstance(args
, dict):
984 assert isinstance(args
, list)
986 if (meta
== 'alternate'
987 or (meta
== 'union' and not expr
.get('discriminator'))):
993 desc
= doc
.args
.get(arg
[1:])
996 desc
= doc
.args
.get(arg
)
999 desc_opt
= "#optional" in str(desc
)
1000 if desc_opt
and not opt
:
1001 raise QAPISemError(info
, "Description has #optional, "
1002 "but the declaration doesn't")
1003 if not desc_opt
and opt
:
1004 # silently fix the doc
1005 # TODO either fix the schema and make this an error,
1006 # or drop #optional entirely
1007 desc
.append("#optional")
1009 doc_args
= set(doc
.args
.keys())
1010 args
= set([name
.strip('*') for name
in args
])
1011 if not doc_args
.issubset(args
):
1012 raise QAPISemError(info
, "The following documented members are not in "
1013 "the declaration: %s" % ", ".join(doc_args
- args
))
1016 def check_docs(docs
):
1018 for section
in doc
.args
.values() + doc
.sections
:
1019 content
= str(section
)
1020 if not content
or content
.isspace():
1021 raise QAPISemError(doc
.info
,
1022 "Empty doc section '%s'" % section
.name
)
1025 check_freeform_doc(doc
)
1027 check_definition_doc(doc
, doc
.expr
, doc
.info
)
1033 # Schema compiler frontend
1036 class QAPISchemaEntity(object):
1037 def __init__(self
, name
, info
):
1038 assert isinstance(name
, str)
1040 # For explicitly defined entities, info points to the (explicit)
1041 # definition. For builtins (and their arrays), info is None.
1042 # For implicitly defined entities, info points to a place that
1043 # triggered the implicit definition (there may be more than one
1048 return c_name(self
.name
)
1050 def check(self
, schema
):
1053 def is_implicit(self
):
1054 return not self
.info
1056 def visit(self
, visitor
):
1060 class QAPISchemaVisitor(object):
1061 def visit_begin(self
, schema
):
1064 def visit_end(self
):
1067 def visit_needed(self
, entity
):
1068 # Default to visiting everything
1071 def visit_builtin_type(self
, name
, info
, json_type
):
1074 def visit_enum_type(self
, name
, info
, values
, prefix
):
1077 def visit_array_type(self
, name
, info
, element_type
):
1080 def visit_object_type(self
, name
, info
, base
, members
, variants
):
1083 def visit_object_type_flat(self
, name
, info
, members
, variants
):
1086 def visit_alternate_type(self
, name
, info
, variants
):
1089 def visit_command(self
, name
, info
, arg_type
, ret_type
,
1090 gen
, success_response
, boxed
):
1093 def visit_event(self
, name
, info
, arg_type
, boxed
):
1097 class QAPISchemaType(QAPISchemaEntity
):
1098 # Return the C type for common use.
1099 # For the types we commonly box, this is a pointer type.
1103 # Return the C type to be used in a parameter list.
1104 def c_param_type(self
):
1105 return self
.c_type()
1107 # Return the C type to be used where we suppress boxing.
1108 def c_unboxed_type(self
):
1109 return self
.c_type()
1111 def json_type(self
):
1114 def alternate_qtype(self
):
1116 'string': 'QTYPE_QSTRING',
1117 'number': 'QTYPE_QFLOAT',
1118 'int': 'QTYPE_QINT',
1119 'boolean': 'QTYPE_QBOOL',
1120 'object': 'QTYPE_QDICT'
1122 return json2qtype
.get(self
.json_type())
1125 class QAPISchemaBuiltinType(QAPISchemaType
):
1126 def __init__(self
, name
, json_type
, c_type
):
1127 QAPISchemaType
.__init
__(self
, name
, None)
1128 assert not c_type
or isinstance(c_type
, str)
1129 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1131 self
._json
_type
_name
= json_type
1132 self
._c
_type
_name
= c_type
1138 return self
._c
_type
_name
1140 def c_param_type(self
):
1141 if self
.name
== 'str':
1142 return 'const ' + self
._c
_type
_name
1143 return self
._c
_type
_name
1145 def json_type(self
):
1146 return self
._json
_type
_name
1148 def visit(self
, visitor
):
1149 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1152 class QAPISchemaEnumType(QAPISchemaType
):
1153 def __init__(self
, name
, info
, values
, prefix
):
1154 QAPISchemaType
.__init
__(self
, name
, info
)
1156 assert isinstance(v
, QAPISchemaMember
)
1158 assert prefix
is None or isinstance(prefix
, str)
1159 self
.values
= values
1160 self
.prefix
= prefix
1162 def check(self
, schema
):
1164 for v
in self
.values
:
1165 v
.check_clash(self
.info
, seen
)
1167 def is_implicit(self
):
1168 # See QAPISchema._make_implicit_enum_type()
1169 return self
.name
.endswith('Kind')
1172 return c_name(self
.name
)
1174 def member_names(self
):
1175 return [v
.name
for v
in self
.values
]
1177 def json_type(self
):
1180 def visit(self
, visitor
):
1181 visitor
.visit_enum_type(self
.name
, self
.info
,
1182 self
.member_names(), self
.prefix
)
1185 class QAPISchemaArrayType(QAPISchemaType
):
1186 def __init__(self
, name
, info
, element_type
):
1187 QAPISchemaType
.__init
__(self
, name
, info
)
1188 assert isinstance(element_type
, str)
1189 self
._element
_type
_name
= element_type
1190 self
.element_type
= None
1192 def check(self
, schema
):
1193 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1194 assert self
.element_type
1196 def is_implicit(self
):
1200 return c_name(self
.name
) + pointer_suffix
1202 def json_type(self
):
1205 def visit(self
, visitor
):
1206 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
1209 class QAPISchemaObjectType(QAPISchemaType
):
1210 def __init__(self
, name
, info
, base
, local_members
, variants
):
1211 # struct has local_members, optional base, and no variants
1212 # flat union has base, variants, and no local_members
1213 # simple union has local_members, variants, and no base
1214 QAPISchemaType
.__init
__(self
, name
, info
)
1215 assert base
is None or isinstance(base
, str)
1216 for m
in local_members
:
1217 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1219 if variants
is not None:
1220 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1221 variants
.set_owner(name
)
1222 self
._base
_name
= base
1224 self
.local_members
= local_members
1225 self
.variants
= variants
1228 def check(self
, schema
):
1229 if self
.members
is False: # check for cycles
1230 raise QAPISemError(self
.info
,
1231 "Object %s contains itself" % self
.name
)
1234 self
.members
= False # mark as being checked
1235 seen
= OrderedDict()
1237 self
.base
= schema
.lookup_type(self
._base
_name
)
1238 assert isinstance(self
.base
, QAPISchemaObjectType
)
1239 self
.base
.check(schema
)
1240 self
.base
.check_clash(schema
, self
.info
, seen
)
1241 for m
in self
.local_members
:
1243 m
.check_clash(self
.info
, seen
)
1244 self
.members
= seen
.values()
1246 self
.variants
.check(schema
, seen
)
1247 assert self
.variants
.tag_member
in self
.members
1248 self
.variants
.check_clash(schema
, self
.info
, seen
)
1250 # Check that the members of this type do not cause duplicate JSON members,
1251 # and update seen to track the members seen so far. Report any errors
1252 # on behalf of info, which is not necessarily self.info
1253 def check_clash(self
, schema
, info
, seen
):
1254 assert not self
.variants
# not implemented
1255 for m
in self
.members
:
1256 m
.check_clash(info
, seen
)
1258 def is_implicit(self
):
1259 # See QAPISchema._make_implicit_object_type(), as well as
1260 # _def_predefineds()
1261 return self
.name
.startswith('q_')
1264 assert self
.members
is not None
1265 return not self
.members
and not self
.variants
1268 assert self
.name
!= 'q_empty'
1269 return QAPISchemaType
.c_name(self
)
1272 assert not self
.is_implicit()
1273 return c_name(self
.name
) + pointer_suffix
1275 def c_unboxed_type(self
):
1276 return c_name(self
.name
)
1278 def json_type(self
):
1281 def visit(self
, visitor
):
1282 visitor
.visit_object_type(self
.name
, self
.info
,
1283 self
.base
, self
.local_members
, self
.variants
)
1284 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1285 self
.members
, self
.variants
)
1288 class QAPISchemaMember(object):
1291 def __init__(self
, name
):
1292 assert isinstance(name
, str)
1296 def set_owner(self
, name
):
1297 assert not self
.owner
1300 def check_clash(self
, info
, seen
):
1301 cname
= c_name(self
.name
)
1302 if cname
.lower() != cname
and self
.owner
not in case_whitelist
:
1303 raise QAPISemError(info
,
1304 "%s should not use uppercase" % self
.describe())
1306 raise QAPISemError(info
, "%s collides with %s" %
1307 (self
.describe(), seen
[cname
].describe()))
1310 def _pretty_owner(self
):
1312 if owner
.startswith('q_obj_'):
1313 # See QAPISchema._make_implicit_object_type() - reverse the
1314 # mapping there to create a nice human-readable description
1316 if owner
.endswith('-arg'):
1317 return '(parameter of %s)' % owner
[:-4]
1318 elif owner
.endswith('-base'):
1319 return '(base of %s)' % owner
[:-5]
1321 assert owner
.endswith('-wrapper')
1322 # Unreachable and not implemented
1324 if owner
.endswith('Kind'):
1325 # See QAPISchema._make_implicit_enum_type()
1326 return '(branch of %s)' % owner
[:-4]
1327 return '(%s of %s)' % (self
.role
, owner
)
1330 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1333 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1334 def __init__(self
, name
, typ
, optional
):
1335 QAPISchemaMember
.__init
__(self
, name
)
1336 assert isinstance(typ
, str)
1337 assert isinstance(optional
, bool)
1338 self
._type
_name
= typ
1340 self
.optional
= optional
1342 def check(self
, schema
):
1344 self
.type = schema
.lookup_type(self
._type
_name
)
1348 class QAPISchemaObjectTypeVariants(object):
1349 def __init__(self
, tag_name
, tag_member
, variants
):
1350 # Flat unions pass tag_name but not tag_member.
1351 # Simple unions and alternates pass tag_member but not tag_name.
1352 # After check(), tag_member is always set, and tag_name remains
1353 # a reliable witness of being used by a flat union.
1354 assert bool(tag_member
) != bool(tag_name
)
1355 assert (isinstance(tag_name
, str) or
1356 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1357 assert len(variants
) > 0
1359 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1360 self
._tag
_name
= tag_name
1361 self
.tag_member
= tag_member
1362 self
.variants
= variants
1364 def set_owner(self
, name
):
1365 for v
in self
.variants
:
1368 def check(self
, schema
, seen
):
1369 if not self
.tag_member
: # flat union
1370 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1371 assert self
._tag
_name
== self
.tag_member
.name
1372 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1373 for v
in self
.variants
:
1375 # Union names must match enum values; alternate names are
1376 # checked separately. Use 'seen' to tell the two apart.
1378 assert v
.name
in self
.tag_member
.type.member_names()
1379 assert isinstance(v
.type, QAPISchemaObjectType
)
1380 v
.type.check(schema
)
1382 def check_clash(self
, schema
, info
, seen
):
1383 for v
in self
.variants
:
1384 # Reset seen map for each variant, since qapi names from one
1385 # branch do not affect another branch
1386 assert isinstance(v
.type, QAPISchemaObjectType
)
1387 v
.type.check_clash(schema
, info
, dict(seen
))
1390 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1393 def __init__(self
, name
, typ
):
1394 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1397 class QAPISchemaAlternateType(QAPISchemaType
):
1398 def __init__(self
, name
, info
, variants
):
1399 QAPISchemaType
.__init
__(self
, name
, info
)
1400 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1401 assert variants
.tag_member
1402 variants
.set_owner(name
)
1403 variants
.tag_member
.set_owner(self
.name
)
1404 self
.variants
= variants
1406 def check(self
, schema
):
1407 self
.variants
.tag_member
.check(schema
)
1408 # Not calling self.variants.check_clash(), because there's nothing
1410 self
.variants
.check(schema
, {})
1411 # Alternate branch names have no relation to the tag enum values;
1412 # so we have to check for potential name collisions ourselves.
1414 for v
in self
.variants
.variants
:
1415 v
.check_clash(self
.info
, seen
)
1418 return c_name(self
.name
) + pointer_suffix
1420 def json_type(self
):
1423 def visit(self
, visitor
):
1424 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1430 class QAPISchemaCommand(QAPISchemaEntity
):
1431 def __init__(self
, name
, info
, arg_type
, ret_type
, gen
, success_response
,
1433 QAPISchemaEntity
.__init
__(self
, name
, info
)
1434 assert not arg_type
or isinstance(arg_type
, str)
1435 assert not ret_type
or isinstance(ret_type
, str)
1436 self
._arg
_type
_name
= arg_type
1437 self
.arg_type
= None
1438 self
._ret
_type
_name
= ret_type
1439 self
.ret_type
= None
1441 self
.success_response
= success_response
1444 def check(self
, schema
):
1445 if self
._arg
_type
_name
:
1446 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1447 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1448 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1449 self
.arg_type
.check(schema
)
1451 if self
.arg_type
.is_empty():
1452 raise QAPISemError(self
.info
,
1453 "Cannot use 'boxed' with empty type")
1455 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1456 assert not self
.arg_type
.variants
1458 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1459 if self
._ret
_type
_name
:
1460 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1461 assert isinstance(self
.ret_type
, QAPISchemaType
)
1463 def visit(self
, visitor
):
1464 visitor
.visit_command(self
.name
, self
.info
,
1465 self
.arg_type
, self
.ret_type
,
1466 self
.gen
, self
.success_response
, self
.boxed
)
1469 class QAPISchemaEvent(QAPISchemaEntity
):
1470 def __init__(self
, name
, info
, arg_type
, boxed
):
1471 QAPISchemaEntity
.__init
__(self
, name
, info
)
1472 assert not arg_type
or isinstance(arg_type
, str)
1473 self
._arg
_type
_name
= arg_type
1474 self
.arg_type
= None
1477 def check(self
, schema
):
1478 if self
._arg
_type
_name
:
1479 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1480 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1481 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1482 self
.arg_type
.check(schema
)
1484 if self
.arg_type
.is_empty():
1485 raise QAPISemError(self
.info
,
1486 "Cannot use 'boxed' with empty type")
1488 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1489 assert not self
.arg_type
.variants
1491 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1493 def visit(self
, visitor
):
1494 visitor
.visit_event(self
.name
, self
.info
, self
.arg_type
, self
.boxed
)
1497 class QAPISchema(object):
1498 def __init__(self
, fname
):
1500 parser
= QAPISchemaParser(open(fname
, "r"))
1501 self
.exprs
= check_exprs(parser
.exprs
)
1502 self
.docs
= check_docs(parser
.docs
)
1503 self
._entity
_dict
= {}
1504 self
._predefining
= True
1505 self
._def
_predefineds
()
1506 self
._predefining
= False
1509 except QAPIError
as err
:
1510 print >>sys
.stderr
, err
1513 def _def_entity(self
, ent
):
1514 # Only the predefined types are allowed to not have info
1515 assert ent
.info
or self
._predefining
1516 assert ent
.name
not in self
._entity
_dict
1517 self
._entity
_dict
[ent
.name
] = ent
1519 def lookup_entity(self
, name
, typ
=None):
1520 ent
= self
._entity
_dict
.get(name
)
1521 if typ
and not isinstance(ent
, typ
):
1525 def lookup_type(self
, name
):
1526 return self
.lookup_entity(name
, QAPISchemaType
)
1528 def _def_builtin_type(self
, name
, json_type
, c_type
):
1529 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1530 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1531 # qapi-types.h from a single .c, all arrays of builtins must be
1532 # declared in the first file whether or not they are used. Nicer
1533 # would be to use lazy instantiation, while figuring out how to
1534 # avoid compilation issues with multiple qapi-types.h.
1535 self
._make
_array
_type
(name
, None)
1537 def _def_predefineds(self
):
1538 for t
in [('str', 'string', 'char' + pointer_suffix
),
1539 ('number', 'number', 'double'),
1540 ('int', 'int', 'int64_t'),
1541 ('int8', 'int', 'int8_t'),
1542 ('int16', 'int', 'int16_t'),
1543 ('int32', 'int', 'int32_t'),
1544 ('int64', 'int', 'int64_t'),
1545 ('uint8', 'int', 'uint8_t'),
1546 ('uint16', 'int', 'uint16_t'),
1547 ('uint32', 'int', 'uint32_t'),
1548 ('uint64', 'int', 'uint64_t'),
1549 ('size', 'int', 'uint64_t'),
1550 ('bool', 'boolean', 'bool'),
1551 ('any', 'value', 'QObject' + pointer_suffix
)]:
1552 self
._def
_builtin
_type
(*t
)
1553 self
.the_empty_object_type
= QAPISchemaObjectType('q_empty', None,
1555 self
._def
_entity
(self
.the_empty_object_type
)
1556 qtype_values
= self
._make
_enum
_members
(['none', 'qnull', 'qint',
1557 'qstring', 'qdict', 'qlist',
1559 self
._def
_entity
(QAPISchemaEnumType('QType', None, qtype_values
,
1562 def _make_enum_members(self
, values
):
1563 return [QAPISchemaMember(v
) for v
in values
]
1565 def _make_implicit_enum_type(self
, name
, info
, values
):
1566 # See also QAPISchemaObjectTypeMember._pretty_owner()
1567 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1568 self
._def
_entity
(QAPISchemaEnumType(
1569 name
, info
, self
._make
_enum
_members
(values
), None))
1572 def _make_array_type(self
, element_type
, info
):
1573 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1574 if not self
.lookup_type(name
):
1575 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1578 def _make_implicit_object_type(self
, name
, info
, role
, members
):
1581 # See also QAPISchemaObjectTypeMember._pretty_owner()
1582 name
= 'q_obj_%s-%s' % (name
, role
)
1583 if not self
.lookup_entity(name
, QAPISchemaObjectType
):
1584 self
._def
_entity
(QAPISchemaObjectType(name
, info
, None,
1588 def _def_enum_type(self
, expr
, info
):
1591 prefix
= expr
.get('prefix')
1592 self
._def
_entity
(QAPISchemaEnumType(
1593 name
, info
, self
._make
_enum
_members
(data
), prefix
))
1595 def _make_member(self
, name
, typ
, info
):
1597 if name
.startswith('*'):
1600 if isinstance(typ
, list):
1601 assert len(typ
) == 1
1602 typ
= self
._make
_array
_type
(typ
[0], info
)
1603 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1605 def _make_members(self
, data
, info
):
1606 return [self
._make
_member
(key
, value
, info
)
1607 for (key
, value
) in data
.iteritems()]
1609 def _def_struct_type(self
, expr
, info
):
1610 name
= expr
['struct']
1611 base
= expr
.get('base')
1613 self
._def
_entity
(QAPISchemaObjectType(name
, info
, base
,
1614 self
._make
_members
(data
, info
),
1617 def _make_variant(self
, case
, typ
):
1618 return QAPISchemaObjectTypeVariant(case
, typ
)
1620 def _make_simple_variant(self
, case
, typ
, info
):
1621 if isinstance(typ
, list):
1622 assert len(typ
) == 1
1623 typ
= self
._make
_array
_type
(typ
[0], info
)
1624 typ
= self
._make
_implicit
_object
_type
(
1625 typ
, info
, 'wrapper', [self
._make
_member
('data', typ
, info
)])
1626 return QAPISchemaObjectTypeVariant(case
, typ
)
1628 def _def_union_type(self
, expr
, info
):
1629 name
= expr
['union']
1631 base
= expr
.get('base')
1632 tag_name
= expr
.get('discriminator')
1634 if isinstance(base
, dict):
1635 base
= (self
._make
_implicit
_object
_type
(
1636 name
, info
, 'base', self
._make
_members
(base
, info
)))
1638 variants
= [self
._make
_variant
(key
, value
)
1639 for (key
, value
) in data
.iteritems()]
1642 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1643 for (key
, value
) in data
.iteritems()]
1644 typ
= self
._make
_implicit
_enum
_type
(name
, info
,
1645 [v
.name
for v
in variants
])
1646 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1647 members
= [tag_member
]
1649 QAPISchemaObjectType(name
, info
, base
, members
,
1650 QAPISchemaObjectTypeVariants(tag_name
,
1654 def _def_alternate_type(self
, expr
, info
):
1655 name
= expr
['alternate']
1657 variants
= [self
._make
_variant
(key
, value
)
1658 for (key
, value
) in data
.iteritems()]
1659 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1661 QAPISchemaAlternateType(name
, info
,
1662 QAPISchemaObjectTypeVariants(None,
1666 def _def_command(self
, expr
, info
):
1667 name
= expr
['command']
1668 data
= expr
.get('data')
1669 rets
= expr
.get('returns')
1670 gen
= expr
.get('gen', True)
1671 success_response
= expr
.get('success-response', True)
1672 boxed
= expr
.get('boxed', False)
1673 if isinstance(data
, OrderedDict
):
1674 data
= self
._make
_implicit
_object
_type
(
1675 name
, info
, 'arg', self
._make
_members
(data
, info
))
1676 if isinstance(rets
, list):
1677 assert len(rets
) == 1
1678 rets
= self
._make
_array
_type
(rets
[0], info
)
1679 self
._def
_entity
(QAPISchemaCommand(name
, info
, data
, rets
, gen
,
1680 success_response
, boxed
))
1682 def _def_event(self
, expr
, info
):
1683 name
= expr
['event']
1684 data
= expr
.get('data')
1685 boxed
= expr
.get('boxed', False)
1686 if isinstance(data
, OrderedDict
):
1687 data
= self
._make
_implicit
_object
_type
(
1688 name
, info
, 'arg', self
._make
_members
(data
, info
))
1689 self
._def
_entity
(QAPISchemaEvent(name
, info
, data
, boxed
))
1691 def _def_exprs(self
):
1692 for expr_elem
in self
.exprs
:
1693 expr
= expr_elem
['expr']
1694 info
= expr_elem
['info']
1696 self
._def
_enum
_type
(expr
, info
)
1697 elif 'struct' in expr
:
1698 self
._def
_struct
_type
(expr
, info
)
1699 elif 'union' in expr
:
1700 self
._def
_union
_type
(expr
, info
)
1701 elif 'alternate' in expr
:
1702 self
._def
_alternate
_type
(expr
, info
)
1703 elif 'command' in expr
:
1704 self
._def
_command
(expr
, info
)
1705 elif 'event' in expr
:
1706 self
._def
_event
(expr
, info
)
1711 for ent
in self
._entity
_dict
.values():
1714 def visit(self
, visitor
):
1715 visitor
.visit_begin(self
)
1716 for (name
, entity
) in sorted(self
._entity
_dict
.items()):
1717 if visitor
.visit_needed(entity
):
1718 entity
.visit(visitor
)
1723 # Code generation helpers
1726 def camel_case(name
):
1730 if ch
in ['_', '-']:
1733 new_name
+= ch
.upper()
1736 new_name
+= ch
.lower()
1740 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1741 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1742 # ENUM24_Name -> ENUM24_NAME
1743 def camel_to_upper(value
):
1744 c_fun_str
= c_name(value
, False)
1752 # When c is upper and no "_" appears before, do more checks
1753 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != "_":
1754 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1756 elif c_fun_str
[i
- 1].isdigit():
1759 return new_name
.lstrip('_').upper()
1762 def c_enum_const(type_name
, const_name
, prefix
=None):
1763 if prefix
is not None:
1765 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1767 c_name_trans
= string
.maketrans('.-', '__')
1770 # Map @name to a valid C identifier.
1771 # If @protect, avoid returning certain ticklish identifiers (like
1772 # C keywords) by prepending "q_".
1774 # Used for converting 'name' from a 'name':'type' qapi definition
1775 # into a generated struct member, as well as converting type names
1776 # into substrings of a generated C function name.
1777 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1778 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1779 def c_name(name
, protect
=True):
1780 # ANSI X3J11/88-090, 3.1.1
1781 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1782 'default', 'do', 'double', 'else', 'enum', 'extern',
1783 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1784 'return', 'short', 'signed', 'sizeof', 'static',
1785 'struct', 'switch', 'typedef', 'union', 'unsigned',
1786 'void', 'volatile', 'while'])
1787 # ISO/IEC 9899:1999, 6.4.1
1788 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1789 # ISO/IEC 9899:2011, 6.4.1
1790 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1791 '_Noreturn', '_Static_assert', '_Thread_local'])
1792 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1794 gcc_words
= set(['asm', 'typeof'])
1795 # C++ ISO/IEC 14882:2003 2.11
1796 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1797 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1798 'namespace', 'new', 'operator', 'private', 'protected',
1799 'public', 'reinterpret_cast', 'static_cast', 'template',
1800 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1801 'using', 'virtual', 'wchar_t',
1802 # alternative representations
1803 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1804 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1805 # namespace pollution:
1806 polluted_words
= set(['unix', 'errno', 'mips', 'sparc'])
1807 name
= name
.translate(c_name_trans
)
1808 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1809 | cpp_words | polluted_words
):
1813 eatspace
= '\033EATSPACE.'
1814 pointer_suffix
= ' *' + eatspace
1817 def genindent(count
):
1819 for _
in range(count
):
1826 def push_indent(indent_amount
=4):
1828 indent_level
+= indent_amount
1831 def pop_indent(indent_amount
=4):
1833 indent_level
-= indent_amount
1836 # Generate @code with @kwds interpolated.
1837 # Obey indent_level, and strip eatspace.
1838 def cgen(code
, **kwds
):
1841 indent
= genindent(indent_level
)
1842 # re.subn() lacks flags support before Python 2.7, use re.compile()
1843 raw
= re
.subn(re
.compile("^.", re
.MULTILINE
),
1844 indent
+ r
'\g<0>', raw
)
1846 return re
.sub(re
.escape(eatspace
) + ' *', '', raw
)
1849 def mcgen(code
, **kwds
):
1852 return cgen(code
, **kwds
)
1855 def guardname(filename
):
1856 return c_name(filename
, protect
=False).upper()
1859 def guardstart(name
):
1866 name
=guardname(name
))
1872 #endif /* %(name)s */
1875 name
=guardname(name
))
1878 def gen_enum_lookup(name
, values
, prefix
=None):
1881 const char *const %(c_name)s_lookup[] = {
1883 c_name
=c_name(name
))
1884 for value
in values
:
1885 index
= c_enum_const(name
, value
, prefix
)
1887 [%(index)s] = "%(value)s",
1889 index
=index
, value
=value
)
1891 max_index
= c_enum_const(name
, '_MAX', prefix
)
1893 [%(max_index)s] = NULL,
1896 max_index
=max_index
)
1900 def gen_enum(name
, values
, prefix
=None):
1901 # append automatically generated _MAX value
1902 enum_values
= values
+ ['_MAX']
1906 typedef enum %(c_name)s {
1908 c_name
=c_name(name
))
1911 for value
in enum_values
:
1915 c_enum
=c_enum_const(name
, value
, prefix
),
1922 c_name
=c_name(name
))
1926 extern const char *const %(c_name)s_lookup[];
1928 c_name
=c_name(name
))
1932 def gen_params(arg_type
, boxed
, extra
):
1939 ret
+= '%s arg' % arg_type
.c_param_type()
1942 assert not arg_type
.variants
1943 for memb
in arg_type
.members
:
1947 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1948 ret
+= '%s %s' % (memb
.type.c_param_type(),
1956 # Common command line parsing
1960 def parse_command_line(extra_options
="", extra_long_options
=[]):
1963 opts
, args
= getopt
.gnu_getopt(sys
.argv
[1:],
1964 "chp:o:" + extra_options
,
1965 ["source", "header", "prefix=",
1966 "output-dir="] + extra_long_options
)
1967 except getopt
.GetoptError
as err
:
1968 print >>sys
.stderr
, "%s: %s" % (sys
.argv
[0], str(err
))
1979 if o
in ("-p", "--prefix"):
1980 match
= re
.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a
)
1981 if match
.end() != len(a
):
1982 print >>sys
.stderr
, \
1983 "%s: 'funny character '%s' in argument of --prefix" \
1984 % (sys
.argv
[0], a
[match
.end()])
1987 elif o
in ("-o", "--output-dir"):
1988 output_dir
= a
+ "/"
1989 elif o
in ("-c", "--source"):
1991 elif o
in ("-h", "--header"):
1994 extra_opts
.append(oa
)
1996 if not do_c
and not do_h
:
2001 print >>sys
.stderr
, "%s: need exactly one argument" % sys
.argv
[0]
2005 return (fname
, output_dir
, do_c
, do_h
, prefix
, extra_opts
)
2008 # Generate output files with boilerplate
2012 def open_output(output_dir
, do_c
, do_h
, prefix
, c_file
, h_file
,
2013 c_comment
, h_comment
):
2014 guard
= guardname(prefix
+ h_file
)
2015 c_file
= output_dir
+ prefix
+ c_file
2016 h_file
= output_dir
+ prefix
+ h_file
2020 os
.makedirs(output_dir
)
2021 except os
.error
as e
:
2022 if e
.errno
!= errno
.EEXIST
:
2025 def maybe_open(really
, name
, opt
):
2027 return open(name
, opt
)
2030 return StringIO
.StringIO()
2032 fdef
= maybe_open(do_c
, c_file
, 'w')
2033 fdecl
= maybe_open(do_h
, h_file
, 'w')
2035 fdef
.write(mcgen('''
2036 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2041 fdecl
.write(mcgen('''
2042 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2048 comment
=h_comment
, guard
=guard
))
2050 return (fdef
, fdecl
)
2053 def close_output(fdef
, fdecl
):