qapi: Change frontend error messages to start with lower case
[qemu/ar7.git] / scripts / qapi / common.py
blob3d73332487b6d3d136a67394cf86d143c76e3e80
2 # QAPI helper library
4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2018 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 from __future__ import print_function
15 from contextlib import contextmanager
16 import copy
17 import errno
18 import os
19 import re
20 import string
21 import sys
22 from collections import OrderedDict
24 builtin_types = {
25 'null': 'QTYPE_QNULL',
26 'str': 'QTYPE_QSTRING',
27 'int': 'QTYPE_QNUM',
28 'number': 'QTYPE_QNUM',
29 'bool': 'QTYPE_QBOOL',
30 'int8': 'QTYPE_QNUM',
31 'int16': 'QTYPE_QNUM',
32 'int32': 'QTYPE_QNUM',
33 'int64': 'QTYPE_QNUM',
34 'uint8': 'QTYPE_QNUM',
35 'uint16': 'QTYPE_QNUM',
36 'uint32': 'QTYPE_QNUM',
37 'uint64': 'QTYPE_QNUM',
38 'size': 'QTYPE_QNUM',
39 'any': None, # any QType possible, actually
40 'QType': 'QTYPE_QSTRING',
43 # Are documentation comments required?
44 doc_required = False
46 # Whitelist of commands allowed to return a non-dictionary
47 returns_whitelist = []
49 # Whitelist of entities allowed to violate case conventions
50 name_case_whitelist = []
52 enum_types = {}
53 struct_types = {}
54 union_types = {}
55 all_names = {}
59 # Parsing the schema into expressions
62 class QAPISourceInfo(object):
63 def __init__(self, fname, line, parent):
64 self.fname = fname
65 self.line = line
66 self.parent = parent
67 self.defn_meta = None
68 self.defn_name = None
70 def set_defn(self, meta, name):
71 self.defn_meta = meta
72 self.defn_name = name
74 def next_line(self):
75 info = copy.copy(self)
76 info.line += 1
77 return info
79 def loc(self):
80 return '%s:%d' % (self.fname, self.line)
82 def in_defn(self):
83 if self.defn_name:
84 return "%s: In %s '%s':\n" % (self.fname,
85 self.defn_meta, self.defn_name)
86 return ''
88 def include_path(self):
89 ret = ''
90 parent = self.parent
91 while parent:
92 ret = 'In file included from %s:\n' % parent.loc() + ret
93 parent = parent.parent
94 return ret
96 def __str__(self):
97 return self.include_path() + self.in_defn() + self.loc()
100 class QAPIError(Exception):
101 def __init__(self, info, col, msg):
102 Exception.__init__(self)
103 self.info = info
104 self.col = col
105 self.msg = msg
107 def __str__(self):
108 loc = str(self.info)
109 if self.col is not None:
110 assert self.info.line is not None
111 loc += ':%s' % self.col
112 return loc + ': ' + self.msg
115 class QAPIParseError(QAPIError):
116 def __init__(self, parser, msg):
117 col = 1
118 for ch in parser.src[parser.line_pos:parser.pos]:
119 if ch == '\t':
120 col = (col + 7) % 8 + 1
121 else:
122 col += 1
123 QAPIError.__init__(self, parser.info, col, msg)
126 class QAPISemError(QAPIError):
127 def __init__(self, info, msg):
128 QAPIError.__init__(self, info, None, msg)
131 class QAPIDoc(object):
133 A documentation comment block, either definition or free-form
135 Definition documentation blocks consist of
137 * a body section: one line naming the definition, followed by an
138 overview (any number of lines)
140 * argument sections: a description of each argument (for commands
141 and events) or member (for structs, unions and alternates)
143 * features sections: a description of each feature flag
145 * additional (non-argument) sections, possibly tagged
147 Free-form documentation blocks consist only of a body section.
150 class Section(object):
151 def __init__(self, name=None):
152 # optional section name (argument/member or section name)
153 self.name = name
154 # the list of lines for this section
155 self.text = ''
157 def append(self, line):
158 self.text += line.rstrip() + '\n'
160 class ArgSection(Section):
161 def __init__(self, name):
162 QAPIDoc.Section.__init__(self, name)
163 self.member = None
165 def connect(self, member):
166 self.member = member
168 def __init__(self, parser, info):
169 # self._parser is used to report errors with QAPIParseError. The
170 # resulting error position depends on the state of the parser.
171 # It happens to be the beginning of the comment. More or less
172 # servicable, but action at a distance.
173 self._parser = parser
174 self.info = info
175 self.symbol = None
176 self.body = QAPIDoc.Section()
177 # dict mapping parameter name to ArgSection
178 self.args = OrderedDict()
179 self.features = OrderedDict()
180 # a list of Section
181 self.sections = []
182 # the current section
183 self._section = self.body
184 self._append_line = self._append_body_line
186 def has_section(self, name):
187 """Return True if we have a section with this name."""
188 for i in self.sections:
189 if i.name == name:
190 return True
191 return False
193 def append(self, line):
195 Parse a comment line and add it to the documentation.
197 The way that the line is dealt with depends on which part of
198 the documentation we're parsing right now:
199 * The body section: ._append_line is ._append_body_line
200 * An argument section: ._append_line is ._append_args_line
201 * A features section: ._append_line is ._append_features_line
202 * An additional section: ._append_line is ._append_various_line
204 line = line[1:]
205 if not line:
206 self._append_freeform(line)
207 return
209 if line[0] != ' ':
210 raise QAPIParseError(self._parser, "missing space after #")
211 line = line[1:]
212 self._append_line(line)
214 def end_comment(self):
215 self._end_section()
217 @staticmethod
218 def _is_section_tag(name):
219 return name in ('Returns:', 'Since:',
220 # those are often singular or plural
221 'Note:', 'Notes:',
222 'Example:', 'Examples:',
223 'TODO:')
225 def _append_body_line(self, line):
227 Process a line of documentation text in the body section.
229 If this a symbol line and it is the section's first line, this
230 is a definition documentation block for that symbol.
232 If it's a definition documentation block, another symbol line
233 begins the argument section for the argument named by it, and
234 a section tag begins an additional section. Start that
235 section and append the line to it.
237 Else, append the line to the current section.
239 name = line.split(' ', 1)[0]
240 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
241 # recognized, and get silently treated as ordinary text
242 if not self.symbol and not self.body.text and line.startswith('@'):
243 if not line.endswith(':'):
244 raise QAPIParseError(self._parser, "line should end with ':'")
245 self.symbol = line[1:-1]
246 # FIXME invalid names other than the empty string aren't flagged
247 if not self.symbol:
248 raise QAPIParseError(self._parser, "invalid name")
249 elif self.symbol:
250 # This is a definition documentation block
251 if name.startswith('@') and name.endswith(':'):
252 self._append_line = self._append_args_line
253 self._append_args_line(line)
254 elif line == 'Features:':
255 self._append_line = self._append_features_line
256 elif self._is_section_tag(name):
257 self._append_line = self._append_various_line
258 self._append_various_line(line)
259 else:
260 self._append_freeform(line.strip())
261 else:
262 # This is a free-form documentation block
263 self._append_freeform(line.strip())
265 def _append_args_line(self, line):
267 Process a line of documentation text in an argument section.
269 A symbol line begins the next argument section, a section tag
270 section or a non-indented line after a blank line begins an
271 additional section. Start that section and append the line to
274 Else, append the line to the current section.
277 name = line.split(' ', 1)[0]
279 if name.startswith('@') and name.endswith(':'):
280 line = line[len(name)+1:]
281 self._start_args_section(name[1:-1])
282 elif self._is_section_tag(name):
283 self._append_line = self._append_various_line
284 self._append_various_line(line)
285 return
286 elif (self._section.text.endswith('\n\n')
287 and line and not line[0].isspace()):
288 if line == 'Features:':
289 self._append_line = self._append_features_line
290 else:
291 self._start_section()
292 self._append_line = self._append_various_line
293 self._append_various_line(line)
294 return
296 self._append_freeform(line.strip())
298 def _append_features_line(self, line):
299 name = line.split(' ', 1)[0]
301 if name.startswith('@') and name.endswith(':'):
302 line = line[len(name)+1:]
303 self._start_features_section(name[1:-1])
304 elif self._is_section_tag(name):
305 self._append_line = self._append_various_line
306 self._append_various_line(line)
307 return
308 elif (self._section.text.endswith('\n\n')
309 and line and not line[0].isspace()):
310 self._start_section()
311 self._append_line = self._append_various_line
312 self._append_various_line(line)
313 return
315 self._append_freeform(line.strip())
317 def _append_various_line(self, line):
319 Process a line of documentation text in an additional section.
321 A symbol line is an error.
323 A section tag begins an additional section. Start that
324 section and append the line to it.
326 Else, append the line to the current section.
328 name = line.split(' ', 1)[0]
330 if name.startswith('@') and name.endswith(':'):
331 raise QAPIParseError(self._parser,
332 "'%s' can't follow '%s' section"
333 % (name, self.sections[0].name))
334 elif self._is_section_tag(name):
335 line = line[len(name)+1:]
336 self._start_section(name[:-1])
338 if (not self._section.name or
339 not self._section.name.startswith('Example')):
340 line = line.strip()
342 self._append_freeform(line)
344 def _start_symbol_section(self, symbols_dict, name):
345 # FIXME invalid names other than the empty string aren't flagged
346 if not name:
347 raise QAPIParseError(self._parser, "invalid parameter name")
348 if name in symbols_dict:
349 raise QAPIParseError(self._parser,
350 "'%s' parameter name duplicated" % name)
351 assert not self.sections
352 self._end_section()
353 self._section = QAPIDoc.ArgSection(name)
354 symbols_dict[name] = self._section
356 def _start_args_section(self, name):
357 self._start_symbol_section(self.args, name)
359 def _start_features_section(self, name):
360 self._start_symbol_section(self.features, name)
362 def _start_section(self, name=None):
363 if name in ('Returns', 'Since') and self.has_section(name):
364 raise QAPIParseError(self._parser,
365 "duplicated '%s' section" % name)
366 self._end_section()
367 self._section = QAPIDoc.Section(name)
368 self.sections.append(self._section)
370 def _end_section(self):
371 if self._section:
372 text = self._section.text = self._section.text.strip()
373 if self._section.name and (not text or text.isspace()):
374 raise QAPIParseError(
375 self._parser,
376 "empty doc section '%s'" % self._section.name)
377 self._section = None
379 def _append_freeform(self, line):
380 match = re.match(r'(@\S+:)', line)
381 if match:
382 raise QAPIParseError(self._parser,
383 "'%s' not allowed in free-form documentation"
384 % match.group(1))
385 self._section.append(line)
387 def connect_member(self, member):
388 if member.name not in self.args:
389 # Undocumented TODO outlaw
390 self.args[member.name] = QAPIDoc.ArgSection(member.name)
391 self.args[member.name].connect(member)
393 def check_expr(self, expr):
394 if self.has_section('Returns') and 'command' not in expr:
395 raise QAPISemError(self.info,
396 "'Returns:' is only valid for commands")
398 def check(self):
399 bogus = [name for name, section in self.args.items()
400 if not section.member]
401 if bogus:
402 raise QAPISemError(
403 self.info,
404 "the following documented members are not in "
405 "the declaration: %s" % ", ".join(bogus))
408 class QAPISchemaParser(object):
410 def __init__(self, fp, previously_included=[], incl_info=None):
411 self.fname = fp.name
412 previously_included.append(os.path.abspath(fp.name))
413 self.src = fp.read()
414 if self.src == '' or self.src[-1] != '\n':
415 self.src += '\n'
416 self.cursor = 0
417 self.info = QAPISourceInfo(self.fname, 1, incl_info)
418 self.line_pos = 0
419 self.exprs = []
420 self.docs = []
421 self.accept()
422 cur_doc = None
424 while self.tok is not None:
425 info = self.info
426 if self.tok == '#':
427 self.reject_expr_doc(cur_doc)
428 cur_doc = self.get_doc(info)
429 self.docs.append(cur_doc)
430 continue
432 expr = self.get_expr(False)
433 if 'include' in expr:
434 self.reject_expr_doc(cur_doc)
435 if len(expr) != 1:
436 raise QAPISemError(info, "invalid 'include' directive")
437 include = expr['include']
438 if not isinstance(include, str):
439 raise QAPISemError(info,
440 "value of 'include' must be a string")
441 incl_fname = os.path.join(os.path.dirname(self.fname),
442 include)
443 self.exprs.append({'expr': {'include': incl_fname},
444 'info': info})
445 exprs_include = self._include(include, info, incl_fname,
446 previously_included)
447 if exprs_include:
448 self.exprs.extend(exprs_include.exprs)
449 self.docs.extend(exprs_include.docs)
450 elif "pragma" in expr:
451 self.reject_expr_doc(cur_doc)
452 if len(expr) != 1:
453 raise QAPISemError(info, "invalid 'pragma' directive")
454 pragma = expr['pragma']
455 if not isinstance(pragma, dict):
456 raise QAPISemError(
457 info, "value of 'pragma' must be an object")
458 for name, value in pragma.items():
459 self._pragma(name, value, info)
460 else:
461 expr_elem = {'expr': expr,
462 'info': info}
463 if cur_doc:
464 if not cur_doc.symbol:
465 raise QAPISemError(
466 cur_doc.info, "definition documentation required")
467 expr_elem['doc'] = cur_doc
468 self.exprs.append(expr_elem)
469 cur_doc = None
470 self.reject_expr_doc(cur_doc)
472 @staticmethod
473 def reject_expr_doc(doc):
474 if doc and doc.symbol:
475 raise QAPISemError(
476 doc.info,
477 "documentation for '%s' is not followed by the definition"
478 % doc.symbol)
480 def _include(self, include, info, incl_fname, previously_included):
481 incl_abs_fname = os.path.abspath(incl_fname)
482 # catch inclusion cycle
483 inf = info
484 while inf:
485 if incl_abs_fname == os.path.abspath(inf.fname):
486 raise QAPISemError(info, "inclusion loop for %s" % include)
487 inf = inf.parent
489 # skip multiple include of the same file
490 if incl_abs_fname in previously_included:
491 return None
493 try:
494 if sys.version_info[0] >= 3:
495 fobj = open(incl_fname, 'r', encoding='utf-8')
496 else:
497 fobj = open(incl_fname, 'r')
498 except IOError as e:
499 raise QAPISemError(info, "%s: %s" % (e.strerror, incl_fname))
500 return QAPISchemaParser(fobj, previously_included, info)
502 def _pragma(self, name, value, info):
503 global doc_required, returns_whitelist, name_case_whitelist
504 if name == 'doc-required':
505 if not isinstance(value, bool):
506 raise QAPISemError(info,
507 "pragma 'doc-required' must be boolean")
508 doc_required = value
509 elif name == 'returns-whitelist':
510 if (not isinstance(value, list)
511 or any([not isinstance(elt, str) for elt in value])):
512 raise QAPISemError(
513 info,
514 "pragma returns-whitelist must be a list of strings")
515 returns_whitelist = value
516 elif name == 'name-case-whitelist':
517 if (not isinstance(value, list)
518 or any([not isinstance(elt, str) for elt in value])):
519 raise QAPISemError(
520 info,
521 "pragma name-case-whitelist must be a list of strings")
522 name_case_whitelist = value
523 else:
524 raise QAPISemError(info, "unknown pragma '%s'" % name)
526 def accept(self, skip_comment=True):
527 while True:
528 self.tok = self.src[self.cursor]
529 self.pos = self.cursor
530 self.cursor += 1
531 self.val = None
533 if self.tok == '#':
534 if self.src[self.cursor] == '#':
535 # Start of doc comment
536 skip_comment = False
537 self.cursor = self.src.find('\n', self.cursor)
538 if not skip_comment:
539 self.val = self.src[self.pos:self.cursor]
540 return
541 elif self.tok in '{}:,[]':
542 return
543 elif self.tok == "'":
544 # Note: we accept only printable ASCII
545 string = ''
546 esc = False
547 while True:
548 ch = self.src[self.cursor]
549 self.cursor += 1
550 if ch == '\n':
551 raise QAPIParseError(self, "missing terminating \"'\"")
552 if esc:
553 # Note: we recognize only \\ because we have
554 # no use for funny characters in strings
555 if ch != '\\':
556 raise QAPIParseError(self,
557 "unknown escape \\%s" % ch)
558 esc = False
559 elif ch == '\\':
560 esc = True
561 continue
562 elif ch == "'":
563 self.val = string
564 return
565 if ord(ch) < 32 or ord(ch) >= 127:
566 raise QAPIParseError(
567 self, "funny character in string")
568 string += ch
569 elif self.src.startswith('true', self.pos):
570 self.val = True
571 self.cursor += 3
572 return
573 elif self.src.startswith('false', self.pos):
574 self.val = False
575 self.cursor += 4
576 return
577 elif self.tok == '\n':
578 if self.cursor == len(self.src):
579 self.tok = None
580 return
581 self.info = self.info.next_line()
582 self.line_pos = self.cursor
583 elif not self.tok.isspace():
584 # Show up to next structural, whitespace or quote
585 # character
586 match = re.match('[^[\\]{}:,\\s\'"]+',
587 self.src[self.cursor-1:])
588 raise QAPIParseError(self, "stray '%s'" % match.group(0))
590 def get_members(self):
591 expr = OrderedDict()
592 if self.tok == '}':
593 self.accept()
594 return expr
595 if self.tok != "'":
596 raise QAPIParseError(self, "expected string or '}'")
597 while True:
598 key = self.val
599 self.accept()
600 if self.tok != ':':
601 raise QAPIParseError(self, "expected ':'")
602 self.accept()
603 if key in expr:
604 raise QAPIParseError(self, "duplicate key '%s'" % key)
605 expr[key] = self.get_expr(True)
606 if self.tok == '}':
607 self.accept()
608 return expr
609 if self.tok != ',':
610 raise QAPIParseError(self, "expected ',' or '}'")
611 self.accept()
612 if self.tok != "'":
613 raise QAPIParseError(self, "expected string")
615 def get_values(self):
616 expr = []
617 if self.tok == ']':
618 self.accept()
619 return expr
620 if self.tok not in "{['tfn":
621 raise QAPIParseError(
622 self, "expected '{', '[', ']', string, boolean or 'null'")
623 while True:
624 expr.append(self.get_expr(True))
625 if self.tok == ']':
626 self.accept()
627 return expr
628 if self.tok != ',':
629 raise QAPIParseError(self, "expected ',' or ']'")
630 self.accept()
632 def get_expr(self, nested):
633 if self.tok != '{' and not nested:
634 raise QAPIParseError(self, "expected '{'")
635 if self.tok == '{':
636 self.accept()
637 expr = self.get_members()
638 elif self.tok == '[':
639 self.accept()
640 expr = self.get_values()
641 elif self.tok in "'tfn":
642 expr = self.val
643 self.accept()
644 else:
645 raise QAPIParseError(
646 self, "expected '{', '[', string, boolean or 'null'")
647 return expr
649 def get_doc(self, info):
650 if self.val != '##':
651 raise QAPIParseError(
652 self, "junk after '##' at start of documentation comment")
654 doc = QAPIDoc(self, info)
655 self.accept(False)
656 while self.tok == '#':
657 if self.val.startswith('##'):
658 # End of doc comment
659 if self.val != '##':
660 raise QAPIParseError(
661 self,
662 "junk after '##' at end of documentation comment")
663 doc.end_comment()
664 self.accept()
665 return doc
666 else:
667 doc.append(self.val)
668 self.accept(False)
670 raise QAPIParseError(self, "documentation comment must end with '##'")
674 # Semantic analysis of schema expressions
675 # TODO fold into QAPISchema
676 # TODO catching name collisions in generated code would be nice
680 def find_base_members(base):
681 if isinstance(base, dict):
682 return base
683 base_struct_define = struct_types.get(base)
684 if not base_struct_define:
685 return None
686 return base_struct_define['data']
689 # Return the qtype of an alternate branch, or None on error.
690 def find_alternate_member_qtype(qapi_type):
691 if qapi_type in builtin_types:
692 return builtin_types[qapi_type]
693 elif qapi_type in struct_types:
694 return 'QTYPE_QDICT'
695 elif qapi_type in enum_types:
696 return 'QTYPE_QSTRING'
697 elif qapi_type in union_types:
698 return 'QTYPE_QDICT'
699 return None
702 # Names must be letters, numbers, -, and _. They must start with letter,
703 # except for downstream extensions which must start with __RFQDN_.
704 # Dots are only valid in the downstream extension prefix.
705 valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
706 '[a-zA-Z][a-zA-Z0-9_-]*$')
709 def check_name(info, source, name,
710 allow_optional=False, enum_member=False, permit_upper=False):
711 global valid_name
712 membername = name
714 if not isinstance(name, str):
715 raise QAPISemError(info, "%s requires a string name" % source)
716 if name.startswith('*'):
717 membername = name[1:]
718 if not allow_optional:
719 raise QAPISemError(info, "%s does not allow optional name '%s'"
720 % (source, name))
721 # Enum members can start with a digit, because the generated C
722 # code always prefixes it with the enum name
723 if enum_member and membername[0].isdigit():
724 membername = 'D' + membername
725 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
726 # and 'q_obj_*' implicit type names.
727 if not valid_name.match(membername) or \
728 c_name(membername, False).startswith('q_'):
729 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
730 if not permit_upper and name.lower() != name:
731 raise QAPISemError(
732 info, "%s uses uppercase in name '%s'" % (source, name))
735 def add_name(name, info, meta):
736 global all_names
737 check_name(info, "'%s'" % meta, name, permit_upper=True)
738 # FIXME should reject names that differ only in '_' vs. '.'
739 # vs. '-', because they're liable to clash in generated C.
740 if name in all_names:
741 raise QAPISemError(info, "%s '%s' is already defined"
742 % (all_names[name], name))
743 if name.endswith('Kind') or name.endswith('List'):
744 raise QAPISemError(info, "%s '%s' should not end in '%s'"
745 % (meta, name, name[-4:]))
746 all_names[name] = meta
749 def check_if(expr, info):
751 def check_if_str(ifcond, info):
752 if not isinstance(ifcond, str):
753 raise QAPISemError(
754 info, "'if' condition must be a string or a list of strings")
755 if ifcond.strip() == '':
756 raise QAPISemError(info, "'if' condition '%s' makes no sense"
757 % ifcond)
759 ifcond = expr.get('if')
760 if ifcond is None:
761 return
762 if isinstance(ifcond, list):
763 if ifcond == []:
764 raise QAPISemError(info, "'if' condition [] is useless")
765 for elt in ifcond:
766 check_if_str(elt, info)
767 else:
768 check_if_str(ifcond, info)
771 def check_type(info, source, value,
772 allow_array=False, allow_dict=False, allow_metas=[]):
773 global all_names
775 if value is None:
776 return
778 # Check if array type for value is okay
779 if isinstance(value, list):
780 if not allow_array:
781 raise QAPISemError(info, "%s cannot be an array" % source)
782 if len(value) != 1 or not isinstance(value[0], str):
783 raise QAPISemError(info,
784 "%s: array type must contain single type name" %
785 source)
786 value = value[0]
788 # Check if type name for value is okay
789 if isinstance(value, str):
790 if value not in all_names:
791 raise QAPISemError(info, "%s uses unknown type '%s'"
792 % (source, value))
793 if not all_names[value] in allow_metas:
794 raise QAPISemError(info, "%s cannot use %s type '%s'" %
795 (source, all_names[value], value))
796 return
798 if not allow_dict:
799 raise QAPISemError(info, "%s should be a type name" % source)
801 if not isinstance(value, OrderedDict):
802 raise QAPISemError(info,
803 "%s should be an object or type name" % source)
805 permit_upper = allow_dict in name_case_whitelist
807 # value is a dictionary, check that each member is okay
808 for (key, arg) in value.items():
809 check_name(info, "member of %s" % source, key,
810 allow_optional=True, permit_upper=permit_upper)
811 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
812 raise QAPISemError(
813 info, "member of %s uses reserved name '%s'" % (source, key))
814 # Todo: allow dictionaries to represent default values of
815 # an optional argument.
816 check_known_keys(info, "member '%s' of %s" % (key, source),
817 arg, ['type'], ['if'])
818 check_if(arg, info)
819 normalize_if(arg)
820 check_type(info, "member '%s' of %s" % (key, source),
821 arg['type'], allow_array=True,
822 allow_metas=['built-in', 'union', 'alternate', 'struct',
823 'enum'])
826 def check_command(expr, info):
827 name = expr['command']
828 boxed = expr.get('boxed', False)
830 args_meta = ['struct']
831 if boxed:
832 args_meta += ['union']
833 check_type(info, "'data' for command '%s'" % name,
834 expr.get('data'), allow_dict=not boxed,
835 allow_metas=args_meta)
836 returns_meta = ['union', 'struct']
837 if name in returns_whitelist:
838 returns_meta += ['built-in', 'alternate', 'enum']
839 check_type(info, "'returns' for command '%s'" % name,
840 expr.get('returns'), allow_array=True,
841 allow_metas=returns_meta)
844 def check_event(expr, info):
845 name = expr['event']
846 boxed = expr.get('boxed', False)
848 meta = ['struct']
849 if boxed:
850 meta += ['union']
851 check_type(info, "'data' for event '%s'" % name,
852 expr.get('data'), allow_dict=not boxed,
853 allow_metas=meta)
856 def enum_get_names(expr):
857 return [e['name'] for e in expr['data']]
860 def check_union(expr, info):
861 name = expr['union']
862 base = expr.get('base')
863 discriminator = expr.get('discriminator')
864 members = expr['data']
866 # Two types of unions, determined by discriminator.
868 # With no discriminator it is a simple union.
869 if discriminator is None:
870 enum_values = members.keys()
871 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
872 if base is not None:
873 raise QAPISemError(
874 info, "simple union '%s' must not have a base" % name)
876 # Else, it's a flat union.
877 else:
878 # The object must have a string or dictionary 'base'.
879 check_type(info, "'base' for union '%s'" % name,
880 base, allow_dict=name,
881 allow_metas=['struct'])
882 if not base:
883 raise QAPISemError(
884 info, "flat union '%s' must have a base" % name)
885 base_members = find_base_members(base)
886 assert base_members is not None
888 # The value of member 'discriminator' must name a non-optional
889 # member of the base struct.
890 check_name(info, "discriminator of flat union '%s'" % name,
891 discriminator)
892 discriminator_value = base_members.get(discriminator)
893 if not discriminator_value:
894 raise QAPISemError(info,
895 "discriminator '%s' is not a member of 'base'"
896 % discriminator)
897 if discriminator_value.get('if'):
898 raise QAPISemError(
899 info,
900 "the discriminator '%s' for union %s must not be conditional"
901 % (discriminator, name))
902 enum_define = enum_types.get(discriminator_value['type'])
903 # Do not allow string discriminator
904 if not enum_define:
905 raise QAPISemError(
906 info,
907 "discriminator '%s' must be of enumeration type"
908 % discriminator)
909 enum_values = enum_get_names(enum_define)
910 allow_metas = ['struct']
912 if (len(enum_values) == 0):
913 raise QAPISemError(info, "union '%s' has no branches" % name)
915 for (key, value) in members.items():
916 check_name(info, "member of union '%s'" % name, key)
918 check_known_keys(info, "member '%s' of union '%s'" % (key, name),
919 value, ['type'], ['if'])
920 check_if(value, info)
921 normalize_if(value)
922 # Each value must name a known type
923 check_type(info, "member '%s' of union '%s'" % (key, name),
924 value['type'],
925 allow_array=not base, allow_metas=allow_metas)
927 # If the discriminator names an enum type, then all members
928 # of 'data' must also be members of the enum type.
929 if discriminator is not None:
930 if key not in enum_values:
931 raise QAPISemError(
932 info,
933 "discriminator value '%s' is not found in enum '%s'"
934 % (key, enum_define['enum']))
937 def check_alternate(expr, info):
938 name = expr['alternate']
939 members = expr['data']
940 types_seen = {}
942 if len(members) == 0:
943 raise QAPISemError(info,
944 "alternate '%s' cannot have empty 'data'" % name)
945 for (key, value) in members.items():
946 check_name(info, "member of alternate '%s'" % name, key)
947 check_known_keys(info,
948 "member '%s' of alternate '%s'" % (key, name),
949 value, ['type'], ['if'])
950 check_if(value, info)
951 normalize_if(value)
952 typ = value['type']
954 # Ensure alternates have no type conflicts.
955 check_type(info, "member '%s' of alternate '%s'" % (key, name), typ,
956 allow_metas=['built-in', 'union', 'struct', 'enum'])
957 qtype = find_alternate_member_qtype(typ)
958 if not qtype:
959 raise QAPISemError(
960 info,
961 "alternate '%s' member '%s' cannot use type '%s'"
962 % (name, key, typ))
963 conflicting = set([qtype])
964 if qtype == 'QTYPE_QSTRING':
965 enum_expr = enum_types.get(typ)
966 if enum_expr:
967 for v in enum_get_names(enum_expr):
968 if v in ['on', 'off']:
969 conflicting.add('QTYPE_QBOOL')
970 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
971 conflicting.add('QTYPE_QNUM')
972 else:
973 conflicting.add('QTYPE_QNUM')
974 conflicting.add('QTYPE_QBOOL')
975 for qt in conflicting:
976 if qt in types_seen:
977 raise QAPISemError(
978 info,
979 "alternate '%s' member '%s' can't be distinguished "
980 "from member '%s'"
981 % (name, key, types_seen[qt]))
982 types_seen[qt] = key
985 def check_enum(expr, info):
986 name = expr['enum']
987 members = expr['data']
988 prefix = expr.get('prefix')
990 if not isinstance(members, list):
991 raise QAPISemError(info,
992 "enum '%s' requires an array for 'data'" % name)
993 if prefix is not None and not isinstance(prefix, str):
994 raise QAPISemError(info,
995 "enum '%s' requires a string for 'prefix'" % name)
997 permit_upper = name in name_case_whitelist
999 for member in members:
1000 check_known_keys(info, "member of enum '%s'" % name, member,
1001 ['name'], ['if'])
1002 check_if(member, info)
1003 normalize_if(member)
1004 check_name(info, "member of enum '%s'" % name, member['name'],
1005 enum_member=True, permit_upper=permit_upper)
1008 def check_struct(expr, info):
1009 name = expr['struct']
1010 members = expr['data']
1011 features = expr.get('features')
1013 check_type(info, "'data' for struct '%s'" % name, members,
1014 allow_dict=name)
1015 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
1016 allow_metas=['struct'])
1018 if features:
1019 if not isinstance(features, list):
1020 raise QAPISemError(
1021 info, "struct '%s' requires an array for 'features'" % name)
1022 for f in features:
1023 assert isinstance(f, dict)
1024 check_known_keys(info, "feature of struct %s" % name, f,
1025 ['name'], ['if'])
1027 check_if(f, info)
1028 normalize_if(f)
1029 check_name(info, "feature of struct %s" % name, f['name'])
1032 def check_known_keys(info, source, value, required, optional):
1034 def pprint(elems):
1035 return ', '.join("'" + e + "'" for e in sorted(elems))
1037 missing = set(required) - set(value)
1038 if missing:
1039 raise QAPISemError(
1040 info,
1041 "key%s %s %s missing from %s"
1042 % ('s' if len(missing) > 1 else '', pprint(missing),
1043 'are' if len(missing) > 1 else 'is', source))
1044 allowed = set(required + optional)
1045 unknown = set(value) - allowed
1046 if unknown:
1047 raise QAPISemError(
1048 info,
1049 "unknown key%s %s in %s\nValid keys are %s."
1050 % ('s' if len(unknown) > 1 else '', pprint(unknown),
1051 source, pprint(allowed)))
1054 def check_keys(expr, info, meta, required, optional=[]):
1055 name = expr[meta]
1056 if not isinstance(name, str):
1057 raise QAPISemError(info, "'%s' key must have a string value" % meta)
1058 required = required + [meta]
1059 source = "%s '%s'" % (meta, name)
1060 check_known_keys(info, source, expr, required, optional)
1061 for (key, value) in expr.items():
1062 if key in ['gen', 'success-response'] and value is not False:
1063 raise QAPISemError(info,
1064 "'%s' of %s '%s' should only use false value"
1065 % (key, meta, name))
1066 if (key in ['boxed', 'allow-oob', 'allow-preconfig']
1067 and value is not True):
1068 raise QAPISemError(info,
1069 "'%s' of %s '%s' should only use true value"
1070 % (key, meta, name))
1071 if key == 'if':
1072 check_if(expr, info)
1075 def normalize_enum(expr):
1076 if isinstance(expr['data'], list):
1077 expr['data'] = [m if isinstance(m, dict) else {'name': m}
1078 for m in expr['data']]
1081 def normalize_members(members):
1082 if isinstance(members, OrderedDict):
1083 for key, arg in members.items():
1084 if isinstance(arg, dict):
1085 continue
1086 members[key] = {'type': arg}
1089 def normalize_features(features):
1090 if isinstance(features, list):
1091 features[:] = [f if isinstance(f, dict) else {'name': f}
1092 for f in features]
1095 def normalize_if(expr):
1096 ifcond = expr.get('if')
1097 if isinstance(ifcond, str):
1098 expr['if'] = [ifcond]
1101 def check_exprs(exprs):
1102 global all_names
1104 # Populate name table with names of built-in types
1105 for builtin in builtin_types.keys():
1106 all_names[builtin] = 'built-in'
1108 # Learn the types and check for valid expression keys
1109 for expr_elem in exprs:
1110 expr = expr_elem['expr']
1111 info = expr_elem['info']
1112 doc = expr_elem.get('doc')
1114 if 'include' in expr:
1115 continue
1117 if not doc and doc_required:
1118 raise QAPISemError(info,
1119 "definition missing documentation comment")
1121 if 'enum' in expr:
1122 meta = 'enum'
1123 check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
1124 normalize_enum(expr)
1125 enum_types[expr[meta]] = expr
1126 elif 'union' in expr:
1127 meta = 'union'
1128 check_keys(expr, info, 'union', ['data'],
1129 ['base', 'discriminator', 'if'])
1130 normalize_members(expr.get('base'))
1131 normalize_members(expr['data'])
1132 union_types[expr[meta]] = expr
1133 elif 'alternate' in expr:
1134 meta = 'alternate'
1135 check_keys(expr, info, 'alternate', ['data'], ['if'])
1136 normalize_members(expr['data'])
1137 elif 'struct' in expr:
1138 meta = 'struct'
1139 check_keys(expr, info, 'struct', ['data'],
1140 ['base', 'if', 'features'])
1141 normalize_members(expr['data'])
1142 normalize_features(expr.get('features'))
1143 struct_types[expr[meta]] = expr
1144 elif 'command' in expr:
1145 meta = 'command'
1146 check_keys(expr, info, 'command', [],
1147 ['data', 'returns', 'gen', 'success-response',
1148 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
1149 normalize_members(expr.get('data'))
1150 elif 'event' in expr:
1151 meta = 'event'
1152 check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
1153 normalize_members(expr.get('data'))
1154 else:
1155 raise QAPISemError(info, "expression is missing metatype")
1156 normalize_if(expr)
1157 name = expr[meta]
1158 add_name(name, info, meta)
1159 info.set_defn(meta, name)
1160 if doc and doc.symbol != name:
1161 raise QAPISemError(
1162 info,
1163 "definition of '%s' follows documentation for '%s'"
1164 % (name, doc.symbol))
1166 # Validate that exprs make sense
1167 for expr_elem in exprs:
1168 expr = expr_elem['expr']
1169 info = expr_elem['info']
1170 doc = expr_elem.get('doc')
1172 if 'include' in expr:
1173 continue
1174 if 'enum' in expr:
1175 check_enum(expr, info)
1176 elif 'union' in expr:
1177 check_union(expr, info)
1178 elif 'alternate' in expr:
1179 check_alternate(expr, info)
1180 elif 'struct' in expr:
1181 check_struct(expr, info)
1182 elif 'command' in expr:
1183 check_command(expr, info)
1184 elif 'event' in expr:
1185 check_event(expr, info)
1186 else:
1187 assert False, 'unexpected meta type'
1189 if doc:
1190 doc.check_expr(expr)
1192 return exprs
1196 # Schema compiler frontend
1199 class QAPISchemaEntity(object):
1200 def __init__(self, name, info, doc, ifcond=None):
1201 assert name is None or isinstance(name, str)
1202 self.name = name
1203 self._module = None
1204 # For explicitly defined entities, info points to the (explicit)
1205 # definition. For builtins (and their arrays), info is None.
1206 # For implicitly defined entities, info points to a place that
1207 # triggered the implicit definition (there may be more than one
1208 # such place).
1209 self.info = info
1210 self.doc = doc
1211 self._ifcond = ifcond or []
1212 self._checked = False
1214 def c_name(self):
1215 return c_name(self.name)
1217 def check(self, schema):
1218 assert not self._checked
1219 if self.info:
1220 self._module = os.path.relpath(self.info.fname,
1221 os.path.dirname(schema.fname))
1222 self._checked = True
1224 @property
1225 def ifcond(self):
1226 assert self._checked
1227 return self._ifcond
1229 @property
1230 def module(self):
1231 assert self._checked
1232 return self._module
1234 def is_implicit(self):
1235 return not self.info
1237 def visit(self, visitor):
1238 assert self._checked
1241 class QAPISchemaVisitor(object):
1242 def visit_begin(self, schema):
1243 pass
1245 def visit_end(self):
1246 pass
1248 def visit_module(self, fname):
1249 pass
1251 def visit_needed(self, entity):
1252 # Default to visiting everything
1253 return True
1255 def visit_include(self, fname, info):
1256 pass
1258 def visit_builtin_type(self, name, info, json_type):
1259 pass
1261 def visit_enum_type(self, name, info, ifcond, members, prefix):
1262 pass
1264 def visit_array_type(self, name, info, ifcond, element_type):
1265 pass
1267 def visit_object_type(self, name, info, ifcond, base, members, variants,
1268 features):
1269 pass
1271 def visit_object_type_flat(self, name, info, ifcond, members, variants,
1272 features):
1273 pass
1275 def visit_alternate_type(self, name, info, ifcond, variants):
1276 pass
1278 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1279 success_response, boxed, allow_oob, allow_preconfig):
1280 pass
1282 def visit_event(self, name, info, ifcond, arg_type, boxed):
1283 pass
1286 class QAPISchemaInclude(QAPISchemaEntity):
1288 def __init__(self, fname, info):
1289 QAPISchemaEntity.__init__(self, None, info, None)
1290 self.fname = fname
1292 def visit(self, visitor):
1293 QAPISchemaEntity.visit(self, visitor)
1294 visitor.visit_include(self.fname, self.info)
1297 class QAPISchemaType(QAPISchemaEntity):
1298 # Return the C type for common use.
1299 # For the types we commonly box, this is a pointer type.
1300 def c_type(self):
1301 pass
1303 # Return the C type to be used in a parameter list.
1304 def c_param_type(self):
1305 return self.c_type()
1307 # Return the C type to be used where we suppress boxing.
1308 def c_unboxed_type(self):
1309 return self.c_type()
1311 def json_type(self):
1312 pass
1314 def alternate_qtype(self):
1315 json2qtype = {
1316 'null': 'QTYPE_QNULL',
1317 'string': 'QTYPE_QSTRING',
1318 'number': 'QTYPE_QNUM',
1319 'int': 'QTYPE_QNUM',
1320 'boolean': 'QTYPE_QBOOL',
1321 'object': 'QTYPE_QDICT'
1323 return json2qtype.get(self.json_type())
1325 def doc_type(self):
1326 if self.is_implicit():
1327 return None
1328 return self.name
1331 class QAPISchemaBuiltinType(QAPISchemaType):
1332 def __init__(self, name, json_type, c_type):
1333 QAPISchemaType.__init__(self, name, None, None)
1334 assert not c_type or isinstance(c_type, str)
1335 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1336 'value')
1337 self._json_type_name = json_type
1338 self._c_type_name = c_type
1340 def c_name(self):
1341 return self.name
1343 def c_type(self):
1344 return self._c_type_name
1346 def c_param_type(self):
1347 if self.name == 'str':
1348 return 'const ' + self._c_type_name
1349 return self._c_type_name
1351 def json_type(self):
1352 return self._json_type_name
1354 def doc_type(self):
1355 return self.json_type()
1357 def visit(self, visitor):
1358 QAPISchemaType.visit(self, visitor)
1359 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1362 class QAPISchemaEnumType(QAPISchemaType):
1363 def __init__(self, name, info, doc, ifcond, members, prefix):
1364 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1365 for m in members:
1366 assert isinstance(m, QAPISchemaEnumMember)
1367 m.set_defined_in(name)
1368 assert prefix is None or isinstance(prefix, str)
1369 self.members = members
1370 self.prefix = prefix
1372 def check(self, schema):
1373 QAPISchemaType.check(self, schema)
1374 seen = {}
1375 for m in self.members:
1376 m.check_clash(self.info, seen)
1377 if self.doc:
1378 self.doc.connect_member(m)
1380 def is_implicit(self):
1381 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1382 return self.name.endswith('Kind') or self.name == 'QType'
1384 def c_type(self):
1385 return c_name(self.name)
1387 def member_names(self):
1388 return [m.name for m in self.members]
1390 def json_type(self):
1391 return 'string'
1393 def visit(self, visitor):
1394 QAPISchemaType.visit(self, visitor)
1395 visitor.visit_enum_type(self.name, self.info, self.ifcond,
1396 self.members, self.prefix)
1399 class QAPISchemaArrayType(QAPISchemaType):
1400 def __init__(self, name, info, element_type):
1401 QAPISchemaType.__init__(self, name, info, None, None)
1402 assert isinstance(element_type, str)
1403 self._element_type_name = element_type
1404 self.element_type = None
1406 def check(self, schema):
1407 QAPISchemaType.check(self, schema)
1408 self.element_type = schema.lookup_type(self._element_type_name)
1409 assert self.element_type
1410 assert not isinstance(self.element_type, QAPISchemaArrayType)
1412 @property
1413 def ifcond(self):
1414 assert self._checked
1415 return self.element_type.ifcond
1417 @property
1418 def module(self):
1419 assert self._checked
1420 return self.element_type.module
1422 def is_implicit(self):
1423 return True
1425 def c_type(self):
1426 return c_name(self.name) + pointer_suffix
1428 def json_type(self):
1429 return 'array'
1431 def doc_type(self):
1432 elt_doc_type = self.element_type.doc_type()
1433 if not elt_doc_type:
1434 return None
1435 return 'array of ' + elt_doc_type
1437 def visit(self, visitor):
1438 QAPISchemaType.visit(self, visitor)
1439 visitor.visit_array_type(self.name, self.info, self.ifcond,
1440 self.element_type)
1443 class QAPISchemaObjectType(QAPISchemaType):
1444 def __init__(self, name, info, doc, ifcond,
1445 base, local_members, variants, features):
1446 # struct has local_members, optional base, and no variants
1447 # flat union has base, variants, and no local_members
1448 # simple union has local_members, variants, and no base
1449 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1450 assert base is None or isinstance(base, str)
1451 for m in local_members:
1452 assert isinstance(m, QAPISchemaObjectTypeMember)
1453 m.set_defined_in(name)
1454 if variants is not None:
1455 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1456 variants.set_defined_in(name)
1457 for f in features:
1458 assert isinstance(f, QAPISchemaFeature)
1459 f.set_defined_in(name)
1460 self._base_name = base
1461 self.base = None
1462 self.local_members = local_members
1463 self.variants = variants
1464 self.members = None
1465 self.features = features
1467 def check(self, schema):
1468 # This calls another type T's .check() exactly when the C
1469 # struct emitted by gen_object() contains that T's C struct
1470 # (pointers don't count).
1471 if self.members is not None:
1472 # A previous .check() completed: nothing to do
1473 return
1474 if self._checked:
1475 # Recursed: C struct contains itself
1476 raise QAPISemError(self.info,
1477 "object %s contains itself" % self.name)
1479 QAPISchemaType.check(self, schema)
1480 assert self._checked and self.members is None
1482 seen = OrderedDict()
1483 if self._base_name:
1484 self.base = schema.lookup_type(self._base_name)
1485 assert isinstance(self.base, QAPISchemaObjectType)
1486 self.base.check(schema)
1487 self.base.check_clash(self.info, seen)
1488 for m in self.local_members:
1489 m.check(schema)
1490 m.check_clash(self.info, seen)
1491 if self.doc:
1492 self.doc.connect_member(m)
1493 members = seen.values()
1495 if self.variants:
1496 self.variants.check(schema, seen)
1497 assert self.variants.tag_member in members
1498 self.variants.check_clash(self.info, seen)
1500 # Features are in a name space separate from members
1501 seen = {}
1502 for f in self.features:
1503 f.check_clash(self.info, seen)
1505 if self.doc:
1506 self.doc.check()
1508 self.members = members # mark completed
1510 # Check that the members of this type do not cause duplicate JSON members,
1511 # and update seen to track the members seen so far. Report any errors
1512 # on behalf of info, which is not necessarily self.info
1513 def check_clash(self, info, seen):
1514 assert self._checked
1515 assert not self.variants # not implemented
1516 for m in self.members:
1517 m.check_clash(info, seen)
1519 @property
1520 def ifcond(self):
1521 assert self._checked
1522 if isinstance(self._ifcond, QAPISchemaType):
1523 # Simple union wrapper type inherits from wrapped type;
1524 # see _make_implicit_object_type()
1525 return self._ifcond.ifcond
1526 return self._ifcond
1528 def is_implicit(self):
1529 # See QAPISchema._make_implicit_object_type(), as well as
1530 # _def_predefineds()
1531 return self.name.startswith('q_')
1533 def is_empty(self):
1534 assert self.members is not None
1535 return not self.members and not self.variants
1537 def c_name(self):
1538 assert self.name != 'q_empty'
1539 return QAPISchemaType.c_name(self)
1541 def c_type(self):
1542 assert not self.is_implicit()
1543 return c_name(self.name) + pointer_suffix
1545 def c_unboxed_type(self):
1546 return c_name(self.name)
1548 def json_type(self):
1549 return 'object'
1551 def visit(self, visitor):
1552 QAPISchemaType.visit(self, visitor)
1553 visitor.visit_object_type(self.name, self.info, self.ifcond,
1554 self.base, self.local_members, self.variants,
1555 self.features)
1556 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1557 self.members, self.variants,
1558 self.features)
1561 class QAPISchemaMember(object):
1562 """ Represents object members, enum members and features """
1563 role = 'member'
1565 def __init__(self, name, ifcond=None):
1566 assert isinstance(name, str)
1567 self.name = name
1568 self.ifcond = ifcond or []
1569 self.defined_in = None
1571 def set_defined_in(self, name):
1572 assert not self.defined_in
1573 self.defined_in = name
1575 def check_clash(self, info, seen):
1576 cname = c_name(self.name)
1577 if cname in seen:
1578 raise QAPISemError(info, "%s collides with %s" %
1579 (self.describe(), seen[cname].describe()))
1580 seen[cname] = self
1582 def _pretty_defined_in(self):
1583 defined_in = self.defined_in
1584 if defined_in.startswith('q_obj_'):
1585 # See QAPISchema._make_implicit_object_type() - reverse the
1586 # mapping there to create a nice human-readable description
1587 defined_in = defined_in[6:]
1588 if defined_in.endswith('-arg'):
1589 return '(parameter of %s)' % defined_in[:-4]
1590 elif defined_in.endswith('-base'):
1591 return '(base of %s)' % defined_in[:-5]
1592 else:
1593 assert defined_in.endswith('-wrapper')
1594 # Unreachable and not implemented
1595 assert False
1596 if defined_in.endswith('Kind'):
1597 # See QAPISchema._make_implicit_enum_type()
1598 return '(branch of %s)' % defined_in[:-4]
1599 return '(%s of %s)' % (self.role, defined_in)
1601 def describe(self):
1602 return "'%s' %s" % (self.name, self._pretty_defined_in())
1605 class QAPISchemaEnumMember(QAPISchemaMember):
1606 role = 'value'
1609 class QAPISchemaFeature(QAPISchemaMember):
1610 role = 'feature'
1613 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1614 def __init__(self, name, typ, optional, ifcond=None):
1615 QAPISchemaMember.__init__(self, name, ifcond)
1616 assert isinstance(typ, str)
1617 assert isinstance(optional, bool)
1618 self._type_name = typ
1619 self.type = None
1620 self.optional = optional
1622 def check(self, schema):
1623 assert self.defined_in
1624 self.type = schema.lookup_type(self._type_name)
1625 assert self.type
1628 class QAPISchemaObjectTypeVariants(object):
1629 def __init__(self, tag_name, tag_member, variants):
1630 # Flat unions pass tag_name but not tag_member.
1631 # Simple unions and alternates pass tag_member but not tag_name.
1632 # After check(), tag_member is always set, and tag_name remains
1633 # a reliable witness of being used by a flat union.
1634 assert bool(tag_member) != bool(tag_name)
1635 assert (isinstance(tag_name, str) or
1636 isinstance(tag_member, QAPISchemaObjectTypeMember))
1637 for v in variants:
1638 assert isinstance(v, QAPISchemaObjectTypeVariant)
1639 self._tag_name = tag_name
1640 self.tag_member = tag_member
1641 self.variants = variants
1643 def set_defined_in(self, name):
1644 for v in self.variants:
1645 v.set_defined_in(name)
1647 def check(self, schema, seen):
1648 if not self.tag_member: # flat union
1649 self.tag_member = seen[c_name(self._tag_name)]
1650 assert self._tag_name == self.tag_member.name
1651 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1652 assert not self.tag_member.optional
1653 assert self.tag_member.ifcond == []
1654 if self._tag_name: # flat union
1655 # branches that are not explicitly covered get an empty type
1656 cases = set([v.name for v in self.variants])
1657 for m in self.tag_member.type.members:
1658 if m.name not in cases:
1659 v = QAPISchemaObjectTypeVariant(m.name, 'q_empty',
1660 m.ifcond)
1661 v.set_defined_in(self.tag_member.defined_in)
1662 self.variants.append(v)
1663 assert self.variants
1664 for v in self.variants:
1665 v.check(schema)
1666 # Union names must match enum values; alternate names are
1667 # checked separately. Use 'seen' to tell the two apart.
1668 if seen:
1669 assert v.name in self.tag_member.type.member_names()
1670 assert (isinstance(v.type, QAPISchemaObjectType)
1671 and not v.type.variants)
1672 v.type.check(schema)
1674 def check_clash(self, info, seen):
1675 for v in self.variants:
1676 # Reset seen map for each variant, since qapi names from one
1677 # branch do not affect another branch
1678 v.type.check_clash(info, dict(seen))
1681 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1682 role = 'branch'
1684 def __init__(self, name, typ, ifcond=None):
1685 QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
1688 class QAPISchemaAlternateType(QAPISchemaType):
1689 def __init__(self, name, info, doc, ifcond, variants):
1690 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1691 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1692 assert variants.tag_member
1693 variants.set_defined_in(name)
1694 variants.tag_member.set_defined_in(self.name)
1695 self.variants = variants
1697 def check(self, schema):
1698 QAPISchemaType.check(self, schema)
1699 self.variants.tag_member.check(schema)
1700 # Not calling self.variants.check_clash(), because there's nothing
1701 # to clash with
1702 self.variants.check(schema, {})
1703 # Alternate branch names have no relation to the tag enum values;
1704 # so we have to check for potential name collisions ourselves.
1705 seen = {}
1706 for v in self.variants.variants:
1707 v.check_clash(self.info, seen)
1708 # TODO check conflicting qtypes
1709 if self.doc:
1710 self.doc.connect_member(v)
1711 if self.doc:
1712 self.doc.check()
1714 def c_type(self):
1715 return c_name(self.name) + pointer_suffix
1717 def json_type(self):
1718 return 'value'
1720 def visit(self, visitor):
1721 QAPISchemaType.visit(self, visitor)
1722 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1723 self.variants)
1726 class QAPISchemaCommand(QAPISchemaEntity):
1727 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1728 gen, success_response, boxed, allow_oob, allow_preconfig):
1729 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1730 assert not arg_type or isinstance(arg_type, str)
1731 assert not ret_type or isinstance(ret_type, str)
1732 self._arg_type_name = arg_type
1733 self.arg_type = None
1734 self._ret_type_name = ret_type
1735 self.ret_type = None
1736 self.gen = gen
1737 self.success_response = success_response
1738 self.boxed = boxed
1739 self.allow_oob = allow_oob
1740 self.allow_preconfig = allow_preconfig
1742 def check(self, schema):
1743 QAPISchemaEntity.check(self, schema)
1744 if self._arg_type_name:
1745 self.arg_type = schema.lookup_type(self._arg_type_name)
1746 assert isinstance(self.arg_type, QAPISchemaObjectType)
1747 assert not self.arg_type.variants or self.boxed
1748 elif self.boxed:
1749 raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
1750 if self._ret_type_name:
1751 self.ret_type = schema.lookup_type(self._ret_type_name)
1752 assert isinstance(self.ret_type, QAPISchemaType)
1754 def visit(self, visitor):
1755 QAPISchemaEntity.visit(self, visitor)
1756 visitor.visit_command(self.name, self.info, self.ifcond,
1757 self.arg_type, self.ret_type,
1758 self.gen, self.success_response,
1759 self.boxed, self.allow_oob,
1760 self.allow_preconfig)
1763 class QAPISchemaEvent(QAPISchemaEntity):
1764 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1765 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1766 assert not arg_type or isinstance(arg_type, str)
1767 self._arg_type_name = arg_type
1768 self.arg_type = None
1769 self.boxed = boxed
1771 def check(self, schema):
1772 QAPISchemaEntity.check(self, schema)
1773 if self._arg_type_name:
1774 self.arg_type = schema.lookup_type(self._arg_type_name)
1775 assert isinstance(self.arg_type, QAPISchemaObjectType)
1776 assert not self.arg_type.variants or self.boxed
1777 elif self.boxed:
1778 raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
1780 def visit(self, visitor):
1781 QAPISchemaEntity.visit(self, visitor)
1782 visitor.visit_event(self.name, self.info, self.ifcond,
1783 self.arg_type, self.boxed)
1786 class QAPISchema(object):
1787 def __init__(self, fname):
1788 self.fname = fname
1789 if sys.version_info[0] >= 3:
1790 f = open(fname, 'r', encoding='utf-8')
1791 else:
1792 f = open(fname, 'r')
1793 parser = QAPISchemaParser(f)
1794 exprs = check_exprs(parser.exprs)
1795 self.docs = parser.docs
1796 self._entity_list = []
1797 self._entity_dict = {}
1798 self._predefining = True
1799 self._def_predefineds()
1800 self._predefining = False
1801 self._def_exprs(exprs)
1802 self.check()
1804 def _def_entity(self, ent):
1805 # Only the predefined types are allowed to not have info
1806 assert ent.info or self._predefining
1807 assert ent.name is None or ent.name not in self._entity_dict
1808 self._entity_list.append(ent)
1809 if ent.name is not None:
1810 self._entity_dict[ent.name] = ent
1812 def lookup_entity(self, name, typ=None):
1813 ent = self._entity_dict.get(name)
1814 if typ and not isinstance(ent, typ):
1815 return None
1816 return ent
1818 def lookup_type(self, name):
1819 return self.lookup_entity(name, QAPISchemaType)
1821 def _def_include(self, expr, info, doc):
1822 include = expr['include']
1823 assert doc is None
1824 main_info = info
1825 while main_info.parent:
1826 main_info = main_info.parent
1827 fname = os.path.relpath(include, os.path.dirname(main_info.fname))
1828 self._def_entity(QAPISchemaInclude(fname, info))
1830 def _def_builtin_type(self, name, json_type, c_type):
1831 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1832 # Instantiating only the arrays that are actually used would
1833 # be nice, but we can't as long as their generated code
1834 # (qapi-builtin-types.[ch]) may be shared by some other
1835 # schema.
1836 self._make_array_type(name, None)
1838 def _def_predefineds(self):
1839 for t in [('str', 'string', 'char' + pointer_suffix),
1840 ('number', 'number', 'double'),
1841 ('int', 'int', 'int64_t'),
1842 ('int8', 'int', 'int8_t'),
1843 ('int16', 'int', 'int16_t'),
1844 ('int32', 'int', 'int32_t'),
1845 ('int64', 'int', 'int64_t'),
1846 ('uint8', 'int', 'uint8_t'),
1847 ('uint16', 'int', 'uint16_t'),
1848 ('uint32', 'int', 'uint32_t'),
1849 ('uint64', 'int', 'uint64_t'),
1850 ('size', 'int', 'uint64_t'),
1851 ('bool', 'boolean', 'bool'),
1852 ('any', 'value', 'QObject' + pointer_suffix),
1853 ('null', 'null', 'QNull' + pointer_suffix)]:
1854 self._def_builtin_type(*t)
1855 self.the_empty_object_type = QAPISchemaObjectType(
1856 'q_empty', None, None, None, None, [], None, [])
1857 self._def_entity(self.the_empty_object_type)
1859 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1860 'qbool']
1861 qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
1863 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1864 qtype_values, 'QTYPE'))
1866 def _make_features(self, features):
1867 return [QAPISchemaFeature(f['name'], f.get('if')) for f in features]
1869 def _make_enum_members(self, values):
1870 return [QAPISchemaEnumMember(v['name'], v.get('if'))
1871 for v in values]
1873 def _make_implicit_enum_type(self, name, info, ifcond, values):
1874 # See also QAPISchemaObjectTypeMember._pretty_defined_in()
1875 name = name + 'Kind' # Use namespace reserved by add_name()
1876 self._def_entity(QAPISchemaEnumType(
1877 name, info, None, ifcond, self._make_enum_members(values), None))
1878 return name
1880 def _make_array_type(self, element_type, info):
1881 name = element_type + 'List' # Use namespace reserved by add_name()
1882 if not self.lookup_type(name):
1883 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1884 return name
1886 def _make_implicit_object_type(self, name, info, doc, ifcond,
1887 role, members):
1888 if not members:
1889 return None
1890 # See also QAPISchemaObjectTypeMember._pretty_defined_in()
1891 name = 'q_obj_%s-%s' % (name, role)
1892 typ = self.lookup_entity(name, QAPISchemaObjectType)
1893 if typ:
1894 # The implicit object type has multiple users. This can
1895 # happen only for simple unions' implicit wrapper types.
1896 # Its ifcond should be the disjunction of its user's
1897 # ifconds. Not implemented. Instead, we always pass the
1898 # wrapped type's ifcond, which is trivially the same for all
1899 # users. It's also necessary for the wrapper to compile.
1900 # But it's not tight: the disjunction need not imply it. We
1901 # may end up compiling useless wrapper types.
1902 # TODO kill simple unions or implement the disjunction
1903 assert ifcond == typ._ifcond # pylint: disable=protected-access
1904 else:
1905 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1906 None, members, None, []))
1907 return name
1909 def _def_enum_type(self, expr, info, doc):
1910 name = expr['enum']
1911 data = expr['data']
1912 prefix = expr.get('prefix')
1913 ifcond = expr.get('if')
1914 self._def_entity(QAPISchemaEnumType(
1915 name, info, doc, ifcond,
1916 self._make_enum_members(data), prefix))
1918 def _make_member(self, name, typ, ifcond, info):
1919 optional = False
1920 if name.startswith('*'):
1921 name = name[1:]
1922 optional = True
1923 if isinstance(typ, list):
1924 assert len(typ) == 1
1925 typ = self._make_array_type(typ[0], info)
1926 return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
1928 def _make_members(self, data, info):
1929 return [self._make_member(key, value['type'], value.get('if'), info)
1930 for (key, value) in data.items()]
1932 def _def_struct_type(self, expr, info, doc):
1933 name = expr['struct']
1934 base = expr.get('base')
1935 data = expr['data']
1936 ifcond = expr.get('if')
1937 features = expr.get('features', [])
1938 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1939 self._make_members(data, info),
1940 None,
1941 self._make_features(features)))
1943 def _make_variant(self, case, typ, ifcond):
1944 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1946 def _make_simple_variant(self, case, typ, ifcond, info):
1947 if isinstance(typ, list):
1948 assert len(typ) == 1
1949 typ = self._make_array_type(typ[0], info)
1950 typ = self._make_implicit_object_type(
1951 typ, info, None, self.lookup_type(typ),
1952 'wrapper', [self._make_member('data', typ, None, info)])
1953 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1955 def _def_union_type(self, expr, info, doc):
1956 name = expr['union']
1957 data = expr['data']
1958 base = expr.get('base')
1959 ifcond = expr.get('if')
1960 tag_name = expr.get('discriminator')
1961 tag_member = None
1962 if isinstance(base, dict):
1963 base = self._make_implicit_object_type(
1964 name, info, doc, ifcond,
1965 'base', self._make_members(base, info))
1966 if tag_name:
1967 variants = [self._make_variant(key, value['type'], value.get('if'))
1968 for (key, value) in data.items()]
1969 members = []
1970 else:
1971 variants = [self._make_simple_variant(key, value['type'],
1972 value.get('if'), info)
1973 for (key, value) in data.items()]
1974 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1975 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1976 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1977 members = [tag_member]
1978 self._def_entity(
1979 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1980 QAPISchemaObjectTypeVariants(tag_name,
1981 tag_member,
1982 variants), []))
1984 def _def_alternate_type(self, expr, info, doc):
1985 name = expr['alternate']
1986 data = expr['data']
1987 ifcond = expr.get('if')
1988 variants = [self._make_variant(key, value['type'], value.get('if'))
1989 for (key, value) in data.items()]
1990 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1991 self._def_entity(
1992 QAPISchemaAlternateType(name, info, doc, ifcond,
1993 QAPISchemaObjectTypeVariants(None,
1994 tag_member,
1995 variants)))
1997 def _def_command(self, expr, info, doc):
1998 name = expr['command']
1999 data = expr.get('data')
2000 rets = expr.get('returns')
2001 gen = expr.get('gen', True)
2002 success_response = expr.get('success-response', True)
2003 boxed = expr.get('boxed', False)
2004 allow_oob = expr.get('allow-oob', False)
2005 allow_preconfig = expr.get('allow-preconfig', False)
2006 ifcond = expr.get('if')
2007 if isinstance(data, OrderedDict):
2008 data = self._make_implicit_object_type(
2009 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2010 if isinstance(rets, list):
2011 assert len(rets) == 1
2012 rets = self._make_array_type(rets[0], info)
2013 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
2014 gen, success_response,
2015 boxed, allow_oob, allow_preconfig))
2017 def _def_event(self, expr, info, doc):
2018 name = expr['event']
2019 data = expr.get('data')
2020 boxed = expr.get('boxed', False)
2021 ifcond = expr.get('if')
2022 if isinstance(data, OrderedDict):
2023 data = self._make_implicit_object_type(
2024 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2025 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
2027 def _def_exprs(self, exprs):
2028 for expr_elem in exprs:
2029 expr = expr_elem['expr']
2030 info = expr_elem['info']
2031 doc = expr_elem.get('doc')
2032 if 'enum' in expr:
2033 self._def_enum_type(expr, info, doc)
2034 elif 'struct' in expr:
2035 self._def_struct_type(expr, info, doc)
2036 elif 'union' in expr:
2037 self._def_union_type(expr, info, doc)
2038 elif 'alternate' in expr:
2039 self._def_alternate_type(expr, info, doc)
2040 elif 'command' in expr:
2041 self._def_command(expr, info, doc)
2042 elif 'event' in expr:
2043 self._def_event(expr, info, doc)
2044 elif 'include' in expr:
2045 self._def_include(expr, info, doc)
2046 else:
2047 assert False
2049 def check(self):
2050 for ent in self._entity_list:
2051 ent.check(self)
2053 def visit(self, visitor):
2054 visitor.visit_begin(self)
2055 module = None
2056 visitor.visit_module(module)
2057 for entity in self._entity_list:
2058 if visitor.visit_needed(entity):
2059 if entity.module != module:
2060 module = entity.module
2061 visitor.visit_module(module)
2062 entity.visit(visitor)
2063 visitor.visit_end()
2067 # Code generation helpers
2070 def camel_case(name):
2071 new_name = ''
2072 first = True
2073 for ch in name:
2074 if ch in ['_', '-']:
2075 first = True
2076 elif first:
2077 new_name += ch.upper()
2078 first = False
2079 else:
2080 new_name += ch.lower()
2081 return new_name
2084 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2085 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2086 # ENUM24_Name -> ENUM24_NAME
2087 def camel_to_upper(value):
2088 c_fun_str = c_name(value, False)
2089 if value.isupper():
2090 return c_fun_str
2092 new_name = ''
2093 length = len(c_fun_str)
2094 for i in range(length):
2095 c = c_fun_str[i]
2096 # When c is upper and no '_' appears before, do more checks
2097 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
2098 if i < length - 1 and c_fun_str[i + 1].islower():
2099 new_name += '_'
2100 elif c_fun_str[i - 1].isdigit():
2101 new_name += '_'
2102 new_name += c
2103 return new_name.lstrip('_').upper()
2106 def c_enum_const(type_name, const_name, prefix=None):
2107 if prefix is not None:
2108 type_name = prefix
2109 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
2112 if hasattr(str, 'maketrans'):
2113 c_name_trans = str.maketrans('.-', '__')
2114 else:
2115 c_name_trans = string.maketrans('.-', '__')
2118 # Map @name to a valid C identifier.
2119 # If @protect, avoid returning certain ticklish identifiers (like
2120 # C keywords) by prepending 'q_'.
2122 # Used for converting 'name' from a 'name':'type' qapi definition
2123 # into a generated struct member, as well as converting type names
2124 # into substrings of a generated C function name.
2125 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2126 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2127 def c_name(name, protect=True):
2128 # ANSI X3J11/88-090, 3.1.1
2129 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
2130 'default', 'do', 'double', 'else', 'enum', 'extern',
2131 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2132 'return', 'short', 'signed', 'sizeof', 'static',
2133 'struct', 'switch', 'typedef', 'union', 'unsigned',
2134 'void', 'volatile', 'while'])
2135 # ISO/IEC 9899:1999, 6.4.1
2136 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2137 # ISO/IEC 9899:2011, 6.4.1
2138 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2139 '_Noreturn', '_Static_assert', '_Thread_local'])
2140 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2141 # excluding _.*
2142 gcc_words = set(['asm', 'typeof'])
2143 # C++ ISO/IEC 14882:2003 2.11
2144 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
2145 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2146 'namespace', 'new', 'operator', 'private', 'protected',
2147 'public', 'reinterpret_cast', 'static_cast', 'template',
2148 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2149 'using', 'virtual', 'wchar_t',
2150 # alternative representations
2151 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2152 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2153 # namespace pollution:
2154 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2155 name = name.translate(c_name_trans)
2156 if protect and (name in c89_words | c99_words | c11_words | gcc_words
2157 | cpp_words | polluted_words):
2158 return 'q_' + name
2159 return name
2162 eatspace = '\033EATSPACE.'
2163 pointer_suffix = ' *' + eatspace
2166 def genindent(count):
2167 ret = ''
2168 for _ in range(count):
2169 ret += ' '
2170 return ret
2173 indent_level = 0
2176 def push_indent(indent_amount=4):
2177 global indent_level
2178 indent_level += indent_amount
2181 def pop_indent(indent_amount=4):
2182 global indent_level
2183 indent_level -= indent_amount
2186 # Generate @code with @kwds interpolated.
2187 # Obey indent_level, and strip eatspace.
2188 def cgen(code, **kwds):
2189 raw = code % kwds
2190 if indent_level:
2191 indent = genindent(indent_level)
2192 # re.subn() lacks flags support before Python 2.7, use re.compile()
2193 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2194 indent, raw)
2195 raw = raw[0]
2196 return re.sub(re.escape(eatspace) + r' *', '', raw)
2199 def mcgen(code, **kwds):
2200 if code[0] == '\n':
2201 code = code[1:]
2202 return cgen(code, **kwds)
2205 def c_fname(filename):
2206 return re.sub(r'[^A-Za-z0-9_]', '_', filename)
2209 def guardstart(name):
2210 return mcgen('''
2211 #ifndef %(name)s
2212 #define %(name)s
2214 ''',
2215 name=c_fname(name).upper())
2218 def guardend(name):
2219 return mcgen('''
2221 #endif /* %(name)s */
2222 ''',
2223 name=c_fname(name).upper())
2226 def gen_if(ifcond):
2227 ret = ''
2228 for ifc in ifcond:
2229 ret += mcgen('''
2230 #if %(cond)s
2231 ''', cond=ifc)
2232 return ret
2235 def gen_endif(ifcond):
2236 ret = ''
2237 for ifc in reversed(ifcond):
2238 ret += mcgen('''
2239 #endif /* %(cond)s */
2240 ''', cond=ifc)
2241 return ret
2244 def _wrap_ifcond(ifcond, before, after):
2245 if before == after:
2246 return after # suppress empty #if ... #endif
2248 assert after.startswith(before)
2249 out = before
2250 added = after[len(before):]
2251 if added[0] == '\n':
2252 out += '\n'
2253 added = added[1:]
2254 out += gen_if(ifcond)
2255 out += added
2256 out += gen_endif(ifcond)
2257 return out
2260 def gen_enum_lookup(name, members, prefix=None):
2261 ret = mcgen('''
2263 const QEnumLookup %(c_name)s_lookup = {
2264 .array = (const char *const[]) {
2265 ''',
2266 c_name=c_name(name))
2267 for m in members:
2268 ret += gen_if(m.ifcond)
2269 index = c_enum_const(name, m.name, prefix)
2270 ret += mcgen('''
2271 [%(index)s] = "%(name)s",
2272 ''',
2273 index=index, name=m.name)
2274 ret += gen_endif(m.ifcond)
2276 ret += mcgen('''
2278 .size = %(max_index)s
2280 ''',
2281 max_index=c_enum_const(name, '_MAX', prefix))
2282 return ret
2285 def gen_enum(name, members, prefix=None):
2286 # append automatically generated _MAX value
2287 enum_members = members + [QAPISchemaEnumMember('_MAX')]
2289 ret = mcgen('''
2291 typedef enum %(c_name)s {
2292 ''',
2293 c_name=c_name(name))
2295 for m in enum_members:
2296 ret += gen_if(m.ifcond)
2297 ret += mcgen('''
2298 %(c_enum)s,
2299 ''',
2300 c_enum=c_enum_const(name, m.name, prefix))
2301 ret += gen_endif(m.ifcond)
2303 ret += mcgen('''
2304 } %(c_name)s;
2305 ''',
2306 c_name=c_name(name))
2308 ret += mcgen('''
2310 #define %(c_name)s_str(val) \\
2311 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2313 extern const QEnumLookup %(c_name)s_lookup;
2314 ''',
2315 c_name=c_name(name))
2316 return ret
2319 def build_params(arg_type, boxed, extra=None):
2320 ret = ''
2321 sep = ''
2322 if boxed:
2323 assert arg_type
2324 ret += '%s arg' % arg_type.c_param_type()
2325 sep = ', '
2326 elif arg_type:
2327 assert not arg_type.variants
2328 for memb in arg_type.members:
2329 ret += sep
2330 sep = ', '
2331 if memb.optional:
2332 ret += 'bool has_%s, ' % c_name(memb.name)
2333 ret += '%s %s' % (memb.type.c_param_type(),
2334 c_name(memb.name))
2335 if extra:
2336 ret += sep + extra
2337 return ret if ret else 'void'
2341 # Accumulate and write output
2344 class QAPIGen(object):
2346 def __init__(self, fname):
2347 self.fname = fname
2348 self._preamble = ''
2349 self._body = ''
2351 def preamble_add(self, text):
2352 self._preamble += text
2354 def add(self, text):
2355 self._body += text
2357 def get_content(self):
2358 return self._top() + self._preamble + self._body + self._bottom()
2360 def _top(self):
2361 return ''
2363 def _bottom(self):
2364 return ''
2366 def write(self, output_dir):
2367 pathname = os.path.join(output_dir, self.fname)
2368 dir = os.path.dirname(pathname)
2369 if dir:
2370 try:
2371 os.makedirs(dir)
2372 except os.error as e:
2373 if e.errno != errno.EEXIST:
2374 raise
2375 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2376 if sys.version_info[0] >= 3:
2377 f = open(fd, 'r+', encoding='utf-8')
2378 else:
2379 f = os.fdopen(fd, 'r+')
2380 text = self.get_content()
2381 oldtext = f.read(len(text) + 1)
2382 if text != oldtext:
2383 f.seek(0)
2384 f.truncate(0)
2385 f.write(text)
2386 f.close()
2389 @contextmanager
2390 def ifcontext(ifcond, *args):
2391 """A 'with' statement context manager to wrap with start_if()/end_if()
2393 *args: any number of QAPIGenCCode
2395 Example::
2397 with ifcontext(ifcond, self._genh, self._genc):
2398 modify self._genh and self._genc ...
2400 Is equivalent to calling::
2402 self._genh.start_if(ifcond)
2403 self._genc.start_if(ifcond)
2404 modify self._genh and self._genc ...
2405 self._genh.end_if()
2406 self._genc.end_if()
2408 for arg in args:
2409 arg.start_if(ifcond)
2410 yield
2411 for arg in args:
2412 arg.end_if()
2415 class QAPIGenCCode(QAPIGen):
2417 def __init__(self, fname):
2418 QAPIGen.__init__(self, fname)
2419 self._start_if = None
2421 def start_if(self, ifcond):
2422 assert self._start_if is None
2423 self._start_if = (ifcond, self._body, self._preamble)
2425 def end_if(self):
2426 assert self._start_if
2427 self._wrap_ifcond()
2428 self._start_if = None
2430 def _wrap_ifcond(self):
2431 self._body = _wrap_ifcond(self._start_if[0],
2432 self._start_if[1], self._body)
2433 self._preamble = _wrap_ifcond(self._start_if[0],
2434 self._start_if[2], self._preamble)
2436 def get_content(self):
2437 assert self._start_if is None
2438 return QAPIGen.get_content(self)
2441 class QAPIGenC(QAPIGenCCode):
2443 def __init__(self, fname, blurb, pydoc):
2444 QAPIGenCCode.__init__(self, fname)
2445 self._blurb = blurb
2446 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2447 re.MULTILINE))
2449 def _top(self):
2450 return mcgen('''
2451 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2454 %(blurb)s
2456 * %(copyright)s
2458 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2459 * See the COPYING.LIB file in the top-level directory.
2462 ''',
2463 blurb=self._blurb, copyright=self._copyright)
2465 def _bottom(self):
2466 return mcgen('''
2468 /* Dummy declaration to prevent empty .o file */
2469 char qapi_dummy_%(name)s;
2470 ''',
2471 name=c_fname(self.fname))
2474 class QAPIGenH(QAPIGenC):
2476 def _top(self):
2477 return QAPIGenC._top(self) + guardstart(self.fname)
2479 def _bottom(self):
2480 return guardend(self.fname)
2483 class QAPIGenDoc(QAPIGen):
2485 def _top(self):
2486 return (QAPIGen._top(self)
2487 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2490 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2492 def __init__(self, prefix, what, blurb, pydoc):
2493 self._prefix = prefix
2494 self._what = what
2495 self._genc = QAPIGenC(self._prefix + self._what + '.c',
2496 blurb, pydoc)
2497 self._genh = QAPIGenH(self._prefix + self._what + '.h',
2498 blurb, pydoc)
2500 def write(self, output_dir):
2501 self._genc.write(output_dir)
2502 self._genh.write(output_dir)
2505 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2507 def __init__(self, prefix, what, blurb, pydoc):
2508 self._prefix = prefix
2509 self._what = what
2510 self._blurb = blurb
2511 self._pydoc = pydoc
2512 self._genc = None
2513 self._genh = None
2514 self._module = {}
2515 self._main_module = None
2517 @staticmethod
2518 def _is_user_module(name):
2519 return name and not name.startswith('./')
2521 @staticmethod
2522 def _is_builtin_module(name):
2523 return not name
2525 def _module_dirname(self, what, name):
2526 if self._is_user_module(name):
2527 return os.path.dirname(name)
2528 return ''
2530 def _module_basename(self, what, name):
2531 ret = '' if self._is_builtin_module(name) else self._prefix
2532 if self._is_user_module(name):
2533 basename = os.path.basename(name)
2534 ret += what
2535 if name != self._main_module:
2536 ret += '-' + os.path.splitext(basename)[0]
2537 else:
2538 name = name[2:] if name else 'builtin'
2539 ret += re.sub(r'-', '-' + name + '-', what)
2540 return ret
2542 def _module_filename(self, what, name):
2543 return os.path.join(self._module_dirname(what, name),
2544 self._module_basename(what, name))
2546 def _add_module(self, name, blurb):
2547 basename = self._module_filename(self._what, name)
2548 genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
2549 genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
2550 self._module[name] = (genc, genh)
2551 self._set_module(name)
2553 def _add_user_module(self, name, blurb):
2554 assert self._is_user_module(name)
2555 if self._main_module is None:
2556 self._main_module = name
2557 self._add_module(name, blurb)
2559 def _add_system_module(self, name, blurb):
2560 self._add_module(name and './' + name, blurb)
2562 def _set_module(self, name):
2563 self._genc, self._genh = self._module[name]
2565 def write(self, output_dir, opt_builtins=False):
2566 for name in self._module:
2567 if self._is_builtin_module(name) and not opt_builtins:
2568 continue
2569 (genc, genh) = self._module[name]
2570 genc.write(output_dir)
2571 genh.write(output_dir)
2573 def _begin_user_module(self, name):
2574 pass
2576 def visit_module(self, name):
2577 if name in self._module:
2578 self._set_module(name)
2579 elif self._is_builtin_module(name):
2580 # The built-in module has not been created. No code may
2581 # be generated.
2582 self._genc = None
2583 self._genh = None
2584 else:
2585 self._add_user_module(name, self._blurb)
2586 self._begin_user_module(name)
2588 def visit_include(self, name, info):
2589 relname = os.path.relpath(self._module_filename(self._what, name),
2590 os.path.dirname(self._genh.fname))
2591 self._genh.preamble_add(mcgen('''
2592 #include "%(relname)s.h"
2593 ''',
2594 relname=relname))