]> Git Repo - qemu.git/blob - scripts/qapi/common.py
qapi: Inline check_name() into check_union()
[qemu.git] / scripts / qapi / common.py
1 #
2 # QAPI helper library
3 #
4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2018 Red Hat Inc.
6 #
7 # Authors:
8 #  Anthony Liguori <[email protected]>
9 #  Markus Armbruster <[email protected]>
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 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
23
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',
41 }
42
43 # Are documentation comments required?
44 doc_required = False
45
46 # Whitelist of commands allowed to return a non-dictionary
47 returns_whitelist = []
48
49 # Whitelist of entities allowed to violate case conventions
50 name_case_whitelist = []
51
52 enum_types = {}
53 struct_types = {}
54 union_types = {}
55 all_names = {}
56
57
58 #
59 # Parsing the schema into expressions
60 #
61
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
69
70     def set_defn(self, meta, name):
71         self.defn_meta = meta
72         self.defn_name = name
73
74     def next_line(self):
75         info = copy.copy(self)
76         info.line += 1
77         return info
78
79     def loc(self):
80         return '%s:%d' % (self.fname, self.line)
81
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 ''
87
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
95
96     def __str__(self):
97         return self.include_path() + self.in_defn() + self.loc()
98
99
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
106
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
113
114
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)
124
125
126 class QAPISemError(QAPIError):
127     def __init__(self, info, msg):
128         QAPIError.__init__(self, info, None, msg)
129
130
131 class QAPIDoc(object):
132     """
133     A documentation comment block, either definition or free-form
134
135     Definition documentation blocks consist of
136
137     * a body section: one line naming the definition, followed by an
138       overview (any number of lines)
139
140     * argument sections: a description of each argument (for commands
141       and events) or member (for structs, unions and alternates)
142
143     * features sections: a description of each feature flag
144
145     * additional (non-argument) sections, possibly tagged
146
147     Free-form documentation blocks consist only of a body section.
148     """
149
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 = ''
156
157         def append(self, line):
158             self.text += line.rstrip() + '\n'
159
160     class ArgSection(Section):
161         def __init__(self, name):
162             QAPIDoc.Section.__init__(self, name)
163             self.member = None
164
165         def connect(self, member):
166             self.member = member
167
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
185
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
192
193     def append(self, line):
194         """
195         Parse a comment line and add it to the documentation.
196
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
203         """
204         line = line[1:]
205         if not line:
206             self._append_freeform(line)
207             return
208
209         if line[0] != ' ':
210             raise QAPIParseError(self._parser, "missing space after #")
211         line = line[1:]
212         self._append_line(line)
213
214     def end_comment(self):
215         self._end_section()
216
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:')
224
225     def _append_body_line(self, line):
226         """
227         Process a line of documentation text in the body section.
228
229         If this a symbol line and it is the section's first line, this
230         is a definition documentation block for that symbol.
231
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.
236
237         Else, append the line to the current section.
238         """
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())
264
265     def _append_args_line(self, line):
266         """
267         Process a line of documentation text in an argument section.
268
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
272         it.
273
274         Else, append the line to the current section.
275
276         """
277         name = line.split(' ', 1)[0]
278
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
295
296         self._append_freeform(line.strip())
297
298     def _append_features_line(self, line):
299         name = line.split(' ', 1)[0]
300
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
314
315         self._append_freeform(line.strip())
316
317     def _append_various_line(self, line):
318         """
319         Process a line of documentation text in an additional section.
320
321         A symbol line is an error.
322
323         A section tag begins an additional section.  Start that
324         section and append the line to it.
325
326         Else, append the line to the current section.
327         """
328         name = line.split(' ', 1)[0]
329
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])
337
338         if (not self._section.name or
339                 not self._section.name.startswith('Example')):
340             line = line.strip()
341
342         self._append_freeform(line)
343
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
355
356     def _start_args_section(self, name):
357         self._start_symbol_section(self.args, name)
358
359     def _start_features_section(self, name):
360         self._start_symbol_section(self.features, name)
361
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)
369
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
378
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)
386
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)
392
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")
397
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))
406
407
408 class QAPISchemaParser(object):
409
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
423
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
431
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)
471
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)
479
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
488
489         # skip multiple include of the same file
490         if incl_abs_fname in previously_included:
491             return None
492
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)
501
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)
525
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
532
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))
589
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")
614
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()
631
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
648
649     def get_doc(self, info):
650         if self.val != '##':
651             raise QAPIParseError(
652                 self, "junk after '##' at start of documentation comment")
653
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)
669
670         raise QAPIParseError(self, "documentation comment must end with '##'")
671
672
673 #
674 # Semantic analysis of schema expressions
675 # TODO fold into QAPISchema
676 # TODO catching name collisions in generated code would be nice
677 #
678
679
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']
687
688
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
700
701
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_-]*$')
707
708
709 def check_name(name, info, source,
710                allow_optional=False, enum_member=False, permit_upper=False):
711     check_name_is_str(name, info, source)
712     check_name_str(name, info, source,
713                    allow_optional, enum_member, permit_upper)
714
715
716 def check_name_is_str(name, info, source):
717     if not isinstance(name, str):
718         raise QAPISemError(info, "%s requires a string name" % source)
719
720
721 def check_name_str(name, info, source,
722                    allow_optional=False, enum_member=False,
723                    permit_upper=False):
724     global valid_name
725     membername = name
726
727     if allow_optional and name.startswith('*'):
728         membername = name[1:]
729     # Enum members can start with a digit, because the generated C
730     # code always prefixes it with the enum name
731     if enum_member and membername[0].isdigit():
732         membername = 'D' + membername
733     # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
734     # and 'q_obj_*' implicit type names.
735     if not valid_name.match(membername) or \
736        c_name(membername, False).startswith('q_'):
737         raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
738     if not permit_upper and name.lower() != name:
739         raise QAPISemError(
740             info, "%s uses uppercase in name '%s'" % (source, name))
741     assert not membername.startswith('*')
742
743
744 def check_defn_name_str(name, info, meta):
745     check_name_str(name, info, meta, permit_upper=True)
746     if name.endswith('Kind') or name.endswith('List'):
747         raise QAPISemError(
748             info, "%s '%s' should not end in '%s'" % (meta, name, name[-4:]))
749
750
751 def add_name(name, info, meta):
752     global all_names
753     # FIXME should reject names that differ only in '_' vs. '.'
754     # vs. '-', because they're liable to clash in generated C.
755     if name in all_names:
756         raise QAPISemError(info, "%s '%s' is already defined"
757                            % (all_names[name], name))
758     all_names[name] = meta
759
760
761 def check_if(expr, info):
762
763     def check_if_str(ifcond, info):
764         if not isinstance(ifcond, str):
765             raise QAPISemError(
766                 info, "'if' condition must be a string or a list of strings")
767         if ifcond.strip() == '':
768             raise QAPISemError(info, "'if' condition '%s' makes no sense"
769                                % ifcond)
770
771     ifcond = expr.get('if')
772     if ifcond is None:
773         return
774     if isinstance(ifcond, list):
775         if ifcond == []:
776             raise QAPISemError(info, "'if' condition [] is useless")
777         for elt in ifcond:
778             check_if_str(elt, info)
779     else:
780         check_if_str(ifcond, info)
781
782
783 def check_type(value, info, source,
784                allow_array=False, allow_dict=False, allow_metas=[]):
785     global all_names
786
787     if value is None:
788         return
789
790     # Check if array type for value is okay
791     if isinstance(value, list):
792         if not allow_array:
793             raise QAPISemError(info, "%s cannot be an array" % source)
794         if len(value) != 1 or not isinstance(value[0], str):
795             raise QAPISemError(info,
796                                "%s: array type must contain single type name" %
797                                source)
798         check_type(value[0], info, source, allow_metas=allow_metas)
799         return
800
801     # Check if type name for value is okay
802     if isinstance(value, str):
803         if value not in all_names:
804             raise QAPISemError(info, "%s uses unknown type '%s'"
805                                % (source, value))
806         if not all_names[value] in allow_metas:
807             raise QAPISemError(info, "%s cannot use %s type '%s'" %
808                                (source, all_names[value], value))
809         return
810
811     if not allow_dict:
812         raise QAPISemError(info, "%s should be a type name" % source)
813
814     if not isinstance(value, OrderedDict):
815         raise QAPISemError(info,
816                            "%s should be an object or type name" % source)
817
818     permit_upper = allow_dict in name_case_whitelist
819
820     # value is a dictionary, check that each member is okay
821     for (key, arg) in value.items():
822         check_name_str(key, info, "member of %s" % source,
823                        allow_optional=True, permit_upper=permit_upper)
824         if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
825             raise QAPISemError(
826                 info, "member of %s uses reserved name '%s'" % (source, key))
827         check_known_keys(arg, info, "member '%s' of %s" % (key, source),
828                          ['type'], ['if'])
829         check_if(arg, info)
830         normalize_if(arg)
831         check_type(arg['type'], info, "member '%s' of %s" % (key, source),
832                    allow_array=True,
833                    allow_metas=['built-in', 'union', 'alternate', 'struct',
834                                 'enum'])
835
836
837 def check_command(expr, info):
838     name = expr['command']
839     boxed = expr.get('boxed', False)
840
841     args_meta = ['struct']
842     if boxed:
843         args_meta += ['union']
844     check_type(expr.get('data'), info,
845                "'data' for command '%s'" % name,
846                allow_dict=not boxed, allow_metas=args_meta)
847     returns_meta = ['union', 'struct']
848     if name in returns_whitelist:
849         returns_meta += ['built-in', 'alternate', 'enum']
850     check_type(expr.get('returns'), info,
851                "'returns' for command '%s'" % name,
852                allow_array=True, allow_metas=returns_meta)
853
854
855 def check_event(expr, info):
856     name = expr['event']
857     boxed = expr.get('boxed', False)
858
859     meta = ['struct']
860     if boxed:
861         meta += ['union']
862     check_type(expr.get('data'), info,
863                "'data' for event '%s'" % name,
864                allow_dict=not boxed, allow_metas=meta)
865
866
867 def enum_get_names(expr):
868     return [e['name'] for e in expr['data']]
869
870
871 def check_union(expr, info):
872     name = expr['union']
873     base = expr.get('base')
874     discriminator = expr.get('discriminator')
875     members = expr['data']
876
877     # Two types of unions, determined by discriminator.
878
879     # With no discriminator it is a simple union.
880     if discriminator is None:
881         enum_values = members.keys()
882         allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
883         if base is not None:
884             raise QAPISemError(
885                 info, "simple union '%s' must not have a base" % name)
886
887     # Else, it's a flat union.
888     else:
889         # The object must have a string or dictionary 'base'.
890         check_type(base, info, "'base' for union '%s'" % name,
891                    allow_dict=name, allow_metas=['struct'])
892         if not base:
893             raise QAPISemError(
894                 info, "flat union '%s' must have a base" % name)
895         base_members = find_base_members(base)
896         assert base_members is not None
897
898         # The value of member 'discriminator' must name a non-optional
899         # member of the base struct.
900         check_name_is_str(discriminator, info,
901                           "discriminator of flat union '%s'" % name)
902         check_name_str(discriminator, info,
903                        "discriminator of flat union '%s'" % name)
904         discriminator_value = base_members.get(discriminator)
905         if not discriminator_value:
906             raise QAPISemError(info,
907                                "discriminator '%s' is not a member of 'base'"
908                                % discriminator)
909         if discriminator_value.get('if'):
910             raise QAPISemError(
911                 info,
912                 "the discriminator '%s' for union %s must not be conditional"
913                 % (discriminator, name))
914         enum_define = enum_types.get(discriminator_value['type'])
915         # Do not allow string discriminator
916         if not enum_define:
917             raise QAPISemError(
918                 info,
919                 "discriminator '%s' must be of enumeration type"
920                 % discriminator)
921         enum_values = enum_get_names(enum_define)
922         allow_metas = ['struct']
923
924     if (len(enum_values) == 0):
925         raise QAPISemError(info, "union '%s' has no branches" % name)
926
927     for (key, value) in members.items():
928         check_name_str(key, info, "member of union '%s'" % name)
929         check_known_keys(value, info,
930                          "member '%s' of union '%s'" % (key, name),
931                          ['type'], ['if'])
932         check_if(value, info)
933         normalize_if(value)
934         # Each value must name a known type
935         check_type(value['type'], info,
936                    "member '%s' of union '%s'" % (key, name),
937                    allow_array=not base, allow_metas=allow_metas)
938
939         # If the discriminator names an enum type, then all members
940         # of 'data' must also be members of the enum type.
941         if discriminator is not None:
942             if key not in enum_values:
943                 raise QAPISemError(
944                     info,
945                     "discriminator value '%s' is not found in enum '%s'"
946                     % (key, enum_define['enum']))
947
948
949 def check_alternate(expr, info):
950     name = expr['alternate']
951     members = expr['data']
952     types_seen = {}
953
954     if len(members) == 0:
955         raise QAPISemError(info,
956                            "alternate '%s' cannot have empty 'data'" % name)
957     for (key, value) in members.items():
958         check_name_str(key, info, "member of alternate '%s'" % name)
959         check_known_keys(value, info,
960                          "member '%s' of alternate '%s'" % (key, name),
961                          ['type'], ['if'])
962         check_if(value, info)
963         normalize_if(value)
964         typ = value['type']
965
966         # Ensure alternates have no type conflicts.
967         check_type(typ, info, "member '%s' of alternate '%s'" % (key, name),
968                    allow_metas=['built-in', 'union', 'struct', 'enum'])
969         qtype = find_alternate_member_qtype(typ)
970         if not qtype:
971             raise QAPISemError(
972                 info,
973                 "alternate '%s' member '%s' cannot use type '%s'"
974                 % (name, key, typ))
975         conflicting = set([qtype])
976         if qtype == 'QTYPE_QSTRING':
977             enum_expr = enum_types.get(typ)
978             if enum_expr:
979                 for v in enum_get_names(enum_expr):
980                     if v in ['on', 'off']:
981                         conflicting.add('QTYPE_QBOOL')
982                     if re.match(r'[-+0-9.]', v): # lazy, could be tightened
983                         conflicting.add('QTYPE_QNUM')
984             else:
985                 conflicting.add('QTYPE_QNUM')
986                 conflicting.add('QTYPE_QBOOL')
987         for qt in conflicting:
988             if qt in types_seen:
989                 raise QAPISemError(
990                     info,
991                     "alternate '%s' member '%s' can't be distinguished "
992                     "from member '%s'"
993                     % (name, key, types_seen[qt]))
994             types_seen[qt] = key
995
996
997 def check_enum(expr, info):
998     name = expr['enum']
999     members = expr['data']
1000     prefix = expr.get('prefix')
1001
1002     if not isinstance(members, list):
1003         raise QAPISemError(info,
1004                            "enum '%s' requires an array for 'data'" % name)
1005     if prefix is not None and not isinstance(prefix, str):
1006         raise QAPISemError(info,
1007                            "enum '%s' requires a string for 'prefix'" % name)
1008
1009     permit_upper = name in name_case_whitelist
1010
1011     for member in members:
1012         check_known_keys(member, info, "member of enum '%s'" % name,
1013                          ['name'], ['if'])
1014         check_if(member, info)
1015         normalize_if(member)
1016         check_name(member['name'], info, "member of enum '%s'" % name,
1017                    enum_member=True, permit_upper=permit_upper)
1018
1019
1020 def check_struct(expr, info):
1021     name = expr['struct']
1022     members = expr['data']
1023     features = expr.get('features')
1024
1025     check_type(members, info, "'data' for struct '%s'" % name,
1026                allow_dict=name)
1027     check_type(expr.get('base'), info, "'base' for struct '%s'" % name,
1028                allow_metas=['struct'])
1029
1030     if features:
1031         if not isinstance(features, list):
1032             raise QAPISemError(
1033                 info, "struct '%s' requires an array for 'features'" % name)
1034         for f in features:
1035             assert isinstance(f, dict)
1036             check_known_keys(f, info, "feature of struct %s" % name,
1037                              ['name'], ['if'])
1038
1039             check_if(f, info)
1040             normalize_if(f)
1041             check_name(f['name'], info, "feature of struct %s" % name)
1042
1043
1044 def check_known_keys(value, info, source, required, optional):
1045
1046     def pprint(elems):
1047         return ', '.join("'" + e + "'" for e in sorted(elems))
1048
1049     missing = set(required) - set(value)
1050     if missing:
1051         raise QAPISemError(
1052             info,
1053             "key%s %s %s missing from %s"
1054             % ('s' if len(missing) > 1 else '', pprint(missing),
1055                'are' if len(missing) > 1 else 'is', source))
1056     allowed = set(required + optional)
1057     unknown = set(value) - allowed
1058     if unknown:
1059         raise QAPISemError(
1060             info,
1061             "unknown key%s %s in %s\nValid keys are %s."
1062             % ('s' if len(unknown) > 1 else '', pprint(unknown),
1063                source, pprint(allowed)))
1064
1065
1066 def check_keys(expr, info, meta, required, optional=[]):
1067     name = expr[meta]
1068     if not isinstance(name, str):
1069         raise QAPISemError(info, "'%s' key must have a string value" % meta)
1070     required = required + [meta]
1071     source = "%s '%s'" % (meta, name)
1072     check_known_keys(expr, info, source, required, optional)
1073     for (key, value) in expr.items():
1074         if key in ['gen', 'success-response'] and value is not False:
1075             raise QAPISemError(info,
1076                                "'%s' of %s '%s' should only use false value"
1077                                % (key, meta, name))
1078         if (key in ['boxed', 'allow-oob', 'allow-preconfig']
1079                 and value is not True):
1080             raise QAPISemError(info,
1081                                "'%s' of %s '%s' should only use true value"
1082                                % (key, meta, name))
1083         if key == 'if':
1084             check_if(expr, info)
1085
1086
1087 def normalize_enum(expr):
1088     if isinstance(expr['data'], list):
1089         expr['data'] = [m if isinstance(m, dict) else {'name': m}
1090                         for m in expr['data']]
1091
1092
1093 def normalize_members(members):
1094     if isinstance(members, OrderedDict):
1095         for key, arg in members.items():
1096             if isinstance(arg, dict):
1097                 continue
1098             members[key] = {'type': arg}
1099
1100
1101 def normalize_features(features):
1102     if isinstance(features, list):
1103         features[:] = [f if isinstance(f, dict) else {'name': f}
1104                        for f in features]
1105
1106
1107 def normalize_if(expr):
1108     ifcond = expr.get('if')
1109     if isinstance(ifcond, str):
1110         expr['if'] = [ifcond]
1111
1112
1113 def check_exprs(exprs):
1114     global all_names
1115
1116     # Populate name table with names of built-in types
1117     for builtin in builtin_types.keys():
1118         all_names[builtin] = 'built-in'
1119
1120     # Learn the types and check for valid expression keys
1121     for expr_elem in exprs:
1122         expr = expr_elem['expr']
1123         info = expr_elem['info']
1124         doc = expr_elem.get('doc')
1125
1126         if 'include' in expr:
1127             continue
1128
1129         if not doc and doc_required:
1130             raise QAPISemError(info,
1131                                "definition missing documentation comment")
1132
1133         if 'enum' in expr:
1134             meta = 'enum'
1135             check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
1136             normalize_enum(expr)
1137             enum_types[expr[meta]] = expr
1138         elif 'union' in expr:
1139             meta = 'union'
1140             check_keys(expr, info, 'union', ['data'],
1141                        ['base', 'discriminator', 'if'])
1142             normalize_members(expr.get('base'))
1143             normalize_members(expr['data'])
1144             union_types[expr[meta]] = expr
1145         elif 'alternate' in expr:
1146             meta = 'alternate'
1147             check_keys(expr, info, 'alternate', ['data'], ['if'])
1148             normalize_members(expr['data'])
1149         elif 'struct' in expr:
1150             meta = 'struct'
1151             check_keys(expr, info, 'struct', ['data'],
1152                        ['base', 'if', 'features'])
1153             normalize_members(expr['data'])
1154             normalize_features(expr.get('features'))
1155             struct_types[expr[meta]] = expr
1156         elif 'command' in expr:
1157             meta = 'command'
1158             check_keys(expr, info, 'command', [],
1159                        ['data', 'returns', 'gen', 'success-response',
1160                         'boxed', 'allow-oob', 'allow-preconfig', 'if'])
1161             normalize_members(expr.get('data'))
1162         elif 'event' in expr:
1163             meta = 'event'
1164             check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
1165             normalize_members(expr.get('data'))
1166         else:
1167             raise QAPISemError(info, "expression is missing metatype")
1168         normalize_if(expr)
1169         name = expr[meta]
1170         check_name_is_str(name, info, "'%s'" % meta)
1171         info.set_defn(meta, name)
1172         check_defn_name_str(name, info, meta)
1173         add_name(name, info, meta)
1174         if doc and doc.symbol != name:
1175             raise QAPISemError(
1176                 info,
1177                 "definition of '%s' follows documentation for '%s'"
1178                 % (name, doc.symbol))
1179
1180     # Validate that exprs make sense
1181     for expr_elem in exprs:
1182         expr = expr_elem['expr']
1183         info = expr_elem['info']
1184         doc = expr_elem.get('doc')
1185
1186         if 'include' in expr:
1187             continue
1188         if 'enum' in expr:
1189             check_enum(expr, info)
1190         elif 'union' in expr:
1191             check_union(expr, info)
1192         elif 'alternate' in expr:
1193             check_alternate(expr, info)
1194         elif 'struct' in expr:
1195             check_struct(expr, info)
1196         elif 'command' in expr:
1197             check_command(expr, info)
1198         elif 'event' in expr:
1199             check_event(expr, info)
1200         else:
1201             assert False, 'unexpected meta type'
1202
1203         if doc:
1204             doc.check_expr(expr)
1205
1206     return exprs
1207
1208
1209 #
1210 # Schema compiler frontend
1211 #
1212
1213 class QAPISchemaEntity(object):
1214     def __init__(self, name, info, doc, ifcond=None):
1215         assert name is None or isinstance(name, str)
1216         self.name = name
1217         self._module = None
1218         # For explicitly defined entities, info points to the (explicit)
1219         # definition.  For builtins (and their arrays), info is None.
1220         # For implicitly defined entities, info points to a place that
1221         # triggered the implicit definition (there may be more than one
1222         # such place).
1223         self.info = info
1224         self.doc = doc
1225         self._ifcond = ifcond or []
1226         self._checked = False
1227
1228     def c_name(self):
1229         return c_name(self.name)
1230
1231     def check(self, schema):
1232         assert not self._checked
1233         if self.info:
1234             self._module = os.path.relpath(self.info.fname,
1235                                            os.path.dirname(schema.fname))
1236         self._checked = True
1237
1238     @property
1239     def ifcond(self):
1240         assert self._checked
1241         return self._ifcond
1242
1243     @property
1244     def module(self):
1245         assert self._checked
1246         return self._module
1247
1248     def is_implicit(self):
1249         return not self.info
1250
1251     def visit(self, visitor):
1252         assert self._checked
1253
1254
1255 class QAPISchemaVisitor(object):
1256     def visit_begin(self, schema):
1257         pass
1258
1259     def visit_end(self):
1260         pass
1261
1262     def visit_module(self, fname):
1263         pass
1264
1265     def visit_needed(self, entity):
1266         # Default to visiting everything
1267         return True
1268
1269     def visit_include(self, fname, info):
1270         pass
1271
1272     def visit_builtin_type(self, name, info, json_type):
1273         pass
1274
1275     def visit_enum_type(self, name, info, ifcond, members, prefix):
1276         pass
1277
1278     def visit_array_type(self, name, info, ifcond, element_type):
1279         pass
1280
1281     def visit_object_type(self, name, info, ifcond, base, members, variants,
1282                           features):
1283         pass
1284
1285     def visit_object_type_flat(self, name, info, ifcond, members, variants,
1286                                features):
1287         pass
1288
1289     def visit_alternate_type(self, name, info, ifcond, variants):
1290         pass
1291
1292     def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1293                       success_response, boxed, allow_oob, allow_preconfig):
1294         pass
1295
1296     def visit_event(self, name, info, ifcond, arg_type, boxed):
1297         pass
1298
1299
1300 class QAPISchemaInclude(QAPISchemaEntity):
1301
1302     def __init__(self, fname, info):
1303         QAPISchemaEntity.__init__(self, None, info, None)
1304         self.fname = fname
1305
1306     def visit(self, visitor):
1307         QAPISchemaEntity.visit(self, visitor)
1308         visitor.visit_include(self.fname, self.info)
1309
1310
1311 class QAPISchemaType(QAPISchemaEntity):
1312     # Return the C type for common use.
1313     # For the types we commonly box, this is a pointer type.
1314     def c_type(self):
1315         pass
1316
1317     # Return the C type to be used in a parameter list.
1318     def c_param_type(self):
1319         return self.c_type()
1320
1321     # Return the C type to be used where we suppress boxing.
1322     def c_unboxed_type(self):
1323         return self.c_type()
1324
1325     def json_type(self):
1326         pass
1327
1328     def alternate_qtype(self):
1329         json2qtype = {
1330             'null':    'QTYPE_QNULL',
1331             'string':  'QTYPE_QSTRING',
1332             'number':  'QTYPE_QNUM',
1333             'int':     'QTYPE_QNUM',
1334             'boolean': 'QTYPE_QBOOL',
1335             'object':  'QTYPE_QDICT'
1336         }
1337         return json2qtype.get(self.json_type())
1338
1339     def doc_type(self):
1340         if self.is_implicit():
1341             return None
1342         return self.name
1343
1344
1345 class QAPISchemaBuiltinType(QAPISchemaType):
1346     def __init__(self, name, json_type, c_type):
1347         QAPISchemaType.__init__(self, name, None, None)
1348         assert not c_type or isinstance(c_type, str)
1349         assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1350                              'value')
1351         self._json_type_name = json_type
1352         self._c_type_name = c_type
1353
1354     def c_name(self):
1355         return self.name
1356
1357     def c_type(self):
1358         return self._c_type_name
1359
1360     def c_param_type(self):
1361         if self.name == 'str':
1362             return 'const ' + self._c_type_name
1363         return self._c_type_name
1364
1365     def json_type(self):
1366         return self._json_type_name
1367
1368     def doc_type(self):
1369         return self.json_type()
1370
1371     def visit(self, visitor):
1372         QAPISchemaType.visit(self, visitor)
1373         visitor.visit_builtin_type(self.name, self.info, self.json_type())
1374
1375
1376 class QAPISchemaEnumType(QAPISchemaType):
1377     def __init__(self, name, info, doc, ifcond, members, prefix):
1378         QAPISchemaType.__init__(self, name, info, doc, ifcond)
1379         for m in members:
1380             assert isinstance(m, QAPISchemaEnumMember)
1381             m.set_defined_in(name)
1382         assert prefix is None or isinstance(prefix, str)
1383         self.members = members
1384         self.prefix = prefix
1385
1386     def check(self, schema):
1387         QAPISchemaType.check(self, schema)
1388         seen = {}
1389         for m in self.members:
1390             m.check_clash(self.info, seen)
1391             if self.doc:
1392                 self.doc.connect_member(m)
1393
1394     def is_implicit(self):
1395         # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1396         return self.name.endswith('Kind') or self.name == 'QType'
1397
1398     def c_type(self):
1399         return c_name(self.name)
1400
1401     def member_names(self):
1402         return [m.name for m in self.members]
1403
1404     def json_type(self):
1405         return 'string'
1406
1407     def visit(self, visitor):
1408         QAPISchemaType.visit(self, visitor)
1409         visitor.visit_enum_type(self.name, self.info, self.ifcond,
1410                                 self.members, self.prefix)
1411
1412
1413 class QAPISchemaArrayType(QAPISchemaType):
1414     def __init__(self, name, info, element_type):
1415         QAPISchemaType.__init__(self, name, info, None, None)
1416         assert isinstance(element_type, str)
1417         self._element_type_name = element_type
1418         self.element_type = None
1419
1420     def check(self, schema):
1421         QAPISchemaType.check(self, schema)
1422         self.element_type = schema.lookup_type(self._element_type_name)
1423         assert self.element_type
1424         assert not isinstance(self.element_type, QAPISchemaArrayType)
1425
1426     @property
1427     def ifcond(self):
1428         assert self._checked
1429         return self.element_type.ifcond
1430
1431     @property
1432     def module(self):
1433         assert self._checked
1434         return self.element_type.module
1435
1436     def is_implicit(self):
1437         return True
1438
1439     def c_type(self):
1440         return c_name(self.name) + pointer_suffix
1441
1442     def json_type(self):
1443         return 'array'
1444
1445     def doc_type(self):
1446         elt_doc_type = self.element_type.doc_type()
1447         if not elt_doc_type:
1448             return None
1449         return 'array of ' + elt_doc_type
1450
1451     def visit(self, visitor):
1452         QAPISchemaType.visit(self, visitor)
1453         visitor.visit_array_type(self.name, self.info, self.ifcond,
1454                                  self.element_type)
1455
1456
1457 class QAPISchemaObjectType(QAPISchemaType):
1458     def __init__(self, name, info, doc, ifcond,
1459                  base, local_members, variants, features):
1460         # struct has local_members, optional base, and no variants
1461         # flat union has base, variants, and no local_members
1462         # simple union has local_members, variants, and no base
1463         QAPISchemaType.__init__(self, name, info, doc, ifcond)
1464         assert base is None or isinstance(base, str)
1465         for m in local_members:
1466             assert isinstance(m, QAPISchemaObjectTypeMember)
1467             m.set_defined_in(name)
1468         if variants is not None:
1469             assert isinstance(variants, QAPISchemaObjectTypeVariants)
1470             variants.set_defined_in(name)
1471         for f in features:
1472             assert isinstance(f, QAPISchemaFeature)
1473             f.set_defined_in(name)
1474         self._base_name = base
1475         self.base = None
1476         self.local_members = local_members
1477         self.variants = variants
1478         self.members = None
1479         self.features = features
1480
1481     def check(self, schema):
1482         # This calls another type T's .check() exactly when the C
1483         # struct emitted by gen_object() contains that T's C struct
1484         # (pointers don't count).
1485         if self.members is not None:
1486             # A previous .check() completed: nothing to do
1487             return
1488         if self._checked:
1489             # Recursed: C struct contains itself
1490             raise QAPISemError(self.info,
1491                                "object %s contains itself" % self.name)
1492
1493         QAPISchemaType.check(self, schema)
1494         assert self._checked and self.members is None
1495
1496         seen = OrderedDict()
1497         if self._base_name:
1498             self.base = schema.lookup_type(self._base_name)
1499             assert isinstance(self.base, QAPISchemaObjectType)
1500             self.base.check(schema)
1501             self.base.check_clash(self.info, seen)
1502         for m in self.local_members:
1503             m.check(schema)
1504             m.check_clash(self.info, seen)
1505             if self.doc:
1506                 self.doc.connect_member(m)
1507         members = seen.values()
1508
1509         if self.variants:
1510             self.variants.check(schema, seen)
1511             assert self.variants.tag_member in members
1512             self.variants.check_clash(self.info, seen)
1513
1514         # Features are in a name space separate from members
1515         seen = {}
1516         for f in self.features:
1517             f.check_clash(self.info, seen)
1518
1519         if self.doc:
1520             self.doc.check()
1521
1522         self.members = members  # mark completed
1523
1524     # Check that the members of this type do not cause duplicate JSON members,
1525     # and update seen to track the members seen so far. Report any errors
1526     # on behalf of info, which is not necessarily self.info
1527     def check_clash(self, info, seen):
1528         assert self._checked
1529         assert not self.variants       # not implemented
1530         for m in self.members:
1531             m.check_clash(info, seen)
1532
1533     @property
1534     def ifcond(self):
1535         assert self._checked
1536         if isinstance(self._ifcond, QAPISchemaType):
1537             # Simple union wrapper type inherits from wrapped type;
1538             # see _make_implicit_object_type()
1539             return self._ifcond.ifcond
1540         return self._ifcond
1541
1542     def is_implicit(self):
1543         # See QAPISchema._make_implicit_object_type(), as well as
1544         # _def_predefineds()
1545         return self.name.startswith('q_')
1546
1547     def is_empty(self):
1548         assert self.members is not None
1549         return not self.members and not self.variants
1550
1551     def c_name(self):
1552         assert self.name != 'q_empty'
1553         return QAPISchemaType.c_name(self)
1554
1555     def c_type(self):
1556         assert not self.is_implicit()
1557         return c_name(self.name) + pointer_suffix
1558
1559     def c_unboxed_type(self):
1560         return c_name(self.name)
1561
1562     def json_type(self):
1563         return 'object'
1564
1565     def visit(self, visitor):
1566         QAPISchemaType.visit(self, visitor)
1567         visitor.visit_object_type(self.name, self.info, self.ifcond,
1568                                   self.base, self.local_members, self.variants,
1569                                   self.features)
1570         visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1571                                        self.members, self.variants,
1572                                        self.features)
1573
1574
1575 class QAPISchemaMember(object):
1576     """ Represents object members, enum members and features """
1577     role = 'member'
1578
1579     def __init__(self, name, info, ifcond=None):
1580         assert isinstance(name, str)
1581         self.name = name
1582         self.info = info
1583         self.ifcond = ifcond or []
1584         self.defined_in = None
1585
1586     def set_defined_in(self, name):
1587         assert not self.defined_in
1588         self.defined_in = name
1589
1590     def check_clash(self, info, seen):
1591         cname = c_name(self.name)
1592         if cname in seen:
1593             raise QAPISemError(
1594                 info,
1595                 "%s collides with %s"
1596                 % (self.describe(info), seen[cname].describe(info)))
1597         seen[cname] = self
1598
1599     def describe(self, info):
1600         role = self.role
1601         defined_in = self.defined_in
1602         assert defined_in
1603
1604         if defined_in.startswith('q_obj_'):
1605             # See QAPISchema._make_implicit_object_type() - reverse the
1606             # mapping there to create a nice human-readable description
1607             defined_in = defined_in[6:]
1608             if defined_in.endswith('-arg'):
1609                 # Implicit type created for a command's dict 'data'
1610                 assert role == 'member'
1611                 role = 'parameter'
1612             elif defined_in.endswith('-base'):
1613                 # Implicit type created for a flat union's dict 'base'
1614                 role = 'base ' + role
1615             else:
1616                 # Implicit type created for a simple union's branch
1617                 assert defined_in.endswith('-wrapper')
1618                 # Unreachable and not implemented
1619                 assert False
1620         elif defined_in.endswith('Kind'):
1621             # See QAPISchema._make_implicit_enum_type()
1622             # Implicit enum created for simple union's branches
1623             assert role == 'value'
1624             role = 'branch'
1625         elif defined_in != info.defn_name:
1626             return "%s '%s' of type '%s'" % (role, self.name, defined_in)
1627         return "%s '%s'" % (role, self.name)
1628
1629
1630 class QAPISchemaEnumMember(QAPISchemaMember):
1631     role = 'value'
1632
1633
1634 class QAPISchemaFeature(QAPISchemaMember):
1635     role = 'feature'
1636
1637
1638 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1639     def __init__(self, name, info, typ, optional, ifcond=None):
1640         QAPISchemaMember.__init__(self, name, info, ifcond)
1641         assert isinstance(typ, str)
1642         assert isinstance(optional, bool)
1643         self._type_name = typ
1644         self.type = None
1645         self.optional = optional
1646
1647     def check(self, schema):
1648         assert self.defined_in
1649         self.type = schema.lookup_type(self._type_name)
1650         assert self.type
1651
1652
1653 class QAPISchemaObjectTypeVariants(object):
1654     def __init__(self, tag_name, info, tag_member, variants):
1655         # Flat unions pass tag_name but not tag_member.
1656         # Simple unions and alternates pass tag_member but not tag_name.
1657         # After check(), tag_member is always set, and tag_name remains
1658         # a reliable witness of being used by a flat union.
1659         assert bool(tag_member) != bool(tag_name)
1660         assert (isinstance(tag_name, str) or
1661                 isinstance(tag_member, QAPISchemaObjectTypeMember))
1662         for v in variants:
1663             assert isinstance(v, QAPISchemaObjectTypeVariant)
1664         self._tag_name = tag_name
1665         self.info = info
1666         self.tag_member = tag_member
1667         self.variants = variants
1668
1669     def set_defined_in(self, name):
1670         for v in self.variants:
1671             v.set_defined_in(name)
1672
1673     def check(self, schema, seen):
1674         if not self.tag_member:    # flat union
1675             self.tag_member = seen[c_name(self._tag_name)]
1676             assert self._tag_name == self.tag_member.name
1677         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1678         assert not self.tag_member.optional
1679         assert self.tag_member.ifcond == []
1680         if self._tag_name:    # flat union
1681             # branches that are not explicitly covered get an empty type
1682             cases = set([v.name for v in self.variants])
1683             for m in self.tag_member.type.members:
1684                 if m.name not in cases:
1685                     v = QAPISchemaObjectTypeVariant(m.name, self.info,
1686                                                     'q_empty', m.ifcond)
1687                     v.set_defined_in(self.tag_member.defined_in)
1688                     self.variants.append(v)
1689         assert self.variants
1690         for v in self.variants:
1691             v.check(schema)
1692             # Union names must match enum values; alternate names are
1693             # checked separately. Use 'seen' to tell the two apart.
1694             if seen:
1695                 assert v.name in self.tag_member.type.member_names()
1696                 assert (isinstance(v.type, QAPISchemaObjectType)
1697                         and not v.type.variants)
1698                 v.type.check(schema)
1699
1700     def check_clash(self, info, seen):
1701         for v in self.variants:
1702             # Reset seen map for each variant, since qapi names from one
1703             # branch do not affect another branch
1704             v.type.check_clash(info, dict(seen))
1705
1706
1707 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1708     role = 'branch'
1709
1710     def __init__(self, name, info, typ, ifcond=None):
1711         QAPISchemaObjectTypeMember.__init__(self, name, info, typ,
1712                                             False, ifcond)
1713
1714
1715 class QAPISchemaAlternateType(QAPISchemaType):
1716     def __init__(self, name, info, doc, ifcond, variants):
1717         QAPISchemaType.__init__(self, name, info, doc, ifcond)
1718         assert isinstance(variants, QAPISchemaObjectTypeVariants)
1719         assert variants.tag_member
1720         variants.set_defined_in(name)
1721         variants.tag_member.set_defined_in(self.name)
1722         self.variants = variants
1723
1724     def check(self, schema):
1725         QAPISchemaType.check(self, schema)
1726         self.variants.tag_member.check(schema)
1727         # Not calling self.variants.check_clash(), because there's nothing
1728         # to clash with
1729         self.variants.check(schema, {})
1730         # Alternate branch names have no relation to the tag enum values;
1731         # so we have to check for potential name collisions ourselves.
1732         seen = {}
1733         for v in self.variants.variants:
1734             v.check_clash(self.info, seen)
1735             # TODO check conflicting qtypes
1736             if self.doc:
1737                 self.doc.connect_member(v)
1738         if self.doc:
1739             self.doc.check()
1740
1741     def c_type(self):
1742         return c_name(self.name) + pointer_suffix
1743
1744     def json_type(self):
1745         return 'value'
1746
1747     def visit(self, visitor):
1748         QAPISchemaType.visit(self, visitor)
1749         visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1750                                      self.variants)
1751
1752
1753 class QAPISchemaCommand(QAPISchemaEntity):
1754     def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1755                  gen, success_response, boxed, allow_oob, allow_preconfig):
1756         QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1757         assert not arg_type or isinstance(arg_type, str)
1758         assert not ret_type or isinstance(ret_type, str)
1759         self._arg_type_name = arg_type
1760         self.arg_type = None
1761         self._ret_type_name = ret_type
1762         self.ret_type = None
1763         self.gen = gen
1764         self.success_response = success_response
1765         self.boxed = boxed
1766         self.allow_oob = allow_oob
1767         self.allow_preconfig = allow_preconfig
1768
1769     def check(self, schema):
1770         QAPISchemaEntity.check(self, schema)
1771         if self._arg_type_name:
1772             self.arg_type = schema.lookup_type(self._arg_type_name)
1773             assert isinstance(self.arg_type, QAPISchemaObjectType)
1774             assert not self.arg_type.variants or self.boxed
1775         elif self.boxed:
1776             raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
1777         if self._ret_type_name:
1778             self.ret_type = schema.lookup_type(self._ret_type_name)
1779             assert isinstance(self.ret_type, QAPISchemaType)
1780
1781     def visit(self, visitor):
1782         QAPISchemaEntity.visit(self, visitor)
1783         visitor.visit_command(self.name, self.info, self.ifcond,
1784                               self.arg_type, self.ret_type,
1785                               self.gen, self.success_response,
1786                               self.boxed, self.allow_oob,
1787                               self.allow_preconfig)
1788
1789
1790 class QAPISchemaEvent(QAPISchemaEntity):
1791     def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1792         QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1793         assert not arg_type or isinstance(arg_type, str)
1794         self._arg_type_name = arg_type
1795         self.arg_type = None
1796         self.boxed = boxed
1797
1798     def check(self, schema):
1799         QAPISchemaEntity.check(self, schema)
1800         if self._arg_type_name:
1801             self.arg_type = schema.lookup_type(self._arg_type_name)
1802             assert isinstance(self.arg_type, QAPISchemaObjectType)
1803             assert not self.arg_type.variants or self.boxed
1804         elif self.boxed:
1805             raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
1806
1807     def visit(self, visitor):
1808         QAPISchemaEntity.visit(self, visitor)
1809         visitor.visit_event(self.name, self.info, self.ifcond,
1810                             self.arg_type, self.boxed)
1811
1812
1813 class QAPISchema(object):
1814     def __init__(self, fname):
1815         self.fname = fname
1816         if sys.version_info[0] >= 3:
1817             f = open(fname, 'r', encoding='utf-8')
1818         else:
1819             f = open(fname, 'r')
1820         parser = QAPISchemaParser(f)
1821         exprs = check_exprs(parser.exprs)
1822         self.docs = parser.docs
1823         self._entity_list = []
1824         self._entity_dict = {}
1825         self._predefining = True
1826         self._def_predefineds()
1827         self._predefining = False
1828         self._def_exprs(exprs)
1829         self.check()
1830
1831     def _def_entity(self, ent):
1832         # Only the predefined types are allowed to not have info
1833         assert ent.info or self._predefining
1834         assert ent.name is None or ent.name not in self._entity_dict
1835         self._entity_list.append(ent)
1836         if ent.name is not None:
1837             self._entity_dict[ent.name] = ent
1838
1839     def lookup_entity(self, name, typ=None):
1840         ent = self._entity_dict.get(name)
1841         if typ and not isinstance(ent, typ):
1842             return None
1843         return ent
1844
1845     def lookup_type(self, name):
1846         return self.lookup_entity(name, QAPISchemaType)
1847
1848     def _def_include(self, expr, info, doc):
1849         include = expr['include']
1850         assert doc is None
1851         main_info = info
1852         while main_info.parent:
1853             main_info = main_info.parent
1854         fname = os.path.relpath(include, os.path.dirname(main_info.fname))
1855         self._def_entity(QAPISchemaInclude(fname, info))
1856
1857     def _def_builtin_type(self, name, json_type, c_type):
1858         self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1859         # Instantiating only the arrays that are actually used would
1860         # be nice, but we can't as long as their generated code
1861         # (qapi-builtin-types.[ch]) may be shared by some other
1862         # schema.
1863         self._make_array_type(name, None)
1864
1865     def _def_predefineds(self):
1866         for t in [('str',    'string',  'char' + pointer_suffix),
1867                   ('number', 'number',  'double'),
1868                   ('int',    'int',     'int64_t'),
1869                   ('int8',   'int',     'int8_t'),
1870                   ('int16',  'int',     'int16_t'),
1871                   ('int32',  'int',     'int32_t'),
1872                   ('int64',  'int',     'int64_t'),
1873                   ('uint8',  'int',     'uint8_t'),
1874                   ('uint16', 'int',     'uint16_t'),
1875                   ('uint32', 'int',     'uint32_t'),
1876                   ('uint64', 'int',     'uint64_t'),
1877                   ('size',   'int',     'uint64_t'),
1878                   ('bool',   'boolean', 'bool'),
1879                   ('any',    'value',   'QObject' + pointer_suffix),
1880                   ('null',   'null',    'QNull' + pointer_suffix)]:
1881             self._def_builtin_type(*t)
1882         self.the_empty_object_type = QAPISchemaObjectType(
1883             'q_empty', None, None, None, None, [], None, [])
1884         self._def_entity(self.the_empty_object_type)
1885
1886         qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1887                   'qbool']
1888         qtype_values = self._make_enum_members(
1889             [{'name': n} for n in qtypes], None)
1890
1891         self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1892                                             qtype_values, 'QTYPE'))
1893
1894     def _make_features(self, features, info):
1895         return [QAPISchemaFeature(f['name'], info, f.get('if'))
1896                 for f in features]
1897
1898     def _make_enum_members(self, values, info):
1899         return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
1900                 for v in values]
1901
1902     def _make_implicit_enum_type(self, name, info, ifcond, values):
1903         # See also QAPISchemaObjectTypeMember.describe()
1904         name = name + 'Kind'    # reserved by check_defn_name_str()
1905         self._def_entity(QAPISchemaEnumType(
1906             name, info, None, ifcond, self._make_enum_members(values, info),
1907             None))
1908         return name
1909
1910     def _make_array_type(self, element_type, info):
1911         name = element_type + 'List'    # reserved by check_defn_name_str()
1912         if not self.lookup_type(name):
1913             self._def_entity(QAPISchemaArrayType(name, info, element_type))
1914         return name
1915
1916     def _make_implicit_object_type(self, name, info, doc, ifcond,
1917                                    role, members):
1918         if not members:
1919             return None
1920         # See also QAPISchemaObjectTypeMember.describe()
1921         name = 'q_obj_%s-%s' % (name, role)
1922         typ = self.lookup_entity(name, QAPISchemaObjectType)
1923         if typ:
1924             # The implicit object type has multiple users.  This can
1925             # happen only for simple unions' implicit wrapper types.
1926             # Its ifcond should be the disjunction of its user's
1927             # ifconds.  Not implemented.  Instead, we always pass the
1928             # wrapped type's ifcond, which is trivially the same for all
1929             # users.  It's also necessary for the wrapper to compile.
1930             # But it's not tight: the disjunction need not imply it.  We
1931             # may end up compiling useless wrapper types.
1932             # TODO kill simple unions or implement the disjunction
1933             assert ifcond == typ._ifcond # pylint: disable=protected-access
1934         else:
1935             self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1936                                                   None, members, None, []))
1937         return name
1938
1939     def _def_enum_type(self, expr, info, doc):
1940         name = expr['enum']
1941         data = expr['data']
1942         prefix = expr.get('prefix')
1943         ifcond = expr.get('if')
1944         self._def_entity(QAPISchemaEnumType(
1945             name, info, doc, ifcond,
1946             self._make_enum_members(data, info), prefix))
1947
1948     def _make_member(self, name, typ, ifcond, info):
1949         optional = False
1950         if name.startswith('*'):
1951             name = name[1:]
1952             optional = True
1953         if isinstance(typ, list):
1954             assert len(typ) == 1
1955             typ = self._make_array_type(typ[0], info)
1956         return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond)
1957
1958     def _make_members(self, data, info):
1959         return [self._make_member(key, value['type'], value.get('if'), info)
1960                 for (key, value) in data.items()]
1961
1962     def _def_struct_type(self, expr, info, doc):
1963         name = expr['struct']
1964         base = expr.get('base')
1965         data = expr['data']
1966         ifcond = expr.get('if')
1967         features = expr.get('features', [])
1968         self._def_entity(QAPISchemaObjectType(
1969             name, info, doc, ifcond, base,
1970             self._make_members(data, info),
1971             None,
1972             self._make_features(features, info)))
1973
1974     def _make_variant(self, case, typ, ifcond, info):
1975         return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
1976
1977     def _make_simple_variant(self, case, typ, ifcond, info):
1978         if isinstance(typ, list):
1979             assert len(typ) == 1
1980             typ = self._make_array_type(typ[0], info)
1981         typ = self._make_implicit_object_type(
1982             typ, info, None, self.lookup_type(typ),
1983             'wrapper', [self._make_member('data', typ, None, info)])
1984         return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
1985
1986     def _def_union_type(self, expr, info, doc):
1987         name = expr['union']
1988         data = expr['data']
1989         base = expr.get('base')
1990         ifcond = expr.get('if')
1991         tag_name = expr.get('discriminator')
1992         tag_member = None
1993         if isinstance(base, dict):
1994             base = self._make_implicit_object_type(
1995                 name, info, doc, ifcond,
1996                 'base', self._make_members(base, info))
1997         if tag_name:
1998             variants = [self._make_variant(key, value['type'],
1999                                            value.get('if'), info)
2000                         for (key, value) in data.items()]
2001             members = []
2002         else:
2003             variants = [self._make_simple_variant(key, value['type'],
2004                                                   value.get('if'), info)
2005                         for (key, value) in data.items()]
2006             enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
2007             typ = self._make_implicit_enum_type(name, info, ifcond, enum)
2008             tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
2009             members = [tag_member]
2010         self._def_entity(
2011             QAPISchemaObjectType(name, info, doc, ifcond, base, members,
2012                                  QAPISchemaObjectTypeVariants(
2013                                      tag_name, info, tag_member, variants),
2014                                  []))
2015
2016     def _def_alternate_type(self, expr, info, doc):
2017         name = expr['alternate']
2018         data = expr['data']
2019         ifcond = expr.get('if')
2020         variants = [self._make_variant(key, value['type'], value.get('if'),
2021                                        info)
2022                     for (key, value) in data.items()]
2023         tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
2024         self._def_entity(
2025             QAPISchemaAlternateType(name, info, doc, ifcond,
2026                                     QAPISchemaObjectTypeVariants(
2027                                         None, info, tag_member, variants)))
2028
2029     def _def_command(self, expr, info, doc):
2030         name = expr['command']
2031         data = expr.get('data')
2032         rets = expr.get('returns')
2033         gen = expr.get('gen', True)
2034         success_response = expr.get('success-response', True)
2035         boxed = expr.get('boxed', False)
2036         allow_oob = expr.get('allow-oob', False)
2037         allow_preconfig = expr.get('allow-preconfig', False)
2038         ifcond = expr.get('if')
2039         if isinstance(data, OrderedDict):
2040             data = self._make_implicit_object_type(
2041                 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2042         if isinstance(rets, list):
2043             assert len(rets) == 1
2044             rets = self._make_array_type(rets[0], info)
2045         self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
2046                                            gen, success_response,
2047                                            boxed, allow_oob, allow_preconfig))
2048
2049     def _def_event(self, expr, info, doc):
2050         name = expr['event']
2051         data = expr.get('data')
2052         boxed = expr.get('boxed', False)
2053         ifcond = expr.get('if')
2054         if isinstance(data, OrderedDict):
2055             data = self._make_implicit_object_type(
2056                 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2057         self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
2058
2059     def _def_exprs(self, exprs):
2060         for expr_elem in exprs:
2061             expr = expr_elem['expr']
2062             info = expr_elem['info']
2063             doc = expr_elem.get('doc')
2064             if 'enum' in expr:
2065                 self._def_enum_type(expr, info, doc)
2066             elif 'struct' in expr:
2067                 self._def_struct_type(expr, info, doc)
2068             elif 'union' in expr:
2069                 self._def_union_type(expr, info, doc)
2070             elif 'alternate' in expr:
2071                 self._def_alternate_type(expr, info, doc)
2072             elif 'command' in expr:
2073                 self._def_command(expr, info, doc)
2074             elif 'event' in expr:
2075                 self._def_event(expr, info, doc)
2076             elif 'include' in expr:
2077                 self._def_include(expr, info, doc)
2078             else:
2079                 assert False
2080
2081     def check(self):
2082         for ent in self._entity_list:
2083             ent.check(self)
2084
2085     def visit(self, visitor):
2086         visitor.visit_begin(self)
2087         module = None
2088         visitor.visit_module(module)
2089         for entity in self._entity_list:
2090             if visitor.visit_needed(entity):
2091                 if entity.module != module:
2092                     module = entity.module
2093                     visitor.visit_module(module)
2094                 entity.visit(visitor)
2095         visitor.visit_end()
2096
2097
2098 #
2099 # Code generation helpers
2100 #
2101
2102 def camel_case(name):
2103     new_name = ''
2104     first = True
2105     for ch in name:
2106         if ch in ['_', '-']:
2107             first = True
2108         elif first:
2109             new_name += ch.upper()
2110             first = False
2111         else:
2112             new_name += ch.lower()
2113     return new_name
2114
2115
2116 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2117 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2118 # ENUM24_Name -> ENUM24_NAME
2119 def camel_to_upper(value):
2120     c_fun_str = c_name(value, False)
2121     if value.isupper():
2122         return c_fun_str
2123
2124     new_name = ''
2125     length = len(c_fun_str)
2126     for i in range(length):
2127         c = c_fun_str[i]
2128         # When c is upper and no '_' appears before, do more checks
2129         if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
2130             if i < length - 1 and c_fun_str[i + 1].islower():
2131                 new_name += '_'
2132             elif c_fun_str[i - 1].isdigit():
2133                 new_name += '_'
2134         new_name += c
2135     return new_name.lstrip('_').upper()
2136
2137
2138 def c_enum_const(type_name, const_name, prefix=None):
2139     if prefix is not None:
2140         type_name = prefix
2141     return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
2142
2143
2144 if hasattr(str, 'maketrans'):
2145     c_name_trans = str.maketrans('.-', '__')
2146 else:
2147     c_name_trans = string.maketrans('.-', '__')
2148
2149
2150 # Map @name to a valid C identifier.
2151 # If @protect, avoid returning certain ticklish identifiers (like
2152 # C keywords) by prepending 'q_'.
2153 #
2154 # Used for converting 'name' from a 'name':'type' qapi definition
2155 # into a generated struct member, as well as converting type names
2156 # into substrings of a generated C function name.
2157 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2158 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2159 def c_name(name, protect=True):
2160     # ANSI X3J11/88-090, 3.1.1
2161     c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
2162                      'default', 'do', 'double', 'else', 'enum', 'extern',
2163                      'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2164                      'return', 'short', 'signed', 'sizeof', 'static',
2165                      'struct', 'switch', 'typedef', 'union', 'unsigned',
2166                      'void', 'volatile', 'while'])
2167     # ISO/IEC 9899:1999, 6.4.1
2168     c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2169     # ISO/IEC 9899:2011, 6.4.1
2170     c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2171                      '_Noreturn', '_Static_assert', '_Thread_local'])
2172     # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2173     # excluding _.*
2174     gcc_words = set(['asm', 'typeof'])
2175     # C++ ISO/IEC 14882:2003 2.11
2176     cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
2177                      'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2178                      'namespace', 'new', 'operator', 'private', 'protected',
2179                      'public', 'reinterpret_cast', 'static_cast', 'template',
2180                      'this', 'throw', 'true', 'try', 'typeid', 'typename',
2181                      'using', 'virtual', 'wchar_t',
2182                      # alternative representations
2183                      'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2184                      'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2185     # namespace pollution:
2186     polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2187     name = name.translate(c_name_trans)
2188     if protect and (name in c89_words | c99_words | c11_words | gcc_words
2189                     | cpp_words | polluted_words):
2190         return 'q_' + name
2191     return name
2192
2193
2194 eatspace = '\033EATSPACE.'
2195 pointer_suffix = ' *' + eatspace
2196
2197
2198 def genindent(count):
2199     ret = ''
2200     for _ in range(count):
2201         ret += ' '
2202     return ret
2203
2204
2205 indent_level = 0
2206
2207
2208 def push_indent(indent_amount=4):
2209     global indent_level
2210     indent_level += indent_amount
2211
2212
2213 def pop_indent(indent_amount=4):
2214     global indent_level
2215     indent_level -= indent_amount
2216
2217
2218 # Generate @code with @kwds interpolated.
2219 # Obey indent_level, and strip eatspace.
2220 def cgen(code, **kwds):
2221     raw = code % kwds
2222     if indent_level:
2223         indent = genindent(indent_level)
2224         # re.subn() lacks flags support before Python 2.7, use re.compile()
2225         raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2226                       indent, raw)
2227         raw = raw[0]
2228     return re.sub(re.escape(eatspace) + r' *', '', raw)
2229
2230
2231 def mcgen(code, **kwds):
2232     if code[0] == '\n':
2233         code = code[1:]
2234     return cgen(code, **kwds)
2235
2236
2237 def c_fname(filename):
2238     return re.sub(r'[^A-Za-z0-9_]', '_', filename)
2239
2240
2241 def guardstart(name):
2242     return mcgen('''
2243 #ifndef %(name)s
2244 #define %(name)s
2245
2246 ''',
2247                  name=c_fname(name).upper())
2248
2249
2250 def guardend(name):
2251     return mcgen('''
2252
2253 #endif /* %(name)s */
2254 ''',
2255                  name=c_fname(name).upper())
2256
2257
2258 def gen_if(ifcond):
2259     ret = ''
2260     for ifc in ifcond:
2261         ret += mcgen('''
2262 #if %(cond)s
2263 ''', cond=ifc)
2264     return ret
2265
2266
2267 def gen_endif(ifcond):
2268     ret = ''
2269     for ifc in reversed(ifcond):
2270         ret += mcgen('''
2271 #endif /* %(cond)s */
2272 ''', cond=ifc)
2273     return ret
2274
2275
2276 def _wrap_ifcond(ifcond, before, after):
2277     if before == after:
2278         return after   # suppress empty #if ... #endif
2279
2280     assert after.startswith(before)
2281     out = before
2282     added = after[len(before):]
2283     if added[0] == '\n':
2284         out += '\n'
2285         added = added[1:]
2286     out += gen_if(ifcond)
2287     out += added
2288     out += gen_endif(ifcond)
2289     return out
2290
2291
2292 def gen_enum_lookup(name, members, prefix=None):
2293     ret = mcgen('''
2294
2295 const QEnumLookup %(c_name)s_lookup = {
2296     .array = (const char *const[]) {
2297 ''',
2298                 c_name=c_name(name))
2299     for m in members:
2300         ret += gen_if(m.ifcond)
2301         index = c_enum_const(name, m.name, prefix)
2302         ret += mcgen('''
2303         [%(index)s] = "%(name)s",
2304 ''',
2305                      index=index, name=m.name)
2306         ret += gen_endif(m.ifcond)
2307
2308     ret += mcgen('''
2309     },
2310     .size = %(max_index)s
2311 };
2312 ''',
2313                  max_index=c_enum_const(name, '_MAX', prefix))
2314     return ret
2315
2316
2317 def gen_enum(name, members, prefix=None):
2318     # append automatically generated _MAX value
2319     enum_members = members + [QAPISchemaEnumMember('_MAX', None)]
2320
2321     ret = mcgen('''
2322
2323 typedef enum %(c_name)s {
2324 ''',
2325                 c_name=c_name(name))
2326
2327     for m in enum_members:
2328         ret += gen_if(m.ifcond)
2329         ret += mcgen('''
2330     %(c_enum)s,
2331 ''',
2332                      c_enum=c_enum_const(name, m.name, prefix))
2333         ret += gen_endif(m.ifcond)
2334
2335     ret += mcgen('''
2336 } %(c_name)s;
2337 ''',
2338                  c_name=c_name(name))
2339
2340     ret += mcgen('''
2341
2342 #define %(c_name)s_str(val) \\
2343     qapi_enum_lookup(&%(c_name)s_lookup, (val))
2344
2345 extern const QEnumLookup %(c_name)s_lookup;
2346 ''',
2347                  c_name=c_name(name))
2348     return ret
2349
2350
2351 def build_params(arg_type, boxed, extra=None):
2352     ret = ''
2353     sep = ''
2354     if boxed:
2355         assert arg_type
2356         ret += '%s arg' % arg_type.c_param_type()
2357         sep = ', '
2358     elif arg_type:
2359         assert not arg_type.variants
2360         for memb in arg_type.members:
2361             ret += sep
2362             sep = ', '
2363             if memb.optional:
2364                 ret += 'bool has_%s, ' % c_name(memb.name)
2365             ret += '%s %s' % (memb.type.c_param_type(),
2366                               c_name(memb.name))
2367     if extra:
2368         ret += sep + extra
2369     return ret if ret else 'void'
2370
2371
2372 #
2373 # Accumulate and write output
2374 #
2375
2376 class QAPIGen(object):
2377
2378     def __init__(self, fname):
2379         self.fname = fname
2380         self._preamble = ''
2381         self._body = ''
2382
2383     def preamble_add(self, text):
2384         self._preamble += text
2385
2386     def add(self, text):
2387         self._body += text
2388
2389     def get_content(self):
2390         return self._top() + self._preamble + self._body + self._bottom()
2391
2392     def _top(self):
2393         return ''
2394
2395     def _bottom(self):
2396         return ''
2397
2398     def write(self, output_dir):
2399         pathname = os.path.join(output_dir, self.fname)
2400         dir = os.path.dirname(pathname)
2401         if dir:
2402             try:
2403                 os.makedirs(dir)
2404             except os.error as e:
2405                 if e.errno != errno.EEXIST:
2406                     raise
2407         fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2408         if sys.version_info[0] >= 3:
2409             f = open(fd, 'r+', encoding='utf-8')
2410         else:
2411             f = os.fdopen(fd, 'r+')
2412         text = self.get_content()
2413         oldtext = f.read(len(text) + 1)
2414         if text != oldtext:
2415             f.seek(0)
2416             f.truncate(0)
2417             f.write(text)
2418         f.close()
2419
2420
2421 @contextmanager
2422 def ifcontext(ifcond, *args):
2423     """A 'with' statement context manager to wrap with start_if()/end_if()
2424
2425     *args: any number of QAPIGenCCode
2426
2427     Example::
2428
2429         with ifcontext(ifcond, self._genh, self._genc):
2430             modify self._genh and self._genc ...
2431
2432     Is equivalent to calling::
2433
2434         self._genh.start_if(ifcond)
2435         self._genc.start_if(ifcond)
2436         modify self._genh and self._genc ...
2437         self._genh.end_if()
2438         self._genc.end_if()
2439     """
2440     for arg in args:
2441         arg.start_if(ifcond)
2442     yield
2443     for arg in args:
2444         arg.end_if()
2445
2446
2447 class QAPIGenCCode(QAPIGen):
2448
2449     def __init__(self, fname):
2450         QAPIGen.__init__(self, fname)
2451         self._start_if = None
2452
2453     def start_if(self, ifcond):
2454         assert self._start_if is None
2455         self._start_if = (ifcond, self._body, self._preamble)
2456
2457     def end_if(self):
2458         assert self._start_if
2459         self._wrap_ifcond()
2460         self._start_if = None
2461
2462     def _wrap_ifcond(self):
2463         self._body = _wrap_ifcond(self._start_if[0],
2464                                   self._start_if[1], self._body)
2465         self._preamble = _wrap_ifcond(self._start_if[0],
2466                                       self._start_if[2], self._preamble)
2467
2468     def get_content(self):
2469         assert self._start_if is None
2470         return QAPIGen.get_content(self)
2471
2472
2473 class QAPIGenC(QAPIGenCCode):
2474
2475     def __init__(self, fname, blurb, pydoc):
2476         QAPIGenCCode.__init__(self, fname)
2477         self._blurb = blurb
2478         self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2479                                                   re.MULTILINE))
2480
2481     def _top(self):
2482         return mcgen('''
2483 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2484
2485 /*
2486 %(blurb)s
2487  *
2488  * %(copyright)s
2489  *
2490  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2491  * See the COPYING.LIB file in the top-level directory.
2492  */
2493
2494 ''',
2495                      blurb=self._blurb, copyright=self._copyright)
2496
2497     def _bottom(self):
2498         return mcgen('''
2499
2500 /* Dummy declaration to prevent empty .o file */
2501 char qapi_dummy_%(name)s;
2502 ''',
2503                      name=c_fname(self.fname))
2504
2505
2506 class QAPIGenH(QAPIGenC):
2507
2508     def _top(self):
2509         return QAPIGenC._top(self) + guardstart(self.fname)
2510
2511     def _bottom(self):
2512         return guardend(self.fname)
2513
2514
2515 class QAPIGenDoc(QAPIGen):
2516
2517     def _top(self):
2518         return (QAPIGen._top(self)
2519                 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2520
2521
2522 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2523
2524     def __init__(self, prefix, what, blurb, pydoc):
2525         self._prefix = prefix
2526         self._what = what
2527         self._genc = QAPIGenC(self._prefix + self._what + '.c',
2528                               blurb, pydoc)
2529         self._genh = QAPIGenH(self._prefix + self._what + '.h',
2530                               blurb, pydoc)
2531
2532     def write(self, output_dir):
2533         self._genc.write(output_dir)
2534         self._genh.write(output_dir)
2535
2536
2537 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2538
2539     def __init__(self, prefix, what, blurb, pydoc):
2540         self._prefix = prefix
2541         self._what = what
2542         self._blurb = blurb
2543         self._pydoc = pydoc
2544         self._genc = None
2545         self._genh = None
2546         self._module = {}
2547         self._main_module = None
2548
2549     @staticmethod
2550     def _is_user_module(name):
2551         return name and not name.startswith('./')
2552
2553     @staticmethod
2554     def _is_builtin_module(name):
2555         return not name
2556
2557     def _module_dirname(self, what, name):
2558         if self._is_user_module(name):
2559             return os.path.dirname(name)
2560         return ''
2561
2562     def _module_basename(self, what, name):
2563         ret = '' if self._is_builtin_module(name) else self._prefix
2564         if self._is_user_module(name):
2565             basename = os.path.basename(name)
2566             ret += what
2567             if name != self._main_module:
2568                 ret += '-' + os.path.splitext(basename)[0]
2569         else:
2570             name = name[2:] if name else 'builtin'
2571             ret += re.sub(r'-', '-' + name + '-', what)
2572         return ret
2573
2574     def _module_filename(self, what, name):
2575         return os.path.join(self._module_dirname(what, name),
2576                             self._module_basename(what, name))
2577
2578     def _add_module(self, name, blurb):
2579         basename = self._module_filename(self._what, name)
2580         genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
2581         genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
2582         self._module[name] = (genc, genh)
2583         self._set_module(name)
2584
2585     def _add_user_module(self, name, blurb):
2586         assert self._is_user_module(name)
2587         if self._main_module is None:
2588             self._main_module = name
2589         self._add_module(name, blurb)
2590
2591     def _add_system_module(self, name, blurb):
2592         self._add_module(name and './' + name, blurb)
2593
2594     def _set_module(self, name):
2595         self._genc, self._genh = self._module[name]
2596
2597     def write(self, output_dir, opt_builtins=False):
2598         for name in self._module:
2599             if self._is_builtin_module(name) and not opt_builtins:
2600                 continue
2601             (genc, genh) = self._module[name]
2602             genc.write(output_dir)
2603             genh.write(output_dir)
2604
2605     def _begin_user_module(self, name):
2606         pass
2607
2608     def visit_module(self, name):
2609         if name in self._module:
2610             self._set_module(name)
2611         elif self._is_builtin_module(name):
2612             # The built-in module has not been created.  No code may
2613             # be generated.
2614             self._genc = None
2615             self._genh = None
2616         else:
2617             self._add_user_module(name, self._blurb)
2618             self._begin_user_module(name)
2619
2620     def visit_include(self, name, info):
2621         relname = os.path.relpath(self._module_filename(self._what, name),
2622                                   os.path.dirname(self._genh.fname))
2623         self._genh.preamble_add(mcgen('''
2624 #include "%(relname)s.h"
2625 ''',
2626                                       relname=relname))
This page took 0.179439 seconds and 4 git commands to generate.