qapi: New QAPISchemaBranches, QAPISchemaAlternatives
[qemu/kevin.git] / scripts / qapi / schema.py
blob5cdedfc2c8a6d58f9b788be6a20e63023d919635
1 # -*- coding: utf-8 -*-
3 # QAPI schema internal representation
5 # Copyright (c) 2015-2019 Red Hat Inc.
7 # Authors:
8 # Markus Armbruster <armbru@redhat.com>
9 # Eric Blake <eblake@redhat.com>
10 # Marc-André Lureau <marcandre.lureau@redhat.com>
12 # This work is licensed under the terms of the GNU GPL, version 2.
13 # See the COPYING file in the top-level directory.
15 # pylint: disable=too-many-lines
17 # TODO catching name collisions in generated code would be nice
19 from __future__ import annotations
21 from abc import ABC, abstractmethod
22 from collections import OrderedDict
23 import os
24 import re
25 from typing import (
26 Any,
27 Callable,
28 Dict,
29 List,
30 Optional,
31 Union,
32 cast,
35 from .common import (
36 POINTER_SUFFIX,
37 c_name,
38 cgen_ifcond,
39 docgen_ifcond,
40 gen_endif,
41 gen_if,
43 from .error import QAPIError, QAPISemError, QAPISourceError
44 from .expr import check_exprs
45 from .parser import QAPIDoc, QAPIExpression, QAPISchemaParser
46 from .source import QAPISourceInfo
49 class QAPISchemaIfCond:
50 def __init__(
51 self,
52 ifcond: Optional[Union[str, Dict[str, object]]] = None,
53 ) -> None:
54 self.ifcond = ifcond
56 def _cgen(self) -> str:
57 return cgen_ifcond(self.ifcond)
59 def gen_if(self) -> str:
60 return gen_if(self._cgen())
62 def gen_endif(self) -> str:
63 return gen_endif(self._cgen())
65 def docgen(self) -> str:
66 return docgen_ifcond(self.ifcond)
68 def is_present(self) -> bool:
69 return bool(self.ifcond)
72 class QAPISchemaEntity:
73 """
74 A schema entity.
76 This is either a directive, such as include, or a definition.
77 The latter uses sub-class `QAPISchemaDefinition`.
78 """
79 def __init__(self, info: Optional[QAPISourceInfo]):
80 self._module: Optional[QAPISchemaModule] = None
81 # For explicitly defined entities, info points to the (explicit)
82 # definition. For builtins (and their arrays), info is None.
83 # For implicitly defined entities, info points to a place that
84 # triggered the implicit definition (there may be more than one
85 # such place).
86 self.info = info
87 self._checked = False
89 def __repr__(self) -> str:
90 return "<%s at 0x%x>" % (type(self).__name__, id(self))
92 def check(self, schema: QAPISchema) -> None:
93 # pylint: disable=unused-argument
94 self._checked = True
96 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
97 pass
99 def _set_module(
100 self, schema: QAPISchema, info: Optional[QAPISourceInfo]
101 ) -> None:
102 assert self._checked
103 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME
104 self._module = schema.module_by_fname(fname)
105 self._module.add_entity(self)
107 def set_module(self, schema: QAPISchema) -> None:
108 self._set_module(schema, self.info)
110 def visit(self, visitor: QAPISchemaVisitor) -> None:
111 # pylint: disable=unused-argument
112 assert self._checked
115 class QAPISchemaDefinition(QAPISchemaEntity):
116 meta: str
118 def __init__(
119 self,
120 name: str,
121 info: Optional[QAPISourceInfo],
122 doc: Optional[QAPIDoc],
123 ifcond: Optional[QAPISchemaIfCond] = None,
124 features: Optional[List[QAPISchemaFeature]] = None,
126 super().__init__(info)
127 for f in features or []:
128 f.set_defined_in(name)
129 self.name = name
130 self.doc = doc
131 self._ifcond = ifcond or QAPISchemaIfCond()
132 self.features = features or []
134 def __repr__(self) -> str:
135 return "<%s:%s at 0x%x>" % (type(self).__name__, self.name,
136 id(self))
138 def c_name(self) -> str:
139 return c_name(self.name)
141 def check(self, schema: QAPISchema) -> None:
142 assert not self._checked
143 super().check(schema)
144 seen: Dict[str, QAPISchemaMember] = {}
145 for f in self.features:
146 f.check_clash(self.info, seen)
148 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
149 super().connect_doc(doc)
150 doc = doc or self.doc
151 if doc:
152 for f in self.features:
153 doc.connect_feature(f)
155 @property
156 def ifcond(self) -> QAPISchemaIfCond:
157 assert self._checked
158 return self._ifcond
160 def is_implicit(self) -> bool:
161 return not self.info
163 def describe(self) -> str:
164 return "%s '%s'" % (self.meta, self.name)
167 class QAPISchemaVisitor:
168 def visit_begin(self, schema: QAPISchema) -> None:
169 pass
171 def visit_end(self) -> None:
172 pass
174 def visit_module(self, name: str) -> None:
175 pass
177 def visit_needed(self, entity: QAPISchemaEntity) -> bool:
178 # pylint: disable=unused-argument
179 # Default to visiting everything
180 return True
182 def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None:
183 pass
185 def visit_builtin_type(
186 self, name: str, info: Optional[QAPISourceInfo], json_type: str
187 ) -> None:
188 pass
190 def visit_enum_type(
191 self,
192 name: str,
193 info: Optional[QAPISourceInfo],
194 ifcond: QAPISchemaIfCond,
195 features: List[QAPISchemaFeature],
196 members: List[QAPISchemaEnumMember],
197 prefix: Optional[str],
198 ) -> None:
199 pass
201 def visit_array_type(
202 self,
203 name: str,
204 info: Optional[QAPISourceInfo],
205 ifcond: QAPISchemaIfCond,
206 element_type: QAPISchemaType,
207 ) -> None:
208 pass
210 def visit_object_type(
211 self,
212 name: str,
213 info: Optional[QAPISourceInfo],
214 ifcond: QAPISchemaIfCond,
215 features: List[QAPISchemaFeature],
216 base: Optional[QAPISchemaObjectType],
217 members: List[QAPISchemaObjectTypeMember],
218 variants: Optional[QAPISchemaBranches],
219 ) -> None:
220 pass
222 def visit_object_type_flat(
223 self,
224 name: str,
225 info: Optional[QAPISourceInfo],
226 ifcond: QAPISchemaIfCond,
227 features: List[QAPISchemaFeature],
228 members: List[QAPISchemaObjectTypeMember],
229 variants: Optional[QAPISchemaBranches],
230 ) -> None:
231 pass
233 def visit_alternate_type(
234 self,
235 name: str,
236 info: Optional[QAPISourceInfo],
237 ifcond: QAPISchemaIfCond,
238 features: List[QAPISchemaFeature],
239 variants: QAPISchemaAlternatives,
240 ) -> None:
241 pass
243 def visit_command(
244 self,
245 name: str,
246 info: Optional[QAPISourceInfo],
247 ifcond: QAPISchemaIfCond,
248 features: List[QAPISchemaFeature],
249 arg_type: Optional[QAPISchemaObjectType],
250 ret_type: Optional[QAPISchemaType],
251 gen: bool,
252 success_response: bool,
253 boxed: bool,
254 allow_oob: bool,
255 allow_preconfig: bool,
256 coroutine: bool,
257 ) -> None:
258 pass
260 def visit_event(
261 self,
262 name: str,
263 info: Optional[QAPISourceInfo],
264 ifcond: QAPISchemaIfCond,
265 features: List[QAPISchemaFeature],
266 arg_type: Optional[QAPISchemaObjectType],
267 boxed: bool,
268 ) -> None:
269 pass
272 class QAPISchemaModule:
274 BUILTIN_MODULE_NAME = './builtin'
276 def __init__(self, name: str):
277 self.name = name
278 self._entity_list: List[QAPISchemaEntity] = []
280 @staticmethod
281 def is_system_module(name: str) -> bool:
283 System modules are internally defined modules.
285 Their names start with the "./" prefix.
287 return name.startswith('./')
289 @classmethod
290 def is_user_module(cls, name: str) -> bool:
292 User modules are those defined by the user in qapi JSON files.
294 They do not start with the "./" prefix.
296 return not cls.is_system_module(name)
298 @classmethod
299 def is_builtin_module(cls, name: str) -> bool:
301 The built-in module is a single System module for the built-in types.
303 It is always "./builtin".
305 return name == cls.BUILTIN_MODULE_NAME
307 def add_entity(self, ent: QAPISchemaEntity) -> None:
308 self._entity_list.append(ent)
310 def visit(self, visitor: QAPISchemaVisitor) -> None:
311 visitor.visit_module(self.name)
312 for entity in self._entity_list:
313 if visitor.visit_needed(entity):
314 entity.visit(visitor)
317 class QAPISchemaInclude(QAPISchemaEntity):
318 def __init__(self, sub_module: QAPISchemaModule, info: QAPISourceInfo):
319 super().__init__(info)
320 self._sub_module = sub_module
322 def visit(self, visitor: QAPISchemaVisitor) -> None:
323 super().visit(visitor)
324 visitor.visit_include(self._sub_module.name, self.info)
327 class QAPISchemaType(QAPISchemaDefinition, ABC):
328 # Return the C type for common use.
329 # For the types we commonly box, this is a pointer type.
330 @abstractmethod
331 def c_type(self) -> str:
332 pass
334 # Return the C type to be used in a parameter list.
335 def c_param_type(self) -> str:
336 return self.c_type()
338 # Return the C type to be used where we suppress boxing.
339 def c_unboxed_type(self) -> str:
340 return self.c_type()
342 @abstractmethod
343 def json_type(self) -> str:
344 pass
346 def alternate_qtype(self) -> Optional[str]:
347 json2qtype = {
348 'null': 'QTYPE_QNULL',
349 'string': 'QTYPE_QSTRING',
350 'number': 'QTYPE_QNUM',
351 'int': 'QTYPE_QNUM',
352 'boolean': 'QTYPE_QBOOL',
353 'array': 'QTYPE_QLIST',
354 'object': 'QTYPE_QDICT'
356 return json2qtype.get(self.json_type())
358 def doc_type(self) -> Optional[str]:
359 if self.is_implicit():
360 return None
361 return self.name
363 def need_has_if_optional(self) -> bool:
364 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
365 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
366 return not self.c_type().endswith(POINTER_SUFFIX)
368 def check(self, schema: QAPISchema) -> None:
369 super().check(schema)
370 for feat in self.features:
371 if feat.is_special():
372 raise QAPISemError(
373 self.info,
374 f"feature '{feat.name}' is not supported for types")
376 def describe(self) -> str:
377 return "%s type '%s'" % (self.meta, self.name)
380 class QAPISchemaBuiltinType(QAPISchemaType):
381 meta = 'built-in'
383 def __init__(self, name: str, json_type: str, c_type: str):
384 super().__init__(name, None, None)
385 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
386 'value')
387 self._json_type_name = json_type
388 self._c_type_name = c_type
390 def c_name(self) -> str:
391 return self.name
393 def c_type(self) -> str:
394 return self._c_type_name
396 def c_param_type(self) -> str:
397 if self.name == 'str':
398 return 'const ' + self._c_type_name
399 return self._c_type_name
401 def json_type(self) -> str:
402 return self._json_type_name
404 def doc_type(self) -> str:
405 return self.json_type()
407 def visit(self, visitor: QAPISchemaVisitor) -> None:
408 super().visit(visitor)
409 visitor.visit_builtin_type(self.name, self.info, self.json_type())
412 class QAPISchemaEnumType(QAPISchemaType):
413 meta = 'enum'
415 def __init__(
416 self,
417 name: str,
418 info: Optional[QAPISourceInfo],
419 doc: Optional[QAPIDoc],
420 ifcond: Optional[QAPISchemaIfCond],
421 features: Optional[List[QAPISchemaFeature]],
422 members: List[QAPISchemaEnumMember],
423 prefix: Optional[str],
425 super().__init__(name, info, doc, ifcond, features)
426 for m in members:
427 m.set_defined_in(name)
428 self.members = members
429 self.prefix = prefix
431 def check(self, schema: QAPISchema) -> None:
432 super().check(schema)
433 seen: Dict[str, QAPISchemaMember] = {}
434 for m in self.members:
435 m.check_clash(self.info, seen)
437 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
438 super().connect_doc(doc)
439 doc = doc or self.doc
440 for m in self.members:
441 m.connect_doc(doc)
443 def is_implicit(self) -> bool:
444 # See QAPISchema._def_predefineds()
445 return self.name == 'QType'
447 def c_type(self) -> str:
448 return c_name(self.name)
450 def member_names(self) -> List[str]:
451 return [m.name for m in self.members]
453 def json_type(self) -> str:
454 return 'string'
456 def visit(self, visitor: QAPISchemaVisitor) -> None:
457 super().visit(visitor)
458 visitor.visit_enum_type(
459 self.name, self.info, self.ifcond, self.features,
460 self.members, self.prefix)
463 class QAPISchemaArrayType(QAPISchemaType):
464 meta = 'array'
466 def __init__(
467 self, name: str, info: Optional[QAPISourceInfo], element_type: str
469 super().__init__(name, info, None)
470 self._element_type_name = element_type
471 self.element_type: QAPISchemaType
473 def need_has_if_optional(self) -> bool:
474 # When FOO is an array, we still need has_FOO to distinguish
475 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
476 return True
478 def check(self, schema: QAPISchema) -> None:
479 super().check(schema)
480 self.element_type = schema.resolve_type(
481 self._element_type_name, self.info,
482 self.info.defn_meta if self.info else None)
483 assert not isinstance(self.element_type, QAPISchemaArrayType)
485 def set_module(self, schema: QAPISchema) -> None:
486 self._set_module(schema, self.element_type.info)
488 @property
489 def ifcond(self) -> QAPISchemaIfCond:
490 assert self._checked
491 return self.element_type.ifcond
493 def is_implicit(self) -> bool:
494 return True
496 def c_type(self) -> str:
497 return c_name(self.name) + POINTER_SUFFIX
499 def json_type(self) -> str:
500 return 'array'
502 def doc_type(self) -> Optional[str]:
503 elt_doc_type = self.element_type.doc_type()
504 if not elt_doc_type:
505 return None
506 return 'array of ' + elt_doc_type
508 def visit(self, visitor: QAPISchemaVisitor) -> None:
509 super().visit(visitor)
510 visitor.visit_array_type(self.name, self.info, self.ifcond,
511 self.element_type)
513 def describe(self) -> str:
514 return "%s type ['%s']" % (self.meta, self._element_type_name)
517 class QAPISchemaObjectType(QAPISchemaType):
518 def __init__(
519 self,
520 name: str,
521 info: Optional[QAPISourceInfo],
522 doc: Optional[QAPIDoc],
523 ifcond: Optional[QAPISchemaIfCond],
524 features: Optional[List[QAPISchemaFeature]],
525 base: Optional[str],
526 local_members: List[QAPISchemaObjectTypeMember],
527 variants: Optional[QAPISchemaBranches],
529 # struct has local_members, optional base, and no variants
530 # union has base, variants, and no local_members
531 super().__init__(name, info, doc, ifcond, features)
532 self.meta = 'union' if variants else 'struct'
533 for m in local_members:
534 m.set_defined_in(name)
535 if variants is not None:
536 variants.set_defined_in(name)
537 self._base_name = base
538 self.base = None
539 self.local_members = local_members
540 self.variants = variants
541 self.members: List[QAPISchemaObjectTypeMember]
542 self._check_complete = False
544 def check(self, schema: QAPISchema) -> None:
545 # This calls another type T's .check() exactly when the C
546 # struct emitted by gen_object() contains that T's C struct
547 # (pointers don't count).
548 if self._check_complete:
549 # A previous .check() completed: nothing to do
550 return
551 if self._checked:
552 # Recursed: C struct contains itself
553 raise QAPISemError(self.info,
554 "object %s contains itself" % self.name)
556 super().check(schema)
557 assert self._checked and not self._check_complete
559 seen = OrderedDict()
560 if self._base_name:
561 self.base = schema.resolve_type(self._base_name, self.info,
562 "'base'")
563 if (not isinstance(self.base, QAPISchemaObjectType)
564 or self.base.variants):
565 raise QAPISemError(
566 self.info,
567 "'base' requires a struct type, %s isn't"
568 % self.base.describe())
569 self.base.check(schema)
570 self.base.check_clash(self.info, seen)
571 for m in self.local_members:
572 m.check(schema)
573 m.check_clash(self.info, seen)
575 # self.check_clash() works in terms of the supertype, but
576 # self.members is declared List[QAPISchemaObjectTypeMember].
577 # Cast down to the subtype.
578 members = cast(List[QAPISchemaObjectTypeMember], list(seen.values()))
580 if self.variants:
581 self.variants.check(schema, seen)
582 self.variants.check_clash(self.info, seen)
584 self.members = members
585 self._check_complete = True # mark completed
587 # Check that the members of this type do not cause duplicate JSON members,
588 # and update seen to track the members seen so far. Report any errors
589 # on behalf of info, which is not necessarily self.info
590 def check_clash(
591 self,
592 info: Optional[QAPISourceInfo],
593 seen: Dict[str, QAPISchemaMember],
594 ) -> None:
595 assert self._checked
596 for m in self.members:
597 m.check_clash(info, seen)
598 if self.variants:
599 self.variants.check_clash(info, seen)
601 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
602 super().connect_doc(doc)
603 doc = doc or self.doc
604 if self.base and self.base.is_implicit():
605 self.base.connect_doc(doc)
606 for m in self.local_members:
607 m.connect_doc(doc)
609 def is_implicit(self) -> bool:
610 # See QAPISchema._make_implicit_object_type(), as well as
611 # _def_predefineds()
612 return self.name.startswith('q_')
614 def is_empty(self) -> bool:
615 return not self.members and not self.variants
617 def has_conditional_members(self) -> bool:
618 return any(m.ifcond.is_present() for m in self.members)
620 def c_name(self) -> str:
621 assert self.name != 'q_empty'
622 return super().c_name()
624 def c_type(self) -> str:
625 assert not self.is_implicit()
626 return c_name(self.name) + POINTER_SUFFIX
628 def c_unboxed_type(self) -> str:
629 return c_name(self.name)
631 def json_type(self) -> str:
632 return 'object'
634 def visit(self, visitor: QAPISchemaVisitor) -> None:
635 super().visit(visitor)
636 visitor.visit_object_type(
637 self.name, self.info, self.ifcond, self.features,
638 self.base, self.local_members, self.variants)
639 visitor.visit_object_type_flat(
640 self.name, self.info, self.ifcond, self.features,
641 self.members, self.variants)
644 class QAPISchemaAlternateType(QAPISchemaType):
645 meta = 'alternate'
647 def __init__(
648 self,
649 name: str,
650 info: QAPISourceInfo,
651 doc: Optional[QAPIDoc],
652 ifcond: Optional[QAPISchemaIfCond],
653 features: List[QAPISchemaFeature],
654 variants: QAPISchemaAlternatives,
656 super().__init__(name, info, doc, ifcond, features)
657 assert variants.tag_member
658 variants.set_defined_in(name)
659 variants.tag_member.set_defined_in(self.name)
660 self.variants = variants
662 def check(self, schema: QAPISchema) -> None:
663 super().check(schema)
664 self.variants.tag_member.check(schema)
665 # Not calling self.variants.check_clash(), because there's nothing
666 # to clash with
667 self.variants.check(schema, {})
668 # Alternate branch names have no relation to the tag enum values;
669 # so we have to check for potential name collisions ourselves.
670 seen: Dict[str, QAPISchemaMember] = {}
671 types_seen: Dict[str, str] = {}
672 for v in self.variants.variants:
673 v.check_clash(self.info, seen)
674 qtype = v.type.alternate_qtype()
675 if not qtype:
676 raise QAPISemError(
677 self.info,
678 "%s cannot use %s"
679 % (v.describe(self.info), v.type.describe()))
680 conflicting = set([qtype])
681 if qtype == 'QTYPE_QSTRING':
682 if isinstance(v.type, QAPISchemaEnumType):
683 for m in v.type.members:
684 if m.name in ['on', 'off']:
685 conflicting.add('QTYPE_QBOOL')
686 if re.match(r'[-+0-9.]', m.name):
687 # lazy, could be tightened
688 conflicting.add('QTYPE_QNUM')
689 else:
690 conflicting.add('QTYPE_QNUM')
691 conflicting.add('QTYPE_QBOOL')
692 for qt in conflicting:
693 if qt in types_seen:
694 raise QAPISemError(
695 self.info,
696 "%s can't be distinguished from '%s'"
697 % (v.describe(self.info), types_seen[qt]))
698 types_seen[qt] = v.name
700 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
701 super().connect_doc(doc)
702 doc = doc or self.doc
703 for v in self.variants.variants:
704 v.connect_doc(doc)
706 def c_type(self) -> str:
707 return c_name(self.name) + POINTER_SUFFIX
709 def json_type(self) -> str:
710 return 'value'
712 def visit(self, visitor: QAPISchemaVisitor) -> None:
713 super().visit(visitor)
714 visitor.visit_alternate_type(
715 self.name, self.info, self.ifcond, self.features, self.variants)
718 class QAPISchemaVariants:
719 def __init__(
720 self,
721 tag_name: Optional[str],
722 info: QAPISourceInfo,
723 tag_member: Optional[QAPISchemaObjectTypeMember],
724 variants: List[QAPISchemaVariant],
726 # Unions pass tag_name but not tag_member.
727 # Alternates pass tag_member but not tag_name.
728 # After check(), tag_member is always set.
729 assert bool(tag_member) != bool(tag_name)
730 assert (isinstance(tag_name, str) or
731 isinstance(tag_member, QAPISchemaObjectTypeMember))
732 self._tag_name = tag_name
733 self.info = info
734 self._tag_member = tag_member
735 self.variants = variants
737 @property
738 def tag_member(self) -> QAPISchemaObjectTypeMember:
739 if self._tag_member is None:
740 raise RuntimeError(
741 "QAPISchemaVariants has no tag_member property until "
742 "after check() has been run."
744 return self._tag_member
746 def set_defined_in(self, name: str) -> None:
747 for v in self.variants:
748 v.set_defined_in(name)
750 def check(
751 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
752 ) -> None:
753 if self._tag_name: # union
754 # We need to narrow the member type:
755 tmp = seen.get(c_name(self._tag_name))
756 assert tmp is None or isinstance(tmp, QAPISchemaObjectTypeMember)
757 self._tag_member = tmp
759 base = "'base'"
760 # Pointing to the base type when not implicit would be
761 # nice, but we don't know it here
762 if not self._tag_member or self._tag_name != self._tag_member.name:
763 raise QAPISemError(
764 self.info,
765 "discriminator '%s' is not a member of %s"
766 % (self._tag_name, base))
767 # Here we do:
768 assert self.tag_member.defined_in
769 base_type = schema.lookup_type(self.tag_member.defined_in)
770 assert base_type
771 if not base_type.is_implicit():
772 base = "base type '%s'" % self.tag_member.defined_in
773 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
774 raise QAPISemError(
775 self.info,
776 "discriminator member '%s' of %s must be of enum type"
777 % (self._tag_name, base))
778 if self.tag_member.optional:
779 raise QAPISemError(
780 self.info,
781 "discriminator member '%s' of %s must not be optional"
782 % (self._tag_name, base))
783 if self.tag_member.ifcond.is_present():
784 raise QAPISemError(
785 self.info,
786 "discriminator member '%s' of %s must not be conditional"
787 % (self._tag_name, base))
788 else: # alternate
789 assert self._tag_member
790 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
791 assert not self.tag_member.optional
792 assert not self.tag_member.ifcond.is_present()
793 if self._tag_name: # union
794 # branches that are not explicitly covered get an empty type
795 assert self.tag_member.defined_in
796 cases = {v.name for v in self.variants}
797 for m in self.tag_member.type.members:
798 if m.name not in cases:
799 v = QAPISchemaVariant(m.name, self.info,
800 'q_empty', m.ifcond)
801 v.set_defined_in(self.tag_member.defined_in)
802 self.variants.append(v)
803 if not self.variants:
804 raise QAPISemError(self.info, "union has no branches")
805 for v in self.variants:
806 v.check(schema)
807 # Union names must match enum values; alternate names are
808 # checked separately. Use 'seen' to tell the two apart.
809 if seen:
810 if v.name not in self.tag_member.type.member_names():
811 raise QAPISemError(
812 self.info,
813 "branch '%s' is not a value of %s"
814 % (v.name, self.tag_member.type.describe()))
815 if not isinstance(v.type, QAPISchemaObjectType):
816 raise QAPISemError(
817 self.info,
818 "%s cannot use %s"
819 % (v.describe(self.info), v.type.describe()))
820 v.type.check(schema)
822 def check_clash(
823 self,
824 info: Optional[QAPISourceInfo],
825 seen: Dict[str, QAPISchemaMember],
826 ) -> None:
827 for v in self.variants:
828 # Reset seen map for each variant, since qapi names from one
829 # branch do not affect another branch.
831 # v.type's typing is enforced in check() above.
832 assert isinstance(v.type, QAPISchemaObjectType)
833 v.type.check_clash(info, dict(seen))
836 class QAPISchemaBranches(QAPISchemaVariants):
837 def __init__(self,
838 info: QAPISourceInfo,
839 variants: List[QAPISchemaVariant],
840 tag_name: str):
841 super().__init__(tag_name, info, None, variants)
844 class QAPISchemaAlternatives(QAPISchemaVariants):
845 def __init__(self,
846 info: QAPISourceInfo,
847 variants: List[QAPISchemaVariant],
848 tag_member: QAPISchemaObjectTypeMember):
849 super().__init__(None, info, tag_member, variants)
852 class QAPISchemaMember:
853 """ Represents object members, enum members and features """
854 role = 'member'
856 def __init__(
857 self,
858 name: str,
859 info: Optional[QAPISourceInfo],
860 ifcond: Optional[QAPISchemaIfCond] = None,
862 self.name = name
863 self.info = info
864 self.ifcond = ifcond or QAPISchemaIfCond()
865 self.defined_in: Optional[str] = None
867 def set_defined_in(self, name: str) -> None:
868 assert not self.defined_in
869 self.defined_in = name
871 def check_clash(
872 self,
873 info: Optional[QAPISourceInfo],
874 seen: Dict[str, QAPISchemaMember],
875 ) -> None:
876 cname = c_name(self.name)
877 if cname in seen:
878 raise QAPISemError(
879 info,
880 "%s collides with %s"
881 % (self.describe(info), seen[cname].describe(info)))
882 seen[cname] = self
884 def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
885 if doc:
886 doc.connect_member(self)
888 def describe(self, info: Optional[QAPISourceInfo]) -> str:
889 role = self.role
890 meta = 'type'
891 defined_in = self.defined_in
892 assert defined_in
894 if defined_in.startswith('q_obj_'):
895 # See QAPISchema._make_implicit_object_type() - reverse the
896 # mapping there to create a nice human-readable description
897 defined_in = defined_in[6:]
898 if defined_in.endswith('-arg'):
899 # Implicit type created for a command's dict 'data'
900 assert role == 'member'
901 role = 'parameter'
902 meta = 'command'
903 defined_in = defined_in[:-4]
904 elif defined_in.endswith('-base'):
905 # Implicit type created for a union's dict 'base'
906 role = 'base ' + role
907 defined_in = defined_in[:-5]
908 else:
909 assert False
911 assert info is not None
912 if defined_in != info.defn_name:
913 return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in)
914 return "%s '%s'" % (role, self.name)
917 class QAPISchemaEnumMember(QAPISchemaMember):
918 role = 'value'
920 def __init__(
921 self,
922 name: str,
923 info: Optional[QAPISourceInfo],
924 ifcond: Optional[QAPISchemaIfCond] = None,
925 features: Optional[List[QAPISchemaFeature]] = None,
927 super().__init__(name, info, ifcond)
928 for f in features or []:
929 f.set_defined_in(name)
930 self.features = features or []
932 def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
933 super().connect_doc(doc)
934 if doc:
935 for f in self.features:
936 doc.connect_feature(f)
939 class QAPISchemaFeature(QAPISchemaMember):
940 role = 'feature'
942 def is_special(self) -> bool:
943 return self.name in ('deprecated', 'unstable')
946 class QAPISchemaObjectTypeMember(QAPISchemaMember):
947 def __init__(
948 self,
949 name: str,
950 info: QAPISourceInfo,
951 typ: str,
952 optional: bool,
953 ifcond: Optional[QAPISchemaIfCond] = None,
954 features: Optional[List[QAPISchemaFeature]] = None,
956 super().__init__(name, info, ifcond)
957 for f in features or []:
958 f.set_defined_in(name)
959 self._type_name = typ
960 self.type: QAPISchemaType # set during check()
961 self.optional = optional
962 self.features = features or []
964 def need_has(self) -> bool:
965 return self.optional and self.type.need_has_if_optional()
967 def check(self, schema: QAPISchema) -> None:
968 assert self.defined_in
969 self.type = schema.resolve_type(self._type_name, self.info,
970 self.describe)
971 seen: Dict[str, QAPISchemaMember] = {}
972 for f in self.features:
973 f.check_clash(self.info, seen)
975 def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
976 super().connect_doc(doc)
977 if doc:
978 for f in self.features:
979 doc.connect_feature(f)
982 class QAPISchemaVariant(QAPISchemaObjectTypeMember):
983 role = 'branch'
985 def __init__(
986 self,
987 name: str,
988 info: QAPISourceInfo,
989 typ: str,
990 ifcond: QAPISchemaIfCond,
992 super().__init__(name, info, typ, False, ifcond)
995 class QAPISchemaCommand(QAPISchemaDefinition):
996 meta = 'command'
998 def __init__(
999 self,
1000 name: str,
1001 info: QAPISourceInfo,
1002 doc: Optional[QAPIDoc],
1003 ifcond: QAPISchemaIfCond,
1004 features: List[QAPISchemaFeature],
1005 arg_type: Optional[str],
1006 ret_type: Optional[str],
1007 gen: bool,
1008 success_response: bool,
1009 boxed: bool,
1010 allow_oob: bool,
1011 allow_preconfig: bool,
1012 coroutine: bool,
1014 super().__init__(name, info, doc, ifcond, features)
1015 self._arg_type_name = arg_type
1016 self.arg_type: Optional[QAPISchemaObjectType] = None
1017 self._ret_type_name = ret_type
1018 self.ret_type: Optional[QAPISchemaType] = None
1019 self.gen = gen
1020 self.success_response = success_response
1021 self.boxed = boxed
1022 self.allow_oob = allow_oob
1023 self.allow_preconfig = allow_preconfig
1024 self.coroutine = coroutine
1026 def check(self, schema: QAPISchema) -> None:
1027 assert self.info is not None
1028 super().check(schema)
1029 if self._arg_type_name:
1030 arg_type = schema.resolve_type(
1031 self._arg_type_name, self.info, "command's 'data'")
1032 if not isinstance(arg_type, QAPISchemaObjectType):
1033 raise QAPISemError(
1034 self.info,
1035 "command's 'data' cannot take %s"
1036 % arg_type.describe())
1037 self.arg_type = arg_type
1038 if self.arg_type.variants and not self.boxed:
1039 raise QAPISemError(
1040 self.info,
1041 "command's 'data' can take %s only with 'boxed': true"
1042 % self.arg_type.describe())
1043 self.arg_type.check(schema)
1044 if self.arg_type.has_conditional_members() and not self.boxed:
1045 raise QAPISemError(
1046 self.info,
1047 "conditional command arguments require 'boxed': true")
1048 if self._ret_type_name:
1049 self.ret_type = schema.resolve_type(
1050 self._ret_type_name, self.info, "command's 'returns'")
1051 if self.name not in self.info.pragma.command_returns_exceptions:
1052 typ = self.ret_type
1053 if isinstance(typ, QAPISchemaArrayType):
1054 typ = typ.element_type
1055 if not isinstance(typ, QAPISchemaObjectType):
1056 raise QAPISemError(
1057 self.info,
1058 "command's 'returns' cannot take %s"
1059 % self.ret_type.describe())
1061 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
1062 super().connect_doc(doc)
1063 doc = doc or self.doc
1064 if doc:
1065 if self.arg_type and self.arg_type.is_implicit():
1066 self.arg_type.connect_doc(doc)
1068 def visit(self, visitor: QAPISchemaVisitor) -> None:
1069 super().visit(visitor)
1070 visitor.visit_command(
1071 self.name, self.info, self.ifcond, self.features,
1072 self.arg_type, self.ret_type, self.gen, self.success_response,
1073 self.boxed, self.allow_oob, self.allow_preconfig,
1074 self.coroutine)
1077 class QAPISchemaEvent(QAPISchemaDefinition):
1078 meta = 'event'
1080 def __init__(
1081 self,
1082 name: str,
1083 info: QAPISourceInfo,
1084 doc: Optional[QAPIDoc],
1085 ifcond: QAPISchemaIfCond,
1086 features: List[QAPISchemaFeature],
1087 arg_type: Optional[str],
1088 boxed: bool,
1090 super().__init__(name, info, doc, ifcond, features)
1091 self._arg_type_name = arg_type
1092 self.arg_type: Optional[QAPISchemaObjectType] = None
1093 self.boxed = boxed
1095 def check(self, schema: QAPISchema) -> None:
1096 super().check(schema)
1097 if self._arg_type_name:
1098 typ = schema.resolve_type(
1099 self._arg_type_name, self.info, "event's 'data'")
1100 if not isinstance(typ, QAPISchemaObjectType):
1101 raise QAPISemError(
1102 self.info,
1103 "event's 'data' cannot take %s"
1104 % typ.describe())
1105 self.arg_type = typ
1106 if self.arg_type.variants and not self.boxed:
1107 raise QAPISemError(
1108 self.info,
1109 "event's 'data' can take %s only with 'boxed': true"
1110 % self.arg_type.describe())
1111 self.arg_type.check(schema)
1112 if self.arg_type.has_conditional_members() and not self.boxed:
1113 raise QAPISemError(
1114 self.info,
1115 "conditional event arguments require 'boxed': true")
1117 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
1118 super().connect_doc(doc)
1119 doc = doc or self.doc
1120 if doc:
1121 if self.arg_type and self.arg_type.is_implicit():
1122 self.arg_type.connect_doc(doc)
1124 def visit(self, visitor: QAPISchemaVisitor) -> None:
1125 super().visit(visitor)
1126 visitor.visit_event(
1127 self.name, self.info, self.ifcond, self.features,
1128 self.arg_type, self.boxed)
1131 class QAPISchema:
1132 def __init__(self, fname: str):
1133 self.fname = fname
1135 try:
1136 parser = QAPISchemaParser(fname)
1137 except OSError as err:
1138 raise QAPIError(
1139 f"can't read schema file '{fname}': {err.strerror}"
1140 ) from err
1142 exprs = check_exprs(parser.exprs)
1143 self.docs = parser.docs
1144 self._entity_list: List[QAPISchemaEntity] = []
1145 self._entity_dict: Dict[str, QAPISchemaDefinition] = {}
1146 self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict()
1147 self._schema_dir = os.path.dirname(fname)
1148 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME)
1149 self._make_module(fname)
1150 self._predefining = True
1151 self._def_predefineds()
1152 self._predefining = False
1153 self._def_exprs(exprs)
1154 self.check()
1156 def _def_entity(self, ent: QAPISchemaEntity) -> None:
1157 self._entity_list.append(ent)
1159 def _def_definition(self, defn: QAPISchemaDefinition) -> None:
1160 # Only the predefined types are allowed to not have info
1161 assert defn.info or self._predefining
1162 self._def_entity(defn)
1163 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1164 # because they're liable to clash in generated C.
1165 other_defn = self._entity_dict.get(defn.name)
1166 if other_defn:
1167 if other_defn.info:
1168 where = QAPISourceError(other_defn.info, "previous definition")
1169 raise QAPISemError(
1170 defn.info,
1171 "'%s' is already defined\n%s" % (defn.name, where))
1172 raise QAPISemError(
1173 defn.info, "%s is already defined" % other_defn.describe())
1174 self._entity_dict[defn.name] = defn
1176 def lookup_entity(self,name: str) -> Optional[QAPISchemaEntity]:
1177 return self._entity_dict.get(name)
1179 def lookup_type(self, name: str) -> Optional[QAPISchemaType]:
1180 typ = self.lookup_entity(name)
1181 if isinstance(typ, QAPISchemaType):
1182 return typ
1183 return None
1185 def resolve_type(
1186 self,
1187 name: str,
1188 info: Optional[QAPISourceInfo],
1189 what: Union[None, str, Callable[[QAPISourceInfo], str]],
1190 ) -> QAPISchemaType:
1191 typ = self.lookup_type(name)
1192 if not typ:
1193 assert info and what # built-in types must not fail lookup
1194 if callable(what):
1195 what = what(info)
1196 raise QAPISemError(
1197 info, "%s uses unknown type '%s'" % (what, name))
1198 return typ
1200 def _module_name(self, fname: str) -> str:
1201 if QAPISchemaModule.is_system_module(fname):
1202 return fname
1203 return os.path.relpath(fname, self._schema_dir)
1205 def _make_module(self, fname: str) -> QAPISchemaModule:
1206 name = self._module_name(fname)
1207 if name not in self._module_dict:
1208 self._module_dict[name] = QAPISchemaModule(name)
1209 return self._module_dict[name]
1211 def module_by_fname(self, fname: str) -> QAPISchemaModule:
1212 name = self._module_name(fname)
1213 return self._module_dict[name]
1215 def _def_include(self, expr: QAPIExpression) -> None:
1216 include = expr['include']
1217 assert expr.doc is None
1218 self._def_entity(
1219 QAPISchemaInclude(self._make_module(include), expr.info))
1221 def _def_builtin_type(
1222 self, name: str, json_type: str, c_type: str
1223 ) -> None:
1224 self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type))
1225 # Instantiating only the arrays that are actually used would
1226 # be nice, but we can't as long as their generated code
1227 # (qapi-builtin-types.[ch]) may be shared by some other
1228 # schema.
1229 self._make_array_type(name, None)
1231 def _def_predefineds(self) -> None:
1232 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
1233 ('number', 'number', 'double'),
1234 ('int', 'int', 'int64_t'),
1235 ('int8', 'int', 'int8_t'),
1236 ('int16', 'int', 'int16_t'),
1237 ('int32', 'int', 'int32_t'),
1238 ('int64', 'int', 'int64_t'),
1239 ('uint8', 'int', 'uint8_t'),
1240 ('uint16', 'int', 'uint16_t'),
1241 ('uint32', 'int', 'uint32_t'),
1242 ('uint64', 'int', 'uint64_t'),
1243 ('size', 'int', 'uint64_t'),
1244 ('bool', 'boolean', 'bool'),
1245 ('any', 'value', 'QObject' + POINTER_SUFFIX),
1246 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
1247 self._def_builtin_type(*t)
1248 self.the_empty_object_type = QAPISchemaObjectType(
1249 'q_empty', None, None, None, None, None, [], None)
1250 self._def_definition(self.the_empty_object_type)
1252 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1253 'qbool']
1254 qtype_values = self._make_enum_members(
1255 [{'name': n} for n in qtypes], None)
1257 self._def_definition(QAPISchemaEnumType(
1258 'QType', None, None, None, None, qtype_values, 'QTYPE'))
1260 def _make_features(
1261 self,
1262 features: Optional[List[Dict[str, Any]]],
1263 info: Optional[QAPISourceInfo],
1264 ) -> List[QAPISchemaFeature]:
1265 if features is None:
1266 return []
1267 return [QAPISchemaFeature(f['name'], info,
1268 QAPISchemaIfCond(f.get('if')))
1269 for f in features]
1271 def _make_enum_member(
1272 self,
1273 name: str,
1274 ifcond: Optional[Union[str, Dict[str, Any]]],
1275 features: Optional[List[Dict[str, Any]]],
1276 info: Optional[QAPISourceInfo],
1277 ) -> QAPISchemaEnumMember:
1278 return QAPISchemaEnumMember(name, info,
1279 QAPISchemaIfCond(ifcond),
1280 self._make_features(features, info))
1282 def _make_enum_members(
1283 self, values: List[Dict[str, Any]], info: Optional[QAPISourceInfo]
1284 ) -> List[QAPISchemaEnumMember]:
1285 return [self._make_enum_member(v['name'], v.get('if'),
1286 v.get('features'), info)
1287 for v in values]
1289 def _make_array_type(
1290 self, element_type: str, info: Optional[QAPISourceInfo]
1291 ) -> str:
1292 name = element_type + 'List' # reserved by check_defn_name_str()
1293 if not self.lookup_type(name):
1294 self._def_definition(QAPISchemaArrayType(
1295 name, info, element_type))
1296 return name
1298 def _make_implicit_object_type(
1299 self,
1300 name: str,
1301 info: QAPISourceInfo,
1302 ifcond: QAPISchemaIfCond,
1303 role: str,
1304 members: List[QAPISchemaObjectTypeMember],
1305 ) -> Optional[str]:
1306 if not members:
1307 return None
1308 # See also QAPISchemaObjectTypeMember.describe()
1309 name = 'q_obj_%s-%s' % (name, role)
1310 typ = self.lookup_entity(name)
1311 if typ:
1312 assert(isinstance(typ, QAPISchemaObjectType))
1313 # The implicit object type has multiple users. This can
1314 # only be a duplicate definition, which will be flagged
1315 # later.
1316 pass
1317 else:
1318 self._def_definition(QAPISchemaObjectType(
1319 name, info, None, ifcond, None, None, members, None))
1320 return name
1322 def _def_enum_type(self, expr: QAPIExpression) -> None:
1323 name = expr['enum']
1324 data = expr['data']
1325 prefix = expr.get('prefix')
1326 ifcond = QAPISchemaIfCond(expr.get('if'))
1327 info = expr.info
1328 features = self._make_features(expr.get('features'), info)
1329 self._def_definition(QAPISchemaEnumType(
1330 name, info, expr.doc, ifcond, features,
1331 self._make_enum_members(data, info), prefix))
1333 def _make_member(
1334 self,
1335 name: str,
1336 typ: Union[List[str], str],
1337 ifcond: QAPISchemaIfCond,
1338 features: Optional[List[Dict[str, Any]]],
1339 info: QAPISourceInfo,
1340 ) -> QAPISchemaObjectTypeMember:
1341 optional = False
1342 if name.startswith('*'):
1343 name = name[1:]
1344 optional = True
1345 if isinstance(typ, list):
1346 assert len(typ) == 1
1347 typ = self._make_array_type(typ[0], info)
1348 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1349 self._make_features(features, info))
1351 def _make_members(
1352 self,
1353 data: Dict[str, Any],
1354 info: QAPISourceInfo,
1355 ) -> List[QAPISchemaObjectTypeMember]:
1356 return [self._make_member(key, value['type'],
1357 QAPISchemaIfCond(value.get('if')),
1358 value.get('features'), info)
1359 for (key, value) in data.items()]
1361 def _def_struct_type(self, expr: QAPIExpression) -> None:
1362 name = expr['struct']
1363 base = expr.get('base')
1364 data = expr['data']
1365 info = expr.info
1366 ifcond = QAPISchemaIfCond(expr.get('if'))
1367 features = self._make_features(expr.get('features'), info)
1368 self._def_definition(QAPISchemaObjectType(
1369 name, info, expr.doc, ifcond, features, base,
1370 self._make_members(data, info),
1371 None))
1373 def _make_variant(
1374 self,
1375 case: str,
1376 typ: str,
1377 ifcond: QAPISchemaIfCond,
1378 info: QAPISourceInfo,
1379 ) -> QAPISchemaVariant:
1380 if isinstance(typ, list):
1381 assert len(typ) == 1
1382 typ = self._make_array_type(typ[0], info)
1383 return QAPISchemaVariant(case, info, typ, ifcond)
1385 def _def_union_type(self, expr: QAPIExpression) -> None:
1386 name = expr['union']
1387 base = expr['base']
1388 tag_name = expr['discriminator']
1389 data = expr['data']
1390 assert isinstance(data, dict)
1391 info = expr.info
1392 ifcond = QAPISchemaIfCond(expr.get('if'))
1393 features = self._make_features(expr.get('features'), info)
1394 if isinstance(base, dict):
1395 base = self._make_implicit_object_type(
1396 name, info, ifcond,
1397 'base', self._make_members(base, info))
1398 variants = [
1399 self._make_variant(key, value['type'],
1400 QAPISchemaIfCond(value.get('if')),
1401 info)
1402 for (key, value) in data.items()]
1403 members: List[QAPISchemaObjectTypeMember] = []
1404 self._def_definition(
1405 QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
1406 base, members,
1407 QAPISchemaBranches(
1408 info, variants, tag_name)))
1410 def _def_alternate_type(self, expr: QAPIExpression) -> None:
1411 name = expr['alternate']
1412 data = expr['data']
1413 assert isinstance(data, dict)
1414 ifcond = QAPISchemaIfCond(expr.get('if'))
1415 info = expr.info
1416 features = self._make_features(expr.get('features'), info)
1417 variants = [
1418 self._make_variant(key, value['type'],
1419 QAPISchemaIfCond(value.get('if')),
1420 info)
1421 for (key, value) in data.items()]
1422 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1423 self._def_definition(
1424 QAPISchemaAlternateType(
1425 name, info, expr.doc, ifcond, features,
1426 QAPISchemaAlternatives(info, variants, tag_member)))
1428 def _def_command(self, expr: QAPIExpression) -> None:
1429 name = expr['command']
1430 data = expr.get('data')
1431 rets = expr.get('returns')
1432 gen = expr.get('gen', True)
1433 success_response = expr.get('success-response', True)
1434 boxed = expr.get('boxed', False)
1435 allow_oob = expr.get('allow-oob', False)
1436 allow_preconfig = expr.get('allow-preconfig', False)
1437 coroutine = expr.get('coroutine', False)
1438 ifcond = QAPISchemaIfCond(expr.get('if'))
1439 info = expr.info
1440 features = self._make_features(expr.get('features'), info)
1441 if isinstance(data, OrderedDict):
1442 data = self._make_implicit_object_type(
1443 name, info, ifcond,
1444 'arg', self._make_members(data, info))
1445 if isinstance(rets, list):
1446 assert len(rets) == 1
1447 rets = self._make_array_type(rets[0], info)
1448 self._def_definition(
1449 QAPISchemaCommand(name, info, expr.doc, ifcond, features, data,
1450 rets, gen, success_response, boxed, allow_oob,
1451 allow_preconfig, coroutine))
1453 def _def_event(self, expr: QAPIExpression) -> None:
1454 name = expr['event']
1455 data = expr.get('data')
1456 boxed = expr.get('boxed', False)
1457 ifcond = QAPISchemaIfCond(expr.get('if'))
1458 info = expr.info
1459 features = self._make_features(expr.get('features'), info)
1460 if isinstance(data, OrderedDict):
1461 data = self._make_implicit_object_type(
1462 name, info, ifcond,
1463 'arg', self._make_members(data, info))
1464 self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond,
1465 features, data, boxed))
1467 def _def_exprs(self, exprs: List[QAPIExpression]) -> None:
1468 for expr in exprs:
1469 if 'enum' in expr:
1470 self._def_enum_type(expr)
1471 elif 'struct' in expr:
1472 self._def_struct_type(expr)
1473 elif 'union' in expr:
1474 self._def_union_type(expr)
1475 elif 'alternate' in expr:
1476 self._def_alternate_type(expr)
1477 elif 'command' in expr:
1478 self._def_command(expr)
1479 elif 'event' in expr:
1480 self._def_event(expr)
1481 elif 'include' in expr:
1482 self._def_include(expr)
1483 else:
1484 assert False
1486 def check(self) -> None:
1487 for ent in self._entity_list:
1488 ent.check(self)
1489 ent.connect_doc()
1490 for ent in self._entity_list:
1491 ent.set_module(self)
1492 for doc in self.docs:
1493 doc.check()
1495 def visit(self, visitor: QAPISchemaVisitor) -> None:
1496 visitor.visit_begin(self)
1497 for mod in self._module_dict.values():
1498 mod.visit(visitor)
1499 visitor.visit_end()