target/arm: Update excret sanity checks for v8M
[qemu/ar7.git] / scripts / qapi.py
blob62dc52ed6e019b6ba62b0464a37ca6fdbb70f67b
2 # QAPI helper library
4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2016 Red Hat Inc.
7 # Authors:
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.
14 import errno
15 import getopt
16 import os
17 import re
18 import string
19 import sys
20 from ordereddict import OrderedDict
22 builtin_types = {
23 'null': 'QTYPE_QNULL',
24 'str': 'QTYPE_QSTRING',
25 'int': 'QTYPE_QNUM',
26 'number': 'QTYPE_QNUM',
27 'bool': 'QTYPE_QBOOL',
28 'int8': 'QTYPE_QNUM',
29 'int16': 'QTYPE_QNUM',
30 'int32': 'QTYPE_QNUM',
31 'int64': 'QTYPE_QNUM',
32 'uint8': 'QTYPE_QNUM',
33 'uint16': 'QTYPE_QNUM',
34 'uint32': 'QTYPE_QNUM',
35 'uint64': 'QTYPE_QNUM',
36 'size': 'QTYPE_QNUM',
37 'any': None, # any QType possible, actually
38 'QType': 'QTYPE_QSTRING',
41 # Are documentation comments required?
42 doc_required = False
44 # Whitelist of commands allowed to return a non-dictionary
45 returns_whitelist = []
47 # Whitelist of entities allowed to violate case conventions
48 name_case_whitelist = []
50 enum_types = {}
51 struct_types = {}
52 union_types = {}
53 all_names = {}
56 # Parsing the schema into expressions
60 def error_path(parent):
61 res = ''
62 while parent:
63 res = ('In file included from %s:%d:\n' % (parent['file'],
64 parent['line'])) + res
65 parent = parent['parent']
66 return res
69 class QAPIError(Exception):
70 def __init__(self, fname, line, col, incl_info, msg):
71 Exception.__init__(self)
72 self.fname = fname
73 self.line = line
74 self.col = col
75 self.info = incl_info
76 self.msg = msg
78 def __str__(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):
87 col = 1
88 for ch in parser.src[parser.line_pos:parser.pos]:
89 if ch == '\t':
90 col = (col + 7) % 8 + 1
91 else:
92 col += 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,
100 info['parent'], msg)
103 class QAPIDoc(object):
104 class Section(object):
105 def __init__(self, name=None):
106 # optional section name (argument/member or section name)
107 self.name = name
108 # the list of lines for this section
109 self.content = []
111 def append(self, line):
112 self.content.append(line)
114 def __repr__(self):
115 return '\n'.join(self.content).strip()
117 class ArgSection(Section):
118 def __init__(self, name):
119 QAPIDoc.Section.__init__(self, name)
120 self.member = None
122 def connect(self, member):
123 self.member = member
125 def __init__(self, parser, info):
126 # self.parser is used to report errors with QAPIParseError. The
127 # resulting error position depends on the state of the parser.
128 # It happens to be the beginning of the comment. More or less
129 # servicable, but action at a distance.
130 self.parser = parser
131 self.info = info
132 self.symbol = None
133 self.body = QAPIDoc.Section()
134 # dict mapping parameter name to ArgSection
135 self.args = OrderedDict()
136 # a list of Section
137 self.sections = []
138 # the current section
139 self.section = self.body
141 def has_section(self, name):
142 """Return True if we have a section with this name."""
143 for i in self.sections:
144 if i.name == name:
145 return True
146 return False
148 def append(self, line):
149 """Parse a comment line and add it to the documentation."""
150 line = line[1:]
151 if not line:
152 self._append_freeform(line)
153 return
155 if line[0] != ' ':
156 raise QAPIParseError(self.parser, "Missing space after #")
157 line = line[1:]
159 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
160 # recognized, and get silently treated as ordinary text
161 if self.symbol:
162 self._append_symbol_line(line)
163 elif not self.body.content and line.startswith('@'):
164 if not line.endswith(':'):
165 raise QAPIParseError(self.parser, "Line should end with :")
166 self.symbol = line[1:-1]
167 # FIXME invalid names other than the empty string aren't flagged
168 if not self.symbol:
169 raise QAPIParseError(self.parser, "Invalid name")
170 else:
171 self._append_freeform(line)
173 def end_comment(self):
174 self._end_section()
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
184 'Note:', 'Notes:',
185 'Example:', 'Examples:',
186 'TODO:'):
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
194 if not name:
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)
199 if self.sections:
200 raise QAPIParseError(self.parser,
201 "'@%s:' can't follow '%s' section"
202 % (name, self.sections[0].name))
203 self._end_section()
204 self.section = QAPIDoc.ArgSection(name)
205 self.args[name] = self.section
207 def _start_section(self, name=''):
208 if name in ('Returns', 'Since') and self.has_section(name):
209 raise QAPIParseError(self.parser,
210 "Duplicated '%s' section" % name)
211 self._end_section()
212 self.section = QAPIDoc.Section(name)
213 self.sections.append(self.section)
215 def _end_section(self):
216 if self.section:
217 contents = str(self.section)
218 if self.section.name and (not contents or contents.isspace()):
219 raise QAPIParseError(self.parser, "Empty doc section '%s'"
220 % self.section.name)
221 self.section = None
223 def _append_freeform(self, line):
224 in_arg = isinstance(self.section, QAPIDoc.ArgSection)
225 if (in_arg and self.section.content
226 and not self.section.content[-1]
227 and line and not line[0].isspace()):
228 self._start_section()
229 if (in_arg or not self.section.name
230 or not self.section.name.startswith('Example')):
231 line = line.strip()
232 match = re.match(r'(@\S+:)', line)
233 if match:
234 raise QAPIParseError(self.parser,
235 "'%s' not allowed in free-form documentation"
236 % match.group(1))
237 # TODO Drop this once the dust has settled
238 if (isinstance(self.section, QAPIDoc.ArgSection)
239 and '#optional' in line):
240 raise QAPISemError(self.info, "Please drop the #optional tag")
241 self.section.append(line)
243 def connect_member(self, member):
244 if member.name not in self.args:
245 # Undocumented TODO outlaw
246 self.args[member.name] = QAPIDoc.ArgSection(member.name)
247 self.args[member.name].connect(member)
249 def check_expr(self, expr):
250 if self.has_section('Returns') and 'command' not in expr:
251 raise QAPISemError(self.info,
252 "'Returns:' is only valid for commands")
254 def check(self):
255 bogus = [name for name, section in self.args.iteritems()
256 if not section.member]
257 if bogus:
258 raise QAPISemError(
259 self.info,
260 "The following documented members are not in "
261 "the declaration: %s" % ", ".join(bogus))
264 class QAPISchemaParser(object):
266 def __init__(self, fp, previously_included=[], incl_info=None):
267 abs_fname = os.path.abspath(fp.name)
268 fname = fp.name
269 self.fname = fname
270 previously_included.append(abs_fname)
271 self.incl_info = incl_info
272 self.src = fp.read()
273 if self.src == '' or self.src[-1] != '\n':
274 self.src += '\n'
275 self.cursor = 0
276 self.line = 1
277 self.line_pos = 0
278 self.exprs = []
279 self.docs = []
280 self.cur_doc = None
281 self.accept()
283 while self.tok is not None:
284 info = {'file': fname, 'line': self.line,
285 'parent': self.incl_info}
286 if self.tok == '#':
287 self.reject_expr_doc()
288 self.cur_doc = self.get_doc(info)
289 self.docs.append(self.cur_doc)
290 continue
292 expr = self.get_expr(False)
293 if 'include' in expr:
294 self.reject_expr_doc()
295 if len(expr) != 1:
296 raise QAPISemError(info, "Invalid 'include' directive")
297 include = expr['include']
298 if not isinstance(include, str):
299 raise QAPISemError(info,
300 "Value of 'include' must be a string")
301 self._include(include, info, os.path.dirname(abs_fname),
302 previously_included)
303 elif "pragma" in expr:
304 self.reject_expr_doc()
305 if len(expr) != 1:
306 raise QAPISemError(info, "Invalid 'pragma' directive")
307 pragma = expr['pragma']
308 if not isinstance(pragma, dict):
309 raise QAPISemError(
310 info, "Value of 'pragma' must be a dictionary")
311 for name, value in pragma.iteritems():
312 self._pragma(name, value, info)
313 else:
314 expr_elem = {'expr': expr,
315 'info': info}
316 if self.cur_doc:
317 if not self.cur_doc.symbol:
318 raise QAPISemError(
319 self.cur_doc.info,
320 "Expression documentation required")
321 expr_elem['doc'] = self.cur_doc
322 self.exprs.append(expr_elem)
323 self.cur_doc = None
324 self.reject_expr_doc()
326 def reject_expr_doc(self):
327 if self.cur_doc and self.cur_doc.symbol:
328 raise QAPISemError(
329 self.cur_doc.info,
330 "Documentation for '%s' is not followed by the definition"
331 % self.cur_doc.symbol)
333 def _include(self, include, info, base_dir, previously_included):
334 incl_abs_fname = os.path.join(base_dir, include)
335 # catch inclusion cycle
336 inf = info
337 while inf:
338 if incl_abs_fname == os.path.abspath(inf['file']):
339 raise QAPISemError(info, "Inclusion loop for %s" % include)
340 inf = inf['parent']
342 # skip multiple include of the same file
343 if incl_abs_fname in previously_included:
344 return
345 try:
346 fobj = open(incl_abs_fname, 'r')
347 except IOError as e:
348 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
349 exprs_include = QAPISchemaParser(fobj, previously_included, info)
350 self.exprs.extend(exprs_include.exprs)
351 self.docs.extend(exprs_include.docs)
353 def _pragma(self, name, value, info):
354 global doc_required, returns_whitelist, name_case_whitelist
355 if name == 'doc-required':
356 if not isinstance(value, bool):
357 raise QAPISemError(info,
358 "Pragma 'doc-required' must be boolean")
359 doc_required = value
360 elif name == 'returns-whitelist':
361 if (not isinstance(value, list)
362 or any([not isinstance(elt, str) for elt in value])):
363 raise QAPISemError(info,
364 "Pragma returns-whitelist must be"
365 " a list of strings")
366 returns_whitelist = value
367 elif name == 'name-case-whitelist':
368 if (not isinstance(value, list)
369 or any([not isinstance(elt, str) for elt in value])):
370 raise QAPISemError(info,
371 "Pragma name-case-whitelist must be"
372 " a list of strings")
373 name_case_whitelist = value
374 else:
375 raise QAPISemError(info, "Unknown pragma '%s'" % name)
377 def accept(self, skip_comment=True):
378 while True:
379 self.tok = self.src[self.cursor]
380 self.pos = self.cursor
381 self.cursor += 1
382 self.val = None
384 if self.tok == '#':
385 if self.src[self.cursor] == '#':
386 # Start of doc comment
387 skip_comment = False
388 self.cursor = self.src.find('\n', self.cursor)
389 if not skip_comment:
390 self.val = self.src[self.pos:self.cursor]
391 return
392 elif self.tok in '{}:,[]':
393 return
394 elif self.tok == "'":
395 string = ''
396 esc = False
397 while True:
398 ch = self.src[self.cursor]
399 self.cursor += 1
400 if ch == '\n':
401 raise QAPIParseError(self, 'Missing terminating "\'"')
402 if esc:
403 if ch == 'b':
404 string += '\b'
405 elif ch == 'f':
406 string += '\f'
407 elif ch == 'n':
408 string += '\n'
409 elif ch == 'r':
410 string += '\r'
411 elif ch == 't':
412 string += '\t'
413 elif ch == 'u':
414 value = 0
415 for _ in range(0, 4):
416 ch = self.src[self.cursor]
417 self.cursor += 1
418 if ch not in '0123456789abcdefABCDEF':
419 raise QAPIParseError(self,
420 '\\u escape needs 4 '
421 'hex digits')
422 value = (value << 4) + int(ch, 16)
423 # If Python 2 and 3 didn't disagree so much on
424 # how to handle Unicode, then we could allow
425 # Unicode string defaults. But most of QAPI is
426 # ASCII-only, so we aren't losing much for now.
427 if not value or value > 0x7f:
428 raise QAPIParseError(self,
429 'For now, \\u escape '
430 'only supports non-zero '
431 'values up to \\u007f')
432 string += chr(value)
433 elif ch in '\\/\'"':
434 string += ch
435 else:
436 raise QAPIParseError(self,
437 "Unknown escape \\%s" % ch)
438 esc = False
439 elif ch == '\\':
440 esc = True
441 elif ch == "'":
442 self.val = string
443 return
444 else:
445 string += ch
446 elif self.src.startswith('true', self.pos):
447 self.val = True
448 self.cursor += 3
449 return
450 elif self.src.startswith('false', self.pos):
451 self.val = False
452 self.cursor += 4
453 return
454 elif self.src.startswith('null', self.pos):
455 self.val = None
456 self.cursor += 3
457 return
458 elif self.tok == '\n':
459 if self.cursor == len(self.src):
460 self.tok = None
461 return
462 self.line += 1
463 self.line_pos = self.cursor
464 elif not self.tok.isspace():
465 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
467 def get_members(self):
468 expr = OrderedDict()
469 if self.tok == '}':
470 self.accept()
471 return expr
472 if self.tok != "'":
473 raise QAPIParseError(self, 'Expected string or "}"')
474 while True:
475 key = self.val
476 self.accept()
477 if self.tok != ':':
478 raise QAPIParseError(self, 'Expected ":"')
479 self.accept()
480 if key in expr:
481 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
482 expr[key] = self.get_expr(True)
483 if self.tok == '}':
484 self.accept()
485 return expr
486 if self.tok != ',':
487 raise QAPIParseError(self, 'Expected "," or "}"')
488 self.accept()
489 if self.tok != "'":
490 raise QAPIParseError(self, 'Expected string')
492 def get_values(self):
493 expr = []
494 if self.tok == ']':
495 self.accept()
496 return expr
497 if self.tok not in "{['tfn":
498 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
499 'boolean or "null"')
500 while True:
501 expr.append(self.get_expr(True))
502 if self.tok == ']':
503 self.accept()
504 return expr
505 if self.tok != ',':
506 raise QAPIParseError(self, 'Expected "," or "]"')
507 self.accept()
509 def get_expr(self, nested):
510 if self.tok != '{' and not nested:
511 raise QAPIParseError(self, 'Expected "{"')
512 if self.tok == '{':
513 self.accept()
514 expr = self.get_members()
515 elif self.tok == '[':
516 self.accept()
517 expr = self.get_values()
518 elif self.tok in "'tfn":
519 expr = self.val
520 self.accept()
521 else:
522 raise QAPIParseError(self, 'Expected "{", "[", string, '
523 'boolean or "null"')
524 return expr
526 def get_doc(self, info):
527 if self.val != '##':
528 raise QAPIParseError(self, "Junk after '##' at start of "
529 "documentation comment")
531 doc = QAPIDoc(self, info)
532 self.accept(False)
533 while self.tok == '#':
534 if self.val.startswith('##'):
535 # End of doc comment
536 if self.val != '##':
537 raise QAPIParseError(self, "Junk after '##' at end of "
538 "documentation comment")
539 doc.end_comment()
540 self.accept()
541 return doc
542 else:
543 doc.append(self.val)
544 self.accept(False)
546 raise QAPIParseError(self, "Documentation comment must end with '##'")
550 # Semantic analysis of schema expressions
551 # TODO fold into QAPISchema
552 # TODO catching name collisions in generated code would be nice
556 def find_base_members(base):
557 if isinstance(base, dict):
558 return base
559 base_struct_define = struct_types.get(base)
560 if not base_struct_define:
561 return None
562 return base_struct_define['data']
565 # Return the qtype of an alternate branch, or None on error.
566 def find_alternate_member_qtype(qapi_type):
567 if qapi_type in builtin_types:
568 return builtin_types[qapi_type]
569 elif qapi_type in struct_types:
570 return 'QTYPE_QDICT'
571 elif qapi_type in enum_types:
572 return 'QTYPE_QSTRING'
573 elif qapi_type in union_types:
574 return 'QTYPE_QDICT'
575 return None
578 # Return the discriminator enum define if discriminator is specified as an
579 # enum type, otherwise return None.
580 def discriminator_find_enum_define(expr):
581 base = expr.get('base')
582 discriminator = expr.get('discriminator')
584 if not (discriminator and base):
585 return None
587 base_members = find_base_members(base)
588 if not base_members:
589 return None
591 discriminator_type = base_members.get(discriminator)
592 if not discriminator_type:
593 return None
595 return enum_types.get(discriminator_type)
598 # Names must be letters, numbers, -, and _. They must start with letter,
599 # except for downstream extensions which must start with __RFQDN_.
600 # Dots are only valid in the downstream extension prefix.
601 valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
602 '[a-zA-Z][a-zA-Z0-9_-]*$')
605 def check_name(info, source, name, allow_optional=False,
606 enum_member=False):
607 global valid_name
608 membername = name
610 if not isinstance(name, str):
611 raise QAPISemError(info, "%s requires a string name" % source)
612 if name.startswith('*'):
613 membername = name[1:]
614 if not allow_optional:
615 raise QAPISemError(info, "%s does not allow optional name '%s'"
616 % (source, name))
617 # Enum members can start with a digit, because the generated C
618 # code always prefixes it with the enum name
619 if enum_member and membername[0].isdigit():
620 membername = 'D' + membername
621 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
622 # and 'q_obj_*' implicit type names.
623 if not valid_name.match(membername) or \
624 c_name(membername, False).startswith('q_'):
625 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
628 def add_name(name, info, meta, implicit=False):
629 global all_names
630 check_name(info, "'%s'" % meta, name)
631 # FIXME should reject names that differ only in '_' vs. '.'
632 # vs. '-', because they're liable to clash in generated C.
633 if name in all_names:
634 raise QAPISemError(info, "%s '%s' is already defined"
635 % (all_names[name], name))
636 if not implicit and (name.endswith('Kind') or name.endswith('List')):
637 raise QAPISemError(info, "%s '%s' should not end in '%s'"
638 % (meta, name, name[-4:]))
639 all_names[name] = meta
642 def check_type(info, source, value, allow_array=False,
643 allow_dict=False, allow_optional=False,
644 allow_metas=[]):
645 global all_names
647 if value is None:
648 return
650 # Check if array type for value is okay
651 if isinstance(value, list):
652 if not allow_array:
653 raise QAPISemError(info, "%s cannot be an array" % source)
654 if len(value) != 1 or not isinstance(value[0], str):
655 raise QAPISemError(info,
656 "%s: array type must contain single type name" %
657 source)
658 value = value[0]
660 # Check if type name for value is okay
661 if isinstance(value, str):
662 if value not in all_names:
663 raise QAPISemError(info, "%s uses unknown type '%s'"
664 % (source, value))
665 if not all_names[value] in allow_metas:
666 raise QAPISemError(info, "%s cannot use %s type '%s'" %
667 (source, all_names[value], value))
668 return
670 if not allow_dict:
671 raise QAPISemError(info, "%s should be a type name" % source)
673 if not isinstance(value, OrderedDict):
674 raise QAPISemError(info,
675 "%s should be a dictionary or type name" % source)
677 # value is a dictionary, check that each member is okay
678 for (key, arg) in value.items():
679 check_name(info, "Member of %s" % source, key,
680 allow_optional=allow_optional)
681 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
682 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
683 % (source, key))
684 # Todo: allow dictionaries to represent default values of
685 # an optional argument.
686 check_type(info, "Member '%s' of %s" % (key, source), arg,
687 allow_array=True,
688 allow_metas=['built-in', 'union', 'alternate', 'struct',
689 'enum'])
692 def check_command(expr, info):
693 name = expr['command']
694 boxed = expr.get('boxed', False)
696 args_meta = ['struct']
697 if boxed:
698 args_meta += ['union', 'alternate']
699 check_type(info, "'data' for command '%s'" % name,
700 expr.get('data'), allow_dict=not boxed, allow_optional=True,
701 allow_metas=args_meta)
702 returns_meta = ['union', 'struct']
703 if name in returns_whitelist:
704 returns_meta += ['built-in', 'alternate', 'enum']
705 check_type(info, "'returns' for command '%s'" % name,
706 expr.get('returns'), allow_array=True,
707 allow_optional=True, allow_metas=returns_meta)
710 def check_event(expr, info):
711 name = expr['event']
712 boxed = expr.get('boxed', False)
714 meta = ['struct']
715 if boxed:
716 meta += ['union', 'alternate']
717 check_type(info, "'data' for event '%s'" % name,
718 expr.get('data'), allow_dict=not boxed, allow_optional=True,
719 allow_metas=meta)
722 def check_union(expr, info):
723 name = expr['union']
724 base = expr.get('base')
725 discriminator = expr.get('discriminator')
726 members = expr['data']
728 # Two types of unions, determined by discriminator.
730 # With no discriminator it is a simple union.
731 if discriminator is None:
732 enum_define = None
733 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
734 if base is not None:
735 raise QAPISemError(info, "Simple union '%s' must not have a base" %
736 name)
738 # Else, it's a flat union.
739 else:
740 # The object must have a string or dictionary 'base'.
741 check_type(info, "'base' for union '%s'" % name,
742 base, allow_dict=True, allow_optional=True,
743 allow_metas=['struct'])
744 if not base:
745 raise QAPISemError(info, "Flat union '%s' must have a base"
746 % name)
747 base_members = find_base_members(base)
748 assert base_members is not None
750 # The value of member 'discriminator' must name a non-optional
751 # member of the base struct.
752 check_name(info, "Discriminator of flat union '%s'" % name,
753 discriminator)
754 discriminator_type = base_members.get(discriminator)
755 if not discriminator_type:
756 raise QAPISemError(info,
757 "Discriminator '%s' is not a member of base "
758 "struct '%s'"
759 % (discriminator, base))
760 enum_define = enum_types.get(discriminator_type)
761 allow_metas = ['struct']
762 # Do not allow string discriminator
763 if not enum_define:
764 raise QAPISemError(info,
765 "Discriminator '%s' must be of enumeration "
766 "type" % discriminator)
768 # Check every branch; don't allow an empty union
769 if len(members) == 0:
770 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
771 for (key, value) in members.items():
772 check_name(info, "Member of union '%s'" % name, key)
774 # Each value must name a known type
775 check_type(info, "Member '%s' of union '%s'" % (key, name),
776 value, allow_array=not base, allow_metas=allow_metas)
778 # If the discriminator names an enum type, then all members
779 # of 'data' must also be members of the enum type.
780 if enum_define:
781 if key not in enum_define['data']:
782 raise QAPISemError(info,
783 "Discriminator value '%s' is not found in "
784 "enum '%s'"
785 % (key, enum_define['enum']))
787 # If discriminator is user-defined, ensure all values are covered
788 if enum_define:
789 for value in enum_define['data']:
790 if value not in members.keys():
791 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
792 % (name, value))
795 def check_alternate(expr, info):
796 name = expr['alternate']
797 members = expr['data']
798 types_seen = {}
800 # Check every branch; require at least two branches
801 if len(members) < 2:
802 raise QAPISemError(info,
803 "Alternate '%s' should have at least two branches "
804 "in 'data'" % name)
805 for (key, value) in members.items():
806 check_name(info, "Member of alternate '%s'" % name, key)
808 # Ensure alternates have no type conflicts.
809 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
810 value,
811 allow_metas=['built-in', 'union', 'struct', 'enum'])
812 qtype = find_alternate_member_qtype(value)
813 if not qtype:
814 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
815 "type '%s'" % (name, key, value))
816 conflicting = set([qtype])
817 if qtype == 'QTYPE_QSTRING':
818 enum_expr = enum_types.get(value)
819 if enum_expr:
820 for v in enum_expr['data']:
821 if v in ['on', 'off']:
822 conflicting.add('QTYPE_QBOOL')
823 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
824 conflicting.add('QTYPE_QNUM')
825 else:
826 conflicting.add('QTYPE_QNUM')
827 conflicting.add('QTYPE_QBOOL')
828 for qt in conflicting:
829 if qt in types_seen:
830 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
831 "be distinguished from member '%s'"
832 % (name, key, types_seen[qt]))
833 types_seen[qt] = key
836 def check_enum(expr, info):
837 name = expr['enum']
838 members = expr.get('data')
839 prefix = expr.get('prefix')
841 if not isinstance(members, list):
842 raise QAPISemError(info,
843 "Enum '%s' requires an array for 'data'" % name)
844 if prefix is not None and not isinstance(prefix, str):
845 raise QAPISemError(info,
846 "Enum '%s' requires a string for 'prefix'" % name)
847 for member in members:
848 check_name(info, "Member of enum '%s'" % name, member,
849 enum_member=True)
852 def check_struct(expr, info):
853 name = expr['struct']
854 members = expr['data']
856 check_type(info, "'data' for struct '%s'" % name, members,
857 allow_dict=True, allow_optional=True)
858 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
859 allow_metas=['struct'])
862 def check_keys(expr_elem, meta, required, optional=[]):
863 expr = expr_elem['expr']
864 info = expr_elem['info']
865 name = expr[meta]
866 if not isinstance(name, str):
867 raise QAPISemError(info, "'%s' key must have a string value" % meta)
868 required = required + [meta]
869 for (key, value) in expr.items():
870 if key not in required and key not in optional:
871 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
872 % (key, meta, name))
873 if (key == 'gen' or key == 'success-response') and value is not False:
874 raise QAPISemError(info,
875 "'%s' of %s '%s' should only use false value"
876 % (key, meta, name))
877 if key == 'boxed' and value is not True:
878 raise QAPISemError(info,
879 "'%s' of %s '%s' should only use true value"
880 % (key, meta, name))
881 for key in required:
882 if key not in expr:
883 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
884 % (key, meta, name))
887 def check_exprs(exprs):
888 global all_names
890 # Populate name table with names of built-in types
891 for builtin in builtin_types.keys():
892 all_names[builtin] = 'built-in'
894 # Learn the types and check for valid expression keys
895 for expr_elem in exprs:
896 expr = expr_elem['expr']
897 info = expr_elem['info']
898 doc = expr_elem.get('doc')
900 if not doc and doc_required:
901 raise QAPISemError(info,
902 "Expression missing documentation comment")
904 if 'enum' in expr:
905 meta = 'enum'
906 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
907 enum_types[expr[meta]] = expr
908 elif 'union' in expr:
909 meta = 'union'
910 check_keys(expr_elem, 'union', ['data'],
911 ['base', 'discriminator'])
912 union_types[expr[meta]] = expr
913 elif 'alternate' in expr:
914 meta = 'alternate'
915 check_keys(expr_elem, 'alternate', ['data'])
916 elif 'struct' in expr:
917 meta = 'struct'
918 check_keys(expr_elem, 'struct', ['data'], ['base'])
919 struct_types[expr[meta]] = expr
920 elif 'command' in expr:
921 meta = 'command'
922 check_keys(expr_elem, 'command', [],
923 ['data', 'returns', 'gen', 'success-response', 'boxed'])
924 elif 'event' in expr:
925 meta = 'event'
926 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
927 else:
928 raise QAPISemError(expr_elem['info'],
929 "Expression is missing metatype")
930 name = expr[meta]
931 add_name(name, info, meta)
932 if doc and doc.symbol != name:
933 raise QAPISemError(info, "Definition of '%s' follows documentation"
934 " for '%s'" % (name, doc.symbol))
936 # Try again for hidden UnionKind enum
937 for expr_elem in exprs:
938 expr = expr_elem['expr']
939 if 'union' in expr and not discriminator_find_enum_define(expr):
940 name = '%sKind' % expr['union']
941 elif 'alternate' in expr:
942 name = '%sKind' % expr['alternate']
943 else:
944 continue
945 enum_types[name] = {'enum': name}
946 add_name(name, info, 'enum', implicit=True)
948 # Validate that exprs make sense
949 for expr_elem in exprs:
950 expr = expr_elem['expr']
951 info = expr_elem['info']
952 doc = expr_elem.get('doc')
954 if 'enum' in expr:
955 check_enum(expr, info)
956 elif 'union' in expr:
957 check_union(expr, info)
958 elif 'alternate' in expr:
959 check_alternate(expr, info)
960 elif 'struct' in expr:
961 check_struct(expr, info)
962 elif 'command' in expr:
963 check_command(expr, info)
964 elif 'event' in expr:
965 check_event(expr, info)
966 else:
967 assert False, 'unexpected meta type'
969 if doc:
970 doc.check_expr(expr)
972 return exprs
976 # Schema compiler frontend
979 class QAPISchemaEntity(object):
980 def __init__(self, name, info, doc):
981 assert isinstance(name, str)
982 self.name = name
983 # For explicitly defined entities, info points to the (explicit)
984 # definition. For builtins (and their arrays), info is None.
985 # For implicitly defined entities, info points to a place that
986 # triggered the implicit definition (there may be more than one
987 # such place).
988 self.info = info
989 self.doc = doc
991 def c_name(self):
992 return c_name(self.name)
994 def check(self, schema):
995 pass
997 def is_implicit(self):
998 return not self.info
1000 def visit(self, visitor):
1001 pass
1004 class QAPISchemaVisitor(object):
1005 def visit_begin(self, schema):
1006 pass
1008 def visit_end(self):
1009 pass
1011 def visit_needed(self, entity):
1012 # Default to visiting everything
1013 return True
1015 def visit_builtin_type(self, name, info, json_type):
1016 pass
1018 def visit_enum_type(self, name, info, values, prefix):
1019 pass
1021 def visit_array_type(self, name, info, element_type):
1022 pass
1024 def visit_object_type(self, name, info, base, members, variants):
1025 pass
1027 def visit_object_type_flat(self, name, info, members, variants):
1028 pass
1030 def visit_alternate_type(self, name, info, variants):
1031 pass
1033 def visit_command(self, name, info, arg_type, ret_type,
1034 gen, success_response, boxed):
1035 pass
1037 def visit_event(self, name, info, arg_type, boxed):
1038 pass
1041 class QAPISchemaType(QAPISchemaEntity):
1042 # Return the C type for common use.
1043 # For the types we commonly box, this is a pointer type.
1044 def c_type(self):
1045 pass
1047 # Return the C type to be used in a parameter list.
1048 def c_param_type(self):
1049 return self.c_type()
1051 # Return the C type to be used where we suppress boxing.
1052 def c_unboxed_type(self):
1053 return self.c_type()
1055 def json_type(self):
1056 pass
1058 def alternate_qtype(self):
1059 json2qtype = {
1060 'null': 'QTYPE_QNULL',
1061 'string': 'QTYPE_QSTRING',
1062 'number': 'QTYPE_QNUM',
1063 'int': 'QTYPE_QNUM',
1064 'boolean': 'QTYPE_QBOOL',
1065 'object': 'QTYPE_QDICT'
1067 return json2qtype.get(self.json_type())
1069 def doc_type(self):
1070 if self.is_implicit():
1071 return None
1072 return self.name
1075 class QAPISchemaBuiltinType(QAPISchemaType):
1076 def __init__(self, name, json_type, c_type):
1077 QAPISchemaType.__init__(self, name, None, None)
1078 assert not c_type or isinstance(c_type, str)
1079 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1080 'value')
1081 self._json_type_name = json_type
1082 self._c_type_name = c_type
1084 def c_name(self):
1085 return self.name
1087 def c_type(self):
1088 return self._c_type_name
1090 def c_param_type(self):
1091 if self.name == 'str':
1092 return 'const ' + self._c_type_name
1093 return self._c_type_name
1095 def json_type(self):
1096 return self._json_type_name
1098 def doc_type(self):
1099 return self.json_type()
1101 def visit(self, visitor):
1102 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1105 class QAPISchemaEnumType(QAPISchemaType):
1106 def __init__(self, name, info, doc, values, prefix):
1107 QAPISchemaType.__init__(self, name, info, doc)
1108 for v in values:
1109 assert isinstance(v, QAPISchemaMember)
1110 v.set_owner(name)
1111 assert prefix is None or isinstance(prefix, str)
1112 self.values = values
1113 self.prefix = prefix
1115 def check(self, schema):
1116 seen = {}
1117 for v in self.values:
1118 v.check_clash(self.info, seen)
1119 if self.doc:
1120 self.doc.connect_member(v)
1122 def is_implicit(self):
1123 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1124 return self.name.endswith('Kind') or self.name == 'QType'
1126 def c_type(self):
1127 return c_name(self.name)
1129 def member_names(self):
1130 return [v.name for v in self.values]
1132 def json_type(self):
1133 return 'string'
1135 def visit(self, visitor):
1136 visitor.visit_enum_type(self.name, self.info,
1137 self.member_names(), self.prefix)
1140 class QAPISchemaArrayType(QAPISchemaType):
1141 def __init__(self, name, info, element_type):
1142 QAPISchemaType.__init__(self, name, info, None)
1143 assert isinstance(element_type, str)
1144 self._element_type_name = element_type
1145 self.element_type = None
1147 def check(self, schema):
1148 self.element_type = schema.lookup_type(self._element_type_name)
1149 assert self.element_type
1151 def is_implicit(self):
1152 return True
1154 def c_type(self):
1155 return c_name(self.name) + pointer_suffix
1157 def json_type(self):
1158 return 'array'
1160 def doc_type(self):
1161 elt_doc_type = self.element_type.doc_type()
1162 if not elt_doc_type:
1163 return None
1164 return 'array of ' + elt_doc_type
1166 def visit(self, visitor):
1167 visitor.visit_array_type(self.name, self.info, self.element_type)
1170 class QAPISchemaObjectType(QAPISchemaType):
1171 def __init__(self, name, info, doc, base, local_members, variants):
1172 # struct has local_members, optional base, and no variants
1173 # flat union has base, variants, and no local_members
1174 # simple union has local_members, variants, and no base
1175 QAPISchemaType.__init__(self, name, info, doc)
1176 assert base is None or isinstance(base, str)
1177 for m in local_members:
1178 assert isinstance(m, QAPISchemaObjectTypeMember)
1179 m.set_owner(name)
1180 if variants is not None:
1181 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1182 variants.set_owner(name)
1183 self._base_name = base
1184 self.base = None
1185 self.local_members = local_members
1186 self.variants = variants
1187 self.members = None
1189 def check(self, schema):
1190 if self.members is False: # check for cycles
1191 raise QAPISemError(self.info,
1192 "Object %s contains itself" % self.name)
1193 if self.members:
1194 return
1195 self.members = False # mark as being checked
1196 seen = OrderedDict()
1197 if self._base_name:
1198 self.base = schema.lookup_type(self._base_name)
1199 assert isinstance(self.base, QAPISchemaObjectType)
1200 self.base.check(schema)
1201 self.base.check_clash(self.info, seen)
1202 for m in self.local_members:
1203 m.check(schema)
1204 m.check_clash(self.info, seen)
1205 if self.doc:
1206 self.doc.connect_member(m)
1207 self.members = seen.values()
1208 if self.variants:
1209 self.variants.check(schema, seen)
1210 assert self.variants.tag_member in self.members
1211 self.variants.check_clash(self.info, seen)
1212 if self.doc:
1213 self.doc.check()
1215 # Check that the members of this type do not cause duplicate JSON members,
1216 # and update seen to track the members seen so far. Report any errors
1217 # on behalf of info, which is not necessarily self.info
1218 def check_clash(self, info, seen):
1219 assert not self.variants # not implemented
1220 for m in self.members:
1221 m.check_clash(info, seen)
1223 def is_implicit(self):
1224 # See QAPISchema._make_implicit_object_type(), as well as
1225 # _def_predefineds()
1226 return self.name.startswith('q_')
1228 def is_empty(self):
1229 assert self.members is not None
1230 return not self.members and not self.variants
1232 def c_name(self):
1233 assert self.name != 'q_empty'
1234 return QAPISchemaType.c_name(self)
1236 def c_type(self):
1237 assert not self.is_implicit()
1238 return c_name(self.name) + pointer_suffix
1240 def c_unboxed_type(self):
1241 return c_name(self.name)
1243 def json_type(self):
1244 return 'object'
1246 def visit(self, visitor):
1247 visitor.visit_object_type(self.name, self.info,
1248 self.base, self.local_members, self.variants)
1249 visitor.visit_object_type_flat(self.name, self.info,
1250 self.members, self.variants)
1253 class QAPISchemaMember(object):
1254 role = 'member'
1256 def __init__(self, name):
1257 assert isinstance(name, str)
1258 self.name = name
1259 self.owner = None
1261 def set_owner(self, name):
1262 assert not self.owner
1263 self.owner = name
1265 def check_clash(self, info, seen):
1266 cname = c_name(self.name)
1267 if cname.lower() != cname and self.owner not in name_case_whitelist:
1268 raise QAPISemError(info,
1269 "%s should not use uppercase" % self.describe())
1270 if cname in seen:
1271 raise QAPISemError(info, "%s collides with %s" %
1272 (self.describe(), seen[cname].describe()))
1273 seen[cname] = self
1275 def _pretty_owner(self):
1276 owner = self.owner
1277 if owner.startswith('q_obj_'):
1278 # See QAPISchema._make_implicit_object_type() - reverse the
1279 # mapping there to create a nice human-readable description
1280 owner = owner[6:]
1281 if owner.endswith('-arg'):
1282 return '(parameter of %s)' % owner[:-4]
1283 elif owner.endswith('-base'):
1284 return '(base of %s)' % owner[:-5]
1285 else:
1286 assert owner.endswith('-wrapper')
1287 # Unreachable and not implemented
1288 assert False
1289 if owner.endswith('Kind'):
1290 # See QAPISchema._make_implicit_enum_type()
1291 return '(branch of %s)' % owner[:-4]
1292 return '(%s of %s)' % (self.role, owner)
1294 def describe(self):
1295 return "'%s' %s" % (self.name, self._pretty_owner())
1298 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1299 def __init__(self, name, typ, optional):
1300 QAPISchemaMember.__init__(self, name)
1301 assert isinstance(typ, str)
1302 assert isinstance(optional, bool)
1303 self._type_name = typ
1304 self.type = None
1305 self.optional = optional
1307 def check(self, schema):
1308 assert self.owner
1309 self.type = schema.lookup_type(self._type_name)
1310 assert self.type
1313 class QAPISchemaObjectTypeVariants(object):
1314 def __init__(self, tag_name, tag_member, variants):
1315 # Flat unions pass tag_name but not tag_member.
1316 # Simple unions and alternates pass tag_member but not tag_name.
1317 # After check(), tag_member is always set, and tag_name remains
1318 # a reliable witness of being used by a flat union.
1319 assert bool(tag_member) != bool(tag_name)
1320 assert (isinstance(tag_name, str) or
1321 isinstance(tag_member, QAPISchemaObjectTypeMember))
1322 assert len(variants) > 0
1323 for v in variants:
1324 assert isinstance(v, QAPISchemaObjectTypeVariant)
1325 self._tag_name = tag_name
1326 self.tag_member = tag_member
1327 self.variants = variants
1329 def set_owner(self, name):
1330 for v in self.variants:
1331 v.set_owner(name)
1333 def check(self, schema, seen):
1334 if not self.tag_member: # flat union
1335 self.tag_member = seen[c_name(self._tag_name)]
1336 assert self._tag_name == self.tag_member.name
1337 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1338 for v in self.variants:
1339 v.check(schema)
1340 # Union names must match enum values; alternate names are
1341 # checked separately. Use 'seen' to tell the two apart.
1342 if seen:
1343 assert v.name in self.tag_member.type.member_names()
1344 assert isinstance(v.type, QAPISchemaObjectType)
1345 v.type.check(schema)
1347 def check_clash(self, info, seen):
1348 for v in self.variants:
1349 # Reset seen map for each variant, since qapi names from one
1350 # branch do not affect another branch
1351 assert isinstance(v.type, QAPISchemaObjectType)
1352 v.type.check_clash(info, dict(seen))
1355 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1356 role = 'branch'
1358 def __init__(self, name, typ):
1359 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1362 class QAPISchemaAlternateType(QAPISchemaType):
1363 def __init__(self, name, info, doc, variants):
1364 QAPISchemaType.__init__(self, name, info, doc)
1365 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1366 assert variants.tag_member
1367 variants.set_owner(name)
1368 variants.tag_member.set_owner(self.name)
1369 self.variants = variants
1371 def check(self, schema):
1372 self.variants.tag_member.check(schema)
1373 # Not calling self.variants.check_clash(), because there's nothing
1374 # to clash with
1375 self.variants.check(schema, {})
1376 # Alternate branch names have no relation to the tag enum values;
1377 # so we have to check for potential name collisions ourselves.
1378 seen = {}
1379 for v in self.variants.variants:
1380 v.check_clash(self.info, seen)
1381 if self.doc:
1382 self.doc.connect_member(v)
1383 if self.doc:
1384 self.doc.check()
1386 def c_type(self):
1387 return c_name(self.name) + pointer_suffix
1389 def json_type(self):
1390 return 'value'
1392 def visit(self, visitor):
1393 visitor.visit_alternate_type(self.name, self.info, self.variants)
1395 def is_empty(self):
1396 return False
1399 class QAPISchemaCommand(QAPISchemaEntity):
1400 def __init__(self, name, info, doc, arg_type, ret_type,
1401 gen, success_response, boxed):
1402 QAPISchemaEntity.__init__(self, name, info, doc)
1403 assert not arg_type or isinstance(arg_type, str)
1404 assert not ret_type or isinstance(ret_type, str)
1405 self._arg_type_name = arg_type
1406 self.arg_type = None
1407 self._ret_type_name = ret_type
1408 self.ret_type = None
1409 self.gen = gen
1410 self.success_response = success_response
1411 self.boxed = boxed
1413 def check(self, schema):
1414 if self._arg_type_name:
1415 self.arg_type = schema.lookup_type(self._arg_type_name)
1416 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1417 isinstance(self.arg_type, QAPISchemaAlternateType))
1418 self.arg_type.check(schema)
1419 if self.boxed:
1420 if self.arg_type.is_empty():
1421 raise QAPISemError(self.info,
1422 "Cannot use 'boxed' with empty type")
1423 else:
1424 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1425 assert not self.arg_type.variants
1426 elif self.boxed:
1427 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1428 if self._ret_type_name:
1429 self.ret_type = schema.lookup_type(self._ret_type_name)
1430 assert isinstance(self.ret_type, QAPISchemaType)
1432 def visit(self, visitor):
1433 visitor.visit_command(self.name, self.info,
1434 self.arg_type, self.ret_type,
1435 self.gen, self.success_response, self.boxed)
1438 class QAPISchemaEvent(QAPISchemaEntity):
1439 def __init__(self, name, info, doc, arg_type, boxed):
1440 QAPISchemaEntity.__init__(self, name, info, doc)
1441 assert not arg_type or isinstance(arg_type, str)
1442 self._arg_type_name = arg_type
1443 self.arg_type = None
1444 self.boxed = boxed
1446 def check(self, schema):
1447 if self._arg_type_name:
1448 self.arg_type = schema.lookup_type(self._arg_type_name)
1449 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1450 isinstance(self.arg_type, QAPISchemaAlternateType))
1451 self.arg_type.check(schema)
1452 if self.boxed:
1453 if self.arg_type.is_empty():
1454 raise QAPISemError(self.info,
1455 "Cannot use 'boxed' with empty type")
1456 else:
1457 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1458 assert not self.arg_type.variants
1459 elif self.boxed:
1460 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1462 def visit(self, visitor):
1463 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
1466 class QAPISchema(object):
1467 def __init__(self, fname):
1468 try:
1469 parser = QAPISchemaParser(open(fname, 'r'))
1470 self.exprs = check_exprs(parser.exprs)
1471 self.docs = parser.docs
1472 self._entity_dict = {}
1473 self._predefining = True
1474 self._def_predefineds()
1475 self._predefining = False
1476 self._def_exprs()
1477 self.check()
1478 except QAPIError as err:
1479 print >>sys.stderr, err
1480 exit(1)
1482 def _def_entity(self, ent):
1483 # Only the predefined types are allowed to not have info
1484 assert ent.info or self._predefining
1485 assert ent.name not in self._entity_dict
1486 self._entity_dict[ent.name] = ent
1488 def lookup_entity(self, name, typ=None):
1489 ent = self._entity_dict.get(name)
1490 if typ and not isinstance(ent, typ):
1491 return None
1492 return ent
1494 def lookup_type(self, name):
1495 return self.lookup_entity(name, QAPISchemaType)
1497 def _def_builtin_type(self, name, json_type, c_type):
1498 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1499 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1500 # qapi-types.h from a single .c, all arrays of builtins must be
1501 # declared in the first file whether or not they are used. Nicer
1502 # would be to use lazy instantiation, while figuring out how to
1503 # avoid compilation issues with multiple qapi-types.h.
1504 self._make_array_type(name, None)
1506 def _def_predefineds(self):
1507 for t in [('str', 'string', 'char' + pointer_suffix),
1508 ('number', 'number', 'double'),
1509 ('int', 'int', 'int64_t'),
1510 ('int8', 'int', 'int8_t'),
1511 ('int16', 'int', 'int16_t'),
1512 ('int32', 'int', 'int32_t'),
1513 ('int64', 'int', 'int64_t'),
1514 ('uint8', 'int', 'uint8_t'),
1515 ('uint16', 'int', 'uint16_t'),
1516 ('uint32', 'int', 'uint32_t'),
1517 ('uint64', 'int', 'uint64_t'),
1518 ('size', 'int', 'uint64_t'),
1519 ('bool', 'boolean', 'bool'),
1520 ('any', 'value', 'QObject' + pointer_suffix),
1521 ('null', 'null', 'QNull' + pointer_suffix)]:
1522 self._def_builtin_type(*t)
1523 self.the_empty_object_type = QAPISchemaObjectType(
1524 'q_empty', None, None, None, [], None)
1525 self._def_entity(self.the_empty_object_type)
1526 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
1527 'qstring', 'qdict', 'qlist',
1528 'qbool'])
1529 self._def_entity(QAPISchemaEnumType('QType', None, None,
1530 qtype_values, 'QTYPE'))
1532 def _make_enum_members(self, values):
1533 return [QAPISchemaMember(v) for v in values]
1535 def _make_implicit_enum_type(self, name, info, values):
1536 # See also QAPISchemaObjectTypeMember._pretty_owner()
1537 name = name + 'Kind' # Use namespace reserved by add_name()
1538 self._def_entity(QAPISchemaEnumType(
1539 name, info, None, self._make_enum_members(values), None))
1540 return name
1542 def _make_array_type(self, element_type, info):
1543 name = element_type + 'List' # Use namespace reserved by add_name()
1544 if not self.lookup_type(name):
1545 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1546 return name
1548 def _make_implicit_object_type(self, name, info, doc, role, members):
1549 if not members:
1550 return None
1551 # See also QAPISchemaObjectTypeMember._pretty_owner()
1552 name = 'q_obj_%s-%s' % (name, role)
1553 if not self.lookup_entity(name, QAPISchemaObjectType):
1554 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
1555 members, None))
1556 return name
1558 def _def_enum_type(self, expr, info, doc):
1559 name = expr['enum']
1560 data = expr['data']
1561 prefix = expr.get('prefix')
1562 self._def_entity(QAPISchemaEnumType(
1563 name, info, doc, self._make_enum_members(data), prefix))
1565 def _make_member(self, name, typ, info):
1566 optional = False
1567 if name.startswith('*'):
1568 name = name[1:]
1569 optional = True
1570 if isinstance(typ, list):
1571 assert len(typ) == 1
1572 typ = self._make_array_type(typ[0], info)
1573 return QAPISchemaObjectTypeMember(name, typ, optional)
1575 def _make_members(self, data, info):
1576 return [self._make_member(key, value, info)
1577 for (key, value) in data.iteritems()]
1579 def _def_struct_type(self, expr, info, doc):
1580 name = expr['struct']
1581 base = expr.get('base')
1582 data = expr['data']
1583 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
1584 self._make_members(data, info),
1585 None))
1587 def _make_variant(self, case, typ):
1588 return QAPISchemaObjectTypeVariant(case, typ)
1590 def _make_simple_variant(self, case, typ, info):
1591 if isinstance(typ, list):
1592 assert len(typ) == 1
1593 typ = self._make_array_type(typ[0], info)
1594 typ = self._make_implicit_object_type(
1595 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
1596 return QAPISchemaObjectTypeVariant(case, typ)
1598 def _def_union_type(self, expr, info, doc):
1599 name = expr['union']
1600 data = expr['data']
1601 base = expr.get('base')
1602 tag_name = expr.get('discriminator')
1603 tag_member = None
1604 if isinstance(base, dict):
1605 base = (self._make_implicit_object_type(
1606 name, info, doc, 'base', self._make_members(base, info)))
1607 if tag_name:
1608 variants = [self._make_variant(key, value)
1609 for (key, value) in data.iteritems()]
1610 members = []
1611 else:
1612 variants = [self._make_simple_variant(key, value, info)
1613 for (key, value) in data.iteritems()]
1614 typ = self._make_implicit_enum_type(name, info,
1615 [v.name for v in variants])
1616 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1617 members = [tag_member]
1618 self._def_entity(
1619 QAPISchemaObjectType(name, info, doc, base, members,
1620 QAPISchemaObjectTypeVariants(tag_name,
1621 tag_member,
1622 variants)))
1624 def _def_alternate_type(self, expr, info, doc):
1625 name = expr['alternate']
1626 data = expr['data']
1627 variants = [self._make_variant(key, value)
1628 for (key, value) in data.iteritems()]
1629 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1630 self._def_entity(
1631 QAPISchemaAlternateType(name, info, doc,
1632 QAPISchemaObjectTypeVariants(None,
1633 tag_member,
1634 variants)))
1636 def _def_command(self, expr, info, doc):
1637 name = expr['command']
1638 data = expr.get('data')
1639 rets = expr.get('returns')
1640 gen = expr.get('gen', True)
1641 success_response = expr.get('success-response', True)
1642 boxed = expr.get('boxed', False)
1643 if isinstance(data, OrderedDict):
1644 data = self._make_implicit_object_type(
1645 name, info, doc, 'arg', self._make_members(data, info))
1646 if isinstance(rets, list):
1647 assert len(rets) == 1
1648 rets = self._make_array_type(rets[0], info)
1649 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1650 gen, success_response, boxed))
1652 def _def_event(self, expr, info, doc):
1653 name = expr['event']
1654 data = expr.get('data')
1655 boxed = expr.get('boxed', False)
1656 if isinstance(data, OrderedDict):
1657 data = self._make_implicit_object_type(
1658 name, info, doc, 'arg', self._make_members(data, info))
1659 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
1661 def _def_exprs(self):
1662 for expr_elem in self.exprs:
1663 expr = expr_elem['expr']
1664 info = expr_elem['info']
1665 doc = expr_elem.get('doc')
1666 if 'enum' in expr:
1667 self._def_enum_type(expr, info, doc)
1668 elif 'struct' in expr:
1669 self._def_struct_type(expr, info, doc)
1670 elif 'union' in expr:
1671 self._def_union_type(expr, info, doc)
1672 elif 'alternate' in expr:
1673 self._def_alternate_type(expr, info, doc)
1674 elif 'command' in expr:
1675 self._def_command(expr, info, doc)
1676 elif 'event' in expr:
1677 self._def_event(expr, info, doc)
1678 else:
1679 assert False
1681 def check(self):
1682 for ent in self._entity_dict.values():
1683 ent.check(self)
1685 def visit(self, visitor):
1686 visitor.visit_begin(self)
1687 for (name, entity) in sorted(self._entity_dict.items()):
1688 if visitor.visit_needed(entity):
1689 entity.visit(visitor)
1690 visitor.visit_end()
1694 # Code generation helpers
1697 def camel_case(name):
1698 new_name = ''
1699 first = True
1700 for ch in name:
1701 if ch in ['_', '-']:
1702 first = True
1703 elif first:
1704 new_name += ch.upper()
1705 first = False
1706 else:
1707 new_name += ch.lower()
1708 return new_name
1711 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1712 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1713 # ENUM24_Name -> ENUM24_NAME
1714 def camel_to_upper(value):
1715 c_fun_str = c_name(value, False)
1716 if value.isupper():
1717 return c_fun_str
1719 new_name = ''
1720 l = len(c_fun_str)
1721 for i in range(l):
1722 c = c_fun_str[i]
1723 # When c is upper and no '_' appears before, do more checks
1724 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1725 if i < l - 1 and c_fun_str[i + 1].islower():
1726 new_name += '_'
1727 elif c_fun_str[i - 1].isdigit():
1728 new_name += '_'
1729 new_name += c
1730 return new_name.lstrip('_').upper()
1733 def c_enum_const(type_name, const_name, prefix=None):
1734 if prefix is not None:
1735 type_name = prefix
1736 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1738 c_name_trans = string.maketrans('.-', '__')
1741 # Map @name to a valid C identifier.
1742 # If @protect, avoid returning certain ticklish identifiers (like
1743 # C keywords) by prepending 'q_'.
1745 # Used for converting 'name' from a 'name':'type' qapi definition
1746 # into a generated struct member, as well as converting type names
1747 # into substrings of a generated C function name.
1748 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1749 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1750 def c_name(name, protect=True):
1751 # ANSI X3J11/88-090, 3.1.1
1752 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1753 'default', 'do', 'double', 'else', 'enum', 'extern',
1754 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1755 'return', 'short', 'signed', 'sizeof', 'static',
1756 'struct', 'switch', 'typedef', 'union', 'unsigned',
1757 'void', 'volatile', 'while'])
1758 # ISO/IEC 9899:1999, 6.4.1
1759 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1760 # ISO/IEC 9899:2011, 6.4.1
1761 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1762 '_Noreturn', '_Static_assert', '_Thread_local'])
1763 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1764 # excluding _.*
1765 gcc_words = set(['asm', 'typeof'])
1766 # C++ ISO/IEC 14882:2003 2.11
1767 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1768 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1769 'namespace', 'new', 'operator', 'private', 'protected',
1770 'public', 'reinterpret_cast', 'static_cast', 'template',
1771 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1772 'using', 'virtual', 'wchar_t',
1773 # alternative representations
1774 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1775 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1776 # namespace pollution:
1777 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
1778 name = name.translate(c_name_trans)
1779 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1780 | cpp_words | polluted_words):
1781 return 'q_' + name
1782 return name
1784 eatspace = '\033EATSPACE.'
1785 pointer_suffix = ' *' + eatspace
1788 def genindent(count):
1789 ret = ''
1790 for _ in range(count):
1791 ret += ' '
1792 return ret
1794 indent_level = 0
1797 def push_indent(indent_amount=4):
1798 global indent_level
1799 indent_level += indent_amount
1802 def pop_indent(indent_amount=4):
1803 global indent_level
1804 indent_level -= indent_amount
1807 # Generate @code with @kwds interpolated.
1808 # Obey indent_level, and strip eatspace.
1809 def cgen(code, **kwds):
1810 raw = code % kwds
1811 if indent_level:
1812 indent = genindent(indent_level)
1813 # re.subn() lacks flags support before Python 2.7, use re.compile()
1814 raw = re.subn(re.compile(r'^.', re.MULTILINE),
1815 indent + r'\g<0>', raw)
1816 raw = raw[0]
1817 return re.sub(re.escape(eatspace) + r' *', '', raw)
1820 def mcgen(code, **kwds):
1821 if code[0] == '\n':
1822 code = code[1:]
1823 return cgen(code, **kwds)
1826 def guardname(filename):
1827 return c_name(filename, protect=False).upper()
1830 def guardstart(name):
1831 return mcgen('''
1833 #ifndef %(name)s
1834 #define %(name)s
1836 ''',
1837 name=guardname(name))
1840 def guardend(name):
1841 return mcgen('''
1843 #endif /* %(name)s */
1845 ''',
1846 name=guardname(name))
1849 def gen_enum_lookup(name, values, prefix=None):
1850 ret = mcgen('''
1852 const QEnumLookup %(c_name)s_lookup = {
1853 .array = (const char *const[]) {
1854 ''',
1855 c_name=c_name(name))
1856 for value in values:
1857 index = c_enum_const(name, value, prefix)
1858 ret += mcgen('''
1859 [%(index)s] = "%(value)s",
1860 ''',
1861 index=index, value=value)
1863 ret += mcgen('''
1865 .size = %(max_index)s
1867 ''',
1868 max_index=c_enum_const(name, '_MAX', prefix))
1869 return ret
1872 def gen_enum(name, values, prefix=None):
1873 # append automatically generated _MAX value
1874 enum_values = values + ['_MAX']
1876 ret = mcgen('''
1878 typedef enum %(c_name)s {
1879 ''',
1880 c_name=c_name(name))
1882 i = 0
1883 for value in enum_values:
1884 ret += mcgen('''
1885 %(c_enum)s = %(i)d,
1886 ''',
1887 c_enum=c_enum_const(name, value, prefix),
1888 i=i)
1889 i += 1
1891 ret += mcgen('''
1892 } %(c_name)s;
1893 ''',
1894 c_name=c_name(name))
1896 ret += mcgen('''
1898 #define %(c_name)s_str(val) \\
1899 qapi_enum_lookup(&%(c_name)s_lookup, (val))
1901 extern const QEnumLookup %(c_name)s_lookup;
1902 ''',
1903 c_name=c_name(name))
1904 return ret
1907 def build_params(arg_type, boxed, extra):
1908 if not arg_type:
1909 assert not boxed
1910 return extra
1911 ret = ''
1912 sep = ''
1913 if boxed:
1914 ret += '%s arg' % arg_type.c_param_type()
1915 sep = ', '
1916 else:
1917 assert not arg_type.variants
1918 for memb in arg_type.members:
1919 ret += sep
1920 sep = ', '
1921 if memb.optional:
1922 ret += 'bool has_%s, ' % c_name(memb.name)
1923 ret += '%s %s' % (memb.type.c_param_type(),
1924 c_name(memb.name))
1925 if extra:
1926 ret += sep + extra
1927 return ret
1931 # Common command line parsing
1935 def parse_command_line(extra_options='', extra_long_options=[]):
1937 try:
1938 opts, args = getopt.gnu_getopt(sys.argv[1:],
1939 'chp:o:' + extra_options,
1940 ['source', 'header', 'prefix=',
1941 'output-dir='] + extra_long_options)
1942 except getopt.GetoptError as err:
1943 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
1944 sys.exit(1)
1946 output_dir = ''
1947 prefix = ''
1948 do_c = False
1949 do_h = False
1950 extra_opts = []
1952 for oa in opts:
1953 o, a = oa
1954 if o in ('-p', '--prefix'):
1955 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1956 if match.end() != len(a):
1957 print >>sys.stderr, \
1958 "%s: 'funny character '%s' in argument of --prefix" \
1959 % (sys.argv[0], a[match.end()])
1960 sys.exit(1)
1961 prefix = a
1962 elif o in ('-o', '--output-dir'):
1963 output_dir = a + '/'
1964 elif o in ('-c', '--source'):
1965 do_c = True
1966 elif o in ('-h', '--header'):
1967 do_h = True
1968 else:
1969 extra_opts.append(oa)
1971 if not do_c and not do_h:
1972 do_c = True
1973 do_h = True
1975 if len(args) != 1:
1976 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
1977 sys.exit(1)
1978 fname = args[0]
1980 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
1983 # Generate output files with boilerplate
1987 def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1988 c_comment, h_comment):
1989 guard = guardname(prefix + h_file)
1990 c_file = output_dir + prefix + c_file
1991 h_file = output_dir + prefix + h_file
1993 if output_dir:
1994 try:
1995 os.makedirs(output_dir)
1996 except os.error as e:
1997 if e.errno != errno.EEXIST:
1998 raise
2000 def maybe_open(really, name, opt):
2001 if really:
2002 return open(name, opt)
2003 else:
2004 import StringIO
2005 return StringIO.StringIO()
2007 fdef = maybe_open(do_c, c_file, 'w')
2008 fdecl = maybe_open(do_h, h_file, 'w')
2010 fdef.write(mcgen('''
2011 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2012 %(comment)s
2013 ''',
2014 comment=c_comment))
2016 fdecl.write(mcgen('''
2017 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2018 %(comment)s
2019 #ifndef %(guard)s
2020 #define %(guard)s
2022 ''',
2023 comment=h_comment, guard=guard))
2025 return (fdef, fdecl)
2028 def close_output(fdef, fdecl):
2029 fdecl.write('''
2030 #endif
2031 ''')
2032 fdecl.close()
2033 fdef.close()