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
49 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
50 'CpuInfoMIPS', # PC, visible through query-cpu
51 'CpuInfoTricore', # PC, visible through query-cpu
52 'QapiErrorClass', # all members, visible through errors
53 'UuidInfo', # UUID, visible through query-uuid
54 'X86CPURegister32', # all members, visible indirectly through qom-get
55 'q_obj_CpuInfo-base', # CPU, visible through query-cpu
65 # Parsing the schema into expressions
69 def error_path(parent
):
72 res
= ("In file included from %s:%d:\n" % (parent
['file'],
73 parent
['line'])) + res
74 parent
= parent
['parent']
78 class QAPIError(Exception):
79 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
80 Exception.__init
__(self
)
88 loc
= "%s:%d" % (self
.fname
, self
.line
)
89 if self
.col
is not None:
90 loc
+= ":%s" % self
.col
91 return error_path(self
.info
) + "%s: %s" % (loc
, self
.msg
)
94 class QAPIParseError(QAPIError
):
95 def __init__(self
, parser
, msg
):
97 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
99 col
= (col
+ 7) % 8 + 1
102 QAPIError
.__init
__(self
, parser
.fname
, parser
.line
, col
,
103 parser
.incl_info
, msg
)
106 class QAPISemError(QAPIError
):
107 def __init__(self
, info
, msg
):
108 QAPIError
.__init
__(self
, info
['file'], info
['line'], None,
112 class QAPIDoc(object):
113 class Section(object):
114 def __init__(self
, name
=None):
115 # optional section name (argument/member or section name)
117 # the list of lines for this section
120 def append(self
, line
):
121 self
.content
.append(line
)
124 return "\n".join(self
.content
).strip()
126 class ArgSection(Section
):
129 def __init__(self
, parser
, info
):
130 # self.parser is used to report errors with QAPIParseError. The
131 # resulting error position depends on the state of the parser.
132 # It happens to be the beginning of the comment. More or less
133 # servicable, but action at a distance.
137 self
.body
= QAPIDoc
.Section()
138 # dict mapping parameter name to ArgSection
139 self
.args
= OrderedDict()
142 # the current section
143 self
.section
= self
.body
144 # associated expression (to be set by expression parser)
147 def has_section(self
, name
):
148 """Return True if we have a section with this name."""
149 for i
in self
.sections
:
154 def append(self
, line
):
155 """Parse a comment line and add it to the documentation."""
158 self
._append
_freeform
(line
)
162 raise QAPIParseError(self
.parser
, "Missing space after #")
165 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
166 # recognized, and get silently treated as ordinary text
168 self
._append
_symbol
_line
(line
)
169 elif not self
.body
.content
and line
.startswith("@"):
170 if not line
.endswith(":"):
171 raise QAPIParseError(self
.parser
, "Line should end with :")
172 self
.symbol
= line
[1:-1]
173 # FIXME invalid names other than the empty string aren't flagged
175 raise QAPIParseError(self
.parser
, "Invalid name")
177 self
._append
_freeform
(line
)
179 def _append_symbol_line(self
, line
):
180 name
= line
.split(' ', 1)[0]
182 if name
.startswith("@") and name
.endswith(":"):
183 line
= line
[len(name
)+1:]
184 self
._start
_args
_section
(name
[1:-1])
185 elif name
in ("Returns:", "Since:",
186 # those are often singular or plural
188 "Example:", "Examples:",
190 line
= line
[len(name
)+1:]
191 self
._start
_section
(name
[:-1])
193 self
._append
_freeform
(line
)
195 def _start_args_section(self
, name
):
196 # FIXME invalid names other than the empty string aren't flagged
198 raise QAPIParseError(self
.parser
, "Invalid parameter name")
199 if name
in self
.args
:
200 raise QAPIParseError(self
.parser
,
201 "'%s' parameter name duplicated" % name
)
203 raise QAPIParseError(self
.parser
,
204 "'@%s:' can't follow '%s' section"
205 % (name
, self
.sections
[0].name
))
206 self
.section
= QAPIDoc
.ArgSection(name
)
207 self
.args
[name
] = self
.section
209 def _start_section(self
, name
=""):
210 if name
in ("Returns", "Since") and self
.has_section(name
):
211 raise QAPIParseError(self
.parser
,
212 "Duplicated '%s' section" % name
)
213 self
.section
= QAPIDoc
.Section(name
)
214 self
.sections
.append(self
.section
)
216 def _append_freeform(self
, line
):
217 in_arg
= isinstance(self
.section
, QAPIDoc
.ArgSection
)
218 if (in_arg
and self
.section
.content
219 and not self
.section
.content
[-1]
220 and line
and not line
[0].isspace()):
221 self
._start
_section
()
222 if (in_arg
or not self
.section
.name
223 or not self
.section
.name
.startswith("Example")):
225 self
.section
.append(line
)
228 class QAPISchemaParser(object):
230 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
231 abs_fname
= os
.path
.abspath(fp
.name
)
234 previously_included
.append(abs_fname
)
235 self
.incl_info
= incl_info
237 if self
.src
== '' or self
.src
[-1] != '\n':
246 while self
.tok
is not None:
247 info
= {'file': fname
, 'line': self
.line
,
248 'parent': self
.incl_info
}
250 doc
= self
.get_doc(info
)
251 self
.docs
.append(doc
)
254 expr
= self
.get_expr(False)
255 if 'include' in expr
:
257 raise QAPISemError(info
, "Invalid 'include' directive")
258 include
= expr
["include"]
259 if not isinstance(include
, str):
260 raise QAPISemError(info
,
261 "Value of 'include' must be a string")
262 self
._include
(include
, info
, os
.path
.dirname(abs_fname
),
264 elif "pragma" in expr
:
266 raise QAPISemError(info
, "Invalid 'pragma' directive")
267 pragma
= expr
['pragma']
268 if not isinstance(pragma
, dict):
270 info
, "Value of 'pragma' must be a dictionary")
271 for name
, value
in pragma
.iteritems():
272 self
._pragma
(name
, value
, info
)
274 expr_elem
= {'expr': expr
,
277 and self
.docs
[-1].info
['file'] == fname
278 and not self
.docs
[-1].expr
):
279 self
.docs
[-1].expr
= expr
280 expr_elem
['doc'] = self
.docs
[-1]
282 self
.exprs
.append(expr_elem
)
284 def _include(self
, include
, info
, base_dir
, previously_included
):
285 incl_abs_fname
= os
.path
.join(base_dir
, include
)
286 # catch inclusion cycle
289 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
290 raise QAPISemError(info
, "Inclusion loop for %s" % include
)
293 # skip multiple include of the same file
294 if incl_abs_fname
in previously_included
:
297 fobj
= open(incl_abs_fname
, 'r')
299 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, include
))
300 exprs_include
= QAPISchemaParser(fobj
, previously_included
, info
)
301 self
.exprs
.extend(exprs_include
.exprs
)
302 self
.docs
.extend(exprs_include
.docs
)
304 def _pragma(self
, name
, value
, info
):
305 global doc_required
, returns_whitelist
306 if name
== 'doc-required':
307 if not isinstance(value
, bool):
308 raise QAPISemError(info
,
309 "Pragma 'doc-required' must be boolean")
311 elif name
== 'returns-whitelist':
312 if (not isinstance(value
, list)
313 or any([not isinstance(elt
, str) for elt
in value
])):
314 raise QAPISemError(info
,
315 "Pragma returns-whitelist must be"
316 " a list of strings")
317 returns_whitelist
= value
319 raise QAPISemError(info
, "Unknown pragma '%s'" % name
)
321 def accept(self
, skip_comment
=True):
323 self
.tok
= self
.src
[self
.cursor
]
324 self
.pos
= self
.cursor
329 if self
.src
[self
.cursor
] == '#':
330 # Start of doc comment
332 self
.cursor
= self
.src
.find('\n', self
.cursor
)
334 self
.val
= self
.src
[self
.pos
:self
.cursor
]
336 elif self
.tok
in "{}:,[]":
338 elif self
.tok
== "'":
342 ch
= self
.src
[self
.cursor
]
345 raise QAPIParseError(self
, 'Missing terminating "\'"')
359 for _
in range(0, 4):
360 ch
= self
.src
[self
.cursor
]
362 if ch
not in "0123456789abcdefABCDEF":
363 raise QAPIParseError(self
,
364 '\\u escape needs 4 '
366 value
= (value
<< 4) + int(ch
, 16)
367 # If Python 2 and 3 didn't disagree so much on
368 # how to handle Unicode, then we could allow
369 # Unicode string defaults. But most of QAPI is
370 # ASCII-only, so we aren't losing much for now.
371 if not value
or value
> 0x7f:
372 raise QAPIParseError(self
,
373 'For now, \\u escape '
374 'only supports non-zero '
375 'values up to \\u007f')
380 raise QAPIParseError(self
,
381 "Unknown escape \\%s" % ch
)
390 elif self
.src
.startswith("true", self
.pos
):
394 elif self
.src
.startswith("false", self
.pos
):
398 elif self
.src
.startswith("null", self
.pos
):
402 elif self
.tok
== '\n':
403 if self
.cursor
== len(self
.src
):
407 self
.line_pos
= self
.cursor
408 elif not self
.tok
.isspace():
409 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
411 def get_members(self
):
417 raise QAPIParseError(self
, 'Expected string or "}"')
422 raise QAPIParseError(self
, 'Expected ":"')
425 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
426 expr
[key
] = self
.get_expr(True)
431 raise QAPIParseError(self
, 'Expected "," or "}"')
434 raise QAPIParseError(self
, 'Expected string')
436 def get_values(self
):
441 if self
.tok
not in "{['tfn":
442 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
445 expr
.append(self
.get_expr(True))
450 raise QAPIParseError(self
, 'Expected "," or "]"')
453 def get_expr(self
, nested
):
454 if self
.tok
!= '{' and not nested
:
455 raise QAPIParseError(self
, 'Expected "{"')
458 expr
= self
.get_members()
459 elif self
.tok
== '[':
461 expr
= self
.get_values()
462 elif self
.tok
in "'tfn":
466 raise QAPIParseError(self
, 'Expected "{", "[" or string')
469 def get_doc(self
, info
):
471 raise QAPIParseError(self
, "Junk after '##' at start of "
472 "documentation comment")
474 doc
= QAPIDoc(self
, info
)
476 while self
.tok
== '#':
477 if self
.val
.startswith('##'):
480 raise QAPIParseError(self
, "Junk after '##' at end of "
481 "documentation comment")
488 raise QAPIParseError(self
, "Documentation comment must end with '##'")
492 # Semantic analysis of schema expressions
493 # TODO fold into QAPISchema
494 # TODO catching name collisions in generated code would be nice
498 def find_base_members(base
):
499 if isinstance(base
, dict):
501 base_struct_define
= find_struct(base
)
502 if not base_struct_define
:
504 return base_struct_define
['data']
507 # Return the qtype of an alternate branch, or None on error.
508 def find_alternate_member_qtype(qapi_type
):
509 if qapi_type
in builtin_types
:
510 return builtin_types
[qapi_type
]
511 elif find_struct(qapi_type
):
513 elif find_enum(qapi_type
):
514 return "QTYPE_QSTRING"
515 elif find_union(qapi_type
):
520 # Return the discriminator enum define if discriminator is specified as an
521 # enum type, otherwise return None.
522 def discriminator_find_enum_define(expr
):
523 base
= expr
.get('base')
524 discriminator
= expr
.get('discriminator')
526 if not (discriminator
and base
):
529 base_members
= find_base_members(base
)
533 discriminator_type
= base_members
.get(discriminator
)
534 if not discriminator_type
:
537 return find_enum(discriminator_type
)
540 # Names must be letters, numbers, -, and _. They must start with letter,
541 # except for downstream extensions which must start with __RFQDN_.
542 # Dots are only valid in the downstream extension prefix.
543 valid_name
= re
.compile('^(__[a-zA-Z0-9.-]+_)?'
544 '[a-zA-Z][a-zA-Z0-9_-]*$')
547 def check_name(info
, source
, name
, allow_optional
=False,
552 if not isinstance(name
, str):
553 raise QAPISemError(info
, "%s requires a string name" % source
)
554 if name
.startswith('*'):
555 membername
= name
[1:]
556 if not allow_optional
:
557 raise QAPISemError(info
, "%s does not allow optional name '%s'"
559 # Enum members can start with a digit, because the generated C
560 # code always prefixes it with the enum name
561 if enum_member
and membername
[0].isdigit():
562 membername
= 'D' + membername
563 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
564 # and 'q_obj_*' implicit type names.
565 if not valid_name
.match(membername
) or \
566 c_name(membername
, False).startswith('q_'):
567 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
570 def add_name(name
, info
, meta
, implicit
=False):
572 check_name(info
, "'%s'" % meta
, name
)
573 # FIXME should reject names that differ only in '_' vs. '.'
574 # vs. '-', because they're liable to clash in generated C.
575 if name
in all_names
:
576 raise QAPISemError(info
, "%s '%s' is already defined"
577 % (all_names
[name
], name
))
578 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
579 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
580 % (meta
, name
, name
[-4:]))
581 all_names
[name
] = meta
584 def add_struct(definition
, info
):
586 name
= definition
['struct']
587 add_name(name
, info
, 'struct')
588 struct_types
.append(definition
)
591 def find_struct(name
):
593 for struct
in struct_types
:
594 if struct
['struct'] == name
:
599 def add_union(definition
, info
):
601 name
= definition
['union']
602 add_name(name
, info
, 'union')
603 union_types
.append(definition
)
606 def find_union(name
):
608 for union
in union_types
:
609 if union
['union'] == name
:
614 def add_enum(name
, info
, enum_values
=None, implicit
=False):
616 add_name(name
, info
, 'enum', implicit
)
617 enum_types
.append({"enum_name": name
, "enum_values": enum_values
})
622 for enum
in enum_types
:
623 if enum
['enum_name'] == name
:
629 return find_enum(name
) is not None
632 def check_type(info
, source
, value
, allow_array
=False,
633 allow_dict
=False, allow_optional
=False,
640 # Check if array type for value is okay
641 if isinstance(value
, list):
643 raise QAPISemError(info
, "%s cannot be an array" % source
)
644 if len(value
) != 1 or not isinstance(value
[0], str):
645 raise QAPISemError(info
,
646 "%s: array type must contain single type name" %
650 # Check if type name for value is okay
651 if isinstance(value
, str):
652 if value
not in all_names
:
653 raise QAPISemError(info
, "%s uses unknown type '%s'"
655 if not all_names
[value
] in allow_metas
:
656 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
657 (source
, all_names
[value
], value
))
661 raise QAPISemError(info
, "%s should be a type name" % source
)
663 if not isinstance(value
, OrderedDict
):
664 raise QAPISemError(info
,
665 "%s should be a dictionary or type name" % source
)
667 # value is a dictionary, check that each member is okay
668 for (key
, arg
) in value
.items():
669 check_name(info
, "Member of %s" % source
, key
,
670 allow_optional
=allow_optional
)
671 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
672 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
674 # Todo: allow dictionaries to represent default values of
675 # an optional argument.
676 check_type(info
, "Member '%s' of %s" % (key
, source
), arg
,
678 allow_metas
=['built-in', 'union', 'alternate', 'struct',
682 def check_command(expr
, info
):
683 name
= expr
['command']
684 boxed
= expr
.get('boxed', False)
686 args_meta
= ['struct']
688 args_meta
+= ['union', 'alternate']
689 check_type(info
, "'data' for command '%s'" % name
,
690 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
691 allow_metas
=args_meta
)
692 returns_meta
= ['union', 'struct']
693 if name
in returns_whitelist
:
694 returns_meta
+= ['built-in', 'alternate', 'enum']
695 check_type(info
, "'returns' for command '%s'" % name
,
696 expr
.get('returns'), allow_array
=True,
697 allow_optional
=True, allow_metas
=returns_meta
)
700 def check_event(expr
, info
):
703 boxed
= expr
.get('boxed', False)
707 meta
+= ['union', 'alternate']
709 check_type(info
, "'data' for event '%s'" % name
,
710 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
714 def check_union(expr
, info
):
716 base
= expr
.get('base')
717 discriminator
= expr
.get('discriminator')
718 members
= expr
['data']
720 # Two types of unions, determined by discriminator.
722 # With no discriminator it is a simple union.
723 if discriminator
is None:
725 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
727 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
730 # Else, it's a flat union.
732 # The object must have a string or dictionary 'base'.
733 check_type(info
, "'base' for union '%s'" % name
,
734 base
, allow_dict
=True, allow_optional
=True,
735 allow_metas
=['struct'])
737 raise QAPISemError(info
, "Flat union '%s' must have a base"
739 base_members
= find_base_members(base
)
742 # The value of member 'discriminator' must name a non-optional
743 # member of the base struct.
744 check_name(info
, "Discriminator of flat union '%s'" % name
,
746 discriminator_type
= base_members
.get(discriminator
)
747 if not discriminator_type
:
748 raise QAPISemError(info
,
749 "Discriminator '%s' is not a member of base "
751 % (discriminator
, base
))
752 enum_define
= find_enum(discriminator_type
)
753 allow_metas
= ['struct']
754 # Do not allow string discriminator
756 raise QAPISemError(info
,
757 "Discriminator '%s' must be of enumeration "
758 "type" % discriminator
)
760 # Check every branch; don't allow an empty union
761 if len(members
) == 0:
762 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
763 for (key
, value
) in members
.items():
764 check_name(info
, "Member of union '%s'" % name
, key
)
766 # Each value must name a known type
767 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
768 value
, allow_array
=not base
, allow_metas
=allow_metas
)
770 # If the discriminator names an enum type, then all members
771 # of 'data' must also be members of the enum type.
773 if key
not in enum_define
['enum_values']:
774 raise QAPISemError(info
,
775 "Discriminator value '%s' is not found in "
777 % (key
, enum_define
["enum_name"]))
779 # If discriminator is user-defined, ensure all values are covered
781 for value
in enum_define
['enum_values']:
782 if value
not in members
.keys():
783 raise QAPISemError(info
, "Union '%s' data missing '%s' branch"
787 def check_alternate(expr
, info
):
788 name
= expr
['alternate']
789 members
= expr
['data']
792 # Check every branch; require at least two branches
794 raise QAPISemError(info
,
795 "Alternate '%s' should have at least two branches "
797 for (key
, value
) in members
.items():
798 check_name(info
, "Member of alternate '%s'" % name
, key
)
800 # Ensure alternates have no type conflicts.
801 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
),
803 allow_metas
=['built-in', 'union', 'struct', 'enum'])
804 qtype
= find_alternate_member_qtype(value
)
806 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
807 "type '%s'" % (name
, key
, value
))
808 if qtype
in types_seen
:
809 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
810 "be distinguished from member '%s'"
811 % (name
, key
, types_seen
[qtype
]))
812 types_seen
[qtype
] = key
815 def check_enum(expr
, info
):
817 members
= expr
.get('data')
818 prefix
= expr
.get('prefix')
820 if not isinstance(members
, list):
821 raise QAPISemError(info
,
822 "Enum '%s' requires an array for 'data'" % name
)
823 if prefix
is not None and not isinstance(prefix
, str):
824 raise QAPISemError(info
,
825 "Enum '%s' requires a string for 'prefix'" % name
)
826 for member
in members
:
827 check_name(info
, "Member of enum '%s'" % name
, member
,
831 def check_struct(expr
, info
):
832 name
= expr
['struct']
833 members
= expr
['data']
835 check_type(info
, "'data' for struct '%s'" % name
, members
,
836 allow_dict
=True, allow_optional
=True)
837 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
838 allow_metas
=['struct'])
841 def check_keys(expr_elem
, meta
, required
, optional
=[]):
842 expr
= expr_elem
['expr']
843 info
= expr_elem
['info']
845 if not isinstance(name
, str):
846 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
847 required
= required
+ [meta
]
848 for (key
, value
) in expr
.items():
849 if key
not in required
and key
not in optional
:
850 raise QAPISemError(info
, "Unknown key '%s' in %s '%s'"
852 if (key
== 'gen' or key
== 'success-response') and value
is not False:
853 raise QAPISemError(info
,
854 "'%s' of %s '%s' should only use false value"
856 if key
== 'boxed' and value
is not True:
857 raise QAPISemError(info
,
858 "'%s' of %s '%s' should only use true value"
862 raise QAPISemError(info
, "Key '%s' is missing from %s '%s'"
866 def check_exprs(exprs
):
869 # Learn the types and check for valid expression keys
870 for builtin
in builtin_types
.keys():
871 all_names
[builtin
] = 'built-in'
872 for expr_elem
in exprs
:
873 expr
= expr_elem
['expr']
874 info
= expr_elem
['info']
876 if 'doc' not in expr_elem
and doc_required
:
877 raise QAPISemError(info
,
878 "Expression missing documentation comment")
881 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
882 add_enum(expr
['enum'], info
, expr
['data'])
883 elif 'union' in expr
:
884 check_keys(expr_elem
, 'union', ['data'],
885 ['base', 'discriminator'])
886 add_union(expr
, info
)
887 elif 'alternate' in expr
:
888 check_keys(expr_elem
, 'alternate', ['data'])
889 add_name(expr
['alternate'], info
, 'alternate')
890 elif 'struct' in expr
:
891 check_keys(expr_elem
, 'struct', ['data'], ['base'])
892 add_struct(expr
, info
)
893 elif 'command' in expr
:
894 check_keys(expr_elem
, 'command', [],
895 ['data', 'returns', 'gen', 'success-response', 'boxed'])
896 add_name(expr
['command'], info
, 'command')
897 elif 'event' in expr
:
898 check_keys(expr_elem
, 'event', [], ['data', 'boxed'])
899 add_name(expr
['event'], info
, 'event')
901 raise QAPISemError(expr_elem
['info'],
902 "Expression is missing metatype")
904 # Try again for hidden UnionKind enum
905 for expr_elem
in exprs
:
906 expr
= expr_elem
['expr']
908 if not discriminator_find_enum_define(expr
):
909 add_enum('%sKind' % expr
['union'], expr_elem
['info'],
911 elif 'alternate' in expr
:
912 add_enum('%sKind' % expr
['alternate'], expr_elem
['info'],
915 # Validate that exprs make sense
916 for expr_elem
in exprs
:
917 expr
= expr_elem
['expr']
918 info
= expr_elem
['info']
921 check_enum(expr
, info
)
922 elif 'union' in expr
:
923 check_union(expr
, info
)
924 elif 'alternate' in expr
:
925 check_alternate(expr
, info
)
926 elif 'struct' in expr
:
927 check_struct(expr
, info
)
928 elif 'command' in expr
:
929 check_command(expr
, info
)
930 elif 'event' in expr
:
931 check_event(expr
, info
)
933 assert False, 'unexpected meta type'
938 def check_freeform_doc(doc
):
940 raise QAPISemError(doc
.info
,
941 "Documention for '%s' is not followed"
942 " by the definition" % doc
.symbol
)
945 if re
.search(r
'@\S+:', body
, re
.MULTILINE
):
946 raise QAPISemError(doc
.info
,
947 "Free-form documentation block must not contain"
951 def check_definition_doc(doc
, expr
, info
):
952 for i
in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
958 if doc
.symbol
!= name
:
959 raise QAPISemError(info
, "Definition of '%s' follows documentation"
960 " for '%s'" % (name
, doc
.symbol
))
961 if doc
.has_section('Returns') and 'command' not in expr
:
962 raise QAPISemError(info
, "'Returns:' is only valid for commands")
965 args
= expr
.get('base', [])
967 args
= expr
.get('data', [])
968 if isinstance(args
, str):
970 if isinstance(args
, dict):
972 assert isinstance(args
, list)
974 if (meta
== 'alternate'
975 or (meta
== 'union' and not expr
.get('discriminator'))):
981 desc
= doc
.args
.get(arg
[1:])
984 desc
= doc
.args
.get(arg
)
987 desc_opt
= "#optional" in str(desc
)
988 if desc_opt
and not opt
:
989 raise QAPISemError(info
, "Description has #optional, "
990 "but the declaration doesn't")
991 if not desc_opt
and opt
:
992 # silently fix the doc
993 # TODO either fix the schema and make this an error,
994 # or drop #optional entirely
995 desc
.append("#optional")
997 doc_args
= set(doc
.args
.keys())
998 args
= set([name
.strip('*') for name
in args
])
999 if not doc_args
.issubset(args
):
1000 raise QAPISemError(info
, "The following documented members are not in "
1001 "the declaration: %s" % ", ".join(doc_args
- args
))
1004 def check_docs(docs
):
1006 for section
in doc
.args
.values() + doc
.sections
:
1007 content
= str(section
)
1008 if not content
or content
.isspace():
1009 raise QAPISemError(doc
.info
,
1010 "Empty doc section '%s'" % section
.name
)
1013 check_freeform_doc(doc
)
1015 check_definition_doc(doc
, doc
.expr
, doc
.info
)
1021 # Schema compiler frontend
1024 class QAPISchemaEntity(object):
1025 def __init__(self
, name
, info
):
1026 assert isinstance(name
, str)
1028 # For explicitly defined entities, info points to the (explicit)
1029 # definition. For builtins (and their arrays), info is None.
1030 # For implicitly defined entities, info points to a place that
1031 # triggered the implicit definition (there may be more than one
1036 return c_name(self
.name
)
1038 def check(self
, schema
):
1041 def is_implicit(self
):
1042 return not self
.info
1044 def visit(self
, visitor
):
1048 class QAPISchemaVisitor(object):
1049 def visit_begin(self
, schema
):
1052 def visit_end(self
):
1055 def visit_needed(self
, entity
):
1056 # Default to visiting everything
1059 def visit_builtin_type(self
, name
, info
, json_type
):
1062 def visit_enum_type(self
, name
, info
, values
, prefix
):
1065 def visit_array_type(self
, name
, info
, element_type
):
1068 def visit_object_type(self
, name
, info
, base
, members
, variants
):
1071 def visit_object_type_flat(self
, name
, info
, members
, variants
):
1074 def visit_alternate_type(self
, name
, info
, variants
):
1077 def visit_command(self
, name
, info
, arg_type
, ret_type
,
1078 gen
, success_response
, boxed
):
1081 def visit_event(self
, name
, info
, arg_type
, boxed
):
1085 class QAPISchemaType(QAPISchemaEntity
):
1086 # Return the C type for common use.
1087 # For the types we commonly box, this is a pointer type.
1091 # Return the C type to be used in a parameter list.
1092 def c_param_type(self
):
1093 return self
.c_type()
1095 # Return the C type to be used where we suppress boxing.
1096 def c_unboxed_type(self
):
1097 return self
.c_type()
1099 def json_type(self
):
1102 def alternate_qtype(self
):
1104 'string': 'QTYPE_QSTRING',
1105 'number': 'QTYPE_QFLOAT',
1106 'int': 'QTYPE_QINT',
1107 'boolean': 'QTYPE_QBOOL',
1108 'object': 'QTYPE_QDICT'
1110 return json2qtype
.get(self
.json_type())
1113 class QAPISchemaBuiltinType(QAPISchemaType
):
1114 def __init__(self
, name
, json_type
, c_type
):
1115 QAPISchemaType
.__init
__(self
, name
, None)
1116 assert not c_type
or isinstance(c_type
, str)
1117 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1119 self
._json
_type
_name
= json_type
1120 self
._c
_type
_name
= c_type
1126 return self
._c
_type
_name
1128 def c_param_type(self
):
1129 if self
.name
== 'str':
1130 return 'const ' + self
._c
_type
_name
1131 return self
._c
_type
_name
1133 def json_type(self
):
1134 return self
._json
_type
_name
1136 def visit(self
, visitor
):
1137 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1140 class QAPISchemaEnumType(QAPISchemaType
):
1141 def __init__(self
, name
, info
, values
, prefix
):
1142 QAPISchemaType
.__init
__(self
, name
, info
)
1144 assert isinstance(v
, QAPISchemaMember
)
1146 assert prefix
is None or isinstance(prefix
, str)
1147 self
.values
= values
1148 self
.prefix
= prefix
1150 def check(self
, schema
):
1152 for v
in self
.values
:
1153 v
.check_clash(self
.info
, seen
)
1155 def is_implicit(self
):
1156 # See QAPISchema._make_implicit_enum_type()
1157 return self
.name
.endswith('Kind')
1160 return c_name(self
.name
)
1162 def member_names(self
):
1163 return [v
.name
for v
in self
.values
]
1165 def json_type(self
):
1168 def visit(self
, visitor
):
1169 visitor
.visit_enum_type(self
.name
, self
.info
,
1170 self
.member_names(), self
.prefix
)
1173 class QAPISchemaArrayType(QAPISchemaType
):
1174 def __init__(self
, name
, info
, element_type
):
1175 QAPISchemaType
.__init
__(self
, name
, info
)
1176 assert isinstance(element_type
, str)
1177 self
._element
_type
_name
= element_type
1178 self
.element_type
= None
1180 def check(self
, schema
):
1181 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1182 assert self
.element_type
1184 def is_implicit(self
):
1188 return c_name(self
.name
) + pointer_suffix
1190 def json_type(self
):
1193 def visit(self
, visitor
):
1194 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
1197 class QAPISchemaObjectType(QAPISchemaType
):
1198 def __init__(self
, name
, info
, base
, local_members
, variants
):
1199 # struct has local_members, optional base, and no variants
1200 # flat union has base, variants, and no local_members
1201 # simple union has local_members, variants, and no base
1202 QAPISchemaType
.__init
__(self
, name
, info
)
1203 assert base
is None or isinstance(base
, str)
1204 for m
in local_members
:
1205 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1207 if variants
is not None:
1208 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1209 variants
.set_owner(name
)
1210 self
._base
_name
= base
1212 self
.local_members
= local_members
1213 self
.variants
= variants
1216 def check(self
, schema
):
1217 if self
.members
is False: # check for cycles
1218 raise QAPISemError(self
.info
,
1219 "Object %s contains itself" % self
.name
)
1222 self
.members
= False # mark as being checked
1223 seen
= OrderedDict()
1225 self
.base
= schema
.lookup_type(self
._base
_name
)
1226 assert isinstance(self
.base
, QAPISchemaObjectType
)
1227 self
.base
.check(schema
)
1228 self
.base
.check_clash(schema
, self
.info
, seen
)
1229 for m
in self
.local_members
:
1231 m
.check_clash(self
.info
, seen
)
1232 self
.members
= seen
.values()
1234 self
.variants
.check(schema
, seen
)
1235 assert self
.variants
.tag_member
in self
.members
1236 self
.variants
.check_clash(schema
, self
.info
, seen
)
1238 # Check that the members of this type do not cause duplicate JSON members,
1239 # and update seen to track the members seen so far. Report any errors
1240 # on behalf of info, which is not necessarily self.info
1241 def check_clash(self
, schema
, info
, seen
):
1242 assert not self
.variants
# not implemented
1243 for m
in self
.members
:
1244 m
.check_clash(info
, seen
)
1246 def is_implicit(self
):
1247 # See QAPISchema._make_implicit_object_type(), as well as
1248 # _def_predefineds()
1249 return self
.name
.startswith('q_')
1252 assert self
.members
is not None
1253 return not self
.members
and not self
.variants
1256 assert self
.name
!= 'q_empty'
1257 return QAPISchemaType
.c_name(self
)
1260 assert not self
.is_implicit()
1261 return c_name(self
.name
) + pointer_suffix
1263 def c_unboxed_type(self
):
1264 return c_name(self
.name
)
1266 def json_type(self
):
1269 def visit(self
, visitor
):
1270 visitor
.visit_object_type(self
.name
, self
.info
,
1271 self
.base
, self
.local_members
, self
.variants
)
1272 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1273 self
.members
, self
.variants
)
1276 class QAPISchemaMember(object):
1279 def __init__(self
, name
):
1280 assert isinstance(name
, str)
1284 def set_owner(self
, name
):
1285 assert not self
.owner
1288 def check_clash(self
, info
, seen
):
1289 cname
= c_name(self
.name
)
1290 if cname
.lower() != cname
and self
.owner
not in case_whitelist
:
1291 raise QAPISemError(info
,
1292 "%s should not use uppercase" % self
.describe())
1294 raise QAPISemError(info
, "%s collides with %s" %
1295 (self
.describe(), seen
[cname
].describe()))
1298 def _pretty_owner(self
):
1300 if owner
.startswith('q_obj_'):
1301 # See QAPISchema._make_implicit_object_type() - reverse the
1302 # mapping there to create a nice human-readable description
1304 if owner
.endswith('-arg'):
1305 return '(parameter of %s)' % owner
[:-4]
1306 elif owner
.endswith('-base'):
1307 return '(base of %s)' % owner
[:-5]
1309 assert owner
.endswith('-wrapper')
1310 # Unreachable and not implemented
1312 if owner
.endswith('Kind'):
1313 # See QAPISchema._make_implicit_enum_type()
1314 return '(branch of %s)' % owner
[:-4]
1315 return '(%s of %s)' % (self
.role
, owner
)
1318 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1321 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1322 def __init__(self
, name
, typ
, optional
):
1323 QAPISchemaMember
.__init
__(self
, name
)
1324 assert isinstance(typ
, str)
1325 assert isinstance(optional
, bool)
1326 self
._type
_name
= typ
1328 self
.optional
= optional
1330 def check(self
, schema
):
1332 self
.type = schema
.lookup_type(self
._type
_name
)
1336 class QAPISchemaObjectTypeVariants(object):
1337 def __init__(self
, tag_name
, tag_member
, variants
):
1338 # Flat unions pass tag_name but not tag_member.
1339 # Simple unions and alternates pass tag_member but not tag_name.
1340 # After check(), tag_member is always set, and tag_name remains
1341 # a reliable witness of being used by a flat union.
1342 assert bool(tag_member
) != bool(tag_name
)
1343 assert (isinstance(tag_name
, str) or
1344 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1345 assert len(variants
) > 0
1347 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1348 self
._tag
_name
= tag_name
1349 self
.tag_member
= tag_member
1350 self
.variants
= variants
1352 def set_owner(self
, name
):
1353 for v
in self
.variants
:
1356 def check(self
, schema
, seen
):
1357 if not self
.tag_member
: # flat union
1358 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1359 assert self
._tag
_name
== self
.tag_member
.name
1360 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1361 for v
in self
.variants
:
1363 # Union names must match enum values; alternate names are
1364 # checked separately. Use 'seen' to tell the two apart.
1366 assert v
.name
in self
.tag_member
.type.member_names()
1367 assert isinstance(v
.type, QAPISchemaObjectType
)
1368 v
.type.check(schema
)
1370 def check_clash(self
, schema
, info
, seen
):
1371 for v
in self
.variants
:
1372 # Reset seen map for each variant, since qapi names from one
1373 # branch do not affect another branch
1374 assert isinstance(v
.type, QAPISchemaObjectType
)
1375 v
.type.check_clash(schema
, info
, dict(seen
))
1378 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1381 def __init__(self
, name
, typ
):
1382 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1385 class QAPISchemaAlternateType(QAPISchemaType
):
1386 def __init__(self
, name
, info
, variants
):
1387 QAPISchemaType
.__init
__(self
, name
, info
)
1388 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1389 assert variants
.tag_member
1390 variants
.set_owner(name
)
1391 variants
.tag_member
.set_owner(self
.name
)
1392 self
.variants
= variants
1394 def check(self
, schema
):
1395 self
.variants
.tag_member
.check(schema
)
1396 # Not calling self.variants.check_clash(), because there's nothing
1398 self
.variants
.check(schema
, {})
1399 # Alternate branch names have no relation to the tag enum values;
1400 # so we have to check for potential name collisions ourselves.
1402 for v
in self
.variants
.variants
:
1403 v
.check_clash(self
.info
, seen
)
1406 return c_name(self
.name
) + pointer_suffix
1408 def json_type(self
):
1411 def visit(self
, visitor
):
1412 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1418 class QAPISchemaCommand(QAPISchemaEntity
):
1419 def __init__(self
, name
, info
, arg_type
, ret_type
, gen
, success_response
,
1421 QAPISchemaEntity
.__init
__(self
, name
, info
)
1422 assert not arg_type
or isinstance(arg_type
, str)
1423 assert not ret_type
or isinstance(ret_type
, str)
1424 self
._arg
_type
_name
= arg_type
1425 self
.arg_type
= None
1426 self
._ret
_type
_name
= ret_type
1427 self
.ret_type
= None
1429 self
.success_response
= success_response
1432 def check(self
, schema
):
1433 if self
._arg
_type
_name
:
1434 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1435 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1436 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1437 self
.arg_type
.check(schema
)
1439 if self
.arg_type
.is_empty():
1440 raise QAPISemError(self
.info
,
1441 "Cannot use 'boxed' with empty type")
1443 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1444 assert not self
.arg_type
.variants
1446 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1447 if self
._ret
_type
_name
:
1448 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1449 assert isinstance(self
.ret_type
, QAPISchemaType
)
1451 def visit(self
, visitor
):
1452 visitor
.visit_command(self
.name
, self
.info
,
1453 self
.arg_type
, self
.ret_type
,
1454 self
.gen
, self
.success_response
, self
.boxed
)
1457 class QAPISchemaEvent(QAPISchemaEntity
):
1458 def __init__(self
, name
, info
, arg_type
, boxed
):
1459 QAPISchemaEntity
.__init
__(self
, name
, info
)
1460 assert not arg_type
or isinstance(arg_type
, str)
1461 self
._arg
_type
_name
= arg_type
1462 self
.arg_type
= None
1465 def check(self
, schema
):
1466 if self
._arg
_type
_name
:
1467 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1468 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1469 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1470 self
.arg_type
.check(schema
)
1472 if self
.arg_type
.is_empty():
1473 raise QAPISemError(self
.info
,
1474 "Cannot use 'boxed' with empty type")
1476 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1477 assert not self
.arg_type
.variants
1479 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1481 def visit(self
, visitor
):
1482 visitor
.visit_event(self
.name
, self
.info
, self
.arg_type
, self
.boxed
)
1485 class QAPISchema(object):
1486 def __init__(self
, fname
):
1488 parser
= QAPISchemaParser(open(fname
, "r"))
1489 self
.exprs
= check_exprs(parser
.exprs
)
1490 self
.docs
= check_docs(parser
.docs
)
1491 self
._entity
_dict
= {}
1492 self
._predefining
= True
1493 self
._def
_predefineds
()
1494 self
._predefining
= False
1497 except QAPIError
as err
:
1498 print >>sys
.stderr
, err
1501 def _def_entity(self
, ent
):
1502 # Only the predefined types are allowed to not have info
1503 assert ent
.info
or self
._predefining
1504 assert ent
.name
not in self
._entity
_dict
1505 self
._entity
_dict
[ent
.name
] = ent
1507 def lookup_entity(self
, name
, typ
=None):
1508 ent
= self
._entity
_dict
.get(name
)
1509 if typ
and not isinstance(ent
, typ
):
1513 def lookup_type(self
, name
):
1514 return self
.lookup_entity(name
, QAPISchemaType
)
1516 def _def_builtin_type(self
, name
, json_type
, c_type
):
1517 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1518 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1519 # qapi-types.h from a single .c, all arrays of builtins must be
1520 # declared in the first file whether or not they are used. Nicer
1521 # would be to use lazy instantiation, while figuring out how to
1522 # avoid compilation issues with multiple qapi-types.h.
1523 self
._make
_array
_type
(name
, None)
1525 def _def_predefineds(self
):
1526 for t
in [('str', 'string', 'char' + pointer_suffix
),
1527 ('number', 'number', 'double'),
1528 ('int', 'int', 'int64_t'),
1529 ('int8', 'int', 'int8_t'),
1530 ('int16', 'int', 'int16_t'),
1531 ('int32', 'int', 'int32_t'),
1532 ('int64', 'int', 'int64_t'),
1533 ('uint8', 'int', 'uint8_t'),
1534 ('uint16', 'int', 'uint16_t'),
1535 ('uint32', 'int', 'uint32_t'),
1536 ('uint64', 'int', 'uint64_t'),
1537 ('size', 'int', 'uint64_t'),
1538 ('bool', 'boolean', 'bool'),
1539 ('any', 'value', 'QObject' + pointer_suffix
)]:
1540 self
._def
_builtin
_type
(*t
)
1541 self
.the_empty_object_type
= QAPISchemaObjectType('q_empty', None,
1543 self
._def
_entity
(self
.the_empty_object_type
)
1544 qtype_values
= self
._make
_enum
_members
(['none', 'qnull', 'qint',
1545 'qstring', 'qdict', 'qlist',
1547 self
._def
_entity
(QAPISchemaEnumType('QType', None, qtype_values
,
1550 def _make_enum_members(self
, values
):
1551 return [QAPISchemaMember(v
) for v
in values
]
1553 def _make_implicit_enum_type(self
, name
, info
, values
):
1554 # See also QAPISchemaObjectTypeMember._pretty_owner()
1555 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1556 self
._def
_entity
(QAPISchemaEnumType(
1557 name
, info
, self
._make
_enum
_members
(values
), None))
1560 def _make_array_type(self
, element_type
, info
):
1561 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1562 if not self
.lookup_type(name
):
1563 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1566 def _make_implicit_object_type(self
, name
, info
, role
, members
):
1569 # See also QAPISchemaObjectTypeMember._pretty_owner()
1570 name
= 'q_obj_%s-%s' % (name
, role
)
1571 if not self
.lookup_entity(name
, QAPISchemaObjectType
):
1572 self
._def
_entity
(QAPISchemaObjectType(name
, info
, None,
1576 def _def_enum_type(self
, expr
, info
):
1579 prefix
= expr
.get('prefix')
1580 self
._def
_entity
(QAPISchemaEnumType(
1581 name
, info
, self
._make
_enum
_members
(data
), prefix
))
1583 def _make_member(self
, name
, typ
, info
):
1585 if name
.startswith('*'):
1588 if isinstance(typ
, list):
1589 assert len(typ
) == 1
1590 typ
= self
._make
_array
_type
(typ
[0], info
)
1591 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1593 def _make_members(self
, data
, info
):
1594 return [self
._make
_member
(key
, value
, info
)
1595 for (key
, value
) in data
.iteritems()]
1597 def _def_struct_type(self
, expr
, info
):
1598 name
= expr
['struct']
1599 base
= expr
.get('base')
1601 self
._def
_entity
(QAPISchemaObjectType(name
, info
, base
,
1602 self
._make
_members
(data
, info
),
1605 def _make_variant(self
, case
, typ
):
1606 return QAPISchemaObjectTypeVariant(case
, typ
)
1608 def _make_simple_variant(self
, case
, typ
, info
):
1609 if isinstance(typ
, list):
1610 assert len(typ
) == 1
1611 typ
= self
._make
_array
_type
(typ
[0], info
)
1612 typ
= self
._make
_implicit
_object
_type
(
1613 typ
, info
, 'wrapper', [self
._make
_member
('data', typ
, info
)])
1614 return QAPISchemaObjectTypeVariant(case
, typ
)
1616 def _def_union_type(self
, expr
, info
):
1617 name
= expr
['union']
1619 base
= expr
.get('base')
1620 tag_name
= expr
.get('discriminator')
1622 if isinstance(base
, dict):
1623 base
= (self
._make
_implicit
_object
_type
(
1624 name
, info
, 'base', self
._make
_members
(base
, info
)))
1626 variants
= [self
._make
_variant
(key
, value
)
1627 for (key
, value
) in data
.iteritems()]
1630 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1631 for (key
, value
) in data
.iteritems()]
1632 typ
= self
._make
_implicit
_enum
_type
(name
, info
,
1633 [v
.name
for v
in variants
])
1634 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1635 members
= [tag_member
]
1637 QAPISchemaObjectType(name
, info
, base
, members
,
1638 QAPISchemaObjectTypeVariants(tag_name
,
1642 def _def_alternate_type(self
, expr
, info
):
1643 name
= expr
['alternate']
1645 variants
= [self
._make
_variant
(key
, value
)
1646 for (key
, value
) in data
.iteritems()]
1647 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1649 QAPISchemaAlternateType(name
, info
,
1650 QAPISchemaObjectTypeVariants(None,
1654 def _def_command(self
, expr
, info
):
1655 name
= expr
['command']
1656 data
= expr
.get('data')
1657 rets
= expr
.get('returns')
1658 gen
= expr
.get('gen', True)
1659 success_response
= expr
.get('success-response', True)
1660 boxed
= expr
.get('boxed', False)
1661 if isinstance(data
, OrderedDict
):
1662 data
= self
._make
_implicit
_object
_type
(
1663 name
, info
, 'arg', self
._make
_members
(data
, info
))
1664 if isinstance(rets
, list):
1665 assert len(rets
) == 1
1666 rets
= self
._make
_array
_type
(rets
[0], info
)
1667 self
._def
_entity
(QAPISchemaCommand(name
, info
, data
, rets
, gen
,
1668 success_response
, boxed
))
1670 def _def_event(self
, expr
, info
):
1671 name
= expr
['event']
1672 data
= expr
.get('data')
1673 boxed
= expr
.get('boxed', False)
1674 if isinstance(data
, OrderedDict
):
1675 data
= self
._make
_implicit
_object
_type
(
1676 name
, info
, 'arg', self
._make
_members
(data
, info
))
1677 self
._def
_entity
(QAPISchemaEvent(name
, info
, data
, boxed
))
1679 def _def_exprs(self
):
1680 for expr_elem
in self
.exprs
:
1681 expr
= expr_elem
['expr']
1682 info
= expr_elem
['info']
1684 self
._def
_enum
_type
(expr
, info
)
1685 elif 'struct' in expr
:
1686 self
._def
_struct
_type
(expr
, info
)
1687 elif 'union' in expr
:
1688 self
._def
_union
_type
(expr
, info
)
1689 elif 'alternate' in expr
:
1690 self
._def
_alternate
_type
(expr
, info
)
1691 elif 'command' in expr
:
1692 self
._def
_command
(expr
, info
)
1693 elif 'event' in expr
:
1694 self
._def
_event
(expr
, info
)
1699 for ent
in self
._entity
_dict
.values():
1702 def visit(self
, visitor
):
1703 visitor
.visit_begin(self
)
1704 for (name
, entity
) in sorted(self
._entity
_dict
.items()):
1705 if visitor
.visit_needed(entity
):
1706 entity
.visit(visitor
)
1711 # Code generation helpers
1714 def camel_case(name
):
1718 if ch
in ['_', '-']:
1721 new_name
+= ch
.upper()
1724 new_name
+= ch
.lower()
1728 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1729 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1730 # ENUM24_Name -> ENUM24_NAME
1731 def camel_to_upper(value
):
1732 c_fun_str
= c_name(value
, False)
1740 # When c is upper and no "_" appears before, do more checks
1741 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != "_":
1742 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1744 elif c_fun_str
[i
- 1].isdigit():
1747 return new_name
.lstrip('_').upper()
1750 def c_enum_const(type_name
, const_name
, prefix
=None):
1751 if prefix
is not None:
1753 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1755 c_name_trans
= string
.maketrans('.-', '__')
1758 # Map @name to a valid C identifier.
1759 # If @protect, avoid returning certain ticklish identifiers (like
1760 # C keywords) by prepending "q_".
1762 # Used for converting 'name' from a 'name':'type' qapi definition
1763 # into a generated struct member, as well as converting type names
1764 # into substrings of a generated C function name.
1765 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1766 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1767 def c_name(name
, protect
=True):
1768 # ANSI X3J11/88-090, 3.1.1
1769 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1770 'default', 'do', 'double', 'else', 'enum', 'extern',
1771 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1772 'return', 'short', 'signed', 'sizeof', 'static',
1773 'struct', 'switch', 'typedef', 'union', 'unsigned',
1774 'void', 'volatile', 'while'])
1775 # ISO/IEC 9899:1999, 6.4.1
1776 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1777 # ISO/IEC 9899:2011, 6.4.1
1778 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1779 '_Noreturn', '_Static_assert', '_Thread_local'])
1780 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1782 gcc_words
= set(['asm', 'typeof'])
1783 # C++ ISO/IEC 14882:2003 2.11
1784 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1785 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1786 'namespace', 'new', 'operator', 'private', 'protected',
1787 'public', 'reinterpret_cast', 'static_cast', 'template',
1788 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1789 'using', 'virtual', 'wchar_t',
1790 # alternative representations
1791 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1792 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1793 # namespace pollution:
1794 polluted_words
= set(['unix', 'errno', 'mips', 'sparc'])
1795 name
= name
.translate(c_name_trans
)
1796 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1797 | cpp_words | polluted_words
):
1801 eatspace
= '\033EATSPACE.'
1802 pointer_suffix
= ' *' + eatspace
1805 def genindent(count
):
1807 for _
in range(count
):
1814 def push_indent(indent_amount
=4):
1816 indent_level
+= indent_amount
1819 def pop_indent(indent_amount
=4):
1821 indent_level
-= indent_amount
1824 # Generate @code with @kwds interpolated.
1825 # Obey indent_level, and strip eatspace.
1826 def cgen(code
, **kwds
):
1829 indent
= genindent(indent_level
)
1830 # re.subn() lacks flags support before Python 2.7, use re.compile()
1831 raw
= re
.subn(re
.compile("^.", re
.MULTILINE
),
1832 indent
+ r
'\g<0>', raw
)
1834 return re
.sub(re
.escape(eatspace
) + ' *', '', raw
)
1837 def mcgen(code
, **kwds
):
1840 return cgen(code
, **kwds
)
1843 def guardname(filename
):
1844 return c_name(filename
, protect
=False).upper()
1847 def guardstart(name
):
1854 name
=guardname(name
))
1860 #endif /* %(name)s */
1863 name
=guardname(name
))
1866 def gen_enum_lookup(name
, values
, prefix
=None):
1869 const char *const %(c_name)s_lookup[] = {
1871 c_name
=c_name(name
))
1872 for value
in values
:
1873 index
= c_enum_const(name
, value
, prefix
)
1875 [%(index)s] = "%(value)s",
1877 index
=index
, value
=value
)
1879 max_index
= c_enum_const(name
, '_MAX', prefix
)
1881 [%(max_index)s] = NULL,
1884 max_index
=max_index
)
1888 def gen_enum(name
, values
, prefix
=None):
1889 # append automatically generated _MAX value
1890 enum_values
= values
+ ['_MAX']
1894 typedef enum %(c_name)s {
1896 c_name
=c_name(name
))
1899 for value
in enum_values
:
1903 c_enum
=c_enum_const(name
, value
, prefix
),
1910 c_name
=c_name(name
))
1914 extern const char *const %(c_name)s_lookup[];
1916 c_name
=c_name(name
))
1920 def gen_params(arg_type
, boxed
, extra
):
1927 ret
+= '%s arg' % arg_type
.c_param_type()
1930 assert not arg_type
.variants
1931 for memb
in arg_type
.members
:
1935 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1936 ret
+= '%s %s' % (memb
.type.c_param_type(),
1944 # Common command line parsing
1948 def parse_command_line(extra_options
="", extra_long_options
=[]):
1951 opts
, args
= getopt
.gnu_getopt(sys
.argv
[1:],
1952 "chp:o:" + extra_options
,
1953 ["source", "header", "prefix=",
1954 "output-dir="] + extra_long_options
)
1955 except getopt
.GetoptError
as err
:
1956 print >>sys
.stderr
, "%s: %s" % (sys
.argv
[0], str(err
))
1967 if o
in ("-p", "--prefix"):
1968 match
= re
.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a
)
1969 if match
.end() != len(a
):
1970 print >>sys
.stderr
, \
1971 "%s: 'funny character '%s' in argument of --prefix" \
1972 % (sys
.argv
[0], a
[match
.end()])
1975 elif o
in ("-o", "--output-dir"):
1976 output_dir
= a
+ "/"
1977 elif o
in ("-c", "--source"):
1979 elif o
in ("-h", "--header"):
1982 extra_opts
.append(oa
)
1984 if not do_c
and not do_h
:
1989 print >>sys
.stderr
, "%s: need exactly one argument" % sys
.argv
[0]
1993 return (fname
, output_dir
, do_c
, do_h
, prefix
, extra_opts
)
1996 # Generate output files with boilerplate
2000 def open_output(output_dir
, do_c
, do_h
, prefix
, c_file
, h_file
,
2001 c_comment
, h_comment
):
2002 guard
= guardname(prefix
+ h_file
)
2003 c_file
= output_dir
+ prefix
+ c_file
2004 h_file
= output_dir
+ prefix
+ h_file
2008 os
.makedirs(output_dir
)
2009 except os
.error
as e
:
2010 if e
.errno
!= errno
.EEXIST
:
2013 def maybe_open(really
, name
, opt
):
2015 return open(name
, opt
)
2018 return StringIO
.StringIO()
2020 fdef
= maybe_open(do_c
, c_file
, 'w')
2021 fdecl
= maybe_open(do_h
, h_file
, 'w')
2023 fdef
.write(mcgen('''
2024 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2029 fdecl
.write(mcgen('''
2030 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2036 comment
=h_comment
, guard
=guard
))
2038 return (fdef
, fdecl
)
2041 def close_output(fdef
, fdecl
):