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