target/s390x: Fix typo
[qemu/ar7.git] / scripts / qapi.py
blob53a44779d0b513c011916d044ba76fb88fe481ac
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 re
15 from ordereddict import OrderedDict
16 import errno
17 import getopt
18 import os
19 import sys
20 import string
22 builtin_types = {
23 'str': 'QTYPE_QSTRING',
24 'int': 'QTYPE_QINT',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
27 'int8': 'QTYPE_QINT',
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',
35 'size': 'QTYPE_QINT',
36 'any': None, # any QType possible, actually
37 'QType': 'QTYPE_QSTRING',
40 # Whitelist of commands allowed to return a non-dictionary
41 returns_whitelist = [
42 # From QMP:
43 'human-monitor-command',
44 'qom-get',
45 'query-migrate-cache-size',
46 'query-tpm-models',
47 'query-tpm-types',
48 'ringbuf-read',
50 # From QGA:
51 'guest-file-open',
52 'guest-fsfreeze-freeze',
53 'guest-fsfreeze-freeze-list',
54 'guest-fsfreeze-status',
55 'guest-fsfreeze-thaw',
56 'guest-get-time',
57 'guest-set-vcpus',
58 'guest-sync',
59 'guest-sync-delimited',
62 # Whitelist of entities allowed to violate case conventions
63 case_whitelist = [
64 # From QMP:
65 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
66 'CpuInfoMIPS', # PC, visible through query-cpu
67 'CpuInfoTricore', # PC, visible through query-cpu
68 'QapiErrorClass', # all members, visible through errors
69 'UuidInfo', # UUID, visible through query-uuid
70 'X86CPURegister32', # all members, visible indirectly through qom-get
71 'q_obj_CpuInfo-base', # CPU, visible through query-cpu
74 enum_types = []
75 struct_types = []
76 union_types = []
77 events = []
78 all_names = {}
81 # Parsing the schema into expressions
85 def error_path(parent):
86 res = ""
87 while parent:
88 res = ("In file included from %s:%d:\n" % (parent['file'],
89 parent['line'])) + res
90 parent = parent['parent']
91 return res
94 class QAPIError(Exception):
95 def __init__(self, fname, line, col, incl_info, msg):
96 Exception.__init__(self)
97 self.fname = fname
98 self.line = line
99 self.col = col
100 self.info = incl_info
101 self.msg = msg
103 def __str__(self):
104 loc = "%s:%d" % (self.fname, self.line)
105 if self.col is not None:
106 loc += ":%s" % self.col
107 return error_path(self.info) + "%s: %s" % (loc, self.msg)
110 class QAPIParseError(QAPIError):
111 def __init__(self, parser, msg):
112 col = 1
113 for ch in parser.src[parser.line_pos:parser.pos]:
114 if ch == '\t':
115 col = (col + 7) % 8 + 1
116 else:
117 col += 1
118 QAPIError.__init__(self, parser.fname, parser.line, col,
119 parser.incl_info, msg)
122 class QAPISemError(QAPIError):
123 def __init__(self, info, msg):
124 QAPIError.__init__(self, info['file'], info['line'], None,
125 info['parent'], msg)
128 class QAPIDoc(object):
129 class Section(object):
130 def __init__(self, name=None):
131 # optional section name (argument/member or section name)
132 self.name = name
133 # the list of lines for this section
134 self.content = []
136 def append(self, line):
137 self.content.append(line)
139 def __repr__(self):
140 return "\n".join(self.content).strip()
142 class ArgSection(Section):
143 pass
145 def __init__(self, parser, info):
146 # self.parser is used to report errors with QAPIParseError. The
147 # resulting error position depends on the state of the parser.
148 # It happens to be the beginning of the comment. More or less
149 # servicable, but action at a distance.
150 self.parser = parser
151 self.info = info
152 self.symbol = None
153 self.body = QAPIDoc.Section()
154 # dict mapping parameter name to ArgSection
155 self.args = OrderedDict()
156 # a list of Section
157 self.sections = []
158 # the current section
159 self.section = self.body
160 # associated expression (to be set by expression parser)
161 self.expr = None
163 def has_section(self, name):
164 """Return True if we have a section with this name."""
165 for i in self.sections:
166 if i.name == name:
167 return True
168 return False
170 def append(self, line):
171 """Parse a comment line and add it to the documentation."""
172 line = line[1:]
173 if not line:
174 self._append_freeform(line)
175 return
177 if line[0] != ' ':
178 raise QAPIParseError(self.parser, "Missing space after #")
179 line = line[1:]
181 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
182 # recognized, and get silently treated as ordinary text
183 if self.symbol:
184 self._append_symbol_line(line)
185 elif not self.body.content and line.startswith("@"):
186 if not line.endswith(":"):
187 raise QAPIParseError(self.parser, "Line should end with :")
188 self.symbol = line[1:-1]
189 # FIXME invalid names other than the empty string aren't flagged
190 if not self.symbol:
191 raise QAPIParseError(self.parser, "Invalid name")
192 else:
193 self._append_freeform(line)
195 def _append_symbol_line(self, line):
196 name = line.split(' ', 1)[0]
198 if name.startswith("@") and name.endswith(":"):
199 line = line[len(name)+1:]
200 self._start_args_section(name[1:-1])
201 elif name in ("Returns:", "Since:",
202 # those are often singular or plural
203 "Note:", "Notes:",
204 "Example:", "Examples:",
205 "TODO:"):
206 line = line[len(name)+1:]
207 self._start_section(name[:-1])
209 self._append_freeform(line)
211 def _start_args_section(self, name):
212 # FIXME invalid names other than the empty string aren't flagged
213 if not name:
214 raise QAPIParseError(self.parser, "Invalid parameter name")
215 if name in self.args:
216 raise QAPIParseError(self.parser,
217 "'%s' parameter name duplicated" % name)
218 if self.sections:
219 raise QAPIParseError(self.parser,
220 "'@%s:' can't follow '%s' section"
221 % (name, self.sections[0].name))
222 self.section = QAPIDoc.ArgSection(name)
223 self.args[name] = self.section
225 def _start_section(self, name=""):
226 if name in ("Returns", "Since") and self.has_section(name):
227 raise QAPIParseError(self.parser,
228 "Duplicated '%s' section" % name)
229 self.section = QAPIDoc.Section(name)
230 self.sections.append(self.section)
232 def _append_freeform(self, line):
233 in_arg = isinstance(self.section, QAPIDoc.ArgSection)
234 if (in_arg and self.section.content
235 and not self.section.content[-1]
236 and line and not line[0].isspace()):
237 self._start_section()
238 if (in_arg or not self.section.name
239 or not self.section.name.startswith("Example")):
240 line = line.strip()
241 self.section.append(line)
244 class QAPISchemaParser(object):
246 def __init__(self, fp, previously_included=[], incl_info=None):
247 abs_fname = os.path.abspath(fp.name)
248 fname = fp.name
249 self.fname = fname
250 previously_included.append(abs_fname)
251 self.incl_info = incl_info
252 self.src = fp.read()
253 if self.src == '' or self.src[-1] != '\n':
254 self.src += '\n'
255 self.cursor = 0
256 self.line = 1
257 self.line_pos = 0
258 self.exprs = []
259 self.docs = []
260 self.accept()
262 while self.tok is not None:
263 info = {'file': fname, 'line': self.line,
264 'parent': self.incl_info}
265 if self.tok == '#':
266 doc = self.get_doc(info)
267 self.docs.append(doc)
268 continue
270 expr = self.get_expr(False)
271 if isinstance(expr, dict) and "include" in expr:
272 if len(expr) != 1:
273 raise QAPISemError(info, "Invalid 'include' directive")
274 include = expr["include"]
275 if not isinstance(include, str):
276 raise QAPISemError(info,
277 "Value of 'include' must be a string")
278 incl_abs_fname = os.path.join(os.path.dirname(abs_fname),
279 include)
280 # catch inclusion cycle
281 inf = info
282 while inf:
283 if incl_abs_fname == os.path.abspath(inf['file']):
284 raise QAPISemError(info, "Inclusion loop for %s"
285 % include)
286 inf = inf['parent']
288 # skip multiple include of the same file
289 if incl_abs_fname in previously_included:
290 continue
291 try:
292 fobj = open(incl_abs_fname, 'r')
293 except IOError as e:
294 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
295 exprs_include = QAPISchemaParser(fobj, previously_included,
296 info)
297 self.exprs.extend(exprs_include.exprs)
298 self.docs.extend(exprs_include.docs)
299 else:
300 expr_elem = {'expr': expr,
301 'info': info}
302 if (self.docs
303 and self.docs[-1].info['file'] == fname
304 and not self.docs[-1].expr):
305 self.docs[-1].expr = expr
306 expr_elem['doc'] = self.docs[-1]
308 self.exprs.append(expr_elem)
310 def accept(self, skip_comment=True):
311 while True:
312 self.tok = self.src[self.cursor]
313 self.pos = self.cursor
314 self.cursor += 1
315 self.val = None
317 if self.tok == '#':
318 if self.src[self.cursor] == '#':
319 # Start of doc comment
320 skip_comment = False
321 self.cursor = self.src.find('\n', self.cursor)
322 if not skip_comment:
323 self.val = self.src[self.pos:self.cursor]
324 return
325 elif self.tok in "{}:,[]":
326 return
327 elif self.tok == "'":
328 string = ''
329 esc = False
330 while True:
331 ch = self.src[self.cursor]
332 self.cursor += 1
333 if ch == '\n':
334 raise QAPIParseError(self, 'Missing terminating "\'"')
335 if esc:
336 if ch == 'b':
337 string += '\b'
338 elif ch == 'f':
339 string += '\f'
340 elif ch == 'n':
341 string += '\n'
342 elif ch == 'r':
343 string += '\r'
344 elif ch == 't':
345 string += '\t'
346 elif ch == 'u':
347 value = 0
348 for _ in range(0, 4):
349 ch = self.src[self.cursor]
350 self.cursor += 1
351 if ch not in "0123456789abcdefABCDEF":
352 raise QAPIParseError(self,
353 '\\u escape needs 4 '
354 'hex digits')
355 value = (value << 4) + int(ch, 16)
356 # If Python 2 and 3 didn't disagree so much on
357 # how to handle Unicode, then we could allow
358 # Unicode string defaults. But most of QAPI is
359 # ASCII-only, so we aren't losing much for now.
360 if not value or value > 0x7f:
361 raise QAPIParseError(self,
362 'For now, \\u escape '
363 'only supports non-zero '
364 'values up to \\u007f')
365 string += chr(value)
366 elif ch in "\\/'\"":
367 string += ch
368 else:
369 raise QAPIParseError(self,
370 "Unknown escape \\%s" % ch)
371 esc = False
372 elif ch == "\\":
373 esc = True
374 elif ch == "'":
375 self.val = string
376 return
377 else:
378 string += ch
379 elif self.src.startswith("true", self.pos):
380 self.val = True
381 self.cursor += 3
382 return
383 elif self.src.startswith("false", self.pos):
384 self.val = False
385 self.cursor += 4
386 return
387 elif self.src.startswith("null", self.pos):
388 self.val = None
389 self.cursor += 3
390 return
391 elif self.tok == '\n':
392 if self.cursor == len(self.src):
393 self.tok = None
394 return
395 self.line += 1
396 self.line_pos = self.cursor
397 elif not self.tok.isspace():
398 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
400 def get_members(self):
401 expr = OrderedDict()
402 if self.tok == '}':
403 self.accept()
404 return expr
405 if self.tok != "'":
406 raise QAPIParseError(self, 'Expected string or "}"')
407 while True:
408 key = self.val
409 self.accept()
410 if self.tok != ':':
411 raise QAPIParseError(self, 'Expected ":"')
412 self.accept()
413 if key in expr:
414 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
415 expr[key] = self.get_expr(True)
416 if self.tok == '}':
417 self.accept()
418 return expr
419 if self.tok != ',':
420 raise QAPIParseError(self, 'Expected "," or "}"')
421 self.accept()
422 if self.tok != "'":
423 raise QAPIParseError(self, 'Expected string')
425 def get_values(self):
426 expr = []
427 if self.tok == ']':
428 self.accept()
429 return expr
430 if self.tok not in "{['tfn":
431 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
432 'boolean or "null"')
433 while True:
434 expr.append(self.get_expr(True))
435 if self.tok == ']':
436 self.accept()
437 return expr
438 if self.tok != ',':
439 raise QAPIParseError(self, 'Expected "," or "]"')
440 self.accept()
442 def get_expr(self, nested):
443 if self.tok != '{' and not nested:
444 raise QAPIParseError(self, 'Expected "{"')
445 if self.tok == '{':
446 self.accept()
447 expr = self.get_members()
448 elif self.tok == '[':
449 self.accept()
450 expr = self.get_values()
451 elif self.tok in "'tfn":
452 expr = self.val
453 self.accept()
454 else:
455 raise QAPIParseError(self, 'Expected "{", "[" or string')
456 return expr
458 def get_doc(self, info):
459 if self.val != '##':
460 raise QAPIParseError(self, "Junk after '##' at start of "
461 "documentation comment")
463 doc = QAPIDoc(self, info)
464 self.accept(False)
465 while self.tok == '#':
466 if self.val.startswith('##'):
467 # End of doc comment
468 if self.val != '##':
469 raise QAPIParseError(self, "Junk after '##' at end of "
470 "documentation comment")
471 self.accept()
472 return doc
473 else:
474 doc.append(self.val)
475 self.accept(False)
477 raise QAPIParseError(self, "Documentation comment must end with '##'")
481 # Semantic analysis of schema expressions
482 # TODO fold into QAPISchema
483 # TODO catching name collisions in generated code would be nice
487 def find_base_members(base):
488 if isinstance(base, dict):
489 return base
490 base_struct_define = find_struct(base)
491 if not base_struct_define:
492 return None
493 return base_struct_define['data']
496 # Return the qtype of an alternate branch, or None on error.
497 def find_alternate_member_qtype(qapi_type):
498 if qapi_type in builtin_types:
499 return builtin_types[qapi_type]
500 elif find_struct(qapi_type):
501 return "QTYPE_QDICT"
502 elif find_enum(qapi_type):
503 return "QTYPE_QSTRING"
504 elif find_union(qapi_type):
505 return "QTYPE_QDICT"
506 return None
509 # Return the discriminator enum define if discriminator is specified as an
510 # enum type, otherwise return None.
511 def discriminator_find_enum_define(expr):
512 base = expr.get('base')
513 discriminator = expr.get('discriminator')
515 if not (discriminator and base):
516 return None
518 base_members = find_base_members(base)
519 if not base_members:
520 return None
522 discriminator_type = base_members.get(discriminator)
523 if not discriminator_type:
524 return None
526 return find_enum(discriminator_type)
529 # Names must be letters, numbers, -, and _. They must start with letter,
530 # except for downstream extensions which must start with __RFQDN_.
531 # Dots are only valid in the downstream extension prefix.
532 valid_name = re.compile('^(__[a-zA-Z0-9.-]+_)?'
533 '[a-zA-Z][a-zA-Z0-9_-]*$')
536 def check_name(info, source, name, allow_optional=False,
537 enum_member=False):
538 global valid_name
539 membername = name
541 if not isinstance(name, str):
542 raise QAPISemError(info, "%s requires a string name" % source)
543 if name.startswith('*'):
544 membername = name[1:]
545 if not allow_optional:
546 raise QAPISemError(info, "%s does not allow optional name '%s'"
547 % (source, name))
548 # Enum members can start with a digit, because the generated C
549 # code always prefixes it with the enum name
550 if enum_member and membername[0].isdigit():
551 membername = 'D' + membername
552 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
553 # and 'q_obj_*' implicit type names.
554 if not valid_name.match(membername) or \
555 c_name(membername, False).startswith('q_'):
556 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
559 def add_name(name, info, meta, implicit=False):
560 global all_names
561 check_name(info, "'%s'" % meta, name)
562 # FIXME should reject names that differ only in '_' vs. '.'
563 # vs. '-', because they're liable to clash in generated C.
564 if name in all_names:
565 raise QAPISemError(info, "%s '%s' is already defined"
566 % (all_names[name], name))
567 if not implicit and (name.endswith('Kind') or name.endswith('List')):
568 raise QAPISemError(info, "%s '%s' should not end in '%s'"
569 % (meta, name, name[-4:]))
570 all_names[name] = meta
573 def add_struct(definition, info):
574 global struct_types
575 name = definition['struct']
576 add_name(name, info, 'struct')
577 struct_types.append(definition)
580 def find_struct(name):
581 global struct_types
582 for struct in struct_types:
583 if struct['struct'] == name:
584 return struct
585 return None
588 def add_union(definition, info):
589 global union_types
590 name = definition['union']
591 add_name(name, info, 'union')
592 union_types.append(definition)
595 def find_union(name):
596 global union_types
597 for union in union_types:
598 if union['union'] == name:
599 return union
600 return None
603 def add_enum(name, info, enum_values=None, implicit=False):
604 global enum_types
605 add_name(name, info, 'enum', implicit)
606 enum_types.append({"enum_name": name, "enum_values": enum_values})
609 def find_enum(name):
610 global enum_types
611 for enum in enum_types:
612 if enum['enum_name'] == name:
613 return enum
614 return None
617 def is_enum(name):
618 return find_enum(name) is not None
621 def check_type(info, source, value, allow_array=False,
622 allow_dict=False, allow_optional=False,
623 allow_metas=[]):
624 global all_names
626 if value is None:
627 return
629 # Check if array type for value is okay
630 if isinstance(value, list):
631 if not allow_array:
632 raise QAPISemError(info, "%s cannot be an array" % source)
633 if len(value) != 1 or not isinstance(value[0], str):
634 raise QAPISemError(info,
635 "%s: array type must contain single type name" %
636 source)
637 value = value[0]
639 # Check if type name for value is okay
640 if isinstance(value, str):
641 if value not in all_names:
642 raise QAPISemError(info, "%s uses unknown type '%s'"
643 % (source, value))
644 if not all_names[value] in allow_metas:
645 raise QAPISemError(info, "%s cannot use %s type '%s'" %
646 (source, all_names[value], value))
647 return
649 if not allow_dict:
650 raise QAPISemError(info, "%s should be a type name" % source)
652 if not isinstance(value, OrderedDict):
653 raise QAPISemError(info,
654 "%s should be a dictionary or type name" % source)
656 # value is a dictionary, check that each member is okay
657 for (key, arg) in value.items():
658 check_name(info, "Member of %s" % source, key,
659 allow_optional=allow_optional)
660 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
661 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
662 % (source, key))
663 # Todo: allow dictionaries to represent default values of
664 # an optional argument.
665 check_type(info, "Member '%s' of %s" % (key, source), arg,
666 allow_array=True,
667 allow_metas=['built-in', 'union', 'alternate', 'struct',
668 'enum'])
671 def check_command(expr, info):
672 name = expr['command']
673 boxed = expr.get('boxed', False)
675 args_meta = ['struct']
676 if boxed:
677 args_meta += ['union', 'alternate']
678 check_type(info, "'data' for command '%s'" % name,
679 expr.get('data'), allow_dict=not boxed, allow_optional=True,
680 allow_metas=args_meta)
681 returns_meta = ['union', 'struct']
682 if name in returns_whitelist:
683 returns_meta += ['built-in', 'alternate', 'enum']
684 check_type(info, "'returns' for command '%s'" % name,
685 expr.get('returns'), allow_array=True,
686 allow_optional=True, allow_metas=returns_meta)
689 def check_event(expr, info):
690 global events
691 name = expr['event']
692 boxed = expr.get('boxed', False)
694 meta = ['struct']
695 if boxed:
696 meta += ['union', 'alternate']
697 events.append(name)
698 check_type(info, "'data' for event '%s'" % name,
699 expr.get('data'), allow_dict=not boxed, allow_optional=True,
700 allow_metas=meta)
703 def check_union(expr, info):
704 name = expr['union']
705 base = expr.get('base')
706 discriminator = expr.get('discriminator')
707 members = expr['data']
709 # Two types of unions, determined by discriminator.
711 # With no discriminator it is a simple union.
712 if discriminator is None:
713 enum_define = None
714 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
715 if base is not None:
716 raise QAPISemError(info, "Simple union '%s' must not have a base" %
717 name)
719 # Else, it's a flat union.
720 else:
721 # The object must have a string or dictionary 'base'.
722 check_type(info, "'base' for union '%s'" % name,
723 base, allow_dict=True, allow_optional=True,
724 allow_metas=['struct'])
725 if not base:
726 raise QAPISemError(info, "Flat union '%s' must have a base"
727 % name)
728 base_members = find_base_members(base)
729 assert base_members
731 # The value of member 'discriminator' must name a non-optional
732 # member of the base struct.
733 check_name(info, "Discriminator of flat union '%s'" % name,
734 discriminator)
735 discriminator_type = base_members.get(discriminator)
736 if not discriminator_type:
737 raise QAPISemError(info,
738 "Discriminator '%s' is not a member of base "
739 "struct '%s'"
740 % (discriminator, base))
741 enum_define = find_enum(discriminator_type)
742 allow_metas = ['struct']
743 # Do not allow string discriminator
744 if not enum_define:
745 raise QAPISemError(info,
746 "Discriminator '%s' must be of enumeration "
747 "type" % discriminator)
749 # Check every branch; don't allow an empty union
750 if len(members) == 0:
751 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
752 for (key, value) in members.items():
753 check_name(info, "Member of union '%s'" % name, key)
755 # Each value must name a known type
756 check_type(info, "Member '%s' of union '%s'" % (key, name),
757 value, allow_array=not base, allow_metas=allow_metas)
759 # If the discriminator names an enum type, then all members
760 # of 'data' must also be members of the enum type.
761 if enum_define:
762 if key not in enum_define['enum_values']:
763 raise QAPISemError(info,
764 "Discriminator value '%s' is not found in "
765 "enum '%s'"
766 % (key, enum_define["enum_name"]))
768 # If discriminator is user-defined, ensure all values are covered
769 if enum_define:
770 for value in enum_define['enum_values']:
771 if value not in members.keys():
772 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
773 % (name, value))
776 def check_alternate(expr, info):
777 name = expr['alternate']
778 members = expr['data']
779 types_seen = {}
781 # Check every branch; require at least two branches
782 if len(members) < 2:
783 raise QAPISemError(info,
784 "Alternate '%s' should have at least two branches "
785 "in 'data'" % name)
786 for (key, value) in members.items():
787 check_name(info, "Member of alternate '%s'" % name, key)
789 # Ensure alternates have no type conflicts.
790 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
791 value,
792 allow_metas=['built-in', 'union', 'struct', 'enum'])
793 qtype = find_alternate_member_qtype(value)
794 if not qtype:
795 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
796 "type '%s'" % (name, key, value))
797 if qtype in types_seen:
798 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
799 "be distinguished from member '%s'"
800 % (name, key, types_seen[qtype]))
801 types_seen[qtype] = key
804 def check_enum(expr, info):
805 name = expr['enum']
806 members = expr.get('data')
807 prefix = expr.get('prefix')
809 if not isinstance(members, list):
810 raise QAPISemError(info,
811 "Enum '%s' requires an array for 'data'" % name)
812 if prefix is not None and not isinstance(prefix, str):
813 raise QAPISemError(info,
814 "Enum '%s' requires a string for 'prefix'" % name)
815 for member in members:
816 check_name(info, "Member of enum '%s'" % name, member,
817 enum_member=True)
820 def check_struct(expr, info):
821 name = expr['struct']
822 members = expr['data']
824 check_type(info, "'data' for struct '%s'" % name, members,
825 allow_dict=True, allow_optional=True)
826 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
827 allow_metas=['struct'])
830 def check_keys(expr_elem, meta, required, optional=[]):
831 expr = expr_elem['expr']
832 info = expr_elem['info']
833 name = expr[meta]
834 if not isinstance(name, str):
835 raise QAPISemError(info, "'%s' key must have a string value" % meta)
836 required = required + [meta]
837 for (key, value) in expr.items():
838 if key not in required and key not in optional:
839 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
840 % (key, meta, name))
841 if (key == 'gen' or key == 'success-response') and value is not False:
842 raise QAPISemError(info,
843 "'%s' of %s '%s' should only use false value"
844 % (key, meta, name))
845 if key == 'boxed' and value is not True:
846 raise QAPISemError(info,
847 "'%s' of %s '%s' should only use true value"
848 % (key, meta, name))
849 for key in required:
850 if key not in expr:
851 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
852 % (key, meta, name))
855 def check_exprs(exprs):
856 global all_names
858 # Learn the types and check for valid expression keys
859 for builtin in builtin_types.keys():
860 all_names[builtin] = 'built-in'
861 for expr_elem in exprs:
862 expr = expr_elem['expr']
863 info = expr_elem['info']
865 if 'doc' not in expr_elem:
866 raise QAPISemError(info,
867 "Expression missing documentation comment")
869 if 'enum' in expr:
870 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
871 add_enum(expr['enum'], info, expr['data'])
872 elif 'union' in expr:
873 check_keys(expr_elem, 'union', ['data'],
874 ['base', 'discriminator'])
875 add_union(expr, info)
876 elif 'alternate' in expr:
877 check_keys(expr_elem, 'alternate', ['data'])
878 add_name(expr['alternate'], info, 'alternate')
879 elif 'struct' in expr:
880 check_keys(expr_elem, 'struct', ['data'], ['base'])
881 add_struct(expr, info)
882 elif 'command' in expr:
883 check_keys(expr_elem, 'command', [],
884 ['data', 'returns', 'gen', 'success-response', 'boxed'])
885 add_name(expr['command'], info, 'command')
886 elif 'event' in expr:
887 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
888 add_name(expr['event'], info, 'event')
889 else:
890 raise QAPISemError(expr_elem['info'],
891 "Expression is missing metatype")
893 # Try again for hidden UnionKind enum
894 for expr_elem in exprs:
895 expr = expr_elem['expr']
896 if 'union' in expr:
897 if not discriminator_find_enum_define(expr):
898 add_enum('%sKind' % expr['union'], expr_elem['info'],
899 implicit=True)
900 elif 'alternate' in expr:
901 add_enum('%sKind' % expr['alternate'], expr_elem['info'],
902 implicit=True)
904 # Validate that exprs make sense
905 for expr_elem in exprs:
906 expr = expr_elem['expr']
907 info = expr_elem['info']
909 if 'enum' in expr:
910 check_enum(expr, info)
911 elif 'union' in expr:
912 check_union(expr, info)
913 elif 'alternate' in expr:
914 check_alternate(expr, info)
915 elif 'struct' in expr:
916 check_struct(expr, info)
917 elif 'command' in expr:
918 check_command(expr, info)
919 elif 'event' in expr:
920 check_event(expr, info)
921 else:
922 assert False, 'unexpected meta type'
924 return exprs
927 def check_freeform_doc(doc):
928 if doc.symbol:
929 raise QAPISemError(doc.info,
930 "Documention for '%s' is not followed"
931 " by the definition" % doc.symbol)
933 body = str(doc.body)
934 if re.search(r'@\S+:', body, re.MULTILINE):
935 raise QAPISemError(doc.info,
936 "Free-form documentation block must not contain"
937 " @NAME: sections")
940 def check_definition_doc(doc, expr, info):
941 for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
942 if i in expr:
943 meta = i
944 break
946 name = expr[meta]
947 if doc.symbol != name:
948 raise QAPISemError(info, "Definition of '%s' follows documentation"
949 " for '%s'" % (name, doc.symbol))
950 if doc.has_section('Returns') and 'command' not in expr:
951 raise QAPISemError(info, "'Returns:' is only valid for commands")
953 if meta == 'union':
954 args = expr.get('base', [])
955 else:
956 args = expr.get('data', [])
957 if isinstance(args, str):
958 return
959 if isinstance(args, dict):
960 args = args.keys()
961 assert isinstance(args, list)
963 if (meta == 'alternate'
964 or (meta == 'union' and not expr.get('discriminator'))):
965 args.append('type')
967 for arg in args:
968 if arg[0] == '*':
969 opt = True
970 desc = doc.args.get(arg[1:])
971 else:
972 opt = False
973 desc = doc.args.get(arg)
974 if not desc:
975 continue
976 desc_opt = "#optional" in str(desc)
977 if desc_opt and not opt:
978 raise QAPISemError(info, "Description has #optional, "
979 "but the declaration doesn't")
980 if not desc_opt and opt:
981 # silently fix the doc
982 # TODO either fix the schema and make this an error,
983 # or drop #optional entirely
984 desc.append("#optional")
986 doc_args = set(doc.args.keys())
987 args = set([name.strip('*') for name in args])
988 if not doc_args.issubset(args):
989 raise QAPISemError(info, "The following documented members are not in "
990 "the declaration: %s" % ", ".join(doc_args - args))
993 def check_docs(docs):
994 for doc in docs:
995 for section in doc.args.values() + doc.sections:
996 content = str(section)
997 if not content or content.isspace():
998 raise QAPISemError(doc.info,
999 "Empty doc section '%s'" % section.name)
1001 if not doc.expr:
1002 check_freeform_doc(doc)
1003 else:
1004 check_definition_doc(doc, doc.expr, doc.info)
1006 return docs
1010 # Schema compiler frontend
1013 class QAPISchemaEntity(object):
1014 def __init__(self, name, info):
1015 assert isinstance(name, str)
1016 self.name = name
1017 # For explicitly defined entities, info points to the (explicit)
1018 # definition. For builtins (and their arrays), info is None.
1019 # For implicitly defined entities, info points to a place that
1020 # triggered the implicit definition (there may be more than one
1021 # such place).
1022 self.info = info
1024 def c_name(self):
1025 return c_name(self.name)
1027 def check(self, schema):
1028 pass
1030 def is_implicit(self):
1031 return not self.info
1033 def visit(self, visitor):
1034 pass
1037 class QAPISchemaVisitor(object):
1038 def visit_begin(self, schema):
1039 pass
1041 def visit_end(self):
1042 pass
1044 def visit_needed(self, entity):
1045 # Default to visiting everything
1046 return True
1048 def visit_builtin_type(self, name, info, json_type):
1049 pass
1051 def visit_enum_type(self, name, info, values, prefix):
1052 pass
1054 def visit_array_type(self, name, info, element_type):
1055 pass
1057 def visit_object_type(self, name, info, base, members, variants):
1058 pass
1060 def visit_object_type_flat(self, name, info, members, variants):
1061 pass
1063 def visit_alternate_type(self, name, info, variants):
1064 pass
1066 def visit_command(self, name, info, arg_type, ret_type,
1067 gen, success_response, boxed):
1068 pass
1070 def visit_event(self, name, info, arg_type, boxed):
1071 pass
1074 class QAPISchemaType(QAPISchemaEntity):
1075 # Return the C type for common use.
1076 # For the types we commonly box, this is a pointer type.
1077 def c_type(self):
1078 pass
1080 # Return the C type to be used in a parameter list.
1081 def c_param_type(self):
1082 return self.c_type()
1084 # Return the C type to be used where we suppress boxing.
1085 def c_unboxed_type(self):
1086 return self.c_type()
1088 def json_type(self):
1089 pass
1091 def alternate_qtype(self):
1092 json2qtype = {
1093 'string': 'QTYPE_QSTRING',
1094 'number': 'QTYPE_QFLOAT',
1095 'int': 'QTYPE_QINT',
1096 'boolean': 'QTYPE_QBOOL',
1097 'object': 'QTYPE_QDICT'
1099 return json2qtype.get(self.json_type())
1102 class QAPISchemaBuiltinType(QAPISchemaType):
1103 def __init__(self, name, json_type, c_type):
1104 QAPISchemaType.__init__(self, name, None)
1105 assert not c_type or isinstance(c_type, str)
1106 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1107 'value')
1108 self._json_type_name = json_type
1109 self._c_type_name = c_type
1111 def c_name(self):
1112 return self.name
1114 def c_type(self):
1115 return self._c_type_name
1117 def c_param_type(self):
1118 if self.name == 'str':
1119 return 'const ' + self._c_type_name
1120 return self._c_type_name
1122 def json_type(self):
1123 return self._json_type_name
1125 def visit(self, visitor):
1126 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1129 class QAPISchemaEnumType(QAPISchemaType):
1130 def __init__(self, name, info, values, prefix):
1131 QAPISchemaType.__init__(self, name, info)
1132 for v in values:
1133 assert isinstance(v, QAPISchemaMember)
1134 v.set_owner(name)
1135 assert prefix is None or isinstance(prefix, str)
1136 self.values = values
1137 self.prefix = prefix
1139 def check(self, schema):
1140 seen = {}
1141 for v in self.values:
1142 v.check_clash(self.info, seen)
1144 def is_implicit(self):
1145 # See QAPISchema._make_implicit_enum_type()
1146 return self.name.endswith('Kind')
1148 def c_type(self):
1149 return c_name(self.name)
1151 def member_names(self):
1152 return [v.name for v in self.values]
1154 def json_type(self):
1155 return 'string'
1157 def visit(self, visitor):
1158 visitor.visit_enum_type(self.name, self.info,
1159 self.member_names(), self.prefix)
1162 class QAPISchemaArrayType(QAPISchemaType):
1163 def __init__(self, name, info, element_type):
1164 QAPISchemaType.__init__(self, name, info)
1165 assert isinstance(element_type, str)
1166 self._element_type_name = element_type
1167 self.element_type = None
1169 def check(self, schema):
1170 self.element_type = schema.lookup_type(self._element_type_name)
1171 assert self.element_type
1173 def is_implicit(self):
1174 return True
1176 def c_type(self):
1177 return c_name(self.name) + pointer_suffix
1179 def json_type(self):
1180 return 'array'
1182 def visit(self, visitor):
1183 visitor.visit_array_type(self.name, self.info, self.element_type)
1186 class QAPISchemaObjectType(QAPISchemaType):
1187 def __init__(self, name, info, base, local_members, variants):
1188 # struct has local_members, optional base, and no variants
1189 # flat union has base, variants, and no local_members
1190 # simple union has local_members, variants, and no base
1191 QAPISchemaType.__init__(self, name, info)
1192 assert base is None or isinstance(base, str)
1193 for m in local_members:
1194 assert isinstance(m, QAPISchemaObjectTypeMember)
1195 m.set_owner(name)
1196 if variants is not None:
1197 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1198 variants.set_owner(name)
1199 self._base_name = base
1200 self.base = None
1201 self.local_members = local_members
1202 self.variants = variants
1203 self.members = None
1205 def check(self, schema):
1206 if self.members is False: # check for cycles
1207 raise QAPISemError(self.info,
1208 "Object %s contains itself" % self.name)
1209 if self.members:
1210 return
1211 self.members = False # mark as being checked
1212 seen = OrderedDict()
1213 if self._base_name:
1214 self.base = schema.lookup_type(self._base_name)
1215 assert isinstance(self.base, QAPISchemaObjectType)
1216 self.base.check(schema)
1217 self.base.check_clash(schema, self.info, seen)
1218 for m in self.local_members:
1219 m.check(schema)
1220 m.check_clash(self.info, seen)
1221 self.members = seen.values()
1222 if self.variants:
1223 self.variants.check(schema, seen)
1224 assert self.variants.tag_member in self.members
1225 self.variants.check_clash(schema, self.info, seen)
1227 # Check that the members of this type do not cause duplicate JSON members,
1228 # and update seen to track the members seen so far. Report any errors
1229 # on behalf of info, which is not necessarily self.info
1230 def check_clash(self, schema, info, seen):
1231 assert not self.variants # not implemented
1232 for m in self.members:
1233 m.check_clash(info, seen)
1235 def is_implicit(self):
1236 # See QAPISchema._make_implicit_object_type(), as well as
1237 # _def_predefineds()
1238 return self.name.startswith('q_')
1240 def is_empty(self):
1241 assert self.members is not None
1242 return not self.members and not self.variants
1244 def c_name(self):
1245 assert self.name != 'q_empty'
1246 return QAPISchemaType.c_name(self)
1248 def c_type(self):
1249 assert not self.is_implicit()
1250 return c_name(self.name) + pointer_suffix
1252 def c_unboxed_type(self):
1253 return c_name(self.name)
1255 def json_type(self):
1256 return 'object'
1258 def visit(self, visitor):
1259 visitor.visit_object_type(self.name, self.info,
1260 self.base, self.local_members, self.variants)
1261 visitor.visit_object_type_flat(self.name, self.info,
1262 self.members, self.variants)
1265 class QAPISchemaMember(object):
1266 role = 'member'
1268 def __init__(self, name):
1269 assert isinstance(name, str)
1270 self.name = name
1271 self.owner = None
1273 def set_owner(self, name):
1274 assert not self.owner
1275 self.owner = name
1277 def check_clash(self, info, seen):
1278 cname = c_name(self.name)
1279 if cname.lower() != cname and self.owner not in case_whitelist:
1280 raise QAPISemError(info,
1281 "%s should not use uppercase" % self.describe())
1282 if cname in seen:
1283 raise QAPISemError(info, "%s collides with %s" %
1284 (self.describe(), seen[cname].describe()))
1285 seen[cname] = self
1287 def _pretty_owner(self):
1288 owner = self.owner
1289 if owner.startswith('q_obj_'):
1290 # See QAPISchema._make_implicit_object_type() - reverse the
1291 # mapping there to create a nice human-readable description
1292 owner = owner[6:]
1293 if owner.endswith('-arg'):
1294 return '(parameter of %s)' % owner[:-4]
1295 elif owner.endswith('-base'):
1296 return '(base of %s)' % owner[:-5]
1297 else:
1298 assert owner.endswith('-wrapper')
1299 # Unreachable and not implemented
1300 assert False
1301 if owner.endswith('Kind'):
1302 # See QAPISchema._make_implicit_enum_type()
1303 return '(branch of %s)' % owner[:-4]
1304 return '(%s of %s)' % (self.role, owner)
1306 def describe(self):
1307 return "'%s' %s" % (self.name, self._pretty_owner())
1310 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1311 def __init__(self, name, typ, optional):
1312 QAPISchemaMember.__init__(self, name)
1313 assert isinstance(typ, str)
1314 assert isinstance(optional, bool)
1315 self._type_name = typ
1316 self.type = None
1317 self.optional = optional
1319 def check(self, schema):
1320 assert self.owner
1321 self.type = schema.lookup_type(self._type_name)
1322 assert self.type
1325 class QAPISchemaObjectTypeVariants(object):
1326 def __init__(self, tag_name, tag_member, variants):
1327 # Flat unions pass tag_name but not tag_member.
1328 # Simple unions and alternates pass tag_member but not tag_name.
1329 # After check(), tag_member is always set, and tag_name remains
1330 # a reliable witness of being used by a flat union.
1331 assert bool(tag_member) != bool(tag_name)
1332 assert (isinstance(tag_name, str) or
1333 isinstance(tag_member, QAPISchemaObjectTypeMember))
1334 assert len(variants) > 0
1335 for v in variants:
1336 assert isinstance(v, QAPISchemaObjectTypeVariant)
1337 self._tag_name = tag_name
1338 self.tag_member = tag_member
1339 self.variants = variants
1341 def set_owner(self, name):
1342 for v in self.variants:
1343 v.set_owner(name)
1345 def check(self, schema, seen):
1346 if not self.tag_member: # flat union
1347 self.tag_member = seen[c_name(self._tag_name)]
1348 assert self._tag_name == self.tag_member.name
1349 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1350 for v in self.variants:
1351 v.check(schema)
1352 # Union names must match enum values; alternate names are
1353 # checked separately. Use 'seen' to tell the two apart.
1354 if seen:
1355 assert v.name in self.tag_member.type.member_names()
1356 assert isinstance(v.type, QAPISchemaObjectType)
1357 v.type.check(schema)
1359 def check_clash(self, schema, info, seen):
1360 for v in self.variants:
1361 # Reset seen map for each variant, since qapi names from one
1362 # branch do not affect another branch
1363 assert isinstance(v.type, QAPISchemaObjectType)
1364 v.type.check_clash(schema, info, dict(seen))
1367 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1368 role = 'branch'
1370 def __init__(self, name, typ):
1371 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1374 class QAPISchemaAlternateType(QAPISchemaType):
1375 def __init__(self, name, info, variants):
1376 QAPISchemaType.__init__(self, name, info)
1377 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1378 assert variants.tag_member
1379 variants.set_owner(name)
1380 variants.tag_member.set_owner(self.name)
1381 self.variants = variants
1383 def check(self, schema):
1384 self.variants.tag_member.check(schema)
1385 # Not calling self.variants.check_clash(), because there's nothing
1386 # to clash with
1387 self.variants.check(schema, {})
1388 # Alternate branch names have no relation to the tag enum values;
1389 # so we have to check for potential name collisions ourselves.
1390 seen = {}
1391 for v in self.variants.variants:
1392 v.check_clash(self.info, seen)
1394 def c_type(self):
1395 return c_name(self.name) + pointer_suffix
1397 def json_type(self):
1398 return 'value'
1400 def visit(self, visitor):
1401 visitor.visit_alternate_type(self.name, self.info, self.variants)
1403 def is_empty(self):
1404 return False
1407 class QAPISchemaCommand(QAPISchemaEntity):
1408 def __init__(self, name, info, arg_type, ret_type, gen, success_response,
1409 boxed):
1410 QAPISchemaEntity.__init__(self, name, info)
1411 assert not arg_type or isinstance(arg_type, str)
1412 assert not ret_type or isinstance(ret_type, str)
1413 self._arg_type_name = arg_type
1414 self.arg_type = None
1415 self._ret_type_name = ret_type
1416 self.ret_type = None
1417 self.gen = gen
1418 self.success_response = success_response
1419 self.boxed = boxed
1421 def check(self, schema):
1422 if self._arg_type_name:
1423 self.arg_type = schema.lookup_type(self._arg_type_name)
1424 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1425 isinstance(self.arg_type, QAPISchemaAlternateType))
1426 self.arg_type.check(schema)
1427 if self.boxed:
1428 if self.arg_type.is_empty():
1429 raise QAPISemError(self.info,
1430 "Cannot use 'boxed' with empty type")
1431 else:
1432 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1433 assert not self.arg_type.variants
1434 elif self.boxed:
1435 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1436 if self._ret_type_name:
1437 self.ret_type = schema.lookup_type(self._ret_type_name)
1438 assert isinstance(self.ret_type, QAPISchemaType)
1440 def visit(self, visitor):
1441 visitor.visit_command(self.name, self.info,
1442 self.arg_type, self.ret_type,
1443 self.gen, self.success_response, self.boxed)
1446 class QAPISchemaEvent(QAPISchemaEntity):
1447 def __init__(self, name, info, arg_type, boxed):
1448 QAPISchemaEntity.__init__(self, name, info)
1449 assert not arg_type or isinstance(arg_type, str)
1450 self._arg_type_name = arg_type
1451 self.arg_type = None
1452 self.boxed = boxed
1454 def check(self, schema):
1455 if self._arg_type_name:
1456 self.arg_type = schema.lookup_type(self._arg_type_name)
1457 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1458 isinstance(self.arg_type, QAPISchemaAlternateType))
1459 self.arg_type.check(schema)
1460 if self.boxed:
1461 if self.arg_type.is_empty():
1462 raise QAPISemError(self.info,
1463 "Cannot use 'boxed' with empty type")
1464 else:
1465 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1466 assert not self.arg_type.variants
1467 elif self.boxed:
1468 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1470 def visit(self, visitor):
1471 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
1474 class QAPISchema(object):
1475 def __init__(self, fname):
1476 try:
1477 parser = QAPISchemaParser(open(fname, "r"))
1478 self.exprs = check_exprs(parser.exprs)
1479 self.docs = check_docs(parser.docs)
1480 self._entity_dict = {}
1481 self._predefining = True
1482 self._def_predefineds()
1483 self._predefining = False
1484 self._def_exprs()
1485 self.check()
1486 except QAPIError as err:
1487 print >>sys.stderr, err
1488 exit(1)
1490 def _def_entity(self, ent):
1491 # Only the predefined types are allowed to not have info
1492 assert ent.info or self._predefining
1493 assert ent.name not in self._entity_dict
1494 self._entity_dict[ent.name] = ent
1496 def lookup_entity(self, name, typ=None):
1497 ent = self._entity_dict.get(name)
1498 if typ and not isinstance(ent, typ):
1499 return None
1500 return ent
1502 def lookup_type(self, name):
1503 return self.lookup_entity(name, QAPISchemaType)
1505 def _def_builtin_type(self, name, json_type, c_type):
1506 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1507 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1508 # qapi-types.h from a single .c, all arrays of builtins must be
1509 # declared in the first file whether or not they are used. Nicer
1510 # would be to use lazy instantiation, while figuring out how to
1511 # avoid compilation issues with multiple qapi-types.h.
1512 self._make_array_type(name, None)
1514 def _def_predefineds(self):
1515 for t in [('str', 'string', 'char' + pointer_suffix),
1516 ('number', 'number', 'double'),
1517 ('int', 'int', 'int64_t'),
1518 ('int8', 'int', 'int8_t'),
1519 ('int16', 'int', 'int16_t'),
1520 ('int32', 'int', 'int32_t'),
1521 ('int64', 'int', 'int64_t'),
1522 ('uint8', 'int', 'uint8_t'),
1523 ('uint16', 'int', 'uint16_t'),
1524 ('uint32', 'int', 'uint32_t'),
1525 ('uint64', 'int', 'uint64_t'),
1526 ('size', 'int', 'uint64_t'),
1527 ('bool', 'boolean', 'bool'),
1528 ('any', 'value', 'QObject' + pointer_suffix)]:
1529 self._def_builtin_type(*t)
1530 self.the_empty_object_type = QAPISchemaObjectType('q_empty', None,
1531 None, [], None)
1532 self._def_entity(self.the_empty_object_type)
1533 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1534 'qstring', 'qdict', 'qlist',
1535 'qfloat', 'qbool'])
1536 self._def_entity(QAPISchemaEnumType('QType', None, qtype_values,
1537 'QTYPE'))
1539 def _make_enum_members(self, values):
1540 return [QAPISchemaMember(v) for v in values]
1542 def _make_implicit_enum_type(self, name, info, values):
1543 # See also QAPISchemaObjectTypeMember._pretty_owner()
1544 name = name + 'Kind' # Use namespace reserved by add_name()
1545 self._def_entity(QAPISchemaEnumType(
1546 name, info, self._make_enum_members(values), None))
1547 return name
1549 def _make_array_type(self, element_type, info):
1550 name = element_type + 'List' # Use namespace reserved by add_name()
1551 if not self.lookup_type(name):
1552 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1553 return name
1555 def _make_implicit_object_type(self, name, info, role, members):
1556 if not members:
1557 return None
1558 # See also QAPISchemaObjectTypeMember._pretty_owner()
1559 name = 'q_obj_%s-%s' % (name, role)
1560 if not self.lookup_entity(name, QAPISchemaObjectType):
1561 self._def_entity(QAPISchemaObjectType(name, info, None,
1562 members, None))
1563 return name
1565 def _def_enum_type(self, expr, info):
1566 name = expr['enum']
1567 data = expr['data']
1568 prefix = expr.get('prefix')
1569 self._def_entity(QAPISchemaEnumType(
1570 name, info, self._make_enum_members(data), prefix))
1572 def _make_member(self, name, typ, info):
1573 optional = False
1574 if name.startswith('*'):
1575 name = name[1:]
1576 optional = True
1577 if isinstance(typ, list):
1578 assert len(typ) == 1
1579 typ = self._make_array_type(typ[0], info)
1580 return QAPISchemaObjectTypeMember(name, typ, optional)
1582 def _make_members(self, data, info):
1583 return [self._make_member(key, value, info)
1584 for (key, value) in data.iteritems()]
1586 def _def_struct_type(self, expr, info):
1587 name = expr['struct']
1588 base = expr.get('base')
1589 data = expr['data']
1590 self._def_entity(QAPISchemaObjectType(name, info, base,
1591 self._make_members(data, info),
1592 None))
1594 def _make_variant(self, case, typ):
1595 return QAPISchemaObjectTypeVariant(case, typ)
1597 def _make_simple_variant(self, case, typ, info):
1598 if isinstance(typ, list):
1599 assert len(typ) == 1
1600 typ = self._make_array_type(typ[0], info)
1601 typ = self._make_implicit_object_type(
1602 typ, info, 'wrapper', [self._make_member('data', typ, info)])
1603 return QAPISchemaObjectTypeVariant(case, typ)
1605 def _def_union_type(self, expr, info):
1606 name = expr['union']
1607 data = expr['data']
1608 base = expr.get('base')
1609 tag_name = expr.get('discriminator')
1610 tag_member = None
1611 if isinstance(base, dict):
1612 base = (self._make_implicit_object_type(
1613 name, info, 'base', self._make_members(base, info)))
1614 if tag_name:
1615 variants = [self._make_variant(key, value)
1616 for (key, value) in data.iteritems()]
1617 members = []
1618 else:
1619 variants = [self._make_simple_variant(key, value, info)
1620 for (key, value) in data.iteritems()]
1621 typ = self._make_implicit_enum_type(name, info,
1622 [v.name for v in variants])
1623 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1624 members = [tag_member]
1625 self._def_entity(
1626 QAPISchemaObjectType(name, info, base, members,
1627 QAPISchemaObjectTypeVariants(tag_name,
1628 tag_member,
1629 variants)))
1631 def _def_alternate_type(self, expr, info):
1632 name = expr['alternate']
1633 data = expr['data']
1634 variants = [self._make_variant(key, value)
1635 for (key, value) in data.iteritems()]
1636 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1637 self._def_entity(
1638 QAPISchemaAlternateType(name, info,
1639 QAPISchemaObjectTypeVariants(None,
1640 tag_member,
1641 variants)))
1643 def _def_command(self, expr, info):
1644 name = expr['command']
1645 data = expr.get('data')
1646 rets = expr.get('returns')
1647 gen = expr.get('gen', True)
1648 success_response = expr.get('success-response', True)
1649 boxed = expr.get('boxed', False)
1650 if isinstance(data, OrderedDict):
1651 data = self._make_implicit_object_type(
1652 name, info, 'arg', self._make_members(data, info))
1653 if isinstance(rets, list):
1654 assert len(rets) == 1
1655 rets = self._make_array_type(rets[0], info)
1656 self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
1657 success_response, boxed))
1659 def _def_event(self, expr, info):
1660 name = expr['event']
1661 data = expr.get('data')
1662 boxed = expr.get('boxed', False)
1663 if isinstance(data, OrderedDict):
1664 data = self._make_implicit_object_type(
1665 name, info, 'arg', self._make_members(data, info))
1666 self._def_entity(QAPISchemaEvent(name, info, data, boxed))
1668 def _def_exprs(self):
1669 for expr_elem in self.exprs:
1670 expr = expr_elem['expr']
1671 info = expr_elem['info']
1672 if 'enum' in expr:
1673 self._def_enum_type(expr, info)
1674 elif 'struct' in expr:
1675 self._def_struct_type(expr, info)
1676 elif 'union' in expr:
1677 self._def_union_type(expr, info)
1678 elif 'alternate' in expr:
1679 self._def_alternate_type(expr, info)
1680 elif 'command' in expr:
1681 self._def_command(expr, info)
1682 elif 'event' in expr:
1683 self._def_event(expr, info)
1684 else:
1685 assert False
1687 def check(self):
1688 for ent in self._entity_dict.values():
1689 ent.check(self)
1691 def visit(self, visitor):
1692 visitor.visit_begin(self)
1693 for (name, entity) in sorted(self._entity_dict.items()):
1694 if visitor.visit_needed(entity):
1695 entity.visit(visitor)
1696 visitor.visit_end()
1700 # Code generation helpers
1703 def camel_case(name):
1704 new_name = ''
1705 first = True
1706 for ch in name:
1707 if ch in ['_', '-']:
1708 first = True
1709 elif first:
1710 new_name += ch.upper()
1711 first = False
1712 else:
1713 new_name += ch.lower()
1714 return new_name
1717 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1718 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1719 # ENUM24_Name -> ENUM24_NAME
1720 def camel_to_upper(value):
1721 c_fun_str = c_name(value, False)
1722 if value.isupper():
1723 return c_fun_str
1725 new_name = ''
1726 l = len(c_fun_str)
1727 for i in range(l):
1728 c = c_fun_str[i]
1729 # When c is upper and no "_" appears before, do more checks
1730 if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
1731 if i < l - 1 and c_fun_str[i + 1].islower():
1732 new_name += '_'
1733 elif c_fun_str[i - 1].isdigit():
1734 new_name += '_'
1735 new_name += c
1736 return new_name.lstrip('_').upper()
1739 def c_enum_const(type_name, const_name, prefix=None):
1740 if prefix is not None:
1741 type_name = prefix
1742 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1744 c_name_trans = string.maketrans('.-', '__')
1747 # Map @name to a valid C identifier.
1748 # If @protect, avoid returning certain ticklish identifiers (like
1749 # C keywords) by prepending "q_".
1751 # Used for converting 'name' from a 'name':'type' qapi definition
1752 # into a generated struct member, as well as converting type names
1753 # into substrings of a generated C function name.
1754 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1755 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1756 def c_name(name, protect=True):
1757 # ANSI X3J11/88-090, 3.1.1
1758 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1759 'default', 'do', 'double', 'else', 'enum', 'extern',
1760 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1761 'return', 'short', 'signed', 'sizeof', 'static',
1762 'struct', 'switch', 'typedef', 'union', 'unsigned',
1763 'void', 'volatile', 'while'])
1764 # ISO/IEC 9899:1999, 6.4.1
1765 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1766 # ISO/IEC 9899:2011, 6.4.1
1767 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1768 '_Noreturn', '_Static_assert', '_Thread_local'])
1769 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1770 # excluding _.*
1771 gcc_words = set(['asm', 'typeof'])
1772 # C++ ISO/IEC 14882:2003 2.11
1773 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1774 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1775 'namespace', 'new', 'operator', 'private', 'protected',
1776 'public', 'reinterpret_cast', 'static_cast', 'template',
1777 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1778 'using', 'virtual', 'wchar_t',
1779 # alternative representations
1780 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1781 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1782 # namespace pollution:
1783 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
1784 name = name.translate(c_name_trans)
1785 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1786 | cpp_words | polluted_words):
1787 return "q_" + name
1788 return name
1790 eatspace = '\033EATSPACE.'
1791 pointer_suffix = ' *' + eatspace
1794 def genindent(count):
1795 ret = ""
1796 for _ in range(count):
1797 ret += " "
1798 return ret
1800 indent_level = 0
1803 def push_indent(indent_amount=4):
1804 global indent_level
1805 indent_level += indent_amount
1808 def pop_indent(indent_amount=4):
1809 global indent_level
1810 indent_level -= indent_amount
1813 # Generate @code with @kwds interpolated.
1814 # Obey indent_level, and strip eatspace.
1815 def cgen(code, **kwds):
1816 raw = code % kwds
1817 if indent_level:
1818 indent = genindent(indent_level)
1819 # re.subn() lacks flags support before Python 2.7, use re.compile()
1820 raw = re.subn(re.compile("^.", re.MULTILINE),
1821 indent + r'\g<0>', raw)
1822 raw = raw[0]
1823 return re.sub(re.escape(eatspace) + ' *', '', raw)
1826 def mcgen(code, **kwds):
1827 if code[0] == '\n':
1828 code = code[1:]
1829 return cgen(code, **kwds)
1832 def guardname(filename):
1833 return c_name(filename, protect=False).upper()
1836 def guardstart(name):
1837 return mcgen('''
1839 #ifndef %(name)s
1840 #define %(name)s
1842 ''',
1843 name=guardname(name))
1846 def guardend(name):
1847 return mcgen('''
1849 #endif /* %(name)s */
1851 ''',
1852 name=guardname(name))
1855 def gen_enum_lookup(name, values, prefix=None):
1856 ret = mcgen('''
1858 const char *const %(c_name)s_lookup[] = {
1859 ''',
1860 c_name=c_name(name))
1861 for value in values:
1862 index = c_enum_const(name, value, prefix)
1863 ret += mcgen('''
1864 [%(index)s] = "%(value)s",
1865 ''',
1866 index=index, value=value)
1868 max_index = c_enum_const(name, '_MAX', prefix)
1869 ret += mcgen('''
1870 [%(max_index)s] = NULL,
1872 ''',
1873 max_index=max_index)
1874 return ret
1877 def gen_enum(name, values, prefix=None):
1878 # append automatically generated _MAX value
1879 enum_values = values + ['_MAX']
1881 ret = mcgen('''
1883 typedef enum %(c_name)s {
1884 ''',
1885 c_name=c_name(name))
1887 i = 0
1888 for value in enum_values:
1889 ret += mcgen('''
1890 %(c_enum)s = %(i)d,
1891 ''',
1892 c_enum=c_enum_const(name, value, prefix),
1893 i=i)
1894 i += 1
1896 ret += mcgen('''
1897 } %(c_name)s;
1898 ''',
1899 c_name=c_name(name))
1901 ret += mcgen('''
1903 extern const char *const %(c_name)s_lookup[];
1904 ''',
1905 c_name=c_name(name))
1906 return ret
1909 def gen_params(arg_type, boxed, extra):
1910 if not arg_type:
1911 assert not boxed
1912 return extra
1913 ret = ''
1914 sep = ''
1915 if boxed:
1916 ret += '%s arg' % arg_type.c_param_type()
1917 sep = ', '
1918 else:
1919 assert not arg_type.variants
1920 for memb in arg_type.members:
1921 ret += sep
1922 sep = ', '
1923 if memb.optional:
1924 ret += 'bool has_%s, ' % c_name(memb.name)
1925 ret += '%s %s' % (memb.type.c_param_type(),
1926 c_name(memb.name))
1927 if extra:
1928 ret += sep + extra
1929 return ret
1933 # Common command line parsing
1937 def parse_command_line(extra_options="", extra_long_options=[]):
1939 try:
1940 opts, args = getopt.gnu_getopt(sys.argv[1:],
1941 "chp:o:" + extra_options,
1942 ["source", "header", "prefix=",
1943 "output-dir="] + extra_long_options)
1944 except getopt.GetoptError as err:
1945 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
1946 sys.exit(1)
1948 output_dir = ""
1949 prefix = ""
1950 do_c = False
1951 do_h = False
1952 extra_opts = []
1954 for oa in opts:
1955 o, a = oa
1956 if o in ("-p", "--prefix"):
1957 match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1958 if match.end() != len(a):
1959 print >>sys.stderr, \
1960 "%s: 'funny character '%s' in argument of --prefix" \
1961 % (sys.argv[0], a[match.end()])
1962 sys.exit(1)
1963 prefix = a
1964 elif o in ("-o", "--output-dir"):
1965 output_dir = a + "/"
1966 elif o in ("-c", "--source"):
1967 do_c = True
1968 elif o in ("-h", "--header"):
1969 do_h = True
1970 else:
1971 extra_opts.append(oa)
1973 if not do_c and not do_h:
1974 do_c = True
1975 do_h = True
1977 if len(args) != 1:
1978 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
1979 sys.exit(1)
1980 fname = args[0]
1982 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
1985 # Generate output files with boilerplate
1989 def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1990 c_comment, h_comment):
1991 guard = guardname(prefix + h_file)
1992 c_file = output_dir + prefix + c_file
1993 h_file = output_dir + prefix + h_file
1995 if output_dir:
1996 try:
1997 os.makedirs(output_dir)
1998 except os.error as e:
1999 if e.errno != errno.EEXIST:
2000 raise
2002 def maybe_open(really, name, opt):
2003 if really:
2004 return open(name, opt)
2005 else:
2006 import StringIO
2007 return StringIO.StringIO()
2009 fdef = maybe_open(do_c, c_file, 'w')
2010 fdecl = maybe_open(do_h, h_file, 'w')
2012 fdef.write(mcgen('''
2013 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2014 %(comment)s
2015 ''',
2016 comment=c_comment))
2018 fdecl.write(mcgen('''
2019 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2020 %(comment)s
2021 #ifndef %(guard)s
2022 #define %(guard)s
2024 ''',
2025 comment=h_comment, guard=guard))
2027 return (fdef, fdecl)
2030 def close_output(fdef, fdecl):
2031 fdecl.write('''
2032 #endif
2033 ''')
2034 fdecl.close()
2035 fdef.close()