qapi: enum_types is a list used like a dict, make it one
[qemu.git] / scripts / qapi.py
1 #
2 # QAPI helper library
3 #
4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2016 Red Hat Inc.
6 #
7 # Authors:
8 # Anthony Liguori <aliguori@us.ibm.com>
9 # Markus Armbruster <armbru@redhat.com>
10 #
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
13
14 import re
15 from ordereddict import OrderedDict
16 import errno
17 import getopt
18 import os
19 import sys
20 import string
21
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',
38 }
39
40 # Are documentation comments required?
41 doc_required = False
42
43 # Whitelist of commands allowed to return a non-dictionary
44 returns_whitelist = []
45
46 # Whitelist of entities allowed to violate case conventions
47 name_case_whitelist = []
48
49 enum_types = {}
50 struct_types = []
51 union_types = []
52 all_names = {}
53
54 #
55 # Parsing the schema into expressions
56 #
57
58
59 def error_path(parent):
60 res = ''
61 while parent:
62 res = ('In file included from %s:%d:\n' % (parent['file'],
63 parent['line'])) + res
64 parent = parent['parent']
65 return res
66
67
68 class QAPIError(Exception):
69 def __init__(self, fname, line, col, incl_info, msg):
70 Exception.__init__(self)
71 self.fname = fname
72 self.line = line
73 self.col = col
74 self.info = incl_info
75 self.msg = msg
76
77 def __str__(self):
78 loc = '%s:%d' % (self.fname, self.line)
79 if self.col is not None:
80 loc += ':%s' % self.col
81 return error_path(self.info) + '%s: %s' % (loc, self.msg)
82
83
84 class QAPIParseError(QAPIError):
85 def __init__(self, parser, msg):
86 col = 1
87 for ch in parser.src[parser.line_pos:parser.pos]:
88 if ch == '\t':
89 col = (col + 7) % 8 + 1
90 else:
91 col += 1
92 QAPIError.__init__(self, parser.fname, parser.line, col,
93 parser.incl_info, msg)
94
95
96 class QAPISemError(QAPIError):
97 def __init__(self, info, msg):
98 QAPIError.__init__(self, info['file'], info['line'], None,
99 info['parent'], msg)
100
101
102 class QAPIDoc(object):
103 class Section(object):
104 def __init__(self, name=None):
105 # optional section name (argument/member or section name)
106 self.name = name
107 # the list of lines for this section
108 self.content = []
109 self.optional = False
110
111 def append(self, line):
112 self.content.append(line)
113
114 def __repr__(self):
115 return '\n'.join(self.content).strip()
116
117 class ArgSection(Section):
118 def __init__(self, name):
119 QAPIDoc.Section.__init__(self, name)
120 self.member = None
121
122 def connect(self, member):
123 self.member = member
124
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
140
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
147
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
154
155 if line[0] != ' ':
156 raise QAPIParseError(self.parser, "Missing space after #")
157 line = line[1:]
158
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)
172
173 def end_comment(self):
174 self._end_section()
175
176 def _append_symbol_line(self, line):
177 name = line.split(' ', 1)[0]
178
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])
189
190 self._append_freeform(line)
191
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
206
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)
214
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
222
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)
242
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)
248
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")
253
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))
262
263
264 class QAPISchemaParser(object):
265
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()
282
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
291
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()
325
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)
332
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']
341
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)
352
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)
376
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
383
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)
466
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')
491
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()
508
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 "{", "[" or string')
523 return expr
524
525 def get_doc(self, info):
526 if self.val != '##':
527 raise QAPIParseError(self, "Junk after '##' at start of "
528 "documentation comment")
529
530 doc = QAPIDoc(self, info)
531 self.accept(False)
532 while self.tok == '#':
533 if self.val.startswith('##'):
534 # End of doc comment
535 if self.val != '##':
536 raise QAPIParseError(self, "Junk after '##' at end of "
537 "documentation comment")
538 doc.end_comment()
539 self.accept()
540 return doc
541 else:
542 doc.append(self.val)
543 self.accept(False)
544
545 raise QAPIParseError(self, "Documentation comment must end with '##'")
546
547
548 #
549 # Semantic analysis of schema expressions
550 # TODO fold into QAPISchema
551 # TODO catching name collisions in generated code would be nice
552 #
553
554
555 def find_base_members(base):
556 if isinstance(base, dict):
557 return base
558 base_struct_define = find_struct(base)
559 if not base_struct_define:
560 return None
561 return base_struct_define['data']
562
563
564 # Return the qtype of an alternate branch, or None on error.
565 def find_alternate_member_qtype(qapi_type):
566 if qapi_type in builtin_types:
567 return builtin_types[qapi_type]
568 elif find_struct(qapi_type):
569 return 'QTYPE_QDICT'
570 elif qapi_type in enum_types:
571 return 'QTYPE_QSTRING'
572 elif find_union(qapi_type):
573 return 'QTYPE_QDICT'
574 return None
575
576
577 # Return the discriminator enum define if discriminator is specified as an
578 # enum type, otherwise return None.
579 def discriminator_find_enum_define(expr):
580 base = expr.get('base')
581 discriminator = expr.get('discriminator')
582
583 if not (discriminator and base):
584 return None
585
586 base_members = find_base_members(base)
587 if not base_members:
588 return None
589
590 discriminator_type = base_members.get(discriminator)
591 if not discriminator_type:
592 return None
593
594 return enum_types.get(discriminator_type)
595
596
597 # Names must be letters, numbers, -, and _. They must start with letter,
598 # except for downstream extensions which must start with __RFQDN_.
599 # Dots are only valid in the downstream extension prefix.
600 valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
601 '[a-zA-Z][a-zA-Z0-9_-]*$')
602
603
604 def check_name(info, source, name, allow_optional=False,
605 enum_member=False):
606 global valid_name
607 membername = name
608
609 if not isinstance(name, str):
610 raise QAPISemError(info, "%s requires a string name" % source)
611 if name.startswith('*'):
612 membername = name[1:]
613 if not allow_optional:
614 raise QAPISemError(info, "%s does not allow optional name '%s'"
615 % (source, name))
616 # Enum members can start with a digit, because the generated C
617 # code always prefixes it with the enum name
618 if enum_member and membername[0].isdigit():
619 membername = 'D' + membername
620 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
621 # and 'q_obj_*' implicit type names.
622 if not valid_name.match(membername) or \
623 c_name(membername, False).startswith('q_'):
624 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
625
626
627 def add_name(name, info, meta, implicit=False):
628 global all_names
629 check_name(info, "'%s'" % meta, name)
630 # FIXME should reject names that differ only in '_' vs. '.'
631 # vs. '-', because they're liable to clash in generated C.
632 if name in all_names:
633 raise QAPISemError(info, "%s '%s' is already defined"
634 % (all_names[name], name))
635 if not implicit and (name.endswith('Kind') or name.endswith('List')):
636 raise QAPISemError(info, "%s '%s' should not end in '%s'"
637 % (meta, name, name[-4:]))
638 all_names[name] = meta
639
640
641 def add_struct(definition, info):
642 global struct_types
643 struct_types.append(definition)
644
645
646 def find_struct(name):
647 global struct_types
648 for struct in struct_types:
649 if struct['struct'] == name:
650 return struct
651 return None
652
653
654 def add_union(definition, info):
655 global union_types
656 union_types.append(definition)
657
658
659 def find_union(name):
660 global union_types
661 for union in union_types:
662 if union['union'] == name:
663 return union
664 return None
665
666
667 def check_type(info, source, value, allow_array=False,
668 allow_dict=False, allow_optional=False,
669 allow_metas=[]):
670 global all_names
671
672 if value is None:
673 return
674
675 # Check if array type for value is okay
676 if isinstance(value, list):
677 if not allow_array:
678 raise QAPISemError(info, "%s cannot be an array" % source)
679 if len(value) != 1 or not isinstance(value[0], str):
680 raise QAPISemError(info,
681 "%s: array type must contain single type name" %
682 source)
683 value = value[0]
684
685 # Check if type name for value is okay
686 if isinstance(value, str):
687 if value not in all_names:
688 raise QAPISemError(info, "%s uses unknown type '%s'"
689 % (source, value))
690 if not all_names[value] in allow_metas:
691 raise QAPISemError(info, "%s cannot use %s type '%s'" %
692 (source, all_names[value], value))
693 return
694
695 if not allow_dict:
696 raise QAPISemError(info, "%s should be a type name" % source)
697
698 if not isinstance(value, OrderedDict):
699 raise QAPISemError(info,
700 "%s should be a dictionary or type name" % source)
701
702 # value is a dictionary, check that each member is okay
703 for (key, arg) in value.items():
704 check_name(info, "Member of %s" % source, key,
705 allow_optional=allow_optional)
706 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
707 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
708 % (source, key))
709 # Todo: allow dictionaries to represent default values of
710 # an optional argument.
711 check_type(info, "Member '%s' of %s" % (key, source), arg,
712 allow_array=True,
713 allow_metas=['built-in', 'union', 'alternate', 'struct',
714 'enum'])
715
716
717 def check_command(expr, info):
718 name = expr['command']
719 boxed = expr.get('boxed', False)
720
721 args_meta = ['struct']
722 if boxed:
723 args_meta += ['union', 'alternate']
724 check_type(info, "'data' for command '%s'" % name,
725 expr.get('data'), allow_dict=not boxed, allow_optional=True,
726 allow_metas=args_meta)
727 returns_meta = ['union', 'struct']
728 if name in returns_whitelist:
729 returns_meta += ['built-in', 'alternate', 'enum']
730 check_type(info, "'returns' for command '%s'" % name,
731 expr.get('returns'), allow_array=True,
732 allow_optional=True, allow_metas=returns_meta)
733
734
735 def check_event(expr, info):
736 name = expr['event']
737 boxed = expr.get('boxed', False)
738
739 meta = ['struct']
740 if boxed:
741 meta += ['union', 'alternate']
742 check_type(info, "'data' for event '%s'" % name,
743 expr.get('data'), allow_dict=not boxed, allow_optional=True,
744 allow_metas=meta)
745
746
747 def check_union(expr, info):
748 name = expr['union']
749 base = expr.get('base')
750 discriminator = expr.get('discriminator')
751 members = expr['data']
752
753 # Two types of unions, determined by discriminator.
754
755 # With no discriminator it is a simple union.
756 if discriminator is None:
757 enum_define = None
758 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
759 if base is not None:
760 raise QAPISemError(info, "Simple union '%s' must not have a base" %
761 name)
762
763 # Else, it's a flat union.
764 else:
765 # The object must have a string or dictionary 'base'.
766 check_type(info, "'base' for union '%s'" % name,
767 base, allow_dict=True, allow_optional=True,
768 allow_metas=['struct'])
769 if not base:
770 raise QAPISemError(info, "Flat union '%s' must have a base"
771 % name)
772 base_members = find_base_members(base)
773 assert base_members is not None
774
775 # The value of member 'discriminator' must name a non-optional
776 # member of the base struct.
777 check_name(info, "Discriminator of flat union '%s'" % name,
778 discriminator)
779 discriminator_type = base_members.get(discriminator)
780 if not discriminator_type:
781 raise QAPISemError(info,
782 "Discriminator '%s' is not a member of base "
783 "struct '%s'"
784 % (discriminator, base))
785 enum_define = enum_types.get(discriminator_type)
786 allow_metas = ['struct']
787 # Do not allow string discriminator
788 if not enum_define:
789 raise QAPISemError(info,
790 "Discriminator '%s' must be of enumeration "
791 "type" % discriminator)
792
793 # Check every branch; don't allow an empty union
794 if len(members) == 0:
795 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
796 for (key, value) in members.items():
797 check_name(info, "Member of union '%s'" % name, key)
798
799 # Each value must name a known type
800 check_type(info, "Member '%s' of union '%s'" % (key, name),
801 value, allow_array=not base, allow_metas=allow_metas)
802
803 # If the discriminator names an enum type, then all members
804 # of 'data' must also be members of the enum type.
805 if enum_define:
806 if key not in enum_define['data']:
807 raise QAPISemError(info,
808 "Discriminator value '%s' is not found in "
809 "enum '%s'"
810 % (key, enum_define['enum']))
811
812 # If discriminator is user-defined, ensure all values are covered
813 if enum_define:
814 for value in enum_define['data']:
815 if value not in members.keys():
816 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
817 % (name, value))
818
819
820 def check_alternate(expr, info):
821 name = expr['alternate']
822 members = expr['data']
823 types_seen = {}
824
825 # Check every branch; require at least two branches
826 if len(members) < 2:
827 raise QAPISemError(info,
828 "Alternate '%s' should have at least two branches "
829 "in 'data'" % name)
830 for (key, value) in members.items():
831 check_name(info, "Member of alternate '%s'" % name, key)
832
833 # Ensure alternates have no type conflicts.
834 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
835 value,
836 allow_metas=['built-in', 'union', 'struct', 'enum'])
837 qtype = find_alternate_member_qtype(value)
838 if not qtype:
839 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
840 "type '%s'" % (name, key, value))
841 if qtype in types_seen:
842 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
843 "be distinguished from member '%s'"
844 % (name, key, types_seen[qtype]))
845 types_seen[qtype] = key
846
847
848 def check_enum(expr, info):
849 name = expr['enum']
850 members = expr.get('data')
851 prefix = expr.get('prefix')
852
853 if not isinstance(members, list):
854 raise QAPISemError(info,
855 "Enum '%s' requires an array for 'data'" % name)
856 if prefix is not None and not isinstance(prefix, str):
857 raise QAPISemError(info,
858 "Enum '%s' requires a string for 'prefix'" % name)
859 for member in members:
860 check_name(info, "Member of enum '%s'" % name, member,
861 enum_member=True)
862
863
864 def check_struct(expr, info):
865 name = expr['struct']
866 members = expr['data']
867
868 check_type(info, "'data' for struct '%s'" % name, members,
869 allow_dict=True, allow_optional=True)
870 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
871 allow_metas=['struct'])
872
873
874 def check_keys(expr_elem, meta, required, optional=[]):
875 expr = expr_elem['expr']
876 info = expr_elem['info']
877 name = expr[meta]
878 if not isinstance(name, str):
879 raise QAPISemError(info, "'%s' key must have a string value" % meta)
880 required = required + [meta]
881 for (key, value) in expr.items():
882 if key not in required and key not in optional:
883 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
884 % (key, meta, name))
885 if (key == 'gen' or key == 'success-response') and value is not False:
886 raise QAPISemError(info,
887 "'%s' of %s '%s' should only use false value"
888 % (key, meta, name))
889 if key == 'boxed' and value is not True:
890 raise QAPISemError(info,
891 "'%s' of %s '%s' should only use true value"
892 % (key, meta, name))
893 for key in required:
894 if key not in expr:
895 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
896 % (key, meta, name))
897
898
899 def check_exprs(exprs):
900 global all_names
901
902 # Populate name table with names of built-in types
903 for builtin in builtin_types.keys():
904 all_names[builtin] = 'built-in'
905
906 # Learn the types and check for valid expression keys
907 for expr_elem in exprs:
908 expr = expr_elem['expr']
909 info = expr_elem['info']
910 doc = expr_elem.get('doc')
911
912 if not doc and doc_required:
913 raise QAPISemError(info,
914 "Expression missing documentation comment")
915
916 if 'enum' in expr:
917 meta = 'enum'
918 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
919 enum_types[expr[meta]] = expr
920 elif 'union' in expr:
921 meta = 'union'
922 check_keys(expr_elem, 'union', ['data'],
923 ['base', 'discriminator'])
924 add_union(expr, info)
925 elif 'alternate' in expr:
926 meta = 'alternate'
927 check_keys(expr_elem, 'alternate', ['data'])
928 elif 'struct' in expr:
929 meta = 'struct'
930 check_keys(expr_elem, 'struct', ['data'], ['base'])
931 add_struct(expr, info)
932 elif 'command' in expr:
933 meta = 'command'
934 check_keys(expr_elem, 'command', [],
935 ['data', 'returns', 'gen', 'success-response', 'boxed'])
936 elif 'event' in expr:
937 meta = 'event'
938 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
939 else:
940 raise QAPISemError(expr_elem['info'],
941 "Expression is missing metatype")
942 name = expr[meta]
943 add_name(name, info, meta)
944 if doc and doc.symbol != name:
945 raise QAPISemError(info, "Definition of '%s' follows documentation"
946 " for '%s'" % (name, doc.symbol))
947
948 # Try again for hidden UnionKind enum
949 for expr_elem in exprs:
950 expr = expr_elem['expr']
951 if 'union' in expr and not discriminator_find_enum_define(expr):
952 name = '%sKind' % expr['union']
953 elif 'alternate' in expr:
954 name = '%sKind' % expr['alternate']
955 else:
956 continue
957 enum_types[name] = {'enum': name}
958 add_name(name, info, 'enum', implicit=True)
959
960 # Validate that exprs make sense
961 for expr_elem in exprs:
962 expr = expr_elem['expr']
963 info = expr_elem['info']
964 doc = expr_elem.get('doc')
965
966 if 'enum' in expr:
967 check_enum(expr, info)
968 elif 'union' in expr:
969 check_union(expr, info)
970 elif 'alternate' in expr:
971 check_alternate(expr, info)
972 elif 'struct' in expr:
973 check_struct(expr, info)
974 elif 'command' in expr:
975 check_command(expr, info)
976 elif 'event' in expr:
977 check_event(expr, info)
978 else:
979 assert False, 'unexpected meta type'
980
981 if doc:
982 doc.check_expr(expr)
983
984 return exprs
985
986
987 #
988 # Schema compiler frontend
989 #
990
991 class QAPISchemaEntity(object):
992 def __init__(self, name, info, doc):
993 assert isinstance(name, str)
994 self.name = name
995 # For explicitly defined entities, info points to the (explicit)
996 # definition. For builtins (and their arrays), info is None.
997 # For implicitly defined entities, info points to a place that
998 # triggered the implicit definition (there may be more than one
999 # such place).
1000 self.info = info
1001 self.doc = doc
1002
1003 def c_name(self):
1004 return c_name(self.name)
1005
1006 def check(self, schema):
1007 pass
1008
1009 def is_implicit(self):
1010 return not self.info
1011
1012 def visit(self, visitor):
1013 pass
1014
1015
1016 class QAPISchemaVisitor(object):
1017 def visit_begin(self, schema):
1018 pass
1019
1020 def visit_end(self):
1021 pass
1022
1023 def visit_needed(self, entity):
1024 # Default to visiting everything
1025 return True
1026
1027 def visit_builtin_type(self, name, info, json_type):
1028 pass
1029
1030 def visit_enum_type(self, name, info, values, prefix):
1031 pass
1032
1033 def visit_array_type(self, name, info, element_type):
1034 pass
1035
1036 def visit_object_type(self, name, info, base, members, variants):
1037 pass
1038
1039 def visit_object_type_flat(self, name, info, members, variants):
1040 pass
1041
1042 def visit_alternate_type(self, name, info, variants):
1043 pass
1044
1045 def visit_command(self, name, info, arg_type, ret_type,
1046 gen, success_response, boxed):
1047 pass
1048
1049 def visit_event(self, name, info, arg_type, boxed):
1050 pass
1051
1052
1053 class QAPISchemaType(QAPISchemaEntity):
1054 # Return the C type for common use.
1055 # For the types we commonly box, this is a pointer type.
1056 def c_type(self):
1057 pass
1058
1059 # Return the C type to be used in a parameter list.
1060 def c_param_type(self):
1061 return self.c_type()
1062
1063 # Return the C type to be used where we suppress boxing.
1064 def c_unboxed_type(self):
1065 return self.c_type()
1066
1067 def json_type(self):
1068 pass
1069
1070 def alternate_qtype(self):
1071 json2qtype = {
1072 'string': 'QTYPE_QSTRING',
1073 'number': 'QTYPE_QFLOAT',
1074 'int': 'QTYPE_QINT',
1075 'boolean': 'QTYPE_QBOOL',
1076 'object': 'QTYPE_QDICT'
1077 }
1078 return json2qtype.get(self.json_type())
1079
1080 def doc_type(self):
1081 if self.is_implicit():
1082 return None
1083 return self.name
1084
1085
1086 class QAPISchemaBuiltinType(QAPISchemaType):
1087 def __init__(self, name, json_type, c_type):
1088 QAPISchemaType.__init__(self, name, None, None)
1089 assert not c_type or isinstance(c_type, str)
1090 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1091 'value')
1092 self._json_type_name = json_type
1093 self._c_type_name = c_type
1094
1095 def c_name(self):
1096 return self.name
1097
1098 def c_type(self):
1099 return self._c_type_name
1100
1101 def c_param_type(self):
1102 if self.name == 'str':
1103 return 'const ' + self._c_type_name
1104 return self._c_type_name
1105
1106 def json_type(self):
1107 return self._json_type_name
1108
1109 def doc_type(self):
1110 return self.json_type()
1111
1112 def visit(self, visitor):
1113 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1114
1115
1116 class QAPISchemaEnumType(QAPISchemaType):
1117 def __init__(self, name, info, doc, values, prefix):
1118 QAPISchemaType.__init__(self, name, info, doc)
1119 for v in values:
1120 assert isinstance(v, QAPISchemaMember)
1121 v.set_owner(name)
1122 assert prefix is None or isinstance(prefix, str)
1123 self.values = values
1124 self.prefix = prefix
1125
1126 def check(self, schema):
1127 seen = {}
1128 for v in self.values:
1129 v.check_clash(self.info, seen)
1130 if self.doc:
1131 self.doc.connect_member(v)
1132
1133 def is_implicit(self):
1134 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1135 return self.name.endswith('Kind') or self.name == 'QType'
1136
1137 def c_type(self):
1138 return c_name(self.name)
1139
1140 def member_names(self):
1141 return [v.name for v in self.values]
1142
1143 def json_type(self):
1144 return 'string'
1145
1146 def visit(self, visitor):
1147 visitor.visit_enum_type(self.name, self.info,
1148 self.member_names(), self.prefix)
1149
1150
1151 class QAPISchemaArrayType(QAPISchemaType):
1152 def __init__(self, name, info, element_type):
1153 QAPISchemaType.__init__(self, name, info, None)
1154 assert isinstance(element_type, str)
1155 self._element_type_name = element_type
1156 self.element_type = None
1157
1158 def check(self, schema):
1159 self.element_type = schema.lookup_type(self._element_type_name)
1160 assert self.element_type
1161
1162 def is_implicit(self):
1163 return True
1164
1165 def c_type(self):
1166 return c_name(self.name) + pointer_suffix
1167
1168 def json_type(self):
1169 return 'array'
1170
1171 def doc_type(self):
1172 elt_doc_type = self.element_type.doc_type()
1173 if not elt_doc_type:
1174 return None
1175 return 'array of ' + elt_doc_type
1176
1177 def visit(self, visitor):
1178 visitor.visit_array_type(self.name, self.info, self.element_type)
1179
1180
1181 class QAPISchemaObjectType(QAPISchemaType):
1182 def __init__(self, name, info, doc, base, local_members, variants):
1183 # struct has local_members, optional base, and no variants
1184 # flat union has base, variants, and no local_members
1185 # simple union has local_members, variants, and no base
1186 QAPISchemaType.__init__(self, name, info, doc)
1187 assert base is None or isinstance(base, str)
1188 for m in local_members:
1189 assert isinstance(m, QAPISchemaObjectTypeMember)
1190 m.set_owner(name)
1191 if variants is not None:
1192 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1193 variants.set_owner(name)
1194 self._base_name = base
1195 self.base = None
1196 self.local_members = local_members
1197 self.variants = variants
1198 self.members = None
1199
1200 def check(self, schema):
1201 if self.members is False: # check for cycles
1202 raise QAPISemError(self.info,
1203 "Object %s contains itself" % self.name)
1204 if self.members:
1205 return
1206 self.members = False # mark as being checked
1207 seen = OrderedDict()
1208 if self._base_name:
1209 self.base = schema.lookup_type(self._base_name)
1210 assert isinstance(self.base, QAPISchemaObjectType)
1211 self.base.check(schema)
1212 self.base.check_clash(schema, self.info, seen)
1213 for m in self.local_members:
1214 m.check(schema)
1215 m.check_clash(self.info, seen)
1216 if self.doc:
1217 self.doc.connect_member(m)
1218 self.members = seen.values()
1219 if self.variants:
1220 self.variants.check(schema, seen)
1221 assert self.variants.tag_member in self.members
1222 self.variants.check_clash(schema, self.info, seen)
1223 if self.doc:
1224 self.doc.check()
1225
1226 # Check that the members of this type do not cause duplicate JSON members,
1227 # and update seen to track the members seen so far. Report any errors
1228 # on behalf of info, which is not necessarily self.info
1229 def check_clash(self, schema, info, seen):
1230 assert not self.variants # not implemented
1231 for m in self.members:
1232 m.check_clash(info, seen)
1233
1234 def is_implicit(self):
1235 # See QAPISchema._make_implicit_object_type(), as well as
1236 # _def_predefineds()
1237 return self.name.startswith('q_')
1238
1239 def is_empty(self):
1240 assert self.members is not None
1241 return not self.members and not self.variants
1242
1243 def c_name(self):
1244 assert self.name != 'q_empty'
1245 return QAPISchemaType.c_name(self)
1246
1247 def c_type(self):
1248 assert not self.is_implicit()
1249 return c_name(self.name) + pointer_suffix
1250
1251 def c_unboxed_type(self):
1252 return c_name(self.name)
1253
1254 def json_type(self):
1255 return 'object'
1256
1257 def visit(self, visitor):
1258 visitor.visit_object_type(self.name, self.info,
1259 self.base, self.local_members, self.variants)
1260 visitor.visit_object_type_flat(self.name, self.info,
1261 self.members, self.variants)
1262
1263
1264 class QAPISchemaMember(object):
1265 role = 'member'
1266
1267 def __init__(self, name):
1268 assert isinstance(name, str)
1269 self.name = name
1270 self.owner = None
1271
1272 def set_owner(self, name):
1273 assert not self.owner
1274 self.owner = name
1275
1276 def check_clash(self, info, seen):
1277 cname = c_name(self.name)
1278 if cname.lower() != cname and self.owner not in name_case_whitelist:
1279 raise QAPISemError(info,
1280 "%s should not use uppercase" % self.describe())
1281 if cname in seen:
1282 raise QAPISemError(info, "%s collides with %s" %
1283 (self.describe(), seen[cname].describe()))
1284 seen[cname] = self
1285
1286 def _pretty_owner(self):
1287 owner = self.owner
1288 if owner.startswith('q_obj_'):
1289 # See QAPISchema._make_implicit_object_type() - reverse the
1290 # mapping there to create a nice human-readable description
1291 owner = owner[6:]
1292 if owner.endswith('-arg'):
1293 return '(parameter of %s)' % owner[:-4]
1294 elif owner.endswith('-base'):
1295 return '(base of %s)' % owner[:-5]
1296 else:
1297 assert owner.endswith('-wrapper')
1298 # Unreachable and not implemented
1299 assert False
1300 if owner.endswith('Kind'):
1301 # See QAPISchema._make_implicit_enum_type()
1302 return '(branch of %s)' % owner[:-4]
1303 return '(%s of %s)' % (self.role, owner)
1304
1305 def describe(self):
1306 return "'%s' %s" % (self.name, self._pretty_owner())
1307
1308
1309 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1310 def __init__(self, name, typ, optional):
1311 QAPISchemaMember.__init__(self, name)
1312 assert isinstance(typ, str)
1313 assert isinstance(optional, bool)
1314 self._type_name = typ
1315 self.type = None
1316 self.optional = optional
1317
1318 def check(self, schema):
1319 assert self.owner
1320 self.type = schema.lookup_type(self._type_name)
1321 assert self.type
1322
1323
1324 class QAPISchemaObjectTypeVariants(object):
1325 def __init__(self, tag_name, tag_member, variants):
1326 # Flat unions pass tag_name but not tag_member.
1327 # Simple unions and alternates pass tag_member but not tag_name.
1328 # After check(), tag_member is always set, and tag_name remains
1329 # a reliable witness of being used by a flat union.
1330 assert bool(tag_member) != bool(tag_name)
1331 assert (isinstance(tag_name, str) or
1332 isinstance(tag_member, QAPISchemaObjectTypeMember))
1333 assert len(variants) > 0
1334 for v in variants:
1335 assert isinstance(v, QAPISchemaObjectTypeVariant)
1336 self._tag_name = tag_name
1337 self.tag_member = tag_member
1338 self.variants = variants
1339
1340 def set_owner(self, name):
1341 for v in self.variants:
1342 v.set_owner(name)
1343
1344 def check(self, schema, seen):
1345 if not self.tag_member: # flat union
1346 self.tag_member = seen[c_name(self._tag_name)]
1347 assert self._tag_name == self.tag_member.name
1348 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1349 for v in self.variants:
1350 v.check(schema)
1351 # Union names must match enum values; alternate names are
1352 # checked separately. Use 'seen' to tell the two apart.
1353 if seen:
1354 assert v.name in self.tag_member.type.member_names()
1355 assert isinstance(v.type, QAPISchemaObjectType)
1356 v.type.check(schema)
1357
1358 def check_clash(self, schema, info, seen):
1359 for v in self.variants:
1360 # Reset seen map for each variant, since qapi names from one
1361 # branch do not affect another branch
1362 assert isinstance(v.type, QAPISchemaObjectType)
1363 v.type.check_clash(schema, info, dict(seen))
1364
1365
1366 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1367 role = 'branch'
1368
1369 def __init__(self, name, typ):
1370 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1371
1372
1373 class QAPISchemaAlternateType(QAPISchemaType):
1374 def __init__(self, name, info, doc, variants):
1375 QAPISchemaType.__init__(self, name, info, doc)
1376 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1377 assert variants.tag_member
1378 variants.set_owner(name)
1379 variants.tag_member.set_owner(self.name)
1380 self.variants = variants
1381
1382 def check(self, schema):
1383 self.variants.tag_member.check(schema)
1384 # Not calling self.variants.check_clash(), because there's nothing
1385 # to clash with
1386 self.variants.check(schema, {})
1387 # Alternate branch names have no relation to the tag enum values;
1388 # so we have to check for potential name collisions ourselves.
1389 seen = {}
1390 for v in self.variants.variants:
1391 v.check_clash(self.info, seen)
1392 if self.doc:
1393 self.doc.connect_member(v)
1394 if self.doc:
1395 self.doc.check()
1396
1397 def c_type(self):
1398 return c_name(self.name) + pointer_suffix
1399
1400 def json_type(self):
1401 return 'value'
1402
1403 def visit(self, visitor):
1404 visitor.visit_alternate_type(self.name, self.info, self.variants)
1405
1406 def is_empty(self):
1407 return False
1408
1409
1410 class QAPISchemaCommand(QAPISchemaEntity):
1411 def __init__(self, name, info, doc, arg_type, ret_type,
1412 gen, success_response, boxed):
1413 QAPISchemaEntity.__init__(self, name, info, doc)
1414 assert not arg_type or isinstance(arg_type, str)
1415 assert not ret_type or isinstance(ret_type, str)
1416 self._arg_type_name = arg_type
1417 self.arg_type = None
1418 self._ret_type_name = ret_type
1419 self.ret_type = None
1420 self.gen = gen
1421 self.success_response = success_response
1422 self.boxed = boxed
1423
1424 def check(self, schema):
1425 if self._arg_type_name:
1426 self.arg_type = schema.lookup_type(self._arg_type_name)
1427 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1428 isinstance(self.arg_type, QAPISchemaAlternateType))
1429 self.arg_type.check(schema)
1430 if self.boxed:
1431 if self.arg_type.is_empty():
1432 raise QAPISemError(self.info,
1433 "Cannot use 'boxed' with empty type")
1434 else:
1435 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1436 assert not self.arg_type.variants
1437 elif self.boxed:
1438 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1439 if self._ret_type_name:
1440 self.ret_type = schema.lookup_type(self._ret_type_name)
1441 assert isinstance(self.ret_type, QAPISchemaType)
1442
1443 def visit(self, visitor):
1444 visitor.visit_command(self.name, self.info,
1445 self.arg_type, self.ret_type,
1446 self.gen, self.success_response, self.boxed)
1447
1448
1449 class QAPISchemaEvent(QAPISchemaEntity):
1450 def __init__(self, name, info, doc, arg_type, boxed):
1451 QAPISchemaEntity.__init__(self, name, info, doc)
1452 assert not arg_type or isinstance(arg_type, str)
1453 self._arg_type_name = arg_type
1454 self.arg_type = None
1455 self.boxed = boxed
1456
1457 def check(self, schema):
1458 if self._arg_type_name:
1459 self.arg_type = schema.lookup_type(self._arg_type_name)
1460 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1461 isinstance(self.arg_type, QAPISchemaAlternateType))
1462 self.arg_type.check(schema)
1463 if self.boxed:
1464 if self.arg_type.is_empty():
1465 raise QAPISemError(self.info,
1466 "Cannot use 'boxed' with empty type")
1467 else:
1468 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1469 assert not self.arg_type.variants
1470 elif self.boxed:
1471 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1472
1473 def visit(self, visitor):
1474 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
1475
1476
1477 class QAPISchema(object):
1478 def __init__(self, fname):
1479 try:
1480 parser = QAPISchemaParser(open(fname, 'r'))
1481 self.exprs = check_exprs(parser.exprs)
1482 self.docs = parser.docs
1483 self._entity_dict = {}
1484 self._predefining = True
1485 self._def_predefineds()
1486 self._predefining = False
1487 self._def_exprs()
1488 self.check()
1489 except QAPIError as err:
1490 print >>sys.stderr, err
1491 exit(1)
1492
1493 def _def_entity(self, ent):
1494 # Only the predefined types are allowed to not have info
1495 assert ent.info or self._predefining
1496 assert ent.name not in self._entity_dict
1497 self._entity_dict[ent.name] = ent
1498
1499 def lookup_entity(self, name, typ=None):
1500 ent = self._entity_dict.get(name)
1501 if typ and not isinstance(ent, typ):
1502 return None
1503 return ent
1504
1505 def lookup_type(self, name):
1506 return self.lookup_entity(name, QAPISchemaType)
1507
1508 def _def_builtin_type(self, name, json_type, c_type):
1509 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1510 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1511 # qapi-types.h from a single .c, all arrays of builtins must be
1512 # declared in the first file whether or not they are used. Nicer
1513 # would be to use lazy instantiation, while figuring out how to
1514 # avoid compilation issues with multiple qapi-types.h.
1515 self._make_array_type(name, None)
1516
1517 def _def_predefineds(self):
1518 for t in [('str', 'string', 'char' + pointer_suffix),
1519 ('number', 'number', 'double'),
1520 ('int', 'int', 'int64_t'),
1521 ('int8', 'int', 'int8_t'),
1522 ('int16', 'int', 'int16_t'),
1523 ('int32', 'int', 'int32_t'),
1524 ('int64', 'int', 'int64_t'),
1525 ('uint8', 'int', 'uint8_t'),
1526 ('uint16', 'int', 'uint16_t'),
1527 ('uint32', 'int', 'uint32_t'),
1528 ('uint64', 'int', 'uint64_t'),
1529 ('size', 'int', 'uint64_t'),
1530 ('bool', 'boolean', 'bool'),
1531 ('any', 'value', 'QObject' + pointer_suffix)]:
1532 self._def_builtin_type(*t)
1533 self.the_empty_object_type = QAPISchemaObjectType(
1534 'q_empty', None, None, None, [], None)
1535 self._def_entity(self.the_empty_object_type)
1536 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1537 'qstring', 'qdict', 'qlist',
1538 'qfloat', 'qbool'])
1539 self._def_entity(QAPISchemaEnumType('QType', None, None,
1540 qtype_values, 'QTYPE'))
1541
1542 def _make_enum_members(self, values):
1543 return [QAPISchemaMember(v) for v in values]
1544
1545 def _make_implicit_enum_type(self, name, info, values):
1546 # See also QAPISchemaObjectTypeMember._pretty_owner()
1547 name = name + 'Kind' # Use namespace reserved by add_name()
1548 self._def_entity(QAPISchemaEnumType(
1549 name, info, None, self._make_enum_members(values), None))
1550 return name
1551
1552 def _make_array_type(self, element_type, info):
1553 name = element_type + 'List' # Use namespace reserved by add_name()
1554 if not self.lookup_type(name):
1555 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1556 return name
1557
1558 def _make_implicit_object_type(self, name, info, doc, role, members):
1559 if not members:
1560 return None
1561 # See also QAPISchemaObjectTypeMember._pretty_owner()
1562 name = 'q_obj_%s-%s' % (name, role)
1563 if not self.lookup_entity(name, QAPISchemaObjectType):
1564 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
1565 members, None))
1566 return name
1567
1568 def _def_enum_type(self, expr, info, doc):
1569 name = expr['enum']
1570 data = expr['data']
1571 prefix = expr.get('prefix')
1572 self._def_entity(QAPISchemaEnumType(
1573 name, info, doc, self._make_enum_members(data), prefix))
1574
1575 def _make_member(self, name, typ, info):
1576 optional = False
1577 if name.startswith('*'):
1578 name = name[1:]
1579 optional = True
1580 if isinstance(typ, list):
1581 assert len(typ) == 1
1582 typ = self._make_array_type(typ[0], info)
1583 return QAPISchemaObjectTypeMember(name, typ, optional)
1584
1585 def _make_members(self, data, info):
1586 return [self._make_member(key, value, info)
1587 for (key, value) in data.iteritems()]
1588
1589 def _def_struct_type(self, expr, info, doc):
1590 name = expr['struct']
1591 base = expr.get('base')
1592 data = expr['data']
1593 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
1594 self._make_members(data, info),
1595 None))
1596
1597 def _make_variant(self, case, typ):
1598 return QAPISchemaObjectTypeVariant(case, typ)
1599
1600 def _make_simple_variant(self, case, typ, info):
1601 if isinstance(typ, list):
1602 assert len(typ) == 1
1603 typ = self._make_array_type(typ[0], info)
1604 typ = self._make_implicit_object_type(
1605 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
1606 return QAPISchemaObjectTypeVariant(case, typ)
1607
1608 def _def_union_type(self, expr, info, doc):
1609 name = expr['union']
1610 data = expr['data']
1611 base = expr.get('base')
1612 tag_name = expr.get('discriminator')
1613 tag_member = None
1614 if isinstance(base, dict):
1615 base = (self._make_implicit_object_type(
1616 name, info, doc, 'base', self._make_members(base, info)))
1617 if tag_name:
1618 variants = [self._make_variant(key, value)
1619 for (key, value) in data.iteritems()]
1620 members = []
1621 else:
1622 variants = [self._make_simple_variant(key, value, info)
1623 for (key, value) in data.iteritems()]
1624 typ = self._make_implicit_enum_type(name, info,
1625 [v.name for v in variants])
1626 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1627 members = [tag_member]
1628 self._def_entity(
1629 QAPISchemaObjectType(name, info, doc, base, members,
1630 QAPISchemaObjectTypeVariants(tag_name,
1631 tag_member,
1632 variants)))
1633
1634 def _def_alternate_type(self, expr, info, doc):
1635 name = expr['alternate']
1636 data = expr['data']
1637 variants = [self._make_variant(key, value)
1638 for (key, value) in data.iteritems()]
1639 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1640 self._def_entity(
1641 QAPISchemaAlternateType(name, info, doc,
1642 QAPISchemaObjectTypeVariants(None,
1643 tag_member,
1644 variants)))
1645
1646 def _def_command(self, expr, info, doc):
1647 name = expr['command']
1648 data = expr.get('data')
1649 rets = expr.get('returns')
1650 gen = expr.get('gen', True)
1651 success_response = expr.get('success-response', True)
1652 boxed = expr.get('boxed', False)
1653 if isinstance(data, OrderedDict):
1654 data = self._make_implicit_object_type(
1655 name, info, doc, 'arg', self._make_members(data, info))
1656 if isinstance(rets, list):
1657 assert len(rets) == 1
1658 rets = self._make_array_type(rets[0], info)
1659 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1660 gen, success_response, boxed))
1661
1662 def _def_event(self, expr, info, doc):
1663 name = expr['event']
1664 data = expr.get('data')
1665 boxed = expr.get('boxed', False)
1666 if isinstance(data, OrderedDict):
1667 data = self._make_implicit_object_type(
1668 name, info, doc, 'arg', self._make_members(data, info))
1669 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
1670
1671 def _def_exprs(self):
1672 for expr_elem in self.exprs:
1673 expr = expr_elem['expr']
1674 info = expr_elem['info']
1675 doc = expr_elem.get('doc')
1676 if 'enum' in expr:
1677 self._def_enum_type(expr, info, doc)
1678 elif 'struct' in expr:
1679 self._def_struct_type(expr, info, doc)
1680 elif 'union' in expr:
1681 self._def_union_type(expr, info, doc)
1682 elif 'alternate' in expr:
1683 self._def_alternate_type(expr, info, doc)
1684 elif 'command' in expr:
1685 self._def_command(expr, info, doc)
1686 elif 'event' in expr:
1687 self._def_event(expr, info, doc)
1688 else:
1689 assert False
1690
1691 def check(self):
1692 for ent in self._entity_dict.values():
1693 ent.check(self)
1694
1695 def visit(self, visitor):
1696 visitor.visit_begin(self)
1697 for (name, entity) in sorted(self._entity_dict.items()):
1698 if visitor.visit_needed(entity):
1699 entity.visit(visitor)
1700 visitor.visit_end()
1701
1702
1703 #
1704 # Code generation helpers
1705 #
1706
1707 def camel_case(name):
1708 new_name = ''
1709 first = True
1710 for ch in name:
1711 if ch in ['_', '-']:
1712 first = True
1713 elif first:
1714 new_name += ch.upper()
1715 first = False
1716 else:
1717 new_name += ch.lower()
1718 return new_name
1719
1720
1721 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1722 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1723 # ENUM24_Name -> ENUM24_NAME
1724 def camel_to_upper(value):
1725 c_fun_str = c_name(value, False)
1726 if value.isupper():
1727 return c_fun_str
1728
1729 new_name = ''
1730 l = len(c_fun_str)
1731 for i in range(l):
1732 c = c_fun_str[i]
1733 # When c is upper and no '_' appears before, do more checks
1734 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1735 if i < l - 1 and c_fun_str[i + 1].islower():
1736 new_name += '_'
1737 elif c_fun_str[i - 1].isdigit():
1738 new_name += '_'
1739 new_name += c
1740 return new_name.lstrip('_').upper()
1741
1742
1743 def c_enum_const(type_name, const_name, prefix=None):
1744 if prefix is not None:
1745 type_name = prefix
1746 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1747
1748 c_name_trans = string.maketrans('.-', '__')
1749
1750
1751 # Map @name to a valid C identifier.
1752 # If @protect, avoid returning certain ticklish identifiers (like
1753 # C keywords) by prepending 'q_'.
1754 #
1755 # Used for converting 'name' from a 'name':'type' qapi definition
1756 # into a generated struct member, as well as converting type names
1757 # into substrings of a generated C function name.
1758 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1759 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1760 def c_name(name, protect=True):
1761 # ANSI X3J11/88-090, 3.1.1
1762 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1763 'default', 'do', 'double', 'else', 'enum', 'extern',
1764 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1765 'return', 'short', 'signed', 'sizeof', 'static',
1766 'struct', 'switch', 'typedef', 'union', 'unsigned',
1767 'void', 'volatile', 'while'])
1768 # ISO/IEC 9899:1999, 6.4.1
1769 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1770 # ISO/IEC 9899:2011, 6.4.1
1771 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1772 '_Noreturn', '_Static_assert', '_Thread_local'])
1773 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1774 # excluding _.*
1775 gcc_words = set(['asm', 'typeof'])
1776 # C++ ISO/IEC 14882:2003 2.11
1777 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1778 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1779 'namespace', 'new', 'operator', 'private', 'protected',
1780 'public', 'reinterpret_cast', 'static_cast', 'template',
1781 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1782 'using', 'virtual', 'wchar_t',
1783 # alternative representations
1784 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1785 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1786 # namespace pollution:
1787 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
1788 name = name.translate(c_name_trans)
1789 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1790 | cpp_words | polluted_words):
1791 return 'q_' + name
1792 return name
1793
1794 eatspace = '\033EATSPACE.'
1795 pointer_suffix = ' *' + eatspace
1796
1797
1798 def genindent(count):
1799 ret = ''
1800 for _ in range(count):
1801 ret += ' '
1802 return ret
1803
1804 indent_level = 0
1805
1806
1807 def push_indent(indent_amount=4):
1808 global indent_level
1809 indent_level += indent_amount
1810
1811
1812 def pop_indent(indent_amount=4):
1813 global indent_level
1814 indent_level -= indent_amount
1815
1816
1817 # Generate @code with @kwds interpolated.
1818 # Obey indent_level, and strip eatspace.
1819 def cgen(code, **kwds):
1820 raw = code % kwds
1821 if indent_level:
1822 indent = genindent(indent_level)
1823 # re.subn() lacks flags support before Python 2.7, use re.compile()
1824 raw = re.subn(re.compile(r'^.', re.MULTILINE),
1825 indent + r'\g<0>', raw)
1826 raw = raw[0]
1827 return re.sub(re.escape(eatspace) + r' *', '', raw)
1828
1829
1830 def mcgen(code, **kwds):
1831 if code[0] == '\n':
1832 code = code[1:]
1833 return cgen(code, **kwds)
1834
1835
1836 def guardname(filename):
1837 return c_name(filename, protect=False).upper()
1838
1839
1840 def guardstart(name):
1841 return mcgen('''
1842
1843 #ifndef %(name)s
1844 #define %(name)s
1845
1846 ''',
1847 name=guardname(name))
1848
1849
1850 def guardend(name):
1851 return mcgen('''
1852
1853 #endif /* %(name)s */
1854
1855 ''',
1856 name=guardname(name))
1857
1858
1859 def gen_enum_lookup(name, values, prefix=None):
1860 ret = mcgen('''
1861
1862 const char *const %(c_name)s_lookup[] = {
1863 ''',
1864 c_name=c_name(name))
1865 for value in values:
1866 index = c_enum_const(name, value, prefix)
1867 ret += mcgen('''
1868 [%(index)s] = "%(value)s",
1869 ''',
1870 index=index, value=value)
1871
1872 max_index = c_enum_const(name, '_MAX', prefix)
1873 ret += mcgen('''
1874 [%(max_index)s] = NULL,
1875 };
1876 ''',
1877 max_index=max_index)
1878 return ret
1879
1880
1881 def gen_enum(name, values, prefix=None):
1882 # append automatically generated _MAX value
1883 enum_values = values + ['_MAX']
1884
1885 ret = mcgen('''
1886
1887 typedef enum %(c_name)s {
1888 ''',
1889 c_name=c_name(name))
1890
1891 i = 0
1892 for value in enum_values:
1893 ret += mcgen('''
1894 %(c_enum)s = %(i)d,
1895 ''',
1896 c_enum=c_enum_const(name, value, prefix),
1897 i=i)
1898 i += 1
1899
1900 ret += mcgen('''
1901 } %(c_name)s;
1902 ''',
1903 c_name=c_name(name))
1904
1905 ret += mcgen('''
1906
1907 extern const char *const %(c_name)s_lookup[];
1908 ''',
1909 c_name=c_name(name))
1910 return ret
1911
1912
1913 def gen_params(arg_type, boxed, extra):
1914 if not arg_type:
1915 assert not boxed
1916 return extra
1917 ret = ''
1918 sep = ''
1919 if boxed:
1920 ret += '%s arg' % arg_type.c_param_type()
1921 sep = ', '
1922 else:
1923 assert not arg_type.variants
1924 for memb in arg_type.members:
1925 ret += sep
1926 sep = ', '
1927 if memb.optional:
1928 ret += 'bool has_%s, ' % c_name(memb.name)
1929 ret += '%s %s' % (memb.type.c_param_type(),
1930 c_name(memb.name))
1931 if extra:
1932 ret += sep + extra
1933 return ret
1934
1935
1936 #
1937 # Common command line parsing
1938 #
1939
1940
1941 def parse_command_line(extra_options='', extra_long_options=[]):
1942
1943 try:
1944 opts, args = getopt.gnu_getopt(sys.argv[1:],
1945 'chp:o:' + extra_options,
1946 ['source', 'header', 'prefix=',
1947 'output-dir='] + extra_long_options)
1948 except getopt.GetoptError as err:
1949 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
1950 sys.exit(1)
1951
1952 output_dir = ''
1953 prefix = ''
1954 do_c = False
1955 do_h = False
1956 extra_opts = []
1957
1958 for oa in opts:
1959 o, a = oa
1960 if o in ('-p', '--prefix'):
1961 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1962 if match.end() != len(a):
1963 print >>sys.stderr, \
1964 "%s: 'funny character '%s' in argument of --prefix" \
1965 % (sys.argv[0], a[match.end()])
1966 sys.exit(1)
1967 prefix = a
1968 elif o in ('-o', '--output-dir'):
1969 output_dir = a + '/'
1970 elif o in ('-c', '--source'):
1971 do_c = True
1972 elif o in ('-h', '--header'):
1973 do_h = True
1974 else:
1975 extra_opts.append(oa)
1976
1977 if not do_c and not do_h:
1978 do_c = True
1979 do_h = True
1980
1981 if len(args) != 1:
1982 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
1983 sys.exit(1)
1984 fname = args[0]
1985
1986 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
1987
1988 #
1989 # Generate output files with boilerplate
1990 #
1991
1992
1993 def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1994 c_comment, h_comment):
1995 guard = guardname(prefix + h_file)
1996 c_file = output_dir + prefix + c_file
1997 h_file = output_dir + prefix + h_file
1998
1999 if output_dir:
2000 try:
2001 os.makedirs(output_dir)
2002 except os.error as e:
2003 if e.errno != errno.EEXIST:
2004 raise
2005
2006 def maybe_open(really, name, opt):
2007 if really:
2008 return open(name, opt)
2009 else:
2010 import StringIO
2011 return StringIO.StringIO()
2012
2013 fdef = maybe_open(do_c, c_file, 'w')
2014 fdecl = maybe_open(do_h, h_file, 'w')
2015
2016 fdef.write(mcgen('''
2017 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2018 %(comment)s
2019 ''',
2020 comment=c_comment))
2021
2022 fdecl.write(mcgen('''
2023 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2024 %(comment)s
2025 #ifndef %(guard)s
2026 #define %(guard)s
2027
2028 ''',
2029 comment=h_comment, guard=guard))
2030
2031 return (fdef, fdecl)
2032
2033
2034 def close_output(fdef, fdecl):
2035 fdecl.write('''
2036 #endif
2037 ''')
2038 fdecl.close()
2039 fdef.close()