4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2016 Red Hat Inc.
8 # Anthony Liguori <aliguori@us.ibm.com>
9 # Markus Armbruster <armbru@redhat.com>
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
15 from ordereddict
import OrderedDict
23 'str': 'QTYPE_QSTRING',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
36 'any': None, # any QType possible, actually
37 'QType': 'QTYPE_QSTRING',
40 # Are documentation comments required?
43 # Whitelist of commands allowed to return a non-dictionary
44 returns_whitelist
= []
46 # Whitelist of entities allowed to violate case conventions
47 name_case_whitelist
= []
56 # Parsing the schema into expressions
60 def error_path(parent
):
63 res
= ('In file included from %s:%d:\n' % (parent
['file'],
64 parent
['line'])) + res
65 parent
= parent
['parent']
69 class QAPIError(Exception):
70 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
71 Exception.__init
__(self
)
79 loc
= '%s:%d' % (self
.fname
, self
.line
)
80 if self
.col
is not None:
81 loc
+= ':%s' % self
.col
82 return error_path(self
.info
) + '%s: %s' % (loc
, self
.msg
)
85 class QAPIParseError(QAPIError
):
86 def __init__(self
, parser
, msg
):
88 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
90 col
= (col
+ 7) % 8 + 1
93 QAPIError
.__init
__(self
, parser
.fname
, parser
.line
, col
,
94 parser
.incl_info
, msg
)
97 class QAPISemError(QAPIError
):
98 def __init__(self
, info
, msg
):
99 QAPIError
.__init
__(self
, info
['file'], info
['line'], None,
103 class QAPIDoc(object):
104 class Section(object):
105 def __init__(self
, name
=None):
106 # optional section name (argument/member or section name)
108 # the list of lines for this section
110 self
.optional
= False
112 def append(self
, line
):
113 self
.content
.append(line
)
116 return '\n'.join(self
.content
).strip()
118 class ArgSection(Section
):
119 def __init__(self
, name
):
120 QAPIDoc
.Section
.__init
__(self
, name
)
123 def connect(self
, member
):
126 def __init__(self
, parser
, info
):
127 # self.parser is used to report errors with QAPIParseError. The
128 # resulting error position depends on the state of the parser.
129 # It happens to be the beginning of the comment. More or less
130 # servicable, but action at a distance.
134 self
.body
= QAPIDoc
.Section()
135 # dict mapping parameter name to ArgSection
136 self
.args
= OrderedDict()
139 # the current section
140 self
.section
= self
.body
141 # associated expression (to be set by expression parser)
144 def has_section(self
, name
):
145 """Return True if we have a section with this name."""
146 for i
in self
.sections
:
151 def append(self
, line
):
152 """Parse a comment line and add it to the documentation."""
155 self
._append
_freeform
(line
)
159 raise QAPIParseError(self
.parser
, "Missing space after #")
162 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
163 # recognized, and get silently treated as ordinary text
165 self
._append
_symbol
_line
(line
)
166 elif not self
.body
.content
and line
.startswith('@'):
167 if not line
.endswith(':'):
168 raise QAPIParseError(self
.parser
, "Line should end with :")
169 self
.symbol
= line
[1:-1]
170 # FIXME invalid names other than the empty string aren't flagged
172 raise QAPIParseError(self
.parser
, "Invalid name")
174 self
._append
_freeform
(line
)
176 def _append_symbol_line(self
, line
):
177 name
= line
.split(' ', 1)[0]
179 if name
.startswith('@') and name
.endswith(':'):
180 line
= line
[len(name
)+1:]
181 self
._start
_args
_section
(name
[1:-1])
182 elif name
in ('Returns:', 'Since:',
183 # those are often singular or plural
185 'Example:', 'Examples:',
187 line
= line
[len(name
)+1:]
188 self
._start
_section
(name
[:-1])
190 self
._append
_freeform
(line
)
192 def _start_args_section(self
, name
):
193 # FIXME invalid names other than the empty string aren't flagged
195 raise QAPIParseError(self
.parser
, "Invalid parameter name")
196 if name
in self
.args
:
197 raise QAPIParseError(self
.parser
,
198 "'%s' parameter name duplicated" % name
)
200 raise QAPIParseError(self
.parser
,
201 "'@%s:' can't follow '%s' section"
202 % (name
, self
.sections
[0].name
))
203 self
.section
= QAPIDoc
.ArgSection(name
)
204 self
.args
[name
] = self
.section
206 def _start_section(self
, name
=''):
207 if name
in ('Returns', 'Since') and self
.has_section(name
):
208 raise QAPIParseError(self
.parser
,
209 "Duplicated '%s' section" % name
)
210 self
.section
= QAPIDoc
.Section(name
)
211 self
.sections
.append(self
.section
)
213 def _append_freeform(self
, line
):
214 in_arg
= isinstance(self
.section
, QAPIDoc
.ArgSection
)
215 if (in_arg
and self
.section
.content
216 and not self
.section
.content
[-1]
217 and line
and not line
[0].isspace()):
218 self
._start
_section
()
219 if (in_arg
or not self
.section
.name
220 or not self
.section
.name
.startswith('Example')):
222 # TODO Drop this once the dust has settled
223 if (isinstance(self
.section
, QAPIDoc
.ArgSection
)
224 and '#optional' in line
):
225 raise QAPISemError(self
.info
, "Please drop the #optional tag")
226 self
.section
.append(line
)
228 def connect_member(self
, member
):
229 if member
.name
not in self
.args
:
230 # Undocumented TODO outlaw
231 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
232 self
.args
[member
.name
].connect(member
)
235 class QAPISchemaParser(object):
237 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
238 abs_fname
= os
.path
.abspath(fp
.name
)
241 previously_included
.append(abs_fname
)
242 self
.incl_info
= incl_info
244 if self
.src
== '' or self
.src
[-1] != '\n':
253 while self
.tok
is not None:
254 info
= {'file': fname
, 'line': self
.line
,
255 'parent': self
.incl_info
}
257 doc
= self
.get_doc(info
)
258 self
.docs
.append(doc
)
261 expr
= self
.get_expr(False)
262 if 'include' in expr
:
264 raise QAPISemError(info
, "Invalid 'include' directive")
265 include
= expr
['include']
266 if not isinstance(include
, str):
267 raise QAPISemError(info
,
268 "Value of 'include' must be a string")
269 self
._include
(include
, info
, os
.path
.dirname(abs_fname
),
271 elif "pragma" in expr
:
273 raise QAPISemError(info
, "Invalid 'pragma' directive")
274 pragma
= expr
['pragma']
275 if not isinstance(pragma
, dict):
277 info
, "Value of 'pragma' must be a dictionary")
278 for name
, value
in pragma
.iteritems():
279 self
._pragma
(name
, value
, info
)
281 expr_elem
= {'expr': expr
,
284 and self
.docs
[-1].info
['file'] == fname
285 and not self
.docs
[-1].expr
):
286 self
.docs
[-1].expr
= expr
287 expr_elem
['doc'] = self
.docs
[-1]
289 self
.exprs
.append(expr_elem
)
291 def _include(self
, include
, info
, base_dir
, previously_included
):
292 incl_abs_fname
= os
.path
.join(base_dir
, include
)
293 # catch inclusion cycle
296 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
297 raise QAPISemError(info
, "Inclusion loop for %s" % include
)
300 # skip multiple include of the same file
301 if incl_abs_fname
in previously_included
:
304 fobj
= open(incl_abs_fname
, 'r')
306 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, include
))
307 exprs_include
= QAPISchemaParser(fobj
, previously_included
, info
)
308 self
.exprs
.extend(exprs_include
.exprs
)
309 self
.docs
.extend(exprs_include
.docs
)
311 def _pragma(self
, name
, value
, info
):
312 global doc_required
, returns_whitelist
, name_case_whitelist
313 if name
== 'doc-required':
314 if not isinstance(value
, bool):
315 raise QAPISemError(info
,
316 "Pragma 'doc-required' must be boolean")
318 elif name
== 'returns-whitelist':
319 if (not isinstance(value
, list)
320 or any([not isinstance(elt
, str) for elt
in value
])):
321 raise QAPISemError(info
,
322 "Pragma returns-whitelist must be"
323 " a list of strings")
324 returns_whitelist
= value
325 elif name
== 'name-case-whitelist':
326 if (not isinstance(value
, list)
327 or any([not isinstance(elt
, str) for elt
in value
])):
328 raise QAPISemError(info
,
329 "Pragma name-case-whitelist must be"
330 " a list of strings")
331 name_case_whitelist
= value
333 raise QAPISemError(info
, "Unknown pragma '%s'" % name
)
335 def accept(self
, skip_comment
=True):
337 self
.tok
= self
.src
[self
.cursor
]
338 self
.pos
= self
.cursor
343 if self
.src
[self
.cursor
] == '#':
344 # Start of doc comment
346 self
.cursor
= self
.src
.find('\n', self
.cursor
)
348 self
.val
= self
.src
[self
.pos
:self
.cursor
]
350 elif self
.tok
in '{}:,[]':
352 elif self
.tok
== "'":
356 ch
= self
.src
[self
.cursor
]
359 raise QAPIParseError(self
, 'Missing terminating "\'"')
373 for _
in range(0, 4):
374 ch
= self
.src
[self
.cursor
]
376 if ch
not in '0123456789abcdefABCDEF':
377 raise QAPIParseError(self
,
378 '\\u escape needs 4 '
380 value
= (value
<< 4) + int(ch
, 16)
381 # If Python 2 and 3 didn't disagree so much on
382 # how to handle Unicode, then we could allow
383 # Unicode string defaults. But most of QAPI is
384 # ASCII-only, so we aren't losing much for now.
385 if not value
or value
> 0x7f:
386 raise QAPIParseError(self
,
387 'For now, \\u escape '
388 'only supports non-zero '
389 'values up to \\u007f')
394 raise QAPIParseError(self
,
395 "Unknown escape \\%s" % ch
)
404 elif self
.src
.startswith('true', self
.pos
):
408 elif self
.src
.startswith('false', self
.pos
):
412 elif self
.src
.startswith('null', self
.pos
):
416 elif self
.tok
== '\n':
417 if self
.cursor
== len(self
.src
):
421 self
.line_pos
= self
.cursor
422 elif not self
.tok
.isspace():
423 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
425 def get_members(self
):
431 raise QAPIParseError(self
, 'Expected string or "}"')
436 raise QAPIParseError(self
, 'Expected ":"')
439 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
440 expr
[key
] = self
.get_expr(True)
445 raise QAPIParseError(self
, 'Expected "," or "}"')
448 raise QAPIParseError(self
, 'Expected string')
450 def get_values(self
):
455 if self
.tok
not in "{['tfn":
456 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
459 expr
.append(self
.get_expr(True))
464 raise QAPIParseError(self
, 'Expected "," or "]"')
467 def get_expr(self
, nested
):
468 if self
.tok
!= '{' and not nested
:
469 raise QAPIParseError(self
, 'Expected "{"')
472 expr
= self
.get_members()
473 elif self
.tok
== '[':
475 expr
= self
.get_values()
476 elif self
.tok
in "'tfn":
480 raise QAPIParseError(self
, 'Expected "{", "[" or string')
483 def get_doc(self
, info
):
485 raise QAPIParseError(self
, "Junk after '##' at start of "
486 "documentation comment")
488 doc
= QAPIDoc(self
, info
)
490 while self
.tok
== '#':
491 if self
.val
.startswith('##'):
494 raise QAPIParseError(self
, "Junk after '##' at end of "
495 "documentation comment")
502 raise QAPIParseError(self
, "Documentation comment must end with '##'")
506 # Semantic analysis of schema expressions
507 # TODO fold into QAPISchema
508 # TODO catching name collisions in generated code would be nice
512 def find_base_members(base
):
513 if isinstance(base
, dict):
515 base_struct_define
= find_struct(base
)
516 if not base_struct_define
:
518 return base_struct_define
['data']
521 # Return the qtype of an alternate branch, or None on error.
522 def find_alternate_member_qtype(qapi_type
):
523 if qapi_type
in builtin_types
:
524 return builtin_types
[qapi_type
]
525 elif find_struct(qapi_type
):
527 elif find_enum(qapi_type
):
528 return 'QTYPE_QSTRING'
529 elif find_union(qapi_type
):
534 # Return the discriminator enum define if discriminator is specified as an
535 # enum type, otherwise return None.
536 def discriminator_find_enum_define(expr
):
537 base
= expr
.get('base')
538 discriminator
= expr
.get('discriminator')
540 if not (discriminator
and base
):
543 base_members
= find_base_members(base
)
547 discriminator_type
= base_members
.get(discriminator
)
548 if not discriminator_type
:
551 return find_enum(discriminator_type
)
554 # Names must be letters, numbers, -, and _. They must start with letter,
555 # except for downstream extensions which must start with __RFQDN_.
556 # Dots are only valid in the downstream extension prefix.
557 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
558 '[a-zA-Z][a-zA-Z0-9_-]*$')
561 def check_name(info
, source
, name
, allow_optional
=False,
566 if not isinstance(name
, str):
567 raise QAPISemError(info
, "%s requires a string name" % source
)
568 if name
.startswith('*'):
569 membername
= name
[1:]
570 if not allow_optional
:
571 raise QAPISemError(info
, "%s does not allow optional name '%s'"
573 # Enum members can start with a digit, because the generated C
574 # code always prefixes it with the enum name
575 if enum_member
and membername
[0].isdigit():
576 membername
= 'D' + membername
577 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
578 # and 'q_obj_*' implicit type names.
579 if not valid_name
.match(membername
) or \
580 c_name(membername
, False).startswith('q_'):
581 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
584 def add_name(name
, info
, meta
, implicit
=False):
586 check_name(info
, "'%s'" % meta
, name
)
587 # FIXME should reject names that differ only in '_' vs. '.'
588 # vs. '-', because they're liable to clash in generated C.
589 if name
in all_names
:
590 raise QAPISemError(info
, "%s '%s' is already defined"
591 % (all_names
[name
], name
))
592 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
593 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
594 % (meta
, name
, name
[-4:]))
595 all_names
[name
] = meta
598 def add_struct(definition
, info
):
600 name
= definition
['struct']
601 add_name(name
, info
, 'struct')
602 struct_types
.append(definition
)
605 def find_struct(name
):
607 for struct
in struct_types
:
608 if struct
['struct'] == name
:
613 def add_union(definition
, info
):
615 name
= definition
['union']
616 add_name(name
, info
, 'union')
617 union_types
.append(definition
)
620 def find_union(name
):
622 for union
in union_types
:
623 if union
['union'] == name
:
628 def add_enum(name
, info
, enum_values
=None, implicit
=False):
630 add_name(name
, info
, 'enum', implicit
)
631 enum_types
.append({'enum_name': name
, 'enum_values': enum_values
})
636 for enum
in enum_types
:
637 if enum
['enum_name'] == name
:
643 return find_enum(name
) is not None
646 def check_type(info
, source
, value
, allow_array
=False,
647 allow_dict
=False, allow_optional
=False,
654 # Check if array type for value is okay
655 if isinstance(value
, list):
657 raise QAPISemError(info
, "%s cannot be an array" % source
)
658 if len(value
) != 1 or not isinstance(value
[0], str):
659 raise QAPISemError(info
,
660 "%s: array type must contain single type name" %
664 # Check if type name for value is okay
665 if isinstance(value
, str):
666 if value
not in all_names
:
667 raise QAPISemError(info
, "%s uses unknown type '%s'"
669 if not all_names
[value
] in allow_metas
:
670 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
671 (source
, all_names
[value
], value
))
675 raise QAPISemError(info
, "%s should be a type name" % source
)
677 if not isinstance(value
, OrderedDict
):
678 raise QAPISemError(info
,
679 "%s should be a dictionary or type name" % source
)
681 # value is a dictionary, check that each member is okay
682 for (key
, arg
) in value
.items():
683 check_name(info
, "Member of %s" % source
, key
,
684 allow_optional
=allow_optional
)
685 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
686 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
688 # Todo: allow dictionaries to represent default values of
689 # an optional argument.
690 check_type(info
, "Member '%s' of %s" % (key
, source
), arg
,
692 allow_metas
=['built-in', 'union', 'alternate', 'struct',
696 def check_command(expr
, info
):
697 name
= expr
['command']
698 boxed
= expr
.get('boxed', False)
700 args_meta
= ['struct']
702 args_meta
+= ['union', 'alternate']
703 check_type(info
, "'data' for command '%s'" % name
,
704 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
705 allow_metas
=args_meta
)
706 returns_meta
= ['union', 'struct']
707 if name
in returns_whitelist
:
708 returns_meta
+= ['built-in', 'alternate', 'enum']
709 check_type(info
, "'returns' for command '%s'" % name
,
710 expr
.get('returns'), allow_array
=True,
711 allow_optional
=True, allow_metas
=returns_meta
)
714 def check_event(expr
, info
):
717 boxed
= expr
.get('boxed', False)
721 meta
+= ['union', 'alternate']
723 check_type(info
, "'data' for event '%s'" % name
,
724 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
728 def check_union(expr
, info
):
730 base
= expr
.get('base')
731 discriminator
= expr
.get('discriminator')
732 members
= expr
['data']
734 # Two types of unions, determined by discriminator.
736 # With no discriminator it is a simple union.
737 if discriminator
is None:
739 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
741 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
744 # Else, it's a flat union.
746 # The object must have a string or dictionary 'base'.
747 check_type(info
, "'base' for union '%s'" % name
,
748 base
, allow_dict
=True, allow_optional
=True,
749 allow_metas
=['struct'])
751 raise QAPISemError(info
, "Flat union '%s' must have a base"
753 base_members
= find_base_members(base
)
754 assert base_members
is not None
756 # The value of member 'discriminator' must name a non-optional
757 # member of the base struct.
758 check_name(info
, "Discriminator of flat union '%s'" % name
,
760 discriminator_type
= base_members
.get(discriminator
)
761 if not discriminator_type
:
762 raise QAPISemError(info
,
763 "Discriminator '%s' is not a member of base "
765 % (discriminator
, base
))
766 enum_define
= find_enum(discriminator_type
)
767 allow_metas
= ['struct']
768 # Do not allow string discriminator
770 raise QAPISemError(info
,
771 "Discriminator '%s' must be of enumeration "
772 "type" % discriminator
)
774 # Check every branch; don't allow an empty union
775 if len(members
) == 0:
776 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
777 for (key
, value
) in members
.items():
778 check_name(info
, "Member of union '%s'" % name
, key
)
780 # Each value must name a known type
781 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
782 value
, allow_array
=not base
, allow_metas
=allow_metas
)
784 # If the discriminator names an enum type, then all members
785 # of 'data' must also be members of the enum type.
787 if key
not in enum_define
['enum_values']:
788 raise QAPISemError(info
,
789 "Discriminator value '%s' is not found in "
791 % (key
, enum_define
['enum_name']))
793 # If discriminator is user-defined, ensure all values are covered
795 for value
in enum_define
['enum_values']:
796 if value
not in members
.keys():
797 raise QAPISemError(info
, "Union '%s' data missing '%s' branch"
801 def check_alternate(expr
, info
):
802 name
= expr
['alternate']
803 members
= expr
['data']
806 # Check every branch; require at least two branches
808 raise QAPISemError(info
,
809 "Alternate '%s' should have at least two branches "
811 for (key
, value
) in members
.items():
812 check_name(info
, "Member of alternate '%s'" % name
, key
)
814 # Ensure alternates have no type conflicts.
815 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
),
817 allow_metas
=['built-in', 'union', 'struct', 'enum'])
818 qtype
= find_alternate_member_qtype(value
)
820 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
821 "type '%s'" % (name
, key
, value
))
822 if qtype
in types_seen
:
823 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
824 "be distinguished from member '%s'"
825 % (name
, key
, types_seen
[qtype
]))
826 types_seen
[qtype
] = key
829 def check_enum(expr
, info
):
831 members
= expr
.get('data')
832 prefix
= expr
.get('prefix')
834 if not isinstance(members
, list):
835 raise QAPISemError(info
,
836 "Enum '%s' requires an array for 'data'" % name
)
837 if prefix
is not None and not isinstance(prefix
, str):
838 raise QAPISemError(info
,
839 "Enum '%s' requires a string for 'prefix'" % name
)
840 for member
in members
:
841 check_name(info
, "Member of enum '%s'" % name
, member
,
845 def check_struct(expr
, info
):
846 name
= expr
['struct']
847 members
= expr
['data']
849 check_type(info
, "'data' for struct '%s'" % name
, members
,
850 allow_dict
=True, allow_optional
=True)
851 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
852 allow_metas
=['struct'])
855 def check_keys(expr_elem
, meta
, required
, optional
=[]):
856 expr
= expr_elem
['expr']
857 info
= expr_elem
['info']
859 if not isinstance(name
, str):
860 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
861 required
= required
+ [meta
]
862 for (key
, value
) in expr
.items():
863 if key
not in required
and key
not in optional
:
864 raise QAPISemError(info
, "Unknown key '%s' in %s '%s'"
866 if (key
== 'gen' or key
== 'success-response') and value
is not False:
867 raise QAPISemError(info
,
868 "'%s' of %s '%s' should only use false value"
870 if key
== 'boxed' and value
is not True:
871 raise QAPISemError(info
,
872 "'%s' of %s '%s' should only use true value"
876 raise QAPISemError(info
, "Key '%s' is missing from %s '%s'"
880 def check_exprs(exprs
):
883 # Learn the types and check for valid expression keys
884 for builtin
in builtin_types
.keys():
885 all_names
[builtin
] = 'built-in'
886 for expr_elem
in exprs
:
887 expr
= expr_elem
['expr']
888 info
= expr_elem
['info']
890 if 'doc' not in expr_elem
and doc_required
:
891 raise QAPISemError(info
,
892 "Expression missing documentation comment")
895 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
896 add_enum(expr
['enum'], info
, expr
['data'])
897 elif 'union' in expr
:
898 check_keys(expr_elem
, 'union', ['data'],
899 ['base', 'discriminator'])
900 add_union(expr
, info
)
901 elif 'alternate' in expr
:
902 check_keys(expr_elem
, 'alternate', ['data'])
903 add_name(expr
['alternate'], info
, 'alternate')
904 elif 'struct' in expr
:
905 check_keys(expr_elem
, 'struct', ['data'], ['base'])
906 add_struct(expr
, info
)
907 elif 'command' in expr
:
908 check_keys(expr_elem
, 'command', [],
909 ['data', 'returns', 'gen', 'success-response', 'boxed'])
910 add_name(expr
['command'], info
, 'command')
911 elif 'event' in expr
:
912 check_keys(expr_elem
, 'event', [], ['data', 'boxed'])
913 add_name(expr
['event'], info
, 'event')
915 raise QAPISemError(expr_elem
['info'],
916 "Expression is missing metatype")
918 # Try again for hidden UnionKind enum
919 for expr_elem
in exprs
:
920 expr
= expr_elem
['expr']
922 if not discriminator_find_enum_define(expr
):
923 add_enum('%sKind' % expr
['union'], expr_elem
['info'],
925 elif 'alternate' in expr
:
926 add_enum('%sKind' % expr
['alternate'], expr_elem
['info'],
929 # Validate that exprs make sense
930 for expr_elem
in exprs
:
931 expr
= expr_elem
['expr']
932 info
= expr_elem
['info']
935 check_enum(expr
, info
)
936 elif 'union' in expr
:
937 check_union(expr
, info
)
938 elif 'alternate' in expr
:
939 check_alternate(expr
, info
)
940 elif 'struct' in expr
:
941 check_struct(expr
, info
)
942 elif 'command' in expr
:
943 check_command(expr
, info
)
944 elif 'event' in expr
:
945 check_event(expr
, info
)
947 assert False, 'unexpected meta type'
952 def check_freeform_doc(doc
):
954 raise QAPISemError(doc
.info
,
955 "Documention for '%s' is not followed"
956 " by the definition" % doc
.symbol
)
959 if re
.search(r
'@\S+:', body
, re
.MULTILINE
):
960 raise QAPISemError(doc
.info
,
961 "Free-form documentation block must not contain"
965 def check_definition_doc(doc
, expr
, info
):
966 for i
in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
972 if doc
.symbol
!= name
:
973 raise QAPISemError(info
, "Definition of '%s' follows documentation"
974 " for '%s'" % (name
, doc
.symbol
))
975 if doc
.has_section('Returns') and 'command' not in expr
:
976 raise QAPISemError(info
, "'Returns:' is only valid for commands")
979 args
= expr
.get('base', [])
981 args
= expr
.get('data', [])
982 if isinstance(args
, str):
984 if isinstance(args
, dict):
986 assert isinstance(args
, list)
988 if (meta
== 'alternate'
989 or (meta
== 'union' and not expr
.get('discriminator'))):
992 doc_args
= set(doc
.args
.keys())
993 args
= set([name
.strip('*') for name
in args
])
994 if not doc_args
.issubset(args
):
995 raise QAPISemError(info
, "The following documented members are not in "
996 "the declaration: %s" % ', '.join(doc_args
- args
))
999 def check_docs(docs
):
1001 for section
in doc
.args
.values() + doc
.sections
:
1002 content
= str(section
)
1003 if not content
or content
.isspace():
1004 raise QAPISemError(doc
.info
,
1005 "Empty doc section '%s'" % section
.name
)
1008 check_freeform_doc(doc
)
1010 check_definition_doc(doc
, doc
.expr
, doc
.info
)
1016 # Schema compiler frontend
1019 class QAPISchemaEntity(object):
1020 def __init__(self
, name
, info
, doc
):
1021 assert isinstance(name
, str)
1023 # For explicitly defined entities, info points to the (explicit)
1024 # definition. For builtins (and their arrays), info is None.
1025 # For implicitly defined entities, info points to a place that
1026 # triggered the implicit definition (there may be more than one
1032 return c_name(self
.name
)
1034 def check(self
, schema
):
1037 def is_implicit(self
):
1038 return not self
.info
1040 def visit(self
, visitor
):
1044 class QAPISchemaVisitor(object):
1045 def visit_begin(self
, schema
):
1048 def visit_end(self
):
1051 def visit_needed(self
, entity
):
1052 # Default to visiting everything
1055 def visit_builtin_type(self
, name
, info
, json_type
):
1058 def visit_enum_type(self
, name
, info
, values
, prefix
):
1061 def visit_array_type(self
, name
, info
, element_type
):
1064 def visit_object_type(self
, name
, info
, base
, members
, variants
):
1067 def visit_object_type_flat(self
, name
, info
, members
, variants
):
1070 def visit_alternate_type(self
, name
, info
, variants
):
1073 def visit_command(self
, name
, info
, arg_type
, ret_type
,
1074 gen
, success_response
, boxed
):
1077 def visit_event(self
, name
, info
, arg_type
, boxed
):
1081 class QAPISchemaType(QAPISchemaEntity
):
1082 # Return the C type for common use.
1083 # For the types we commonly box, this is a pointer type.
1087 # Return the C type to be used in a parameter list.
1088 def c_param_type(self
):
1089 return self
.c_type()
1091 # Return the C type to be used where we suppress boxing.
1092 def c_unboxed_type(self
):
1093 return self
.c_type()
1095 def json_type(self
):
1098 def alternate_qtype(self
):
1100 'string': 'QTYPE_QSTRING',
1101 'number': 'QTYPE_QFLOAT',
1102 'int': 'QTYPE_QINT',
1103 'boolean': 'QTYPE_QBOOL',
1104 'object': 'QTYPE_QDICT'
1106 return json2qtype
.get(self
.json_type())
1109 if self
.is_implicit():
1114 class QAPISchemaBuiltinType(QAPISchemaType
):
1115 def __init__(self
, name
, json_type
, c_type
):
1116 QAPISchemaType
.__init
__(self
, name
, None, None)
1117 assert not c_type
or isinstance(c_type
, str)
1118 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1120 self
._json
_type
_name
= json_type
1121 self
._c
_type
_name
= c_type
1127 return self
._c
_type
_name
1129 def c_param_type(self
):
1130 if self
.name
== 'str':
1131 return 'const ' + self
._c
_type
_name
1132 return self
._c
_type
_name
1134 def json_type(self
):
1135 return self
._json
_type
_name
1138 return self
.json_type()
1140 def visit(self
, visitor
):
1141 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1144 class QAPISchemaEnumType(QAPISchemaType
):
1145 def __init__(self
, name
, info
, doc
, values
, prefix
):
1146 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1148 assert isinstance(v
, QAPISchemaMember
)
1150 assert prefix
is None or isinstance(prefix
, str)
1151 self
.values
= values
1152 self
.prefix
= prefix
1154 def check(self
, schema
):
1156 for v
in self
.values
:
1157 v
.check_clash(self
.info
, seen
)
1159 self
.doc
.connect_member(v
)
1161 def is_implicit(self
):
1162 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1163 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1166 return c_name(self
.name
)
1168 def member_names(self
):
1169 return [v
.name
for v
in self
.values
]
1171 def json_type(self
):
1174 def visit(self
, visitor
):
1175 visitor
.visit_enum_type(self
.name
, self
.info
,
1176 self
.member_names(), self
.prefix
)
1179 class QAPISchemaArrayType(QAPISchemaType
):
1180 def __init__(self
, name
, info
, element_type
):
1181 QAPISchemaType
.__init
__(self
, name
, info
, None)
1182 assert isinstance(element_type
, str)
1183 self
._element
_type
_name
= element_type
1184 self
.element_type
= None
1186 def check(self
, schema
):
1187 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1188 assert self
.element_type
1190 def is_implicit(self
):
1194 return c_name(self
.name
) + pointer_suffix
1196 def json_type(self
):
1200 elt_doc_type
= self
.element_type
.doc_type()
1201 if not elt_doc_type
:
1203 return 'array of ' + elt_doc_type
1205 def visit(self
, visitor
):
1206 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
1209 class QAPISchemaObjectType(QAPISchemaType
):
1210 def __init__(self
, name
, info
, doc
, base
, local_members
, variants
):
1211 # struct has local_members, optional base, and no variants
1212 # flat union has base, variants, and no local_members
1213 # simple union has local_members, variants, and no base
1214 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1215 assert base
is None or isinstance(base
, str)
1216 for m
in local_members
:
1217 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1219 if variants
is not None:
1220 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1221 variants
.set_owner(name
)
1222 self
._base
_name
= base
1224 self
.local_members
= local_members
1225 self
.variants
= variants
1228 def check(self
, schema
):
1229 if self
.members
is False: # check for cycles
1230 raise QAPISemError(self
.info
,
1231 "Object %s contains itself" % self
.name
)
1234 self
.members
= False # mark as being checked
1235 seen
= OrderedDict()
1237 self
.base
= schema
.lookup_type(self
._base
_name
)
1238 assert isinstance(self
.base
, QAPISchemaObjectType
)
1239 self
.base
.check(schema
)
1240 self
.base
.check_clash(schema
, self
.info
, seen
)
1241 for m
in self
.local_members
:
1243 m
.check_clash(self
.info
, seen
)
1245 self
.doc
.connect_member(m
)
1246 self
.members
= seen
.values()
1248 self
.variants
.check(schema
, seen
)
1249 assert self
.variants
.tag_member
in self
.members
1250 self
.variants
.check_clash(schema
, self
.info
, seen
)
1252 # Check that the members of this type do not cause duplicate JSON members,
1253 # and update seen to track the members seen so far. Report any errors
1254 # on behalf of info, which is not necessarily self.info
1255 def check_clash(self
, schema
, info
, seen
):
1256 assert not self
.variants
# not implemented
1257 for m
in self
.members
:
1258 m
.check_clash(info
, seen
)
1260 def is_implicit(self
):
1261 # See QAPISchema._make_implicit_object_type(), as well as
1262 # _def_predefineds()
1263 return self
.name
.startswith('q_')
1266 assert self
.members
is not None
1267 return not self
.members
and not self
.variants
1270 assert self
.name
!= 'q_empty'
1271 return QAPISchemaType
.c_name(self
)
1274 assert not self
.is_implicit()
1275 return c_name(self
.name
) + pointer_suffix
1277 def c_unboxed_type(self
):
1278 return c_name(self
.name
)
1280 def json_type(self
):
1283 def visit(self
, visitor
):
1284 visitor
.visit_object_type(self
.name
, self
.info
,
1285 self
.base
, self
.local_members
, self
.variants
)
1286 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1287 self
.members
, self
.variants
)
1290 class QAPISchemaMember(object):
1293 def __init__(self
, name
):
1294 assert isinstance(name
, str)
1298 def set_owner(self
, name
):
1299 assert not self
.owner
1302 def check_clash(self
, info
, seen
):
1303 cname
= c_name(self
.name
)
1304 if cname
.lower() != cname
and self
.owner
not in name_case_whitelist
:
1305 raise QAPISemError(info
,
1306 "%s should not use uppercase" % self
.describe())
1308 raise QAPISemError(info
, "%s collides with %s" %
1309 (self
.describe(), seen
[cname
].describe()))
1312 def _pretty_owner(self
):
1314 if owner
.startswith('q_obj_'):
1315 # See QAPISchema._make_implicit_object_type() - reverse the
1316 # mapping there to create a nice human-readable description
1318 if owner
.endswith('-arg'):
1319 return '(parameter of %s)' % owner
[:-4]
1320 elif owner
.endswith('-base'):
1321 return '(base of %s)' % owner
[:-5]
1323 assert owner
.endswith('-wrapper')
1324 # Unreachable and not implemented
1326 if owner
.endswith('Kind'):
1327 # See QAPISchema._make_implicit_enum_type()
1328 return '(branch of %s)' % owner
[:-4]
1329 return '(%s of %s)' % (self
.role
, owner
)
1332 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1335 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1336 def __init__(self
, name
, typ
, optional
):
1337 QAPISchemaMember
.__init
__(self
, name
)
1338 assert isinstance(typ
, str)
1339 assert isinstance(optional
, bool)
1340 self
._type
_name
= typ
1342 self
.optional
= optional
1344 def check(self
, schema
):
1346 self
.type = schema
.lookup_type(self
._type
_name
)
1350 class QAPISchemaObjectTypeVariants(object):
1351 def __init__(self
, tag_name
, tag_member
, variants
):
1352 # Flat unions pass tag_name but not tag_member.
1353 # Simple unions and alternates pass tag_member but not tag_name.
1354 # After check(), tag_member is always set, and tag_name remains
1355 # a reliable witness of being used by a flat union.
1356 assert bool(tag_member
) != bool(tag_name
)
1357 assert (isinstance(tag_name
, str) or
1358 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1359 assert len(variants
) > 0
1361 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1362 self
._tag
_name
= tag_name
1363 self
.tag_member
= tag_member
1364 self
.variants
= variants
1366 def set_owner(self
, name
):
1367 for v
in self
.variants
:
1370 def check(self
, schema
, seen
):
1371 if not self
.tag_member
: # flat union
1372 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1373 assert self
._tag
_name
== self
.tag_member
.name
1374 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1375 for v
in self
.variants
:
1377 # Union names must match enum values; alternate names are
1378 # checked separately. Use 'seen' to tell the two apart.
1380 assert v
.name
in self
.tag_member
.type.member_names()
1381 assert isinstance(v
.type, QAPISchemaObjectType
)
1382 v
.type.check(schema
)
1384 def check_clash(self
, schema
, info
, seen
):
1385 for v
in self
.variants
:
1386 # Reset seen map for each variant, since qapi names from one
1387 # branch do not affect another branch
1388 assert isinstance(v
.type, QAPISchemaObjectType
)
1389 v
.type.check_clash(schema
, info
, dict(seen
))
1392 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1395 def __init__(self
, name
, typ
):
1396 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1399 class QAPISchemaAlternateType(QAPISchemaType
):
1400 def __init__(self
, name
, info
, doc
, variants
):
1401 QAPISchemaType
.__init
__(self
, name
, info
, doc
)
1402 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1403 assert variants
.tag_member
1404 variants
.set_owner(name
)
1405 variants
.tag_member
.set_owner(self
.name
)
1406 self
.variants
= variants
1408 def check(self
, schema
):
1409 self
.variants
.tag_member
.check(schema
)
1410 # Not calling self.variants.check_clash(), because there's nothing
1412 self
.variants
.check(schema
, {})
1413 # Alternate branch names have no relation to the tag enum values;
1414 # so we have to check for potential name collisions ourselves.
1416 for v
in self
.variants
.variants
:
1417 v
.check_clash(self
.info
, seen
)
1419 self
.doc
.connect_member(v
)
1422 return c_name(self
.name
) + pointer_suffix
1424 def json_type(self
):
1427 def visit(self
, visitor
):
1428 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1434 class QAPISchemaCommand(QAPISchemaEntity
):
1435 def __init__(self
, name
, info
, doc
, arg_type
, ret_type
,
1436 gen
, success_response
, boxed
):
1437 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1438 assert not arg_type
or isinstance(arg_type
, str)
1439 assert not ret_type
or isinstance(ret_type
, str)
1440 self
._arg
_type
_name
= arg_type
1441 self
.arg_type
= None
1442 self
._ret
_type
_name
= ret_type
1443 self
.ret_type
= None
1445 self
.success_response
= success_response
1448 def check(self
, schema
):
1449 if self
._arg
_type
_name
:
1450 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1451 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1452 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1453 self
.arg_type
.check(schema
)
1455 if self
.arg_type
.is_empty():
1456 raise QAPISemError(self
.info
,
1457 "Cannot use 'boxed' with empty type")
1459 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1460 assert not self
.arg_type
.variants
1462 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1463 if self
._ret
_type
_name
:
1464 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1465 assert isinstance(self
.ret_type
, QAPISchemaType
)
1467 def visit(self
, visitor
):
1468 visitor
.visit_command(self
.name
, self
.info
,
1469 self
.arg_type
, self
.ret_type
,
1470 self
.gen
, self
.success_response
, self
.boxed
)
1473 class QAPISchemaEvent(QAPISchemaEntity
):
1474 def __init__(self
, name
, info
, doc
, arg_type
, boxed
):
1475 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
)
1476 assert not arg_type
or isinstance(arg_type
, str)
1477 self
._arg
_type
_name
= arg_type
1478 self
.arg_type
= None
1481 def check(self
, schema
):
1482 if self
._arg
_type
_name
:
1483 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1484 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1485 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1486 self
.arg_type
.check(schema
)
1488 if self
.arg_type
.is_empty():
1489 raise QAPISemError(self
.info
,
1490 "Cannot use 'boxed' with empty type")
1492 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1493 assert not self
.arg_type
.variants
1495 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1497 def visit(self
, visitor
):
1498 visitor
.visit_event(self
.name
, self
.info
, self
.arg_type
, self
.boxed
)
1501 class QAPISchema(object):
1502 def __init__(self
, fname
):
1504 parser
= QAPISchemaParser(open(fname
, 'r'))
1505 self
.exprs
= check_exprs(parser
.exprs
)
1506 self
.docs
= check_docs(parser
.docs
)
1507 self
._entity
_dict
= {}
1508 self
._predefining
= True
1509 self
._def
_predefineds
()
1510 self
._predefining
= False
1513 except QAPIError
as err
:
1514 print >>sys
.stderr
, err
1517 def _def_entity(self
, ent
):
1518 # Only the predefined types are allowed to not have info
1519 assert ent
.info
or self
._predefining
1520 assert ent
.name
not in self
._entity
_dict
1521 self
._entity
_dict
[ent
.name
] = ent
1523 def lookup_entity(self
, name
, typ
=None):
1524 ent
= self
._entity
_dict
.get(name
)
1525 if typ
and not isinstance(ent
, typ
):
1529 def lookup_type(self
, name
):
1530 return self
.lookup_entity(name
, QAPISchemaType
)
1532 def _def_builtin_type(self
, name
, json_type
, c_type
):
1533 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1534 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1535 # qapi-types.h from a single .c, all arrays of builtins must be
1536 # declared in the first file whether or not they are used. Nicer
1537 # would be to use lazy instantiation, while figuring out how to
1538 # avoid compilation issues with multiple qapi-types.h.
1539 self
._make
_array
_type
(name
, None)
1541 def _def_predefineds(self
):
1542 for t
in [('str', 'string', 'char' + pointer_suffix
),
1543 ('number', 'number', 'double'),
1544 ('int', 'int', 'int64_t'),
1545 ('int8', 'int', 'int8_t'),
1546 ('int16', 'int', 'int16_t'),
1547 ('int32', 'int', 'int32_t'),
1548 ('int64', 'int', 'int64_t'),
1549 ('uint8', 'int', 'uint8_t'),
1550 ('uint16', 'int', 'uint16_t'),
1551 ('uint32', 'int', 'uint32_t'),
1552 ('uint64', 'int', 'uint64_t'),
1553 ('size', 'int', 'uint64_t'),
1554 ('bool', 'boolean', 'bool'),
1555 ('any', 'value', 'QObject' + pointer_suffix
)]:
1556 self
._def
_builtin
_type
(*t
)
1557 self
.the_empty_object_type
= QAPISchemaObjectType(
1558 'q_empty', None, None, None, [], None)
1559 self
._def
_entity
(self
.the_empty_object_type
)
1560 qtype_values
= self
._make
_enum
_members
(['none', 'qnull', 'qint',
1561 'qstring', 'qdict', 'qlist',
1563 self
._def
_entity
(QAPISchemaEnumType('QType', None, None,
1564 qtype_values
, 'QTYPE'))
1566 def _make_enum_members(self
, values
):
1567 return [QAPISchemaMember(v
) for v
in values
]
1569 def _make_implicit_enum_type(self
, name
, info
, values
):
1570 # See also QAPISchemaObjectTypeMember._pretty_owner()
1571 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1572 self
._def
_entity
(QAPISchemaEnumType(
1573 name
, info
, None, self
._make
_enum
_members
(values
), None))
1576 def _make_array_type(self
, element_type
, info
):
1577 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1578 if not self
.lookup_type(name
):
1579 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1582 def _make_implicit_object_type(self
, name
, info
, doc
, role
, members
):
1585 # See also QAPISchemaObjectTypeMember._pretty_owner()
1586 name
= 'q_obj_%s-%s' % (name
, role
)
1587 if not self
.lookup_entity(name
, QAPISchemaObjectType
):
1588 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, None,
1592 def _def_enum_type(self
, expr
, info
, doc
):
1595 prefix
= expr
.get('prefix')
1596 self
._def
_entity
(QAPISchemaEnumType(
1597 name
, info
, doc
, self
._make
_enum
_members
(data
), prefix
))
1599 def _make_member(self
, name
, typ
, info
):
1601 if name
.startswith('*'):
1604 if isinstance(typ
, list):
1605 assert len(typ
) == 1
1606 typ
= self
._make
_array
_type
(typ
[0], info
)
1607 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1609 def _make_members(self
, data
, info
):
1610 return [self
._make
_member
(key
, value
, info
)
1611 for (key
, value
) in data
.iteritems()]
1613 def _def_struct_type(self
, expr
, info
, doc
):
1614 name
= expr
['struct']
1615 base
= expr
.get('base')
1617 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, base
,
1618 self
._make
_members
(data
, info
),
1621 def _make_variant(self
, case
, typ
):
1622 return QAPISchemaObjectTypeVariant(case
, typ
)
1624 def _make_simple_variant(self
, case
, typ
, info
):
1625 if isinstance(typ
, list):
1626 assert len(typ
) == 1
1627 typ
= self
._make
_array
_type
(typ
[0], info
)
1628 typ
= self
._make
_implicit
_object
_type
(
1629 typ
, info
, None, 'wrapper', [self
._make
_member
('data', typ
, info
)])
1630 return QAPISchemaObjectTypeVariant(case
, typ
)
1632 def _def_union_type(self
, expr
, info
, doc
):
1633 name
= expr
['union']
1635 base
= expr
.get('base')
1636 tag_name
= expr
.get('discriminator')
1638 if isinstance(base
, dict):
1639 base
= (self
._make
_implicit
_object
_type
(
1640 name
, info
, doc
, 'base', self
._make
_members
(base
, info
)))
1642 variants
= [self
._make
_variant
(key
, value
)
1643 for (key
, value
) in data
.iteritems()]
1646 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1647 for (key
, value
) in data
.iteritems()]
1648 typ
= self
._make
_implicit
_enum
_type
(name
, info
,
1649 [v
.name
for v
in variants
])
1650 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1651 members
= [tag_member
]
1653 QAPISchemaObjectType(name
, info
, doc
, base
, members
,
1654 QAPISchemaObjectTypeVariants(tag_name
,
1658 def _def_alternate_type(self
, expr
, info
, doc
):
1659 name
= expr
['alternate']
1661 variants
= [self
._make
_variant
(key
, value
)
1662 for (key
, value
) in data
.iteritems()]
1663 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1665 QAPISchemaAlternateType(name
, info
, doc
,
1666 QAPISchemaObjectTypeVariants(None,
1670 def _def_command(self
, expr
, info
, doc
):
1671 name
= expr
['command']
1672 data
= expr
.get('data')
1673 rets
= expr
.get('returns')
1674 gen
= expr
.get('gen', True)
1675 success_response
= expr
.get('success-response', True)
1676 boxed
= expr
.get('boxed', False)
1677 if isinstance(data
, OrderedDict
):
1678 data
= self
._make
_implicit
_object
_type
(
1679 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1680 if isinstance(rets
, list):
1681 assert len(rets
) == 1
1682 rets
= self
._make
_array
_type
(rets
[0], info
)
1683 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, data
, rets
,
1684 gen
, success_response
, boxed
))
1686 def _def_event(self
, expr
, info
, doc
):
1687 name
= expr
['event']
1688 data
= expr
.get('data')
1689 boxed
= expr
.get('boxed', False)
1690 if isinstance(data
, OrderedDict
):
1691 data
= self
._make
_implicit
_object
_type
(
1692 name
, info
, doc
, 'arg', self
._make
_members
(data
, info
))
1693 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, data
, boxed
))
1695 def _def_exprs(self
):
1696 for expr_elem
in self
.exprs
:
1697 expr
= expr_elem
['expr']
1698 info
= expr_elem
['info']
1699 doc
= expr_elem
.get('doc')
1701 self
._def
_enum
_type
(expr
, info
, doc
)
1702 elif 'struct' in expr
:
1703 self
._def
_struct
_type
(expr
, info
, doc
)
1704 elif 'union' in expr
:
1705 self
._def
_union
_type
(expr
, info
, doc
)
1706 elif 'alternate' in expr
:
1707 self
._def
_alternate
_type
(expr
, info
, doc
)
1708 elif 'command' in expr
:
1709 self
._def
_command
(expr
, info
, doc
)
1710 elif 'event' in expr
:
1711 self
._def
_event
(expr
, info
, doc
)
1716 for ent
in self
._entity
_dict
.values():
1719 def visit(self
, visitor
):
1720 visitor
.visit_begin(self
)
1721 for (name
, entity
) in sorted(self
._entity
_dict
.items()):
1722 if visitor
.visit_needed(entity
):
1723 entity
.visit(visitor
)
1728 # Code generation helpers
1731 def camel_case(name
):
1735 if ch
in ['_', '-']:
1738 new_name
+= ch
.upper()
1741 new_name
+= ch
.lower()
1745 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1746 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1747 # ENUM24_Name -> ENUM24_NAME
1748 def camel_to_upper(value
):
1749 c_fun_str
= c_name(value
, False)
1757 # When c is upper and no '_' appears before, do more checks
1758 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
1759 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1761 elif c_fun_str
[i
- 1].isdigit():
1764 return new_name
.lstrip('_').upper()
1767 def c_enum_const(type_name
, const_name
, prefix
=None):
1768 if prefix
is not None:
1770 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1772 c_name_trans
= string
.maketrans('.-', '__')
1775 # Map @name to a valid C identifier.
1776 # If @protect, avoid returning certain ticklish identifiers (like
1777 # C keywords) by prepending 'q_'.
1779 # Used for converting 'name' from a 'name':'type' qapi definition
1780 # into a generated struct member, as well as converting type names
1781 # into substrings of a generated C function name.
1782 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1783 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1784 def c_name(name
, protect
=True):
1785 # ANSI X3J11/88-090, 3.1.1
1786 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1787 'default', 'do', 'double', 'else', 'enum', 'extern',
1788 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1789 'return', 'short', 'signed', 'sizeof', 'static',
1790 'struct', 'switch', 'typedef', 'union', 'unsigned',
1791 'void', 'volatile', 'while'])
1792 # ISO/IEC 9899:1999, 6.4.1
1793 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1794 # ISO/IEC 9899:2011, 6.4.1
1795 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1796 '_Noreturn', '_Static_assert', '_Thread_local'])
1797 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1799 gcc_words
= set(['asm', 'typeof'])
1800 # C++ ISO/IEC 14882:2003 2.11
1801 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1802 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1803 'namespace', 'new', 'operator', 'private', 'protected',
1804 'public', 'reinterpret_cast', 'static_cast', 'template',
1805 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1806 'using', 'virtual', 'wchar_t',
1807 # alternative representations
1808 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1809 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1810 # namespace pollution:
1811 polluted_words
= set(['unix', 'errno', 'mips', 'sparc'])
1812 name
= name
.translate(c_name_trans
)
1813 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1814 | cpp_words | polluted_words
):
1818 eatspace
= '\033EATSPACE.'
1819 pointer_suffix
= ' *' + eatspace
1822 def genindent(count
):
1824 for _
in range(count
):
1831 def push_indent(indent_amount
=4):
1833 indent_level
+= indent_amount
1836 def pop_indent(indent_amount
=4):
1838 indent_level
-= indent_amount
1841 # Generate @code with @kwds interpolated.
1842 # Obey indent_level, and strip eatspace.
1843 def cgen(code
, **kwds
):
1846 indent
= genindent(indent_level
)
1847 # re.subn() lacks flags support before Python 2.7, use re.compile()
1848 raw
= re
.subn(re
.compile(r
'^.', re
.MULTILINE
),
1849 indent
+ r
'\g<0>', raw
)
1851 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
1854 def mcgen(code
, **kwds
):
1857 return cgen(code
, **kwds
)
1860 def guardname(filename
):
1861 return c_name(filename
, protect
=False).upper()
1864 def guardstart(name
):
1871 name
=guardname(name
))
1877 #endif /* %(name)s */
1880 name
=guardname(name
))
1883 def gen_enum_lookup(name
, values
, prefix
=None):
1886 const char *const %(c_name)s_lookup[] = {
1888 c_name
=c_name(name
))
1889 for value
in values
:
1890 index
= c_enum_const(name
, value
, prefix
)
1892 [%(index)s] = "%(value)s",
1894 index
=index
, value
=value
)
1896 max_index
= c_enum_const(name
, '_MAX', prefix
)
1898 [%(max_index)s] = NULL,
1901 max_index
=max_index
)
1905 def gen_enum(name
, values
, prefix
=None):
1906 # append automatically generated _MAX value
1907 enum_values
= values
+ ['_MAX']
1911 typedef enum %(c_name)s {
1913 c_name
=c_name(name
))
1916 for value
in enum_values
:
1920 c_enum
=c_enum_const(name
, value
, prefix
),
1927 c_name
=c_name(name
))
1931 extern const char *const %(c_name)s_lookup[];
1933 c_name
=c_name(name
))
1937 def gen_params(arg_type
, boxed
, extra
):
1944 ret
+= '%s arg' % arg_type
.c_param_type()
1947 assert not arg_type
.variants
1948 for memb
in arg_type
.members
:
1952 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1953 ret
+= '%s %s' % (memb
.type.c_param_type(),
1961 # Common command line parsing
1965 def parse_command_line(extra_options
='', extra_long_options
=[]):
1968 opts
, args
= getopt
.gnu_getopt(sys
.argv
[1:],
1969 'chp:o:' + extra_options
,
1970 ['source', 'header', 'prefix=',
1971 'output-dir='] + extra_long_options
)
1972 except getopt
.GetoptError
as err
:
1973 print >>sys
.stderr
, "%s: %s" % (sys
.argv
[0], str(err
))
1984 if o
in ('-p', '--prefix'):
1985 match
= re
.match(r
'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a
)
1986 if match
.end() != len(a
):
1987 print >>sys
.stderr
, \
1988 "%s: 'funny character '%s' in argument of --prefix" \
1989 % (sys
.argv
[0], a
[match
.end()])
1992 elif o
in ('-o', '--output-dir'):
1993 output_dir
= a
+ '/'
1994 elif o
in ('-c', '--source'):
1996 elif o
in ('-h', '--header'):
1999 extra_opts
.append(oa
)
2001 if not do_c
and not do_h
:
2006 print >>sys
.stderr
, "%s: need exactly one argument" % sys
.argv
[0]
2010 return (fname
, output_dir
, do_c
, do_h
, prefix
, extra_opts
)
2013 # Generate output files with boilerplate
2017 def open_output(output_dir
, do_c
, do_h
, prefix
, c_file
, h_file
,
2018 c_comment
, h_comment
):
2019 guard
= guardname(prefix
+ h_file
)
2020 c_file
= output_dir
+ prefix
+ c_file
2021 h_file
= output_dir
+ prefix
+ h_file
2025 os
.makedirs(output_dir
)
2026 except os
.error
as e
:
2027 if e
.errno
!= errno
.EEXIST
:
2030 def maybe_open(really
, name
, opt
):
2032 return open(name
, opt
)
2035 return StringIO
.StringIO()
2037 fdef
= maybe_open(do_c
, c_file
, 'w')
2038 fdecl
= maybe_open(do_h
, h_file
, 'w')
2040 fdef
.write(mcgen('''
2041 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2046 fdecl
.write(mcgen('''
2047 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2053 comment
=h_comment
, guard
=guard
))
2055 return (fdef
, fdecl
)
2058 def close_output(fdef
, fdecl
):