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
44 returns_whitelist
= []
46 # Whitelist of entities allowed to violate case conventions
47 name_case_whitelist
= []
56 # Parsing the schema into expressions
60 def error_path(parent
):
63 res
= ('In file included from %s:%d:\n' % (parent
['file'],
64 parent
['line'])) + res
65 parent
= parent
['parent']
69 class QAPIError(Exception):
70 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
71 Exception.__init
__(self
)
79 loc
= '%s:%d' % (self
.fname
, self
.line
)
80 if self
.col
is not None:
81 loc
+= ':%s' % self
.col
82 return error_path(self
.info
) + '%s: %s' % (loc
, self
.msg
)
85 class QAPIParseError(QAPIError
):
86 def __init__(self
, parser
, msg
):
88 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
90 col
= (col
+ 7) % 8 + 1
93 QAPIError
.__init
__(self
, parser
.fname
, parser
.line
, col
,
94 parser
.incl_info
, msg
)
97 class QAPISemError(QAPIError
):
98 def __init__(self
, info
, msg
):
99 QAPIError
.__init
__(self
, info
['file'], info
['line'], None,
103 class QAPIDoc(object):
104 class Section(object):
105 def __init__(self
, name
=None):
106 # optional section name (argument/member or section name)
108 # the list of lines for this section
110 self
.optional
= False
112 def append(self
, line
):
113 self
.content
.append(line
)
116 return '\n'.join(self
.content
).strip()
118 class ArgSection(Section
):
119 def __init__(self
, name
):
120 QAPIDoc
.Section
.__init
__(self
, name
)
123 def connect(self
, member
):
126 def __init__(self
, parser
, info
):
127 # self.parser is used to report errors with QAPIParseError. The
128 # resulting error position depends on the state of the parser.
129 # It happens to be the beginning of the comment. More or less
130 # servicable, but action at a distance.
134 self
.body
= QAPIDoc
.Section()
135 # dict mapping parameter name to ArgSection
136 self
.args
= OrderedDict()
139 # the current section
140 self
.section
= self
.body
141 # associated expression (to be set by expression parser)
144 def has_section(self
, name
):
145 """Return True if we have a section with this name."""
146 for i
in self
.sections
:
151 def append(self
, line
):
152 """Parse a comment line and add it to the documentation."""
155 self
._append
_freeform
(line
)
159 raise QAPIParseError(self
.parser
, "Missing space after #")
162 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
163 # recognized, and get silently treated as ordinary text
165 self
._append
_symbol
_line
(line
)
166 elif not self
.body
.content
and line
.startswith('@'):
167 if not line
.endswith(':'):
168 raise QAPIParseError(self
.parser
, "Line should end with :")
169 self
.symbol
= line
[1:-1]
170 # FIXME invalid names other than the empty string aren't flagged
172 raise QAPIParseError(self
.parser
, "Invalid name")
174 self
._append
_freeform
(line
)
176 def _append_symbol_line(self
, line
):
177 name
= line
.split(' ', 1)[0]
179 if name
.startswith('@') and name
.endswith(':'):
180 line
= line
[len(name
)+1:]
181 self
._start
_args
_section
(name
[1:-1])
182 elif name
in ('Returns:', 'Since:',
183 # those are often singular or plural
185 'Example:', 'Examples:',
187 line
= line
[len(name
)+1:]
188 self
._start
_section
(name
[:-1])
190 self
._append
_freeform
(line
)
192 def _start_args_section(self
, name
):
193 # FIXME invalid names other than the empty string aren't flagged
195 raise QAPIParseError(self
.parser
, "Invalid parameter name")
196 if name
in self
.args
:
197 raise QAPIParseError(self
.parser
,
198 "'%s' parameter name duplicated" % name
)
200 raise QAPIParseError(self
.parser
,
201 "'@%s:' can't follow '%s' section"
202 % (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
)
210 self
.section
= QAPIDoc
.Section(name
)
211 self
.sections
.append(self
.section
)
213 def _append_freeform(self
, line
):
214 in_arg
= isinstance(self
.section
, QAPIDoc
.ArgSection
)
215 if (in_arg
and self
.section
.content
216 and not self
.section
.content
[-1]
217 and line
and not line
[0].isspace()):
218 self
._start
_section
()
219 if (in_arg
or not self
.section
.name
220 or not self
.section
.name
.startswith('Example')):
222 # TODO Drop this once the dust has settled
223 if (isinstance(self
.section
, QAPIDoc
.ArgSection
)
224 and '#optional' in line
):
225 raise QAPISemError(self
.info
, "Please drop the #optional tag")
226 self
.section
.append(line
)
228 def connect_member(self
, member
):
229 if member
.name
not in self
.args
:
230 # Undocumented TODO outlaw
231 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
232 self
.args
[member
.name
].connect(member
)
235 class QAPISchemaParser(object):
237 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
238 abs_fname
= os
.path
.abspath(fp
.name
)
241 previously_included
.append(abs_fname
)
242 self
.incl_info
= incl_info
244 if self
.src
== '' or self
.src
[-1] != '\n':
254 while self
.tok
is not None:
255 info
= {'file': fname
, 'line': self
.line
,
256 'parent': self
.incl_info
}
258 self
.reject_expr_doc()
259 self
.cur_doc
= self
.get_doc(info
)
260 self
.docs
.append(self
.cur_doc
)
263 expr
= self
.get_expr(False)
264 if 'include' in expr
:
265 self
.reject_expr_doc()
267 raise QAPISemError(info
, "Invalid 'include' directive")
268 include
= expr
['include']
269 if not isinstance(include
, str):
270 raise QAPISemError(info
,
271 "Value of 'include' must be a string")
272 self
._include
(include
, info
, os
.path
.dirname(abs_fname
),
274 elif "pragma" in expr
:
275 self
.reject_expr_doc()
277 raise QAPISemError(info
, "Invalid 'pragma' directive")
278 pragma
= expr
['pragma']
279 if not isinstance(pragma
, dict):
281 info
, "Value of 'pragma' must be a dictionary")
282 for name
, value
in pragma
.iteritems():
283 self
._pragma
(name
, value
, info
)
285 expr_elem
= {'expr': expr
,
288 if not self
.cur_doc
.symbol
:
291 "Expression documentation required")
292 self
.cur_doc
.expr
= expr
293 expr_elem
['doc'] = self
.cur_doc
294 self
.exprs
.append(expr_elem
)
296 self
.reject_expr_doc()
298 def reject_expr_doc(self
):
299 if self
.cur_doc
and self
.cur_doc
.symbol
:
302 "Documentation for '%s' is not followed by the definition"
303 % self
.cur_doc
.symbol
)
305 def _include(self
, include
, info
, base_dir
, previously_included
):
306 incl_abs_fname
= os
.path
.join(base_dir
, include
)
307 # catch inclusion cycle
310 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
311 raise QAPISemError(info
, "Inclusion loop for %s" % include
)
314 # skip multiple include of the same file
315 if incl_abs_fname
in previously_included
:
318 fobj
= open(incl_abs_fname
, 'r')
320 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, include
))
321 exprs_include
= QAPISchemaParser(fobj
, previously_included
, info
)
322 self
.exprs
.extend(exprs_include
.exprs
)
323 self
.docs
.extend(exprs_include
.docs
)
325 def _pragma(self
, name
, value
, info
):
326 global doc_required
, returns_whitelist
, name_case_whitelist
327 if name
== 'doc-required':
328 if not isinstance(value
, bool):
329 raise QAPISemError(info
,
330 "Pragma 'doc-required' must be boolean")
332 elif name
== 'returns-whitelist':
333 if (not isinstance(value
, list)
334 or any([not isinstance(elt
, str) for elt
in value
])):
335 raise QAPISemError(info
,
336 "Pragma returns-whitelist must be"
337 " a list of strings")
338 returns_whitelist
= value
339 elif name
== 'name-case-whitelist':
340 if (not isinstance(value
, list)
341 or any([not isinstance(elt
, str) for elt
in value
])):
342 raise QAPISemError(info
,
343 "Pragma name-case-whitelist must be"
344 " a list of strings")
345 name_case_whitelist
= value
347 raise QAPISemError(info
, "Unknown pragma '%s'" % name
)
349 def accept(self
, skip_comment
=True):
351 self
.tok
= self
.src
[self
.cursor
]
352 self
.pos
= self
.cursor
357 if self
.src
[self
.cursor
] == '#':
358 # Start of doc comment
360 self
.cursor
= self
.src
.find('\n', self
.cursor
)
362 self
.val
= self
.src
[self
.pos
:self
.cursor
]
364 elif self
.tok
in '{}:,[]':
366 elif self
.tok
== "'":
370 ch
= self
.src
[self
.cursor
]
373 raise QAPIParseError(self
, 'Missing terminating "\'"')
387 for _
in range(0, 4):
388 ch
= self
.src
[self
.cursor
]
390 if ch
not in '0123456789abcdefABCDEF':
391 raise QAPIParseError(self
,
392 '\\u escape needs 4 '
394 value
= (value
<< 4) + int(ch
, 16)
395 # If Python 2 and 3 didn't disagree so much on
396 # how to handle Unicode, then we could allow
397 # Unicode string defaults. But most of QAPI is
398 # ASCII-only, so we aren't losing much for now.
399 if not value
or value
> 0x7f:
400 raise QAPIParseError(self
,
401 'For now, \\u escape '
402 'only supports non-zero '
403 'values up to \\u007f')
408 raise QAPIParseError(self
,
409 "Unknown escape \\%s" % ch
)
418 elif self
.src
.startswith('true', self
.pos
):
422 elif self
.src
.startswith('false', self
.pos
):
426 elif self
.src
.startswith('null', self
.pos
):
430 elif self
.tok
== '\n':
431 if self
.cursor
== len(self
.src
):
435 self
.line_pos
= self
.cursor
436 elif not self
.tok
.isspace():
437 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
439 def get_members(self
):
445 raise QAPIParseError(self
, 'Expected string or "}"')
450 raise QAPIParseError(self
, 'Expected ":"')
453 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
454 expr
[key
] = self
.get_expr(True)
459 raise QAPIParseError(self
, 'Expected "," or "}"')
462 raise QAPIParseError(self
, 'Expected string')
464 def get_values(self
):
469 if self
.tok
not in "{['tfn":
470 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
473 expr
.append(self
.get_expr(True))
478 raise QAPIParseError(self
, 'Expected "," or "]"')
481 def get_expr(self
, nested
):
482 if self
.tok
!= '{' and not nested
:
483 raise QAPIParseError(self
, 'Expected "{"')
486 expr
= self
.get_members()
487 elif self
.tok
== '[':
489 expr
= self
.get_values()
490 elif self
.tok
in "'tfn":
494 raise QAPIParseError(self
, 'Expected "{", "[" or string')
497 def get_doc(self
, info
):
499 raise QAPIParseError(self
, "Junk after '##' at start of "
500 "documentation comment")
502 doc
= QAPIDoc(self
, info
)
504 while self
.tok
== '#':
505 if self
.val
.startswith('##'):
508 raise QAPIParseError(self
, "Junk after '##' at end of "
509 "documentation comment")
516 raise QAPIParseError(self
, "Documentation comment must end with '##'")
520 # Semantic analysis of schema expressions
521 # TODO fold into QAPISchema
522 # TODO catching name collisions in generated code would be nice
526 def find_base_members(base
):
527 if isinstance(base
, dict):
529 base_struct_define
= find_struct(base
)
530 if not base_struct_define
:
532 return base_struct_define
['data']
535 # Return the qtype of an alternate branch, or None on error.
536 def find_alternate_member_qtype(qapi_type
):
537 if qapi_type
in builtin_types
:
538 return builtin_types
[qapi_type
]
539 elif find_struct(qapi_type
):
541 elif find_enum(qapi_type
):
542 return 'QTYPE_QSTRING'
543 elif find_union(qapi_type
):
548 # Return the discriminator enum define if discriminator is specified as an
549 # enum type, otherwise return None.
550 def discriminator_find_enum_define(expr
):
551 base
= expr
.get('base')
552 discriminator
= expr
.get('discriminator')
554 if not (discriminator
and base
):
557 base_members
= find_base_members(base
)
561 discriminator_type
= base_members
.get(discriminator
)
562 if not discriminator_type
:
565 return find_enum(discriminator_type
)
568 # Names must be letters, numbers, -, and _. They must start with letter,
569 # except for downstream extensions which must start with __RFQDN_.
570 # Dots are only valid in the downstream extension prefix.
571 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
572 '[a-zA-Z][a-zA-Z0-9_-]*$')
575 def check_name(info
, source
, name
, allow_optional
=False,
580 if not isinstance(name
, str):
581 raise QAPISemError(info
, "%s requires a string name" % source
)
582 if name
.startswith('*'):
583 membername
= name
[1:]
584 if not allow_optional
:
585 raise QAPISemError(info
, "%s does not allow optional name '%s'"
587 # Enum members can start with a digit, because the generated C
588 # code always prefixes it with the enum name
589 if enum_member
and membername
[0].isdigit():
590 membername
= 'D' + membername
591 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
592 # and 'q_obj_*' implicit type names.
593 if not valid_name
.match(membername
) or \
594 c_name(membername
, False).startswith('q_'):
595 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
598 def add_name(name
, info
, meta
, implicit
=False):
600 check_name(info
, "'%s'" % meta
, name
)
601 # FIXME should reject names that differ only in '_' vs. '.'
602 # vs. '-', because they're liable to clash in generated C.
603 if name
in all_names
:
604 raise QAPISemError(info
, "%s '%s' is already defined"
605 % (all_names
[name
], name
))
606 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
607 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
608 % (meta
, name
, name
[-4:]))
609 all_names
[name
] = meta
612 def add_struct(definition
, info
):
614 name
= definition
['struct']
615 add_name(name
, info
, 'struct')
616 struct_types
.append(definition
)
619 def find_struct(name
):
621 for struct
in struct_types
:
622 if struct
['struct'] == name
:
627 def add_union(definition
, info
):
629 name
= definition
['union']
630 add_name(name
, info
, 'union')
631 union_types
.append(definition
)
634 def find_union(name
):
636 for union
in union_types
:
637 if union
['union'] == name
:
642 def add_enum(name
, info
, enum_values
=None, implicit
=False):
644 add_name(name
, info
, 'enum', implicit
)
645 enum_types
.append({'enum_name': name
, 'enum_values': enum_values
})
650 for enum
in enum_types
:
651 if enum
['enum_name'] == name
:
657 return find_enum(name
) is not None
660 def check_type(info
, source
, value
, allow_array
=False,
661 allow_dict
=False, allow_optional
=False,
668 # Check if array type for value is okay
669 if isinstance(value
, list):
671 raise QAPISemError(info
, "%s cannot be an array" % source
)
672 if len(value
) != 1 or not isinstance(value
[0], str):
673 raise QAPISemError(info
,
674 "%s: array type must contain single type name" %
678 # Check if type name for value is okay
679 if isinstance(value
, str):
680 if value
not in all_names
:
681 raise QAPISemError(info
, "%s uses unknown type '%s'"
683 if not all_names
[value
] in allow_metas
:
684 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
685 (source
, all_names
[value
], value
))
689 raise QAPISemError(info
, "%s should be a type name" % source
)
691 if not isinstance(value
, OrderedDict
):
692 raise QAPISemError(info
,
693 "%s should be a dictionary or type name" % source
)
695 # value is a dictionary, check that each member is okay
696 for (key
, arg
) in value
.items():
697 check_name(info
, "Member of %s" % source
, key
,
698 allow_optional
=allow_optional
)
699 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
700 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
702 # Todo: allow dictionaries to represent default values of
703 # an optional argument.
704 check_type(info
, "Member '%s' of %s" % (key
, source
), arg
,
706 allow_metas
=['built-in', 'union', 'alternate', 'struct',
710 def check_command(expr
, info
):
711 name
= expr
['command']
712 boxed
= expr
.get('boxed', False)
714 args_meta
= ['struct']
716 args_meta
+= ['union', 'alternate']
717 check_type(info
, "'data' for command '%s'" % name
,
718 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
719 allow_metas
=args_meta
)
720 returns_meta
= ['union', 'struct']
721 if name
in returns_whitelist
:
722 returns_meta
+= ['built-in', 'alternate', 'enum']
723 check_type(info
, "'returns' for command '%s'" % name
,
724 expr
.get('returns'), allow_array
=True,
725 allow_optional
=True, allow_metas
=returns_meta
)
728 def check_event(expr
, info
):
731 boxed
= expr
.get('boxed', False)
735 meta
+= ['union', 'alternate']
737 check_type(info
, "'data' for event '%s'" % name
,
738 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
742 def check_union(expr
, info
):
744 base
= expr
.get('base')
745 discriminator
= expr
.get('discriminator')
746 members
= expr
['data']
748 # Two types of unions, determined by discriminator.
750 # With no discriminator it is a simple union.
751 if discriminator
is None:
753 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
755 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
758 # Else, it's a flat union.
760 # The object must have a string or dictionary 'base'.
761 check_type(info
, "'base' for union '%s'" % name
,
762 base
, allow_dict
=True, allow_optional
=True,
763 allow_metas
=['struct'])
765 raise QAPISemError(info
, "Flat union '%s' must have a base"
767 base_members
= find_base_members(base
)
768 assert base_members
is not None
770 # The value of member 'discriminator' must name a non-optional
771 # member of the base struct.
772 check_name(info
, "Discriminator of flat union '%s'" % name
,
774 discriminator_type
= base_members
.get(discriminator
)
775 if not discriminator_type
:
776 raise QAPISemError(info
,
777 "Discriminator '%s' is not a member of base "
779 % (discriminator
, base
))
780 enum_define
= find_enum(discriminator_type
)
781 allow_metas
= ['struct']
782 # Do not allow string discriminator
784 raise QAPISemError(info
,
785 "Discriminator '%s' must be of enumeration "
786 "type" % discriminator
)
788 # Check every branch; don't allow an empty union
789 if len(members
) == 0:
790 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
791 for (key
, value
) in members
.items():
792 check_name(info
, "Member of union '%s'" % name
, key
)
794 # Each value must name a known type
795 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
796 value
, allow_array
=not base
, allow_metas
=allow_metas
)
798 # If the discriminator names an enum type, then all members
799 # of 'data' must also be members of the enum type.
801 if key
not in enum_define
['enum_values']:
802 raise QAPISemError(info
,
803 "Discriminator value '%s' is not found in "
805 % (key
, enum_define
['enum_name']))
807 # If discriminator is user-defined, ensure all values are covered
809 for value
in enum_define
['enum_values']:
810 if value
not in members
.keys():
811 raise QAPISemError(info
, "Union '%s' data missing '%s' branch"
815 def check_alternate(expr
, info
):
816 name
= expr
['alternate']
817 members
= expr
['data']
820 # Check every branch; require at least two branches
822 raise QAPISemError(info
,
823 "Alternate '%s' should have at least two branches "
825 for (key
, value
) in members
.items():
826 check_name(info
, "Member of alternate '%s'" % name
, key
)
828 # Ensure alternates have no type conflicts.
829 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
),
831 allow_metas
=['built-in', 'union', 'struct', 'enum'])
832 qtype
= find_alternate_member_qtype(value
)
834 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
835 "type '%s'" % (name
, key
, value
))
836 if qtype
in types_seen
:
837 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
838 "be distinguished from member '%s'"
839 % (name
, key
, types_seen
[qtype
]))
840 types_seen
[qtype
] = key
843 def check_enum(expr
, info
):
845 members
= expr
.get('data')
846 prefix
= expr
.get('prefix')
848 if not isinstance(members
, list):
849 raise QAPISemError(info
,
850 "Enum '%s' requires an array for 'data'" % name
)
851 if prefix
is not None and not isinstance(prefix
, str):
852 raise QAPISemError(info
,
853 "Enum '%s' requires a string for 'prefix'" % name
)
854 for member
in members
:
855 check_name(info
, "Member of enum '%s'" % name
, member
,
859 def check_struct(expr
, info
):
860 name
= expr
['struct']
861 members
= expr
['data']
863 check_type(info
, "'data' for struct '%s'" % name
, members
,
864 allow_dict
=True, allow_optional
=True)
865 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
866 allow_metas
=['struct'])
869 def check_keys(expr_elem
, meta
, required
, optional
=[]):
870 expr
= expr_elem
['expr']
871 info
= expr_elem
['info']
873 if not isinstance(name
, str):
874 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
875 required
= required
+ [meta
]
876 for (key
, value
) in expr
.items():
877 if key
not in required
and key
not in optional
:
878 raise QAPISemError(info
, "Unknown key '%s' in %s '%s'"
880 if (key
== 'gen' or key
== 'success-response') and value
is not False:
881 raise QAPISemError(info
,
882 "'%s' of %s '%s' should only use false value"
884 if key
== 'boxed' and value
is not True:
885 raise QAPISemError(info
,
886 "'%s' of %s '%s' should only use true value"
890 raise QAPISemError(info
, "Key '%s' is missing from %s '%s'"
894 def check_exprs(exprs
):
897 # Learn the types and check for valid expression keys
898 for builtin
in builtin_types
.keys():
899 all_names
[builtin
] = 'built-in'
900 for expr_elem
in exprs
:
901 expr
= expr_elem
['expr']
902 info
= expr_elem
['info']
904 if 'doc' not in expr_elem
and doc_required
:
905 raise QAPISemError(info
,
906 "Expression missing documentation comment")
909 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
910 add_enum(expr
['enum'], info
, expr
['data'])
911 elif 'union' in expr
:
912 check_keys(expr_elem
, 'union', ['data'],
913 ['base', 'discriminator'])
914 add_union(expr
, info
)
915 elif 'alternate' in expr
:
916 check_keys(expr_elem
, 'alternate', ['data'])
917 add_name(expr
['alternate'], info
, 'alternate')
918 elif 'struct' in expr
:
919 check_keys(expr_elem
, 'struct', ['data'], ['base'])
920 add_struct(expr
, info
)
921 elif 'command' in expr
:
922 check_keys(expr_elem
, 'command', [],
923 ['data', 'returns', 'gen', 'success-response', 'boxed'])
924 add_name(expr
['command'], info
, 'command')
925 elif 'event' in expr
:
926 check_keys(expr_elem
, 'event', [], ['data', 'boxed'])
927 add_name(expr
['event'], info
, 'event')
929 raise QAPISemError(expr_elem
['info'],
930 "Expression is missing metatype")
932 # Try again for hidden UnionKind enum
933 for expr_elem
in exprs
:
934 expr
= expr_elem
['expr']
936 if not discriminator_find_enum_define(expr
):
937 add_enum('%sKind' % expr
['union'], expr_elem
['info'],
939 elif 'alternate' in expr
:
940 add_enum('%sKind' % expr
['alternate'], expr_elem
['info'],
943 # Validate that exprs make sense
944 for expr_elem
in exprs
:
945 expr
= expr_elem
['expr']
946 info
= expr_elem
['info']
949 check_enum(expr
, info
)
950 elif 'union' in expr
:
951 check_union(expr
, info
)
952 elif 'alternate' in expr
:
953 check_alternate(expr
, info
)
954 elif 'struct' in expr
:
955 check_struct(expr
, info
)
956 elif 'command' in expr
:
957 check_command(expr
, info
)
958 elif 'event' in expr
:
959 check_event(expr
, info
)
961 assert False, 'unexpected meta type'
966 def check_freeform_doc(doc
):
968 if re
.search(r
'@\S+:', body
, re
.MULTILINE
):
969 raise QAPISemError(doc
.info
,
970 "Free-form documentation block must not contain"
974 def check_definition_doc(doc
, expr
, info
):
975 for i
in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
981 if doc
.symbol
!= name
:
982 raise QAPISemError(info
, "Definition of '%s' follows documentation"
983 " for '%s'" % (name
, doc
.symbol
))
984 if doc
.has_section('Returns') and 'command' not in expr
:
985 raise QAPISemError(info
, "'Returns:' is only valid for commands")
988 args
= expr
.get('base', [])
990 args
= expr
.get('data', [])
991 if isinstance(args
, str):
993 if isinstance(args
, dict):
995 assert isinstance(args
, list)
997 if (meta
== 'alternate'
998 or (meta
== 'union' and not expr
.get('discriminator'))):
1001 doc_args
= set(doc
.args
.keys())
1002 args
= set([name
.strip('*') for name
in args
])
1003 if not doc_args
.issubset(args
):
1004 raise QAPISemError(info
, "The following documented members are not in "
1005 "the declaration: %s" % ', '.join(doc_args
- args
))
1008 def check_docs(docs
):
1010 for section
in doc
.args
.values() + doc
.sections
:
1011 content
= str(section
)
1012 if not content
or content
.isspace():
1013 raise QAPISemError(doc
.info
,
1014 "Empty doc section '%s'" % section
.name
)
1017 check_freeform_doc(doc
)
1019 check_definition_doc(doc
, doc
.expr
, doc
.info
)
1025 # Schema compiler frontend
1028 class QAPISchemaEntity(object):
1029 def __init__(self
, name
, info
, doc
):
1030 assert isinstance(name
, str)
1032 # For explicitly defined entities, info points to the (explicit)
1033 # definition. For builtins (and their arrays), info is None.
1034 # For implicitly defined entities, info points to a place that
1035 # triggered the implicit definition (there may be more than one
1041 return c_name(self
.name
)
1043 def check(self
, schema
):
1046 def is_implicit(self
):
1047 return not self
.info
1049 def visit(self
, visitor
):
1053 class QAPISchemaVisitor(object):
1054 def visit_begin(self
, schema
):
1057 def visit_end(self
):
1060 def visit_needed(self
, entity
):
1061 # Default to visiting everything
1064 def visit_builtin_type(self
, name
, info
, json_type
):
1067 def visit_enum_type(self
, name
, info
, values
, prefix
):
1070 def visit_array_type(self
, name
, info
, element_type
):
1073 def visit_object_type(self
, name
, info
, base
, members
, variants
):
1076 def visit_object_type_flat(self
, name
, info
, members
, variants
):
1079 def visit_alternate_type(self
, name
, info
, variants
):
1082 def visit_command(self
, name
, info
, arg_type
, ret_type
,
1083 gen
, success_response
, boxed
):
1086 def visit_event(self
, name
, info
, arg_type
, boxed
):
1090 class QAPISchemaType(QAPISchemaEntity
):
1091 # Return the C type for common use.
1092 # For the types we commonly box, this is a pointer type.
1096 # Return the C type to be used in a parameter list.
1097 def c_param_type(self
):
1098 return self
.c_type()
1100 # Return the C type to be used where we suppress boxing.
1101 def c_unboxed_type(self
):
1102 return self
.c_type()
1104 def json_type(self
):
1107 def alternate_qtype(self
):
1109 'string': 'QTYPE_QSTRING',
1110 'number': 'QTYPE_QFLOAT',
1111 'int': 'QTYPE_QINT',
1112 'boolean': 'QTYPE_QBOOL',
1113 'object': 'QTYPE_QDICT'
1115 return json2qtype
.get(self
.json_type())
1118 if self
.is_implicit():
1123 class QAPISchemaBuiltinType(QAPISchemaType
):
1124 def __init__(self
, name
, json_type
, c_type
):
1125 QAPISchemaType
.__init
__(self
, name
, None, None)
1126 assert not c_type
or isinstance(c_type
, str)
1127 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1129 self
._json
_type
_name
= json_type
1130 self
._c
_type
_name
= c_type
1136 return self
._c
_type
_name
1138 def c_param_type(self
):
1139 if self
.name
== 'str':
1140 return 'const ' + self
._c
_type
_name
1141 return self
._c
_type
_name
1143 def json_type(self
):
1144 return self
._json
_type
_name
1147 return self
.json_type()
1149 def visit(self
, visitor
):
1150 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1153 class QAPISchemaEnumType(QAPISchemaType
):
1154 def __init__(self
, name
, info
, doc
, values
, prefix
):
1155 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1157 assert isinstance(v
, QAPISchemaMember
)
1159 assert prefix
is None or isinstance(prefix
, str)
1160 self
.values
= values
1161 self
.prefix
= prefix
1163 def check(self
, schema
):
1165 for v
in self
.values
:
1166 v
.check_clash(self
.info
, seen
)
1168 self
.doc
.connect_member(v
)
1170 def is_implicit(self
):
1171 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1172 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1175 return c_name(self
.name
)
1177 def member_names(self
):
1178 return [v
.name
for v
in self
.values
]
1180 def json_type(self
):
1183 def visit(self
, visitor
):
1184 visitor
.visit_enum_type(self
.name
, self
.info
,
1185 self
.member_names(), self
.prefix
)
1188 class QAPISchemaArrayType(QAPISchemaType
):
1189 def __init__(self
, name
, info
, element_type
):
1190 QAPISchemaType
.__init
__(self
, name
, info
, None)
1191 assert isinstance(element_type
, str)
1192 self
._element
_type
_name
= element_type
1193 self
.element_type
= None
1195 def check(self
, schema
):
1196 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1197 assert self
.element_type
1199 def is_implicit(self
):
1203 return c_name(self
.name
) + pointer_suffix
1205 def json_type(self
):
1209 elt_doc_type
= self
.element_type
.doc_type()
1210 if not elt_doc_type
:
1212 return 'array of ' + elt_doc_type
1214 def visit(self
, visitor
):
1215 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
1218 class QAPISchemaObjectType(QAPISchemaType
):
1219 def __init__(self
, name
, info
, doc
, base
, local_members
, variants
):
1220 # struct has local_members, optional base, and no variants
1221 # flat union has base, variants, and no local_members
1222 # simple union has local_members, variants, and no base
1223 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1224 assert base
is None or isinstance(base
, str)
1225 for m
in local_members
:
1226 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1228 if variants
is not None:
1229 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1230 variants
.set_owner(name
)
1231 self
._base
_name
= base
1233 self
.local_members
= local_members
1234 self
.variants
= variants
1237 def check(self
, schema
):
1238 if self
.members
is False: # check for cycles
1239 raise QAPISemError(self
.info
,
1240 "Object %s contains itself" % self
.name
)
1243 self
.members
= False # mark as being checked
1244 seen
= OrderedDict()
1246 self
.base
= schema
.lookup_type(self
._base
_name
)
1247 assert isinstance(self
.base
, QAPISchemaObjectType
)
1248 self
.base
.check(schema
)
1249 self
.base
.check_clash(schema
, self
.info
, seen
)
1250 for m
in self
.local_members
:
1252 m
.check_clash(self
.info
, seen
)
1254 self
.doc
.connect_member(m
)
1255 self
.members
= seen
.values()
1257 self
.variants
.check(schema
, seen
)
1258 assert self
.variants
.tag_member
in self
.members
1259 self
.variants
.check_clash(schema
, self
.info
, seen
)
1261 # Check that the members of this type do not cause duplicate JSON members,
1262 # and update seen to track the members seen so far. Report any errors
1263 # on behalf of info, which is not necessarily self.info
1264 def check_clash(self
, schema
, info
, seen
):
1265 assert not self
.variants
# not implemented
1266 for m
in self
.members
:
1267 m
.check_clash(info
, seen
)
1269 def is_implicit(self
):
1270 # See QAPISchema._make_implicit_object_type(), as well as
1271 # _def_predefineds()
1272 return self
.name
.startswith('q_')
1275 assert self
.members
is not None
1276 return not self
.members
and not self
.variants
1279 assert self
.name
!= 'q_empty'
1280 return QAPISchemaType
.c_name(self
)
1283 assert not self
.is_implicit()
1284 return c_name(self
.name
) + pointer_suffix
1286 def c_unboxed_type(self
):
1287 return c_name(self
.name
)
1289 def json_type(self
):
1292 def visit(self
, visitor
):
1293 visitor
.visit_object_type(self
.name
, self
.info
,
1294 self
.base
, self
.local_members
, self
.variants
)
1295 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1296 self
.members
, self
.variants
)
1299 class QAPISchemaMember(object):
1302 def __init__(self
, name
):
1303 assert isinstance(name
, str)
1307 def set_owner(self
, name
):
1308 assert not self
.owner
1311 def check_clash(self
, info
, seen
):
1312 cname
= c_name(self
.name
)
1313 if cname
.lower() != cname
and self
.owner
not in name_case_whitelist
:
1314 raise QAPISemError(info
,
1315 "%s should not use uppercase" % self
.describe())
1317 raise QAPISemError(info
, "%s collides with %s" %
1318 (self
.describe(), seen
[cname
].describe()))
1321 def _pretty_owner(self
):
1323 if owner
.startswith('q_obj_'):
1324 # See QAPISchema._make_implicit_object_type() - reverse the
1325 # mapping there to create a nice human-readable description
1327 if owner
.endswith('-arg'):
1328 return '(parameter of %s)' % owner
[:-4]
1329 elif owner
.endswith('-base'):
1330 return '(base of %s)' % owner
[:-5]
1332 assert owner
.endswith('-wrapper')
1333 # Unreachable and not implemented
1335 if owner
.endswith('Kind'):
1336 # See QAPISchema._make_implicit_enum_type()
1337 return '(branch of %s)' % owner
[:-4]
1338 return '(%s of %s)' % (self
.role
, owner
)
1341 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1344 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1345 def __init__(self
, name
, typ
, optional
):
1346 QAPISchemaMember
.__init
__(self
, name
)
1347 assert isinstance(typ
, str)
1348 assert isinstance(optional
, bool)
1349 self
._type
_name
= typ
1351 self
.optional
= optional
1353 def check(self
, schema
):
1355 self
.type = schema
.lookup_type(self
._type
_name
)
1359 class QAPISchemaObjectTypeVariants(object):
1360 def __init__(self
, tag_name
, tag_member
, variants
):
1361 # Flat unions pass tag_name but not tag_member.
1362 # Simple unions and alternates pass tag_member but not tag_name.
1363 # After check(), tag_member is always set, and tag_name remains
1364 # a reliable witness of being used by a flat union.
1365 assert bool(tag_member
) != bool(tag_name
)
1366 assert (isinstance(tag_name
, str) or
1367 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1368 assert len(variants
) > 0
1370 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1371 self
._tag
_name
= tag_name
1372 self
.tag_member
= tag_member
1373 self
.variants
= variants
1375 def set_owner(self
, name
):
1376 for v
in self
.variants
:
1379 def check(self
, schema
, seen
):
1380 if not self
.tag_member
: # flat union
1381 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1382 assert self
._tag
_name
== self
.tag_member
.name
1383 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1384 for v
in self
.variants
:
1386 # Union names must match enum values; alternate names are
1387 # checked separately. Use 'seen' to tell the two apart.
1389 assert v
.name
in self
.tag_member
.type.member_names()
1390 assert isinstance(v
.type, QAPISchemaObjectType
)
1391 v
.type.check(schema
)
1393 def check_clash(self
, schema
, info
, seen
):
1394 for v
in self
.variants
:
1395 # Reset seen map for each variant, since qapi names from one
1396 # branch do not affect another branch
1397 assert isinstance(v
.type, QAPISchemaObjectType
)
1398 v
.type.check_clash(schema
, info
, dict(seen
))
1401 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1404 def __init__(self
, name
, typ
):
1405 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1408 class QAPISchemaAlternateType(QAPISchemaType
):
1409 def __init__(self
, name
, info
, doc
, variants
):
1410 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1411 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1412 assert variants
.tag_member
1413 variants
.set_owner(name
)
1414 variants
.tag_member
.set_owner(self
.name
)
1415 self
.variants
= variants
1417 def check(self
, schema
):
1418 self
.variants
.tag_member
.check(schema
)
1419 # Not calling self.variants.check_clash(), because there's nothing
1421 self
.variants
.check(schema
, {})
1422 # Alternate branch names have no relation to the tag enum values;
1423 # so we have to check for potential name collisions ourselves.
1425 for v
in self
.variants
.variants
:
1426 v
.check_clash(self
.info
, seen
)
1428 self
.doc
.connect_member(v
)
1431 return c_name(self
.name
) + pointer_suffix
1433 def json_type(self
):
1436 def visit(self
, visitor
):
1437 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1443 class QAPISchemaCommand(QAPISchemaEntity
):
1444 def __init__(self
, name
, info
, doc
, arg_type
, ret_type
,
1445 gen
, success_response
, boxed
):
1446 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1447 assert not arg_type
or isinstance(arg_type
, str)
1448 assert not ret_type
or isinstance(ret_type
, str)
1449 self
._arg
_type
_name
= arg_type
1450 self
.arg_type
= None
1451 self
._ret
_type
_name
= ret_type
1452 self
.ret_type
= None
1454 self
.success_response
= success_response
1457 def check(self
, schema
):
1458 if self
._arg
_type
_name
:
1459 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1460 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1461 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1462 self
.arg_type
.check(schema
)
1464 if self
.arg_type
.is_empty():
1465 raise QAPISemError(self
.info
,
1466 "Cannot use 'boxed' with empty type")
1468 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1469 assert not self
.arg_type
.variants
1471 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1472 if self
._ret
_type
_name
:
1473 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1474 assert isinstance(self
.ret_type
, QAPISchemaType
)
1476 def visit(self
, visitor
):
1477 visitor
.visit_command(self
.name
, self
.info
,
1478 self
.arg_type
, self
.ret_type
,
1479 self
.gen
, self
.success_response
, self
.boxed
)
1482 class QAPISchemaEvent(QAPISchemaEntity
):
1483 def __init__(self
, name
, info
, doc
, arg_type
, boxed
):
1484 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1485 assert not arg_type
or isinstance(arg_type
, str)
1486 self
._arg
_type
_name
= arg_type
1487 self
.arg_type
= None
1490 def check(self
, schema
):
1491 if self
._arg
_type
_name
:
1492 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1493 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1494 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1495 self
.arg_type
.check(schema
)
1497 if self
.arg_type
.is_empty():
1498 raise QAPISemError(self
.info
,
1499 "Cannot use 'boxed' with empty type")
1501 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1502 assert not self
.arg_type
.variants
1504 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1506 def visit(self
, visitor
):
1507 visitor
.visit_event(self
.name
, self
.info
, self
.arg_type
, self
.boxed
)
1510 class QAPISchema(object):
1511 def __init__(self
, fname
):
1513 parser
= QAPISchemaParser(open(fname
, 'r'))
1514 self
.exprs
= check_exprs(parser
.exprs
)
1515 self
.docs
= check_docs(parser
.docs
)
1516 self
._entity
_dict
= {}
1517 self
._predefining
= True
1518 self
._def
_predefineds
()
1519 self
._predefining
= False
1522 except QAPIError
as err
:
1523 print >>sys
.stderr
, err
1526 def _def_entity(self
, ent
):
1527 # Only the predefined types are allowed to not have info
1528 assert ent
.info
or self
._predefining
1529 assert ent
.name
not in self
._entity
_dict
1530 self
._entity
_dict
[ent
.name
] = ent
1532 def lookup_entity(self
, name
, typ
=None):
1533 ent
= self
._entity
_dict
.get(name
)
1534 if typ
and not isinstance(ent
, typ
):
1538 def lookup_type(self
, name
):
1539 return self
.lookup_entity(name
, QAPISchemaType
)
1541 def _def_builtin_type(self
, name
, json_type
, c_type
):
1542 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1543 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1544 # qapi-types.h from a single .c, all arrays of builtins must be
1545 # declared in the first file whether or not they are used. Nicer
1546 # would be to use lazy instantiation, while figuring out how to
1547 # avoid compilation issues with multiple qapi-types.h.
1548 self
._make
_array
_type
(name
, None)
1550 def _def_predefineds(self
):
1551 for t
in [('str', 'string', 'char' + pointer_suffix
),
1552 ('number', 'number', 'double'),
1553 ('int', 'int', 'int64_t'),
1554 ('int8', 'int', 'int8_t'),
1555 ('int16', 'int', 'int16_t'),
1556 ('int32', 'int', 'int32_t'),
1557 ('int64', 'int', 'int64_t'),
1558 ('uint8', 'int', 'uint8_t'),
1559 ('uint16', 'int', 'uint16_t'),
1560 ('uint32', 'int', 'uint32_t'),
1561 ('uint64', 'int', 'uint64_t'),
1562 ('size', 'int', 'uint64_t'),
1563 ('bool', 'boolean', 'bool'),
1564 ('any', 'value', 'QObject' + pointer_suffix
)]:
1565 self
._def
_builtin
_type
(*t
)
1566 self
.the_empty_object_type
= QAPISchemaObjectType(
1567 'q_empty', None, None, None, [], None)
1568 self
._def
_entity
(self
.the_empty_object_type
)
1569 qtype_values
= self
._make
_enum
_members
(['none', 'qnull', 'qint',
1570 'qstring', 'qdict', 'qlist',
1572 self
._def
_entity
(QAPISchemaEnumType('QType', None, None,
1573 qtype_values
, 'QTYPE'))
1575 def _make_enum_members(self
, values
):
1576 return [QAPISchemaMember(v
) for v
in values
]
1578 def _make_implicit_enum_type(self
, name
, info
, values
):
1579 # See also QAPISchemaObjectTypeMember._pretty_owner()
1580 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1581 self
._def
_entity
(QAPISchemaEnumType(
1582 name
, info
, None, self
._make
_enum
_members
(values
), None))
1585 def _make_array_type(self
, element_type
, info
):
1586 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1587 if not self
.lookup_type(name
):
1588 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1591 def _make_implicit_object_type(self
, name
, info
, doc
, role
, members
):
1594 # See also QAPISchemaObjectTypeMember._pretty_owner()
1595 name
= 'q_obj_%s-%s' % (name
, role
)
1596 if not self
.lookup_entity(name
, QAPISchemaObjectType
):
1597 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, None,
1601 def _def_enum_type(self
, expr
, info
, doc
):
1604 prefix
= expr
.get('prefix')
1605 self
._def
_entity
(QAPISchemaEnumType(
1606 name
, info
, doc
, self
._make
_enum
_members
(data
), prefix
))
1608 def _make_member(self
, name
, typ
, info
):
1610 if name
.startswith('*'):
1613 if isinstance(typ
, list):
1614 assert len(typ
) == 1
1615 typ
= self
._make
_array
_type
(typ
[0], info
)
1616 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1618 def _make_members(self
, data
, info
):
1619 return [self
._make
_member
(key
, value
, info
)
1620 for (key
, value
) in data
.iteritems()]
1622 def _def_struct_type(self
, expr
, info
, doc
):
1623 name
= expr
['struct']
1624 base
= expr
.get('base')
1626 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, base
,
1627 self
._make
_members
(data
, info
),
1630 def _make_variant(self
, case
, typ
):
1631 return QAPISchemaObjectTypeVariant(case
, typ
)
1633 def _make_simple_variant(self
, case
, typ
, info
):
1634 if isinstance(typ
, list):
1635 assert len(typ
) == 1
1636 typ
= self
._make
_array
_type
(typ
[0], info
)
1637 typ
= self
._make
_implicit
_object
_type
(
1638 typ
, info
, None, 'wrapper', [self
._make
_member
('data', typ
, info
)])
1639 return QAPISchemaObjectTypeVariant(case
, typ
)
1641 def _def_union_type(self
, expr
, info
, doc
):
1642 name
= expr
['union']
1644 base
= expr
.get('base')
1645 tag_name
= expr
.get('discriminator')
1647 if isinstance(base
, dict):
1648 base
= (self
._make
_implicit
_object
_type
(
1649 name
, info
, doc
, 'base', self
._make
_members
(base
, info
)))
1651 variants
= [self
._make
_variant
(key
, value
)
1652 for (key
, value
) in data
.iteritems()]
1655 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1656 for (key
, value
) in data
.iteritems()]
1657 typ
= self
._make
_implicit
_enum
_type
(name
, info
,
1658 [v
.name
for v
in variants
])
1659 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1660 members
= [tag_member
]
1662 QAPISchemaObjectType(name
, info
, doc
, base
, members
,
1663 QAPISchemaObjectTypeVariants(tag_name
,
1667 def _def_alternate_type(self
, expr
, info
, doc
):
1668 name
= expr
['alternate']
1670 variants
= [self
._make
_variant
(key
, value
)
1671 for (key
, value
) in data
.iteritems()]
1672 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1674 QAPISchemaAlternateType(name
, info
, doc
,
1675 QAPISchemaObjectTypeVariants(None,
1679 def _def_command(self
, expr
, info
, doc
):
1680 name
= expr
['command']
1681 data
= expr
.get('data')
1682 rets
= expr
.get('returns')
1683 gen
= expr
.get('gen', True)
1684 success_response
= expr
.get('success-response', True)
1685 boxed
= expr
.get('boxed', False)
1686 if isinstance(data
, OrderedDict
):
1687 data
= self
._make
_implicit
_object
_type
(
1688 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1689 if isinstance(rets
, list):
1690 assert len(rets
) == 1
1691 rets
= self
._make
_array
_type
(rets
[0], info
)
1692 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, data
, rets
,
1693 gen
, success_response
, boxed
))
1695 def _def_event(self
, expr
, info
, doc
):
1696 name
= expr
['event']
1697 data
= expr
.get('data')
1698 boxed
= expr
.get('boxed', False)
1699 if isinstance(data
, OrderedDict
):
1700 data
= self
._make
_implicit
_object
_type
(
1701 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1702 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, data
, boxed
))
1704 def _def_exprs(self
):
1705 for expr_elem
in self
.exprs
:
1706 expr
= expr_elem
['expr']
1707 info
= expr_elem
['info']
1708 doc
= expr_elem
.get('doc')
1710 self
._def
_enum
_type
(expr
, info
, doc
)
1711 elif 'struct' in expr
:
1712 self
._def
_struct
_type
(expr
, info
, doc
)
1713 elif 'union' in expr
:
1714 self
._def
_union
_type
(expr
, info
, doc
)
1715 elif 'alternate' in expr
:
1716 self
._def
_alternate
_type
(expr
, info
, doc
)
1717 elif 'command' in expr
:
1718 self
._def
_command
(expr
, info
, doc
)
1719 elif 'event' in expr
:
1720 self
._def
_event
(expr
, info
, doc
)
1725 for ent
in self
._entity
_dict
.values():
1728 def visit(self
, visitor
):
1729 visitor
.visit_begin(self
)
1730 for (name
, entity
) in sorted(self
._entity
_dict
.items()):
1731 if visitor
.visit_needed(entity
):
1732 entity
.visit(visitor
)
1737 # Code generation helpers
1740 def camel_case(name
):
1744 if ch
in ['_', '-']:
1747 new_name
+= ch
.upper()
1750 new_name
+= ch
.lower()
1754 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1755 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1756 # ENUM24_Name -> ENUM24_NAME
1757 def camel_to_upper(value
):
1758 c_fun_str
= c_name(value
, False)
1766 # When c is upper and no '_' appears before, do more checks
1767 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
1768 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1770 elif c_fun_str
[i
- 1].isdigit():
1773 return new_name
.lstrip('_').upper()
1776 def c_enum_const(type_name
, const_name
, prefix
=None):
1777 if prefix
is not None:
1779 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1781 c_name_trans
= string
.maketrans('.-', '__')
1784 # Map @name to a valid C identifier.
1785 # If @protect, avoid returning certain ticklish identifiers (like
1786 # C keywords) by prepending 'q_'.
1788 # Used for converting 'name' from a 'name':'type' qapi definition
1789 # into a generated struct member, as well as converting type names
1790 # into substrings of a generated C function name.
1791 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1792 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1793 def c_name(name
, protect
=True):
1794 # ANSI X3J11/88-090, 3.1.1
1795 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1796 'default', 'do', 'double', 'else', 'enum', 'extern',
1797 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1798 'return', 'short', 'signed', 'sizeof', 'static',
1799 'struct', 'switch', 'typedef', 'union', 'unsigned',
1800 'void', 'volatile', 'while'])
1801 # ISO/IEC 9899:1999, 6.4.1
1802 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1803 # ISO/IEC 9899:2011, 6.4.1
1804 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1805 '_Noreturn', '_Static_assert', '_Thread_local'])
1806 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1808 gcc_words
= set(['asm', 'typeof'])
1809 # C++ ISO/IEC 14882:2003 2.11
1810 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1811 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1812 'namespace', 'new', 'operator', 'private', 'protected',
1813 'public', 'reinterpret_cast', 'static_cast', 'template',
1814 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1815 'using', 'virtual', 'wchar_t',
1816 # alternative representations
1817 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1818 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1819 # namespace pollution:
1820 polluted_words
= set(['unix', 'errno', 'mips', 'sparc'])
1821 name
= name
.translate(c_name_trans
)
1822 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1823 | cpp_words | polluted_words
):
1827 eatspace
= '\033EATSPACE.'
1828 pointer_suffix
= ' *' + eatspace
1831 def genindent(count
):
1833 for _
in range(count
):
1840 def push_indent(indent_amount
=4):
1842 indent_level
+= indent_amount
1845 def pop_indent(indent_amount
=4):
1847 indent_level
-= indent_amount
1850 # Generate @code with @kwds interpolated.
1851 # Obey indent_level, and strip eatspace.
1852 def cgen(code
, **kwds
):
1855 indent
= genindent(indent_level
)
1856 # re.subn() lacks flags support before Python 2.7, use re.compile()
1857 raw
= re
.subn(re
.compile(r
'^.', re
.MULTILINE
),
1858 indent
+ r
'\g<0>', raw
)
1860 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
1863 def mcgen(code
, **kwds
):
1866 return cgen(code
, **kwds
)
1869 def guardname(filename
):
1870 return c_name(filename
, protect
=False).upper()
1873 def guardstart(name
):
1880 name
=guardname(name
))
1886 #endif /* %(name)s */
1889 name
=guardname(name
))
1892 def gen_enum_lookup(name
, values
, prefix
=None):
1895 const char *const %(c_name)s_lookup[] = {
1897 c_name
=c_name(name
))
1898 for value
in values
:
1899 index
= c_enum_const(name
, value
, prefix
)
1901 [%(index)s] = "%(value)s",
1903 index
=index
, value
=value
)
1905 max_index
= c_enum_const(name
, '_MAX', prefix
)
1907 [%(max_index)s] = NULL,
1910 max_index
=max_index
)
1914 def gen_enum(name
, values
, prefix
=None):
1915 # append automatically generated _MAX value
1916 enum_values
= values
+ ['_MAX']
1920 typedef enum %(c_name)s {
1922 c_name
=c_name(name
))
1925 for value
in enum_values
:
1929 c_enum
=c_enum_const(name
, value
, prefix
),
1936 c_name
=c_name(name
))
1940 extern const char *const %(c_name)s_lookup[];
1942 c_name
=c_name(name
))
1946 def gen_params(arg_type
, boxed
, extra
):
1953 ret
+= '%s arg' % arg_type
.c_param_type()
1956 assert not arg_type
.variants
1957 for memb
in arg_type
.members
:
1961 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1962 ret
+= '%s %s' % (memb
.type.c_param_type(),
1970 # Common command line parsing
1974 def parse_command_line(extra_options
='', extra_long_options
=[]):
1977 opts
, args
= getopt
.gnu_getopt(sys
.argv
[1:],
1978 'chp:o:' + extra_options
,
1979 ['source', 'header', 'prefix=',
1980 'output-dir='] + extra_long_options
)
1981 except getopt
.GetoptError
as err
:
1982 print >>sys
.stderr
, "%s: %s" % (sys
.argv
[0], str(err
))
1993 if o
in ('-p', '--prefix'):
1994 match
= re
.match(r
'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a
)
1995 if match
.end() != len(a
):
1996 print >>sys
.stderr
, \
1997 "%s: 'funny character '%s' in argument of --prefix" \
1998 % (sys
.argv
[0], a
[match
.end()])
2001 elif o
in ('-o', '--output-dir'):
2002 output_dir
= a
+ '/'
2003 elif o
in ('-c', '--source'):
2005 elif o
in ('-h', '--header'):
2008 extra_opts
.append(oa
)
2010 if not do_c
and not do_h
:
2015 print >>sys
.stderr
, "%s: need exactly one argument" % sys
.argv
[0]
2019 return (fname
, output_dir
, do_c
, do_h
, prefix
, extra_opts
)
2022 # Generate output files with boilerplate
2026 def open_output(output_dir
, do_c
, do_h
, prefix
, c_file
, h_file
,
2027 c_comment
, h_comment
):
2028 guard
= guardname(prefix
+ h_file
)
2029 c_file
= output_dir
+ prefix
+ c_file
2030 h_file
= output_dir
+ prefix
+ h_file
2034 os
.makedirs(output_dir
)
2035 except os
.error
as e
:
2036 if e
.errno
!= errno
.EEXIST
:
2039 def maybe_open(really
, name
, opt
):
2041 return open(name
, opt
)
2044 return StringIO
.StringIO()
2046 fdef
= maybe_open(do_c
, c_file
, 'w')
2047 fdecl
= maybe_open(do_h
, h_file
, 'w')
2049 fdef
.write(mcgen('''
2050 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2055 fdecl
.write(mcgen('''
2056 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2062 comment
=h_comment
, guard
=guard
))
2064 return (fdef
, fdecl
)
2067 def close_output(fdef
, fdecl
):