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 # Whitelist of commands allowed to return a non-dictionary
43 'human-monitor-command',
45 'query-migrate-cache-size',
52 'guest-fsfreeze-freeze',
53 'guest-fsfreeze-freeze-list',
54 'guest-fsfreeze-status',
55 'guest-fsfreeze-thaw',
59 'guest-sync-delimited',
62 # Whitelist of entities allowed to violate case conventions
65 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
66 'CpuInfoMIPS', # PC, visible through query-cpu
67 'CpuInfoTricore', # PC, visible through query-cpu
68 'QapiErrorClass', # all members, visible through errors
69 'UuidInfo', # UUID, visible through query-uuid
70 'X86CPURegister32', # all members, visible indirectly through qom-get
71 'q_obj_CpuInfo-base', # CPU, visible through query-cpu
81 # Parsing the schema into expressions
85 def error_path(parent
):
88 res
= ("In file included from %s:%d:\n" % (parent
['file'],
89 parent
['line'])) + res
90 parent
= parent
['parent']
94 class QAPIError(Exception):
95 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
96 Exception.__init
__(self
)
100 self
.info
= incl_info
104 loc
= "%s:%d" % (self
.fname
, self
.line
)
105 if self
.col
is not None:
106 loc
+= ":%s" % self
.col
107 return error_path(self
.info
) + "%s: %s" % (loc
, self
.msg
)
110 class QAPIParseError(QAPIError
):
111 def __init__(self
, parser
, msg
):
113 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
115 col
= (col
+ 7) % 8 + 1
118 QAPIError
.__init
__(self
, parser
.fname
, parser
.line
, col
,
119 parser
.incl_info
, msg
)
122 class QAPISemError(QAPIError
):
123 def __init__(self
, info
, msg
):
124 QAPIError
.__init
__(self
, info
['file'], info
['line'], None,
128 class QAPIDoc(object):
129 class Section(object):
130 def __init__(self
, name
=None):
131 # optional section name (argument/member or section name)
133 # the list of lines for this section
136 def append(self
, line
):
137 self
.content
.append(line
)
140 return "\n".join(self
.content
).strip()
142 class ArgSection(Section
):
145 def __init__(self
, parser
, info
):
146 # self.parser is used to report errors with QAPIParseError. The
147 # resulting error position depends on the state of the parser.
148 # It happens to be the beginning of the comment. More or less
149 # servicable, but action at a distance.
153 self
.body
= QAPIDoc
.Section()
154 # dict mapping parameter name to ArgSection
155 self
.args
= OrderedDict()
158 # the current section
159 self
.section
= self
.body
160 # associated expression (to be set by expression parser)
163 def has_section(self
, name
):
164 """Return True if we have a section with this name."""
165 for i
in self
.sections
:
170 def append(self
, line
):
171 """Parse a comment line and add it to the documentation."""
174 self
._append
_freeform
(line
)
178 raise QAPIParseError(self
.parser
, "Missing space after #")
181 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
182 # recognized, and get silently treated as ordinary text
184 self
._append
_symbol
_line
(line
)
185 elif not self
.body
.content
and line
.startswith("@"):
186 if not line
.endswith(":"):
187 raise QAPIParseError(self
.parser
, "Line should end with :")
188 self
.symbol
= line
[1:-1]
189 # FIXME invalid names other than the empty string aren't flagged
191 raise QAPIParseError(self
.parser
, "Invalid name")
193 self
._append
_freeform
(line
)
195 def _append_symbol_line(self
, line
):
196 name
= line
.split(' ', 1)[0]
198 if name
.startswith("@") and name
.endswith(":"):
199 line
= line
[len(name
)+1:]
200 self
._start
_args
_section
(name
[1:-1])
201 elif name
in ("Returns:", "Since:",
202 # those are often singular or plural
204 "Example:", "Examples:",
206 line
= line
[len(name
)+1:]
207 self
._start
_section
(name
[:-1])
209 self
._append
_freeform
(line
)
211 def _start_args_section(self
, name
):
212 # FIXME invalid names other than the empty string aren't flagged
214 raise QAPIParseError(self
.parser
, "Invalid parameter name")
215 if name
in self
.args
:
216 raise QAPIParseError(self
.parser
,
217 "'%s' parameter name duplicated" % name
)
219 raise QAPIParseError(self
.parser
,
220 "'@%s:' can't follow '%s' section"
221 % (name
, self
.sections
[0].name
))
222 self
.section
= QAPIDoc
.ArgSection(name
)
223 self
.args
[name
] = self
.section
225 def _start_section(self
, name
=""):
226 if name
in ("Returns", "Since") and self
.has_section(name
):
227 raise QAPIParseError(self
.parser
,
228 "Duplicated '%s' section" % name
)
229 self
.section
= QAPIDoc
.Section(name
)
230 self
.sections
.append(self
.section
)
232 def _append_freeform(self
, line
):
233 in_arg
= isinstance(self
.section
, QAPIDoc
.ArgSection
)
234 if (in_arg
and self
.section
.content
235 and not self
.section
.content
[-1]
236 and line
and not line
[0].isspace()):
237 self
._start
_section
()
238 if (in_arg
or not self
.section
.name
239 or not self
.section
.name
.startswith("Example")):
241 self
.section
.append(line
)
244 class QAPISchemaParser(object):
246 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
247 abs_fname
= os
.path
.abspath(fp
.name
)
250 previously_included
.append(abs_fname
)
251 self
.incl_info
= incl_info
253 if self
.src
== '' or self
.src
[-1] != '\n':
262 while self
.tok
is not None:
263 info
= {'file': fname
, 'line': self
.line
,
264 'parent': self
.incl_info
}
266 doc
= self
.get_doc(info
)
267 self
.docs
.append(doc
)
270 expr
= self
.get_expr(False)
271 if isinstance(expr
, dict) and "include" in expr
:
273 raise QAPISemError(info
, "Invalid 'include' directive")
274 include
= expr
["include"]
275 if not isinstance(include
, str):
276 raise QAPISemError(info
,
277 "Value of 'include' must be a string")
278 incl_abs_fname
= os
.path
.join(os
.path
.dirname(abs_fname
),
280 # catch inclusion cycle
283 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
284 raise QAPISemError(info
, "Inclusion loop for %s"
288 # skip multiple include of the same file
289 if incl_abs_fname
in previously_included
:
292 fobj
= open(incl_abs_fname
, 'r')
294 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, include
))
295 exprs_include
= QAPISchemaParser(fobj
, previously_included
,
297 self
.exprs
.extend(exprs_include
.exprs
)
298 self
.docs
.extend(exprs_include
.docs
)
300 expr_elem
= {'expr': expr
,
303 and self
.docs
[-1].info
['file'] == fname
304 and not self
.docs
[-1].expr
):
305 self
.docs
[-1].expr
= expr
306 expr_elem
['doc'] = self
.docs
[-1]
308 self
.exprs
.append(expr_elem
)
310 def accept(self
, skip_comment
=True):
312 self
.tok
= self
.src
[self
.cursor
]
313 self
.pos
= self
.cursor
318 if self
.src
[self
.cursor
] == '#':
319 # Start of doc comment
321 self
.cursor
= self
.src
.find('\n', self
.cursor
)
323 self
.val
= self
.src
[self
.pos
:self
.cursor
]
325 elif self
.tok
in "{}:,[]":
327 elif self
.tok
== "'":
331 ch
= self
.src
[self
.cursor
]
334 raise QAPIParseError(self
, 'Missing terminating "\'"')
348 for _
in range(0, 4):
349 ch
= self
.src
[self
.cursor
]
351 if ch
not in "0123456789abcdefABCDEF":
352 raise QAPIParseError(self
,
353 '\\u escape needs 4 '
355 value
= (value
<< 4) + int(ch
, 16)
356 # If Python 2 and 3 didn't disagree so much on
357 # how to handle Unicode, then we could allow
358 # Unicode string defaults. But most of QAPI is
359 # ASCII-only, so we aren't losing much for now.
360 if not value
or value
> 0x7f:
361 raise QAPIParseError(self
,
362 'For now, \\u escape '
363 'only supports non-zero '
364 'values up to \\u007f')
369 raise QAPIParseError(self
,
370 "Unknown escape \\%s" % ch
)
379 elif self
.src
.startswith("true", self
.pos
):
383 elif self
.src
.startswith("false", self
.pos
):
387 elif self
.src
.startswith("null", self
.pos
):
391 elif self
.tok
== '\n':
392 if self
.cursor
== len(self
.src
):
396 self
.line_pos
= self
.cursor
397 elif not self
.tok
.isspace():
398 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
400 def get_members(self
):
406 raise QAPIParseError(self
, 'Expected string or "}"')
411 raise QAPIParseError(self
, 'Expected ":"')
414 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
415 expr
[key
] = self
.get_expr(True)
420 raise QAPIParseError(self
, 'Expected "," or "}"')
423 raise QAPIParseError(self
, 'Expected string')
425 def get_values(self
):
430 if self
.tok
not in "{['tfn":
431 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
434 expr
.append(self
.get_expr(True))
439 raise QAPIParseError(self
, 'Expected "," or "]"')
442 def get_expr(self
, nested
):
443 if self
.tok
!= '{' and not nested
:
444 raise QAPIParseError(self
, 'Expected "{"')
447 expr
= self
.get_members()
448 elif self
.tok
== '[':
450 expr
= self
.get_values()
451 elif self
.tok
in "'tfn":
455 raise QAPIParseError(self
, 'Expected "{", "[" or string')
458 def get_doc(self
, info
):
460 raise QAPIParseError(self
, "Junk after '##' at start of "
461 "documentation comment")
463 doc
= QAPIDoc(self
, info
)
465 while self
.tok
== '#':
466 if self
.val
.startswith('##'):
469 raise QAPIParseError(self
, "Junk after '##' at end of "
470 "documentation comment")
477 raise QAPIParseError(self
, "Documentation comment must end with '##'")
481 # Semantic analysis of schema expressions
482 # TODO fold into QAPISchema
483 # TODO catching name collisions in generated code would be nice
487 def find_base_members(base
):
488 if isinstance(base
, dict):
490 base_struct_define
= find_struct(base
)
491 if not base_struct_define
:
493 return base_struct_define
['data']
496 # Return the qtype of an alternate branch, or None on error.
497 def find_alternate_member_qtype(qapi_type
):
498 if qapi_type
in builtin_types
:
499 return builtin_types
[qapi_type
]
500 elif find_struct(qapi_type
):
502 elif find_enum(qapi_type
):
503 return "QTYPE_QSTRING"
504 elif find_union(qapi_type
):
509 # Return the discriminator enum define if discriminator is specified as an
510 # enum type, otherwise return None.
511 def discriminator_find_enum_define(expr
):
512 base
= expr
.get('base')
513 discriminator
= expr
.get('discriminator')
515 if not (discriminator
and base
):
518 base_members
= find_base_members(base
)
522 discriminator_type
= base_members
.get(discriminator
)
523 if not discriminator_type
:
526 return find_enum(discriminator_type
)
529 # Names must be letters, numbers, -, and _. They must start with letter,
530 # except for downstream extensions which must start with __RFQDN_.
531 # Dots are only valid in the downstream extension prefix.
532 valid_name
= re
.compile('^(__[a-zA-Z0-9.-]+_)?'
533 '[a-zA-Z][a-zA-Z0-9_-]*$')
536 def check_name(info
, source
, name
, allow_optional
=False,
541 if not isinstance(name
, str):
542 raise QAPISemError(info
, "%s requires a string name" % source
)
543 if name
.startswith('*'):
544 membername
= name
[1:]
545 if not allow_optional
:
546 raise QAPISemError(info
, "%s does not allow optional name '%s'"
548 # Enum members can start with a digit, because the generated C
549 # code always prefixes it with the enum name
550 if enum_member
and membername
[0].isdigit():
551 membername
= 'D' + membername
552 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
553 # and 'q_obj_*' implicit type names.
554 if not valid_name
.match(membername
) or \
555 c_name(membername
, False).startswith('q_'):
556 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
559 def add_name(name
, info
, meta
, implicit
=False):
561 check_name(info
, "'%s'" % meta
, name
)
562 # FIXME should reject names that differ only in '_' vs. '.'
563 # vs. '-', because they're liable to clash in generated C.
564 if name
in all_names
:
565 raise QAPISemError(info
, "%s '%s' is already defined"
566 % (all_names
[name
], name
))
567 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
568 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
569 % (meta
, name
, name
[-4:]))
570 all_names
[name
] = meta
573 def add_struct(definition
, info
):
575 name
= definition
['struct']
576 add_name(name
, info
, 'struct')
577 struct_types
.append(definition
)
580 def find_struct(name
):
582 for struct
in struct_types
:
583 if struct
['struct'] == name
:
588 def add_union(definition
, info
):
590 name
= definition
['union']
591 add_name(name
, info
, 'union')
592 union_types
.append(definition
)
595 def find_union(name
):
597 for union
in union_types
:
598 if union
['union'] == name
:
603 def add_enum(name
, info
, enum_values
=None, implicit
=False):
605 add_name(name
, info
, 'enum', implicit
)
606 enum_types
.append({"enum_name": name
, "enum_values": enum_values
})
611 for enum
in enum_types
:
612 if enum
['enum_name'] == name
:
618 return find_enum(name
) is not None
621 def check_type(info
, source
, value
, allow_array
=False,
622 allow_dict
=False, allow_optional
=False,
629 # Check if array type for value is okay
630 if isinstance(value
, list):
632 raise QAPISemError(info
, "%s cannot be an array" % source
)
633 if len(value
) != 1 or not isinstance(value
[0], str):
634 raise QAPISemError(info
,
635 "%s: array type must contain single type name" %
639 # Check if type name for value is okay
640 if isinstance(value
, str):
641 if value
not in all_names
:
642 raise QAPISemError(info
, "%s uses unknown type '%s'"
644 if not all_names
[value
] in allow_metas
:
645 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
646 (source
, all_names
[value
], value
))
650 raise QAPISemError(info
, "%s should be a type name" % source
)
652 if not isinstance(value
, OrderedDict
):
653 raise QAPISemError(info
,
654 "%s should be a dictionary or type name" % source
)
656 # value is a dictionary, check that each member is okay
657 for (key
, arg
) in value
.items():
658 check_name(info
, "Member of %s" % source
, key
,
659 allow_optional
=allow_optional
)
660 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
661 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
663 # Todo: allow dictionaries to represent default values of
664 # an optional argument.
665 check_type(info
, "Member '%s' of %s" % (key
, source
), arg
,
667 allow_metas
=['built-in', 'union', 'alternate', 'struct',
671 def check_command(expr
, info
):
672 name
= expr
['command']
673 boxed
= expr
.get('boxed', False)
675 args_meta
= ['struct']
677 args_meta
+= ['union', 'alternate']
678 check_type(info
, "'data' for command '%s'" % name
,
679 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
680 allow_metas
=args_meta
)
681 returns_meta
= ['union', 'struct']
682 if name
in returns_whitelist
:
683 returns_meta
+= ['built-in', 'alternate', 'enum']
684 check_type(info
, "'returns' for command '%s'" % name
,
685 expr
.get('returns'), allow_array
=True,
686 allow_optional
=True, allow_metas
=returns_meta
)
689 def check_event(expr
, info
):
692 boxed
= expr
.get('boxed', False)
696 meta
+= ['union', 'alternate']
698 check_type(info
, "'data' for event '%s'" % name
,
699 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
703 def check_union(expr
, info
):
705 base
= expr
.get('base')
706 discriminator
= expr
.get('discriminator')
707 members
= expr
['data']
709 # Two types of unions, determined by discriminator.
711 # With no discriminator it is a simple union.
712 if discriminator
is None:
714 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
716 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
719 # Else, it's a flat union.
721 # The object must have a string or dictionary 'base'.
722 check_type(info
, "'base' for union '%s'" % name
,
723 base
, allow_dict
=True, allow_optional
=True,
724 allow_metas
=['struct'])
726 raise QAPISemError(info
, "Flat union '%s' must have a base"
728 base_members
= find_base_members(base
)
731 # The value of member 'discriminator' must name a non-optional
732 # member of the base struct.
733 check_name(info
, "Discriminator of flat union '%s'" % name
,
735 discriminator_type
= base_members
.get(discriminator
)
736 if not discriminator_type
:
737 raise QAPISemError(info
,
738 "Discriminator '%s' is not a member of base "
740 % (discriminator
, base
))
741 enum_define
= find_enum(discriminator_type
)
742 allow_metas
= ['struct']
743 # Do not allow string discriminator
745 raise QAPISemError(info
,
746 "Discriminator '%s' must be of enumeration "
747 "type" % discriminator
)
749 # Check every branch; don't allow an empty union
750 if len(members
) == 0:
751 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
752 for (key
, value
) in members
.items():
753 check_name(info
, "Member of union '%s'" % name
, key
)
755 # Each value must name a known type
756 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
757 value
, allow_array
=not base
, allow_metas
=allow_metas
)
759 # If the discriminator names an enum type, then all members
760 # of 'data' must also be members of the enum type.
762 if key
not in enum_define
['enum_values']:
763 raise QAPISemError(info
,
764 "Discriminator value '%s' is not found in "
766 % (key
, enum_define
["enum_name"]))
768 # If discriminator is user-defined, ensure all values are covered
770 for value
in enum_define
['enum_values']:
771 if value
not in members
.keys():
772 raise QAPISemError(info
, "Union '%s' data missing '%s' branch"
776 def check_alternate(expr
, info
):
777 name
= expr
['alternate']
778 members
= expr
['data']
781 # Check every branch; require at least two branches
783 raise QAPISemError(info
,
784 "Alternate '%s' should have at least two branches "
786 for (key
, value
) in members
.items():
787 check_name(info
, "Member of alternate '%s'" % name
, key
)
789 # Ensure alternates have no type conflicts.
790 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
),
792 allow_metas
=['built-in', 'union', 'struct', 'enum'])
793 qtype
= find_alternate_member_qtype(value
)
795 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
796 "type '%s'" % (name
, key
, value
))
797 if qtype
in types_seen
:
798 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
799 "be distinguished from member '%s'"
800 % (name
, key
, types_seen
[qtype
]))
801 types_seen
[qtype
] = key
804 def check_enum(expr
, info
):
806 members
= expr
.get('data')
807 prefix
= expr
.get('prefix')
809 if not isinstance(members
, list):
810 raise QAPISemError(info
,
811 "Enum '%s' requires an array for 'data'" % name
)
812 if prefix
is not None and not isinstance(prefix
, str):
813 raise QAPISemError(info
,
814 "Enum '%s' requires a string for 'prefix'" % name
)
815 for member
in members
:
816 check_name(info
, "Member of enum '%s'" % name
, member
,
820 def check_struct(expr
, info
):
821 name
= expr
['struct']
822 members
= expr
['data']
824 check_type(info
, "'data' for struct '%s'" % name
, members
,
825 allow_dict
=True, allow_optional
=True)
826 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
827 allow_metas
=['struct'])
830 def check_keys(expr_elem
, meta
, required
, optional
=[]):
831 expr
= expr_elem
['expr']
832 info
= expr_elem
['info']
834 if not isinstance(name
, str):
835 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
836 required
= required
+ [meta
]
837 for (key
, value
) in expr
.items():
838 if key
not in required
and key
not in optional
:
839 raise QAPISemError(info
, "Unknown key '%s' in %s '%s'"
841 if (key
== 'gen' or key
== 'success-response') and value
is not False:
842 raise QAPISemError(info
,
843 "'%s' of %s '%s' should only use false value"
845 if key
== 'boxed' and value
is not True:
846 raise QAPISemError(info
,
847 "'%s' of %s '%s' should only use true value"
851 raise QAPISemError(info
, "Key '%s' is missing from %s '%s'"
855 def check_exprs(exprs
):
858 # Learn the types and check for valid expression keys
859 for builtin
in builtin_types
.keys():
860 all_names
[builtin
] = 'built-in'
861 for expr_elem
in exprs
:
862 expr
= expr_elem
['expr']
863 info
= expr_elem
['info']
865 if 'doc' not in expr_elem
:
866 raise QAPISemError(info
,
867 "Expression missing documentation comment")
870 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
871 add_enum(expr
['enum'], info
, expr
['data'])
872 elif 'union' in expr
:
873 check_keys(expr_elem
, 'union', ['data'],
874 ['base', 'discriminator'])
875 add_union(expr
, info
)
876 elif 'alternate' in expr
:
877 check_keys(expr_elem
, 'alternate', ['data'])
878 add_name(expr
['alternate'], info
, 'alternate')
879 elif 'struct' in expr
:
880 check_keys(expr_elem
, 'struct', ['data'], ['base'])
881 add_struct(expr
, info
)
882 elif 'command' in expr
:
883 check_keys(expr_elem
, 'command', [],
884 ['data', 'returns', 'gen', 'success-response', 'boxed'])
885 add_name(expr
['command'], info
, 'command')
886 elif 'event' in expr
:
887 check_keys(expr_elem
, 'event', [], ['data', 'boxed'])
888 add_name(expr
['event'], info
, 'event')
890 raise QAPISemError(expr_elem
['info'],
891 "Expression is missing metatype")
893 # Try again for hidden UnionKind enum
894 for expr_elem
in exprs
:
895 expr
= expr_elem
['expr']
897 if not discriminator_find_enum_define(expr
):
898 add_enum('%sKind' % expr
['union'], expr_elem
['info'],
900 elif 'alternate' in expr
:
901 add_enum('%sKind' % expr
['alternate'], expr_elem
['info'],
904 # Validate that exprs make sense
905 for expr_elem
in exprs
:
906 expr
= expr_elem
['expr']
907 info
= expr_elem
['info']
910 check_enum(expr
, info
)
911 elif 'union' in expr
:
912 check_union(expr
, info
)
913 elif 'alternate' in expr
:
914 check_alternate(expr
, info
)
915 elif 'struct' in expr
:
916 check_struct(expr
, info
)
917 elif 'command' in expr
:
918 check_command(expr
, info
)
919 elif 'event' in expr
:
920 check_event(expr
, info
)
922 assert False, 'unexpected meta type'
927 def check_freeform_doc(doc
):
929 raise QAPISemError(doc
.info
,
930 "Documention for '%s' is not followed"
931 " by the definition" % doc
.symbol
)
934 if re
.search(r
'@\S+:', body
, re
.MULTILINE
):
935 raise QAPISemError(doc
.info
,
936 "Free-form documentation block must not contain"
940 def check_definition_doc(doc
, expr
, info
):
941 for i
in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
947 if doc
.symbol
!= name
:
948 raise QAPISemError(info
, "Definition of '%s' follows documentation"
949 " for '%s'" % (name
, doc
.symbol
))
950 if doc
.has_section('Returns') and 'command' not in expr
:
951 raise QAPISemError(info
, "'Returns:' is only valid for commands")
954 args
= expr
.get('base', [])
956 args
= expr
.get('data', [])
957 if isinstance(args
, str):
959 if isinstance(args
, dict):
961 assert isinstance(args
, list)
963 if (meta
== 'alternate'
964 or (meta
== 'union' and not expr
.get('discriminator'))):
970 desc
= doc
.args
.get(arg
[1:])
973 desc
= doc
.args
.get(arg
)
976 desc_opt
= "#optional" in str(desc
)
977 if desc_opt
and not opt
:
978 raise QAPISemError(info
, "Description has #optional, "
979 "but the declaration doesn't")
980 if not desc_opt
and opt
:
981 # silently fix the doc
982 # TODO either fix the schema and make this an error,
983 # or drop #optional entirely
984 desc
.append("#optional")
986 doc_args
= set(doc
.args
.keys())
987 args
= set([name
.strip('*') for name
in args
])
988 if not doc_args
.issubset(args
):
989 raise QAPISemError(info
, "The following documented members are not in "
990 "the declaration: %s" % ", ".join(doc_args
- args
))
993 def check_docs(docs
):
995 for section
in doc
.args
.values() + doc
.sections
:
996 content
= str(section
)
997 if not content
or content
.isspace():
998 raise QAPISemError(doc
.info
,
999 "Empty doc section '%s'" % section
.name
)
1002 check_freeform_doc(doc
)
1004 check_definition_doc(doc
, doc
.expr
, doc
.info
)
1010 # Schema compiler frontend
1013 class QAPISchemaEntity(object):
1014 def __init__(self
, name
, info
):
1015 assert isinstance(name
, str)
1017 # For explicitly defined entities, info points to the (explicit)
1018 # definition. For builtins (and their arrays), info is None.
1019 # For implicitly defined entities, info points to a place that
1020 # triggered the implicit definition (there may be more than one
1025 return c_name(self
.name
)
1027 def check(self
, schema
):
1030 def is_implicit(self
):
1031 return not self
.info
1033 def visit(self
, visitor
):
1037 class QAPISchemaVisitor(object):
1038 def visit_begin(self
, schema
):
1041 def visit_end(self
):
1044 def visit_needed(self
, entity
):
1045 # Default to visiting everything
1048 def visit_builtin_type(self
, name
, info
, json_type
):
1051 def visit_enum_type(self
, name
, info
, values
, prefix
):
1054 def visit_array_type(self
, name
, info
, element_type
):
1057 def visit_object_type(self
, name
, info
, base
, members
, variants
):
1060 def visit_object_type_flat(self
, name
, info
, members
, variants
):
1063 def visit_alternate_type(self
, name
, info
, variants
):
1066 def visit_command(self
, name
, info
, arg_type
, ret_type
,
1067 gen
, success_response
, boxed
):
1070 def visit_event(self
, name
, info
, arg_type
, boxed
):
1074 class QAPISchemaType(QAPISchemaEntity
):
1075 # Return the C type for common use.
1076 # For the types we commonly box, this is a pointer type.
1080 # Return the C type to be used in a parameter list.
1081 def c_param_type(self
):
1082 return self
.c_type()
1084 # Return the C type to be used where we suppress boxing.
1085 def c_unboxed_type(self
):
1086 return self
.c_type()
1088 def json_type(self
):
1091 def alternate_qtype(self
):
1093 'string': 'QTYPE_QSTRING',
1094 'number': 'QTYPE_QFLOAT',
1095 'int': 'QTYPE_QINT',
1096 'boolean': 'QTYPE_QBOOL',
1097 'object': 'QTYPE_QDICT'
1099 return json2qtype
.get(self
.json_type())
1102 class QAPISchemaBuiltinType(QAPISchemaType
):
1103 def __init__(self
, name
, json_type
, c_type
):
1104 QAPISchemaType
.__init
__(self
, name
, None)
1105 assert not c_type
or isinstance(c_type
, str)
1106 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1108 self
._json
_type
_name
= json_type
1109 self
._c
_type
_name
= c_type
1115 return self
._c
_type
_name
1117 def c_param_type(self
):
1118 if self
.name
== 'str':
1119 return 'const ' + self
._c
_type
_name
1120 return self
._c
_type
_name
1122 def json_type(self
):
1123 return self
._json
_type
_name
1125 def visit(self
, visitor
):
1126 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1129 class QAPISchemaEnumType(QAPISchemaType
):
1130 def __init__(self
, name
, info
, values
, prefix
):
1131 QAPISchemaType
.__init
__(self
, name
, info
)
1133 assert isinstance(v
, QAPISchemaMember
)
1135 assert prefix
is None or isinstance(prefix
, str)
1136 self
.values
= values
1137 self
.prefix
= prefix
1139 def check(self
, schema
):
1141 for v
in self
.values
:
1142 v
.check_clash(self
.info
, seen
)
1144 def is_implicit(self
):
1145 # See QAPISchema._make_implicit_enum_type()
1146 return self
.name
.endswith('Kind')
1149 return c_name(self
.name
)
1151 def member_names(self
):
1152 return [v
.name
for v
in self
.values
]
1154 def json_type(self
):
1157 def visit(self
, visitor
):
1158 visitor
.visit_enum_type(self
.name
, self
.info
,
1159 self
.member_names(), self
.prefix
)
1162 class QAPISchemaArrayType(QAPISchemaType
):
1163 def __init__(self
, name
, info
, element_type
):
1164 QAPISchemaType
.__init
__(self
, name
, info
)
1165 assert isinstance(element_type
, str)
1166 self
._element
_type
_name
= element_type
1167 self
.element_type
= None
1169 def check(self
, schema
):
1170 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1171 assert self
.element_type
1173 def is_implicit(self
):
1177 return c_name(self
.name
) + pointer_suffix
1179 def json_type(self
):
1182 def visit(self
, visitor
):
1183 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
1186 class QAPISchemaObjectType(QAPISchemaType
):
1187 def __init__(self
, name
, info
, base
, local_members
, variants
):
1188 # struct has local_members, optional base, and no variants
1189 # flat union has base, variants, and no local_members
1190 # simple union has local_members, variants, and no base
1191 QAPISchemaType
.__init
__(self
, name
, info
)
1192 assert base
is None or isinstance(base
, str)
1193 for m
in local_members
:
1194 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1196 if variants
is not None:
1197 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1198 variants
.set_owner(name
)
1199 self
._base
_name
= base
1201 self
.local_members
= local_members
1202 self
.variants
= variants
1205 def check(self
, schema
):
1206 if self
.members
is False: # check for cycles
1207 raise QAPISemError(self
.info
,
1208 "Object %s contains itself" % self
.name
)
1211 self
.members
= False # mark as being checked
1212 seen
= OrderedDict()
1214 self
.base
= schema
.lookup_type(self
._base
_name
)
1215 assert isinstance(self
.base
, QAPISchemaObjectType
)
1216 self
.base
.check(schema
)
1217 self
.base
.check_clash(schema
, self
.info
, seen
)
1218 for m
in self
.local_members
:
1220 m
.check_clash(self
.info
, seen
)
1221 self
.members
= seen
.values()
1223 self
.variants
.check(schema
, seen
)
1224 assert self
.variants
.tag_member
in self
.members
1225 self
.variants
.check_clash(schema
, self
.info
, seen
)
1227 # Check that the members of this type do not cause duplicate JSON members,
1228 # and update seen to track the members seen so far. Report any errors
1229 # on behalf of info, which is not necessarily self.info
1230 def check_clash(self
, schema
, info
, seen
):
1231 assert not self
.variants
# not implemented
1232 for m
in self
.members
:
1233 m
.check_clash(info
, seen
)
1235 def is_implicit(self
):
1236 # See QAPISchema._make_implicit_object_type(), as well as
1237 # _def_predefineds()
1238 return self
.name
.startswith('q_')
1241 assert self
.members
is not None
1242 return not self
.members
and not self
.variants
1245 assert self
.name
!= 'q_empty'
1246 return QAPISchemaType
.c_name(self
)
1249 assert not self
.is_implicit()
1250 return c_name(self
.name
) + pointer_suffix
1252 def c_unboxed_type(self
):
1253 return c_name(self
.name
)
1255 def json_type(self
):
1258 def visit(self
, visitor
):
1259 visitor
.visit_object_type(self
.name
, self
.info
,
1260 self
.base
, self
.local_members
, self
.variants
)
1261 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1262 self
.members
, self
.variants
)
1265 class QAPISchemaMember(object):
1268 def __init__(self
, name
):
1269 assert isinstance(name
, str)
1273 def set_owner(self
, name
):
1274 assert not self
.owner
1277 def check_clash(self
, info
, seen
):
1278 cname
= c_name(self
.name
)
1279 if cname
.lower() != cname
and self
.owner
not in case_whitelist
:
1280 raise QAPISemError(info
,
1281 "%s should not use uppercase" % self
.describe())
1283 raise QAPISemError(info
, "%s collides with %s" %
1284 (self
.describe(), seen
[cname
].describe()))
1287 def _pretty_owner(self
):
1289 if owner
.startswith('q_obj_'):
1290 # See QAPISchema._make_implicit_object_type() - reverse the
1291 # mapping there to create a nice human-readable description
1293 if owner
.endswith('-arg'):
1294 return '(parameter of %s)' % owner
[:-4]
1295 elif owner
.endswith('-base'):
1296 return '(base of %s)' % owner
[:-5]
1298 assert owner
.endswith('-wrapper')
1299 # Unreachable and not implemented
1301 if owner
.endswith('Kind'):
1302 # See QAPISchema._make_implicit_enum_type()
1303 return '(branch of %s)' % owner
[:-4]
1304 return '(%s of %s)' % (self
.role
, owner
)
1307 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1310 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1311 def __init__(self
, name
, typ
, optional
):
1312 QAPISchemaMember
.__init
__(self
, name
)
1313 assert isinstance(typ
, str)
1314 assert isinstance(optional
, bool)
1315 self
._type
_name
= typ
1317 self
.optional
= optional
1319 def check(self
, schema
):
1321 self
.type = schema
.lookup_type(self
._type
_name
)
1325 class QAPISchemaObjectTypeVariants(object):
1326 def __init__(self
, tag_name
, tag_member
, variants
):
1327 # Flat unions pass tag_name but not tag_member.
1328 # Simple unions and alternates pass tag_member but not tag_name.
1329 # After check(), tag_member is always set, and tag_name remains
1330 # a reliable witness of being used by a flat union.
1331 assert bool(tag_member
) != bool(tag_name
)
1332 assert (isinstance(tag_name
, str) or
1333 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1334 assert len(variants
) > 0
1336 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1337 self
._tag
_name
= tag_name
1338 self
.tag_member
= tag_member
1339 self
.variants
= variants
1341 def set_owner(self
, name
):
1342 for v
in self
.variants
:
1345 def check(self
, schema
, seen
):
1346 if not self
.tag_member
: # flat union
1347 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1348 assert self
._tag
_name
== self
.tag_member
.name
1349 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1350 for v
in self
.variants
:
1352 # Union names must match enum values; alternate names are
1353 # checked separately. Use 'seen' to tell the two apart.
1355 assert v
.name
in self
.tag_member
.type.member_names()
1356 assert isinstance(v
.type, QAPISchemaObjectType
)
1357 v
.type.check(schema
)
1359 def check_clash(self
, schema
, info
, seen
):
1360 for v
in self
.variants
:
1361 # Reset seen map for each variant, since qapi names from one
1362 # branch do not affect another branch
1363 assert isinstance(v
.type, QAPISchemaObjectType
)
1364 v
.type.check_clash(schema
, info
, dict(seen
))
1367 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1370 def __init__(self
, name
, typ
):
1371 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1374 class QAPISchemaAlternateType(QAPISchemaType
):
1375 def __init__(self
, name
, info
, variants
):
1376 QAPISchemaType
.__init
__(self
, name
, info
)
1377 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1378 assert variants
.tag_member
1379 variants
.set_owner(name
)
1380 variants
.tag_member
.set_owner(self
.name
)
1381 self
.variants
= variants
1383 def check(self
, schema
):
1384 self
.variants
.tag_member
.check(schema
)
1385 # Not calling self.variants.check_clash(), because there's nothing
1387 self
.variants
.check(schema
, {})
1388 # Alternate branch names have no relation to the tag enum values;
1389 # so we have to check for potential name collisions ourselves.
1391 for v
in self
.variants
.variants
:
1392 v
.check_clash(self
.info
, seen
)
1395 return c_name(self
.name
) + pointer_suffix
1397 def json_type(self
):
1400 def visit(self
, visitor
):
1401 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1407 class QAPISchemaCommand(QAPISchemaEntity
):
1408 def __init__(self
, name
, info
, arg_type
, ret_type
, gen
, success_response
,
1410 QAPISchemaEntity
.__init
__(self
, name
, info
)
1411 assert not arg_type
or isinstance(arg_type
, str)
1412 assert not ret_type
or isinstance(ret_type
, str)
1413 self
._arg
_type
_name
= arg_type
1414 self
.arg_type
= None
1415 self
._ret
_type
_name
= ret_type
1416 self
.ret_type
= None
1418 self
.success_response
= success_response
1421 def check(self
, schema
):
1422 if self
._arg
_type
_name
:
1423 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1424 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1425 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1426 self
.arg_type
.check(schema
)
1428 if self
.arg_type
.is_empty():
1429 raise QAPISemError(self
.info
,
1430 "Cannot use 'boxed' with empty type")
1432 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1433 assert not self
.arg_type
.variants
1435 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1436 if self
._ret
_type
_name
:
1437 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1438 assert isinstance(self
.ret_type
, QAPISchemaType
)
1440 def visit(self
, visitor
):
1441 visitor
.visit_command(self
.name
, self
.info
,
1442 self
.arg_type
, self
.ret_type
,
1443 self
.gen
, self
.success_response
, self
.boxed
)
1446 class QAPISchemaEvent(QAPISchemaEntity
):
1447 def __init__(self
, name
, info
, arg_type
, boxed
):
1448 QAPISchemaEntity
.__init
__(self
, name
, info
)
1449 assert not arg_type
or isinstance(arg_type
, str)
1450 self
._arg
_type
_name
= arg_type
1451 self
.arg_type
= None
1454 def check(self
, schema
):
1455 if self
._arg
_type
_name
:
1456 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1457 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1458 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1459 self
.arg_type
.check(schema
)
1461 if self
.arg_type
.is_empty():
1462 raise QAPISemError(self
.info
,
1463 "Cannot use 'boxed' with empty type")
1465 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1466 assert not self
.arg_type
.variants
1468 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1470 def visit(self
, visitor
):
1471 visitor
.visit_event(self
.name
, self
.info
, self
.arg_type
, self
.boxed
)
1474 class QAPISchema(object):
1475 def __init__(self
, fname
):
1477 parser
= QAPISchemaParser(open(fname
, "r"))
1478 self
.exprs
= check_exprs(parser
.exprs
)
1479 self
.docs
= check_docs(parser
.docs
)
1480 self
._entity
_dict
= {}
1481 self
._predefining
= True
1482 self
._def
_predefineds
()
1483 self
._predefining
= False
1486 except QAPIError
as err
:
1487 print >>sys
.stderr
, err
1490 def _def_entity(self
, ent
):
1491 # Only the predefined types are allowed to not have info
1492 assert ent
.info
or self
._predefining
1493 assert ent
.name
not in self
._entity
_dict
1494 self
._entity
_dict
[ent
.name
] = ent
1496 def lookup_entity(self
, name
, typ
=None):
1497 ent
= self
._entity
_dict
.get(name
)
1498 if typ
and not isinstance(ent
, typ
):
1502 def lookup_type(self
, name
):
1503 return self
.lookup_entity(name
, QAPISchemaType
)
1505 def _def_builtin_type(self
, name
, json_type
, c_type
):
1506 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1507 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1508 # qapi-types.h from a single .c, all arrays of builtins must be
1509 # declared in the first file whether or not they are used. Nicer
1510 # would be to use lazy instantiation, while figuring out how to
1511 # avoid compilation issues with multiple qapi-types.h.
1512 self
._make
_array
_type
(name
, None)
1514 def _def_predefineds(self
):
1515 for t
in [('str', 'string', 'char' + pointer_suffix
),
1516 ('number', 'number', 'double'),
1517 ('int', 'int', 'int64_t'),
1518 ('int8', 'int', 'int8_t'),
1519 ('int16', 'int', 'int16_t'),
1520 ('int32', 'int', 'int32_t'),
1521 ('int64', 'int', 'int64_t'),
1522 ('uint8', 'int', 'uint8_t'),
1523 ('uint16', 'int', 'uint16_t'),
1524 ('uint32', 'int', 'uint32_t'),
1525 ('uint64', 'int', 'uint64_t'),
1526 ('size', 'int', 'uint64_t'),
1527 ('bool', 'boolean', 'bool'),
1528 ('any', 'value', 'QObject' + pointer_suffix
)]:
1529 self
._def
_builtin
_type
(*t
)
1530 self
.the_empty_object_type
= QAPISchemaObjectType('q_empty', None,
1532 self
._def
_entity
(self
.the_empty_object_type
)
1533 qtype_values
= self
._make
_enum
_members
(['none', 'qnull', 'qint',
1534 'qstring', 'qdict', 'qlist',
1536 self
._def
_entity
(QAPISchemaEnumType('QType', None, qtype_values
,
1539 def _make_enum_members(self
, values
):
1540 return [QAPISchemaMember(v
) for v
in values
]
1542 def _make_implicit_enum_type(self
, name
, info
, values
):
1543 # See also QAPISchemaObjectTypeMember._pretty_owner()
1544 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1545 self
._def
_entity
(QAPISchemaEnumType(
1546 name
, info
, self
._make
_enum
_members
(values
), None))
1549 def _make_array_type(self
, element_type
, info
):
1550 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1551 if not self
.lookup_type(name
):
1552 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1555 def _make_implicit_object_type(self
, name
, info
, role
, members
):
1558 # See also QAPISchemaObjectTypeMember._pretty_owner()
1559 name
= 'q_obj_%s-%s' % (name
, role
)
1560 if not self
.lookup_entity(name
, QAPISchemaObjectType
):
1561 self
._def
_entity
(QAPISchemaObjectType(name
, info
, None,
1565 def _def_enum_type(self
, expr
, info
):
1568 prefix
= expr
.get('prefix')
1569 self
._def
_entity
(QAPISchemaEnumType(
1570 name
, info
, self
._make
_enum
_members
(data
), prefix
))
1572 def _make_member(self
, name
, typ
, info
):
1574 if name
.startswith('*'):
1577 if isinstance(typ
, list):
1578 assert len(typ
) == 1
1579 typ
= self
._make
_array
_type
(typ
[0], info
)
1580 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1582 def _make_members(self
, data
, info
):
1583 return [self
._make
_member
(key
, value
, info
)
1584 for (key
, value
) in data
.iteritems()]
1586 def _def_struct_type(self
, expr
, info
):
1587 name
= expr
['struct']
1588 base
= expr
.get('base')
1590 self
._def
_entity
(QAPISchemaObjectType(name
, info
, base
,
1591 self
._make
_members
(data
, info
),
1594 def _make_variant(self
, case
, typ
):
1595 return QAPISchemaObjectTypeVariant(case
, typ
)
1597 def _make_simple_variant(self
, case
, typ
, info
):
1598 if isinstance(typ
, list):
1599 assert len(typ
) == 1
1600 typ
= self
._make
_array
_type
(typ
[0], info
)
1601 typ
= self
._make
_implicit
_object
_type
(
1602 typ
, info
, 'wrapper', [self
._make
_member
('data', typ
, info
)])
1603 return QAPISchemaObjectTypeVariant(case
, typ
)
1605 def _def_union_type(self
, expr
, info
):
1606 name
= expr
['union']
1608 base
= expr
.get('base')
1609 tag_name
= expr
.get('discriminator')
1611 if isinstance(base
, dict):
1612 base
= (self
._make
_implicit
_object
_type
(
1613 name
, info
, 'base', self
._make
_members
(base
, info
)))
1615 variants
= [self
._make
_variant
(key
, value
)
1616 for (key
, value
) in data
.iteritems()]
1619 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1620 for (key
, value
) in data
.iteritems()]
1621 typ
= self
._make
_implicit
_enum
_type
(name
, info
,
1622 [v
.name
for v
in variants
])
1623 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1624 members
= [tag_member
]
1626 QAPISchemaObjectType(name
, info
, base
, members
,
1627 QAPISchemaObjectTypeVariants(tag_name
,
1631 def _def_alternate_type(self
, expr
, info
):
1632 name
= expr
['alternate']
1634 variants
= [self
._make
_variant
(key
, value
)
1635 for (key
, value
) in data
.iteritems()]
1636 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1638 QAPISchemaAlternateType(name
, info
,
1639 QAPISchemaObjectTypeVariants(None,
1643 def _def_command(self
, expr
, info
):
1644 name
= expr
['command']
1645 data
= expr
.get('data')
1646 rets
= expr
.get('returns')
1647 gen
= expr
.get('gen', True)
1648 success_response
= expr
.get('success-response', True)
1649 boxed
= expr
.get('boxed', False)
1650 if isinstance(data
, OrderedDict
):
1651 data
= self
._make
_implicit
_object
_type
(
1652 name
, info
, 'arg', self
._make
_members
(data
, info
))
1653 if isinstance(rets
, list):
1654 assert len(rets
) == 1
1655 rets
= self
._make
_array
_type
(rets
[0], info
)
1656 self
._def
_entity
(QAPISchemaCommand(name
, info
, data
, rets
, gen
,
1657 success_response
, boxed
))
1659 def _def_event(self
, expr
, info
):
1660 name
= expr
['event']
1661 data
= expr
.get('data')
1662 boxed
= expr
.get('boxed', False)
1663 if isinstance(data
, OrderedDict
):
1664 data
= self
._make
_implicit
_object
_type
(
1665 name
, info
, 'arg', self
._make
_members
(data
, info
))
1666 self
._def
_entity
(QAPISchemaEvent(name
, info
, data
, boxed
))
1668 def _def_exprs(self
):
1669 for expr_elem
in self
.exprs
:
1670 expr
= expr_elem
['expr']
1671 info
= expr_elem
['info']
1673 self
._def
_enum
_type
(expr
, info
)
1674 elif 'struct' in expr
:
1675 self
._def
_struct
_type
(expr
, info
)
1676 elif 'union' in expr
:
1677 self
._def
_union
_type
(expr
, info
)
1678 elif 'alternate' in expr
:
1679 self
._def
_alternate
_type
(expr
, info
)
1680 elif 'command' in expr
:
1681 self
._def
_command
(expr
, info
)
1682 elif 'event' in expr
:
1683 self
._def
_event
(expr
, info
)
1688 for ent
in self
._entity
_dict
.values():
1691 def visit(self
, visitor
):
1692 visitor
.visit_begin(self
)
1693 for (name
, entity
) in sorted(self
._entity
_dict
.items()):
1694 if visitor
.visit_needed(entity
):
1695 entity
.visit(visitor
)
1700 # Code generation helpers
1703 def camel_case(name
):
1707 if ch
in ['_', '-']:
1710 new_name
+= ch
.upper()
1713 new_name
+= ch
.lower()
1717 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1718 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1719 # ENUM24_Name -> ENUM24_NAME
1720 def camel_to_upper(value
):
1721 c_fun_str
= c_name(value
, False)
1729 # When c is upper and no "_" appears before, do more checks
1730 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != "_":
1731 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1733 elif c_fun_str
[i
- 1].isdigit():
1736 return new_name
.lstrip('_').upper()
1739 def c_enum_const(type_name
, const_name
, prefix
=None):
1740 if prefix
is not None:
1742 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1744 c_name_trans
= string
.maketrans('.-', '__')
1747 # Map @name to a valid C identifier.
1748 # If @protect, avoid returning certain ticklish identifiers (like
1749 # C keywords) by prepending "q_".
1751 # Used for converting 'name' from a 'name':'type' qapi definition
1752 # into a generated struct member, as well as converting type names
1753 # into substrings of a generated C function name.
1754 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1755 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1756 def c_name(name
, protect
=True):
1757 # ANSI X3J11/88-090, 3.1.1
1758 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1759 'default', 'do', 'double', 'else', 'enum', 'extern',
1760 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1761 'return', 'short', 'signed', 'sizeof', 'static',
1762 'struct', 'switch', 'typedef', 'union', 'unsigned',
1763 'void', 'volatile', 'while'])
1764 # ISO/IEC 9899:1999, 6.4.1
1765 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1766 # ISO/IEC 9899:2011, 6.4.1
1767 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1768 '_Noreturn', '_Static_assert', '_Thread_local'])
1769 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1771 gcc_words
= set(['asm', 'typeof'])
1772 # C++ ISO/IEC 14882:2003 2.11
1773 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1774 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1775 'namespace', 'new', 'operator', 'private', 'protected',
1776 'public', 'reinterpret_cast', 'static_cast', 'template',
1777 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1778 'using', 'virtual', 'wchar_t',
1779 # alternative representations
1780 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1781 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1782 # namespace pollution:
1783 polluted_words
= set(['unix', 'errno', 'mips', 'sparc'])
1784 name
= name
.translate(c_name_trans
)
1785 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1786 | cpp_words | polluted_words
):
1790 eatspace
= '\033EATSPACE.'
1791 pointer_suffix
= ' *' + eatspace
1794 def genindent(count
):
1796 for _
in range(count
):
1803 def push_indent(indent_amount
=4):
1805 indent_level
+= indent_amount
1808 def pop_indent(indent_amount
=4):
1810 indent_level
-= indent_amount
1813 # Generate @code with @kwds interpolated.
1814 # Obey indent_level, and strip eatspace.
1815 def cgen(code
, **kwds
):
1818 indent
= genindent(indent_level
)
1819 # re.subn() lacks flags support before Python 2.7, use re.compile()
1820 raw
= re
.subn(re
.compile("^.", re
.MULTILINE
),
1821 indent
+ r
'\g<0>', raw
)
1823 return re
.sub(re
.escape(eatspace
) + ' *', '', raw
)
1826 def mcgen(code
, **kwds
):
1829 return cgen(code
, **kwds
)
1832 def guardname(filename
):
1833 return c_name(filename
, protect
=False).upper()
1836 def guardstart(name
):
1843 name
=guardname(name
))
1849 #endif /* %(name)s */
1852 name
=guardname(name
))
1855 def gen_enum_lookup(name
, values
, prefix
=None):
1858 const char *const %(c_name)s_lookup[] = {
1860 c_name
=c_name(name
))
1861 for value
in values
:
1862 index
= c_enum_const(name
, value
, prefix
)
1864 [%(index)s] = "%(value)s",
1866 index
=index
, value
=value
)
1868 max_index
= c_enum_const(name
, '_MAX', prefix
)
1870 [%(max_index)s] = NULL,
1873 max_index
=max_index
)
1877 def gen_enum(name
, values
, prefix
=None):
1878 # append automatically generated _MAX value
1879 enum_values
= values
+ ['_MAX']
1883 typedef enum %(c_name)s {
1885 c_name
=c_name(name
))
1888 for value
in enum_values
:
1892 c_enum
=c_enum_const(name
, value
, prefix
),
1899 c_name
=c_name(name
))
1903 extern const char *const %(c_name)s_lookup[];
1905 c_name
=c_name(name
))
1909 def gen_params(arg_type
, boxed
, extra
):
1916 ret
+= '%s arg' % arg_type
.c_param_type()
1919 assert not arg_type
.variants
1920 for memb
in arg_type
.members
:
1924 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1925 ret
+= '%s %s' % (memb
.type.c_param_type(),
1933 # Common command line parsing
1937 def parse_command_line(extra_options
="", extra_long_options
=[]):
1940 opts
, args
= getopt
.gnu_getopt(sys
.argv
[1:],
1941 "chp:o:" + extra_options
,
1942 ["source", "header", "prefix=",
1943 "output-dir="] + extra_long_options
)
1944 except getopt
.GetoptError
as err
:
1945 print >>sys
.stderr
, "%s: %s" % (sys
.argv
[0], str(err
))
1956 if o
in ("-p", "--prefix"):
1957 match
= re
.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a
)
1958 if match
.end() != len(a
):
1959 print >>sys
.stderr
, \
1960 "%s: 'funny character '%s' in argument of --prefix" \
1961 % (sys
.argv
[0], a
[match
.end()])
1964 elif o
in ("-o", "--output-dir"):
1965 output_dir
= a
+ "/"
1966 elif o
in ("-c", "--source"):
1968 elif o
in ("-h", "--header"):
1971 extra_opts
.append(oa
)
1973 if not do_c
and not do_h
:
1978 print >>sys
.stderr
, "%s: need exactly one argument" % sys
.argv
[0]
1982 return (fname
, output_dir
, do_c
, do_h
, prefix
, extra_opts
)
1985 # Generate output files with boilerplate
1989 def open_output(output_dir
, do_c
, do_h
, prefix
, c_file
, h_file
,
1990 c_comment
, h_comment
):
1991 guard
= guardname(prefix
+ h_file
)
1992 c_file
= output_dir
+ prefix
+ c_file
1993 h_file
= output_dir
+ prefix
+ h_file
1997 os
.makedirs(output_dir
)
1998 except os
.error
as e
:
1999 if e
.errno
!= errno
.EEXIST
:
2002 def maybe_open(really
, name
, opt
):
2004 return open(name
, opt
)
2007 return StringIO
.StringIO()
2009 fdef
= maybe_open(do_c
, c_file
, 'w')
2010 fdecl
= maybe_open(do_h
, h_file
, 'w')
2012 fdef
.write(mcgen('''
2013 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2018 fdecl
.write(mcgen('''
2019 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2025 comment
=h_comment
, guard
=guard
))
2027 return (fdef
, fdecl
)
2030 def close_output(fdef
, fdecl
):