]> Git Repo - qemu.git/blob - scripts/qapi/common.py
Merge remote-tracking branch 'remotes/kraxel/tags/usb-20181214-pull-request' into...
[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 errno
17 import os
18 import re
19 import string
20 import sys
21 from collections import OrderedDict
22
23 builtin_types = {
24     'null':     'QTYPE_QNULL',
25     'str':      'QTYPE_QSTRING',
26     'int':      'QTYPE_QNUM',
27     'number':   'QTYPE_QNUM',
28     'bool':     'QTYPE_QBOOL',
29     'int8':     'QTYPE_QNUM',
30     'int16':    'QTYPE_QNUM',
31     'int32':    'QTYPE_QNUM',
32     'int64':    'QTYPE_QNUM',
33     'uint8':    'QTYPE_QNUM',
34     'uint16':   'QTYPE_QNUM',
35     'uint32':   'QTYPE_QNUM',
36     'uint64':   'QTYPE_QNUM',
37     'size':     'QTYPE_QNUM',
38     'any':      None,           # any QType possible, actually
39     'QType':    'QTYPE_QSTRING',
40 }
41
42 # Are documentation comments required?
43 doc_required = False
44
45 # Whitelist of commands allowed to return a non-dictionary
46 returns_whitelist = []
47
48 # Whitelist of entities allowed to violate case conventions
49 name_case_whitelist = []
50
51 enum_types = {}
52 struct_types = {}
53 union_types = {}
54 all_names = {}
55
56 #
57 # Parsing the schema into expressions
58 #
59
60
61 def error_path(parent):
62     res = ''
63     while parent:
64         res = ('In file included from %s:%d:\n' % (parent['file'],
65                                                    parent['line'])) + res
66         parent = parent['parent']
67     return res
68
69
70 class QAPIError(Exception):
71     def __init__(self, fname, line, col, incl_info, msg):
72         Exception.__init__(self)
73         self.fname = fname
74         self.line = line
75         self.col = col
76         self.info = incl_info
77         self.msg = msg
78
79     def __str__(self):
80         loc = '%s:%d' % (self.fname, self.line)
81         if self.col is not None:
82             loc += ':%s' % self.col
83         return error_path(self.info) + '%s: %s' % (loc, self.msg)
84
85
86 class QAPIParseError(QAPIError):
87     def __init__(self, parser, msg):
88         col = 1
89         for ch in parser.src[parser.line_pos:parser.pos]:
90             if ch == '\t':
91                 col = (col + 7) % 8 + 1
92             else:
93                 col += 1
94         QAPIError.__init__(self, parser.fname, parser.line, col,
95                            parser.incl_info, msg)
96
97
98 class QAPISemError(QAPIError):
99     def __init__(self, info, msg):
100         QAPIError.__init__(self, info['file'], info['line'], None,
101                            info['parent'], msg)
102
103
104 class QAPIDoc(object):
105     class Section(object):
106         def __init__(self, name=None):
107             # optional section name (argument/member or section name)
108             self.name = name
109             # the list of lines for this section
110             self.text = ''
111
112         def append(self, line):
113             self.text += line.rstrip() + '\n'
114
115     class ArgSection(Section):
116         def __init__(self, name):
117             QAPIDoc.Section.__init__(self, name)
118             self.member = None
119
120         def connect(self, member):
121             self.member = member
122
123     def __init__(self, parser, info):
124         # self._parser is used to report errors with QAPIParseError.  The
125         # resulting error position depends on the state of the parser.
126         # It happens to be the beginning of the comment.  More or less
127         # servicable, but action at a distance.
128         self._parser = parser
129         self.info = info
130         self.symbol = None
131         self.body = QAPIDoc.Section()
132         # dict mapping parameter name to ArgSection
133         self.args = OrderedDict()
134         # a list of Section
135         self.sections = []
136         # the current section
137         self._section = self.body
138
139     def has_section(self, name):
140         """Return True if we have a section with this name."""
141         for i in self.sections:
142             if i.name == name:
143                 return True
144         return False
145
146     def append(self, line):
147         """Parse a comment line and add it to the documentation."""
148         line = line[1:]
149         if not line:
150             self._append_freeform(line)
151             return
152
153         if line[0] != ' ':
154             raise QAPIParseError(self._parser, "Missing space after #")
155         line = line[1:]
156
157         # FIXME not nice: things like '#  @foo:' and '# @foo: ' aren't
158         # recognized, and get silently treated as ordinary text
159         if self.symbol:
160             self._append_symbol_line(line)
161         elif not self.body.text and line.startswith('@'):
162             if not line.endswith(':'):
163                 raise QAPIParseError(self._parser, "Line should end with :")
164             self.symbol = line[1:-1]
165             # FIXME invalid names other than the empty string aren't flagged
166             if not self.symbol:
167                 raise QAPIParseError(self._parser, "Invalid name")
168         else:
169             self._append_freeform(line)
170
171     def end_comment(self):
172         self._end_section()
173
174     def _append_symbol_line(self, line):
175         name = line.split(' ', 1)[0]
176
177         if name.startswith('@') and name.endswith(':'):
178             line = line[len(name)+1:]
179             self._start_args_section(name[1:-1])
180         elif name in ('Returns:', 'Since:',
181                       # those are often singular or plural
182                       'Note:', 'Notes:',
183                       'Example:', 'Examples:',
184                       'TODO:'):
185             line = line[len(name)+1:]
186             self._start_section(name[:-1])
187
188         self._append_freeform(line)
189
190     def _start_args_section(self, name):
191         # FIXME invalid names other than the empty string aren't flagged
192         if not name:
193             raise QAPIParseError(self._parser, "Invalid parameter name")
194         if name in self.args:
195             raise QAPIParseError(self._parser,
196                                  "'%s' parameter name duplicated" % name)
197         if self.sections:
198             raise QAPIParseError(self._parser,
199                                  "'@%s:' can't follow '%s' section"
200                                  % (name, self.sections[0].name))
201         self._end_section()
202         self._section = QAPIDoc.ArgSection(name)
203         self.args[name] = self._section
204
205     def _start_section(self, name=None):
206         if name in ('Returns', 'Since') and self.has_section(name):
207             raise QAPIParseError(self._parser,
208                                  "Duplicated '%s' section" % name)
209         self._end_section()
210         self._section = QAPIDoc.Section(name)
211         self.sections.append(self._section)
212
213     def _end_section(self):
214         if self._section:
215             text = self._section.text = self._section.text.strip()
216             if self._section.name and (not text or text.isspace()):
217                 raise QAPIParseError(self._parser, "Empty doc section '%s'"
218                                      % self._section.name)
219             self._section = None
220
221     def _append_freeform(self, line):
222         in_arg = isinstance(self._section, QAPIDoc.ArgSection)
223         if (in_arg and self._section.text.endswith('\n\n')
224                 and line and not line[0].isspace()):
225             self._start_section()
226         if (in_arg or not self._section.name
227                 or not self._section.name.startswith('Example')):
228             line = line.strip()
229         match = re.match(r'(@\S+:)', line)
230         if match:
231             raise QAPIParseError(self._parser,
232                                  "'%s' not allowed in free-form documentation"
233                                  % match.group(1))
234         self._section.append(line)
235
236     def connect_member(self, member):
237         if member.name not in self.args:
238             # Undocumented TODO outlaw
239             self.args[member.name] = QAPIDoc.ArgSection(member.name)
240         self.args[member.name].connect(member)
241
242     def check_expr(self, expr):
243         if self.has_section('Returns') and 'command' not in expr:
244             raise QAPISemError(self.info,
245                                "'Returns:' is only valid for commands")
246
247     def check(self):
248         bogus = [name for name, section in self.args.items()
249                  if not section.member]
250         if bogus:
251             raise QAPISemError(
252                 self.info,
253                 "The following documented members are not in "
254                 "the declaration: %s" % ", ".join(bogus))
255
256
257 class QAPISchemaParser(object):
258
259     def __init__(self, fp, previously_included=[], incl_info=None):
260         self.fname = fp.name
261         previously_included.append(os.path.abspath(fp.name))
262         self.incl_info = incl_info
263         self.src = fp.read()
264         if self.src == '' or self.src[-1] != '\n':
265             self.src += '\n'
266         self.cursor = 0
267         self.line = 1
268         self.line_pos = 0
269         self.exprs = []
270         self.docs = []
271         self.accept()
272         cur_doc = None
273
274         while self.tok is not None:
275             info = {'file': self.fname, 'line': self.line,
276                     'parent': self.incl_info}
277             if self.tok == '#':
278                 self.reject_expr_doc(cur_doc)
279                 cur_doc = self.get_doc(info)
280                 self.docs.append(cur_doc)
281                 continue
282
283             expr = self.get_expr(False)
284             if 'include' in expr:
285                 self.reject_expr_doc(cur_doc)
286                 if len(expr) != 1:
287                     raise QAPISemError(info, "Invalid 'include' directive")
288                 include = expr['include']
289                 if not isinstance(include, str):
290                     raise QAPISemError(info,
291                                        "Value of 'include' must be a string")
292                 incl_fname = os.path.join(os.path.dirname(self.fname),
293                                           include)
294                 self.exprs.append({'expr': {'include': incl_fname},
295                                    'info': info})
296                 exprs_include = self._include(include, info, incl_fname,
297                                               previously_included)
298                 if exprs_include:
299                     self.exprs.extend(exprs_include.exprs)
300                     self.docs.extend(exprs_include.docs)
301             elif "pragma" in expr:
302                 self.reject_expr_doc(cur_doc)
303                 if len(expr) != 1:
304                     raise QAPISemError(info, "Invalid 'pragma' directive")
305                 pragma = expr['pragma']
306                 if not isinstance(pragma, dict):
307                     raise QAPISemError(
308                         info, "Value of 'pragma' must be a dictionary")
309                 for name, value in pragma.items():
310                     self._pragma(name, value, info)
311             else:
312                 expr_elem = {'expr': expr,
313                              'info': info}
314                 if cur_doc:
315                     if not cur_doc.symbol:
316                         raise QAPISemError(
317                             cur_doc.info, "Expression documentation required")
318                     expr_elem['doc'] = cur_doc
319                 self.exprs.append(expr_elem)
320             cur_doc = None
321         self.reject_expr_doc(cur_doc)
322
323     @staticmethod
324     def reject_expr_doc(doc):
325         if doc and doc.symbol:
326             raise QAPISemError(
327                 doc.info,
328                 "Documentation for '%s' is not followed by the definition"
329                 % doc.symbol)
330
331     def _include(self, include, info, incl_fname, previously_included):
332         incl_abs_fname = os.path.abspath(incl_fname)
333         # catch inclusion cycle
334         inf = info
335         while inf:
336             if incl_abs_fname == os.path.abspath(inf['file']):
337                 raise QAPISemError(info, "Inclusion loop for %s" % include)
338             inf = inf['parent']
339
340         # skip multiple include of the same file
341         if incl_abs_fname in previously_included:
342             return None
343
344         try:
345             if sys.version_info[0] >= 3:
346                 fobj = open(incl_fname, 'r', encoding='utf-8')
347             else:
348                 fobj = open(incl_fname, 'r')
349         except IOError as e:
350             raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname))
351         return QAPISchemaParser(fobj, previously_included, info)
352
353     def _pragma(self, name, value, info):
354         global doc_required, returns_whitelist, name_case_whitelist
355         if name == 'doc-required':
356             if not isinstance(value, bool):
357                 raise QAPISemError(info,
358                                    "Pragma 'doc-required' must be boolean")
359             doc_required = value
360         elif name == 'returns-whitelist':
361             if (not isinstance(value, list)
362                     or any([not isinstance(elt, str) for elt in value])):
363                 raise QAPISemError(info,
364                                    "Pragma returns-whitelist must be"
365                                    " a list of strings")
366             returns_whitelist = value
367         elif name == 'name-case-whitelist':
368             if (not isinstance(value, list)
369                     or any([not isinstance(elt, str) for elt in value])):
370                 raise QAPISemError(info,
371                                    "Pragma name-case-whitelist must be"
372                                    " a list of strings")
373             name_case_whitelist = value
374         else:
375             raise QAPISemError(info, "Unknown pragma '%s'" % name)
376
377     def accept(self, skip_comment=True):
378         while True:
379             self.tok = self.src[self.cursor]
380             self.pos = self.cursor
381             self.cursor += 1
382             self.val = None
383
384             if self.tok == '#':
385                 if self.src[self.cursor] == '#':
386                     # Start of doc comment
387                     skip_comment = False
388                 self.cursor = self.src.find('\n', self.cursor)
389                 if not skip_comment:
390                     self.val = self.src[self.pos:self.cursor]
391                     return
392             elif self.tok in '{}:,[]':
393                 return
394             elif self.tok == "'":
395                 string = ''
396                 esc = False
397                 while True:
398                     ch = self.src[self.cursor]
399                     self.cursor += 1
400                     if ch == '\n':
401                         raise QAPIParseError(self, 'Missing terminating "\'"')
402                     if esc:
403                         if ch == 'b':
404                             string += '\b'
405                         elif ch == 'f':
406                             string += '\f'
407                         elif ch == 'n':
408                             string += '\n'
409                         elif ch == 'r':
410                             string += '\r'
411                         elif ch == 't':
412                             string += '\t'
413                         elif ch == 'u':
414                             value = 0
415                             for _ in range(0, 4):
416                                 ch = self.src[self.cursor]
417                                 self.cursor += 1
418                                 if ch not in '0123456789abcdefABCDEF':
419                                     raise QAPIParseError(self,
420                                                          '\\u escape needs 4 '
421                                                          'hex digits')
422                                 value = (value << 4) + int(ch, 16)
423                             # If Python 2 and 3 didn't disagree so much on
424                             # how to handle Unicode, then we could allow
425                             # Unicode string defaults.  But most of QAPI is
426                             # ASCII-only, so we aren't losing much for now.
427                             if not value or value > 0x7f:
428                                 raise QAPIParseError(self,
429                                                      'For now, \\u escape '
430                                                      'only supports non-zero '
431                                                      'values up to \\u007f')
432                             string += chr(value)
433                         elif ch in '\\/\'"':
434                             string += ch
435                         else:
436                             raise QAPIParseError(self,
437                                                  "Unknown escape \\%s" % ch)
438                         esc = False
439                     elif ch == '\\':
440                         esc = True
441                     elif ch == "'":
442                         self.val = string
443                         return
444                     else:
445                         string += ch
446             elif self.src.startswith('true', self.pos):
447                 self.val = True
448                 self.cursor += 3
449                 return
450             elif self.src.startswith('false', self.pos):
451                 self.val = False
452                 self.cursor += 4
453                 return
454             elif self.src.startswith('null', self.pos):
455                 self.val = None
456                 self.cursor += 3
457                 return
458             elif self.tok == '\n':
459                 if self.cursor == len(self.src):
460                     self.tok = None
461                     return
462                 self.line += 1
463                 self.line_pos = self.cursor
464             elif not self.tok.isspace():
465                 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
466
467     def get_members(self):
468         expr = OrderedDict()
469         if self.tok == '}':
470             self.accept()
471             return expr
472         if self.tok != "'":
473             raise QAPIParseError(self, 'Expected string or "}"')
474         while True:
475             key = self.val
476             self.accept()
477             if self.tok != ':':
478                 raise QAPIParseError(self, 'Expected ":"')
479             self.accept()
480             if key in expr:
481                 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
482             expr[key] = self.get_expr(True)
483             if self.tok == '}':
484                 self.accept()
485                 return expr
486             if self.tok != ',':
487                 raise QAPIParseError(self, 'Expected "," or "}"')
488             self.accept()
489             if self.tok != "'":
490                 raise QAPIParseError(self, 'Expected string')
491
492     def get_values(self):
493         expr = []
494         if self.tok == ']':
495             self.accept()
496             return expr
497         if self.tok not in "{['tfn":
498             raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
499                                  'boolean or "null"')
500         while True:
501             expr.append(self.get_expr(True))
502             if self.tok == ']':
503                 self.accept()
504                 return expr
505             if self.tok != ',':
506                 raise QAPIParseError(self, 'Expected "," or "]"')
507             self.accept()
508
509     def get_expr(self, nested):
510         if self.tok != '{' and not nested:
511             raise QAPIParseError(self, 'Expected "{"')
512         if self.tok == '{':
513             self.accept()
514             expr = self.get_members()
515         elif self.tok == '[':
516             self.accept()
517             expr = self.get_values()
518         elif self.tok in "'tfn":
519             expr = self.val
520             self.accept()
521         else:
522             raise QAPIParseError(self, 'Expected "{", "[", string, '
523                                  'boolean or "null"')
524         return expr
525
526     def get_doc(self, info):
527         if self.val != '##':
528             raise QAPIParseError(self, "Junk after '##' at start of "
529                                  "documentation comment")
530
531         doc = QAPIDoc(self, info)
532         self.accept(False)
533         while self.tok == '#':
534             if self.val.startswith('##'):
535                 # End of doc comment
536                 if self.val != '##':
537                     raise QAPIParseError(self, "Junk after '##' at end of "
538                                          "documentation comment")
539                 doc.end_comment()
540                 self.accept()
541                 return doc
542             else:
543                 doc.append(self.val)
544             self.accept(False)
545
546         raise QAPIParseError(self, "Documentation comment must end with '##'")
547
548
549 #
550 # Semantic analysis of schema expressions
551 # TODO fold into QAPISchema
552 # TODO catching name collisions in generated code would be nice
553 #
554
555
556 def find_base_members(base):
557     if isinstance(base, dict):
558         return base
559     base_struct_define = struct_types.get(base)
560     if not base_struct_define:
561         return None
562     return base_struct_define['data']
563
564
565 # Return the qtype of an alternate branch, or None on error.
566 def find_alternate_member_qtype(qapi_type):
567     if qapi_type in builtin_types:
568         return builtin_types[qapi_type]
569     elif qapi_type in struct_types:
570         return 'QTYPE_QDICT'
571     elif qapi_type in enum_types:
572         return 'QTYPE_QSTRING'
573     elif qapi_type in union_types:
574         return 'QTYPE_QDICT'
575     return None
576
577
578 # Return the discriminator enum define if discriminator is specified as an
579 # enum type, otherwise return None.
580 def discriminator_find_enum_define(expr):
581     base = expr.get('base')
582     discriminator = expr.get('discriminator')
583
584     if not (discriminator and base):
585         return None
586
587     base_members = find_base_members(base)
588     if not base_members:
589         return None
590
591     discriminator_value = base_members.get(discriminator)
592     if not discriminator_value:
593         return None
594
595     return enum_types.get(discriminator_value['type'])
596
597
598 # Names must be letters, numbers, -, and _.  They must start with letter,
599 # except for downstream extensions which must start with __RFQDN_.
600 # Dots are only valid in the downstream extension prefix.
601 valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
602                         '[a-zA-Z][a-zA-Z0-9_-]*$')
603
604
605 def check_name(info, source, name, allow_optional=False,
606                enum_member=False):
607     global valid_name
608     membername = name
609
610     if not isinstance(name, str):
611         raise QAPISemError(info, "%s requires a string name" % source)
612     if name.startswith('*'):
613         membername = name[1:]
614         if not allow_optional:
615             raise QAPISemError(info, "%s does not allow optional name '%s'"
616                                % (source, name))
617     # Enum members can start with a digit, because the generated C
618     # code always prefixes it with the enum name
619     if enum_member and membername[0].isdigit():
620         membername = 'D' + membername
621     # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
622     # and 'q_obj_*' implicit type names.
623     if not valid_name.match(membername) or \
624        c_name(membername, False).startswith('q_'):
625         raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
626
627
628 def add_name(name, info, meta, implicit=False):
629     global all_names
630     check_name(info, "'%s'" % meta, name)
631     # FIXME should reject names that differ only in '_' vs. '.'
632     # vs. '-', because they're liable to clash in generated C.
633     if name in all_names:
634         raise QAPISemError(info, "%s '%s' is already defined"
635                            % (all_names[name], name))
636     if not implicit and (name.endswith('Kind') or name.endswith('List')):
637         raise QAPISemError(info, "%s '%s' should not end in '%s'"
638                            % (meta, name, name[-4:]))
639     all_names[name] = meta
640
641
642 def check_if(expr, info):
643
644     def check_if_str(ifcond, info):
645         if not isinstance(ifcond, str):
646             raise QAPISemError(
647                 info, "'if' condition must be a string or a list of strings")
648         if ifcond == '':
649             raise QAPISemError(info, "'if' condition '' makes no sense")
650
651     ifcond = expr.get('if')
652     if ifcond is None:
653         return
654     if isinstance(ifcond, list):
655         if ifcond == []:
656             raise QAPISemError(info, "'if' condition [] is useless")
657         for elt in ifcond:
658             check_if_str(elt, info)
659     else:
660         check_if_str(ifcond, info)
661
662
663 def check_type(info, source, value, allow_array=False,
664                allow_dict=False, allow_optional=False,
665                allow_metas=[]):
666     global all_names
667
668     if value is None:
669         return
670
671     # Check if array type for value is okay
672     if isinstance(value, list):
673         if not allow_array:
674             raise QAPISemError(info, "%s cannot be an array" % source)
675         if len(value) != 1 or not isinstance(value[0], str):
676             raise QAPISemError(info,
677                                "%s: array type must contain single type name" %
678                                source)
679         value = value[0]
680
681     # Check if type name for value is okay
682     if isinstance(value, str):
683         if value not in all_names:
684             raise QAPISemError(info, "%s uses unknown type '%s'"
685                                % (source, value))
686         if not all_names[value] in allow_metas:
687             raise QAPISemError(info, "%s cannot use %s type '%s'" %
688                                (source, all_names[value], value))
689         return
690
691     if not allow_dict:
692         raise QAPISemError(info, "%s should be a type name" % source)
693
694     if not isinstance(value, OrderedDict):
695         raise QAPISemError(info,
696                            "%s should be a dictionary or type name" % source)
697
698     # value is a dictionary, check that each member is okay
699     for (key, arg) in value.items():
700         check_name(info, "Member of %s" % source, key,
701                    allow_optional=allow_optional)
702         if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
703             raise QAPISemError(info, "Member of %s uses reserved name '%s'"
704                                % (source, key))
705         # Todo: allow dictionaries to represent default values of
706         # an optional argument.
707         check_known_keys(info, "member '%s' of %s" % (key, source),
708                          arg, ['type'], ['if'])
709         check_type(info, "Member '%s' of %s" % (key, source),
710                    arg['type'], allow_array=True,
711                    allow_metas=['built-in', 'union', 'alternate', 'struct',
712                                 'enum'])
713
714
715 def check_command(expr, info):
716     name = expr['command']
717     boxed = expr.get('boxed', False)
718
719     args_meta = ['struct']
720     if boxed:
721         args_meta += ['union', 'alternate']
722     check_type(info, "'data' for command '%s'" % name,
723                expr.get('data'), allow_dict=not boxed, allow_optional=True,
724                allow_metas=args_meta)
725     returns_meta = ['union', 'struct']
726     if name in returns_whitelist:
727         returns_meta += ['built-in', 'alternate', 'enum']
728     check_type(info, "'returns' for command '%s'" % name,
729                expr.get('returns'), allow_array=True,
730                allow_optional=True, allow_metas=returns_meta)
731
732
733 def check_event(expr, info):
734     name = expr['event']
735     boxed = expr.get('boxed', False)
736
737     meta = ['struct']
738     if boxed:
739         meta += ['union', 'alternate']
740     check_type(info, "'data' for event '%s'" % name,
741                expr.get('data'), allow_dict=not boxed, allow_optional=True,
742                allow_metas=meta)
743
744
745 def enum_get_names(expr):
746     return [e['name'] for e in expr['data']]
747
748
749 def check_union(expr, info):
750     name = expr['union']
751     base = expr.get('base')
752     discriminator = expr.get('discriminator')
753     members = expr['data']
754
755     # Two types of unions, determined by discriminator.
756
757     # With no discriminator it is a simple union.
758     if discriminator is None:
759         enum_define = None
760         allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
761         if base is not None:
762             raise QAPISemError(info, "Simple union '%s' must not have a base" %
763                                name)
764
765     # Else, it's a flat union.
766     else:
767         # The object must have a string or dictionary 'base'.
768         check_type(info, "'base' for union '%s'" % name,
769                    base, allow_dict=True, allow_optional=True,
770                    allow_metas=['struct'])
771         if not base:
772             raise QAPISemError(info, "Flat union '%s' must have a base"
773                                % name)
774         base_members = find_base_members(base)
775         assert base_members is not None
776
777         # The value of member 'discriminator' must name a non-optional
778         # member of the base struct.
779         check_name(info, "Discriminator of flat union '%s'" % name,
780                    discriminator)
781         discriminator_value = base_members.get(discriminator)
782         if not discriminator_value:
783             raise QAPISemError(info,
784                                "Discriminator '%s' is not a member of base "
785                                "struct '%s'"
786                                % (discriminator, base))
787         if discriminator_value.get('if'):
788             raise QAPISemError(info, 'The discriminator %s.%s for union %s '
789                                'must not be conditional' %
790                                (base, discriminator, name))
791         enum_define = enum_types.get(discriminator_value['type'])
792         allow_metas = ['struct']
793         # Do not allow string discriminator
794         if not enum_define:
795             raise QAPISemError(info,
796                                "Discriminator '%s' must be of enumeration "
797                                "type" % discriminator)
798
799     # Check every branch; don't allow an empty union
800     if len(members) == 0:
801         raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
802     for (key, value) in members.items():
803         check_name(info, "Member of union '%s'" % name, key)
804
805         check_known_keys(info, "member '%s' of union '%s'" % (key, name),
806                          value, ['type'], ['if'])
807         # Each value must name a known type
808         check_type(info, "Member '%s' of union '%s'" % (key, name),
809                    value['type'],
810                    allow_array=not base, allow_metas=allow_metas)
811
812         # If the discriminator names an enum type, then all members
813         # of 'data' must also be members of the enum type.
814         if enum_define:
815             if key not in enum_get_names(enum_define):
816                 raise QAPISemError(info,
817                                    "Discriminator value '%s' is not found in "
818                                    "enum '%s'"
819                                    % (key, enum_define['enum']))
820
821
822 def check_alternate(expr, info):
823     name = expr['alternate']
824     members = expr['data']
825     types_seen = {}
826
827     # Check every branch; require at least two branches
828     if len(members) < 2:
829         raise QAPISemError(info,
830                            "Alternate '%s' should have at least two branches "
831                            "in 'data'" % name)
832     for (key, value) in members.items():
833         check_name(info, "Member of alternate '%s'" % name, key)
834         check_known_keys(info,
835                          "member '%s' of alternate '%s'" % (key, name),
836                          value, ['type'], ['if'])
837         typ = value['type']
838
839         # Ensure alternates have no type conflicts.
840         check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ,
841                    allow_metas=['built-in', 'union', 'struct', 'enum'])
842         qtype = find_alternate_member_qtype(typ)
843         if not qtype:
844             raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
845                                "type '%s'" % (name, key, typ))
846         conflicting = set([qtype])
847         if qtype == 'QTYPE_QSTRING':
848             enum_expr = enum_types.get(typ)
849             if enum_expr:
850                 for v in enum_get_names(enum_expr):
851                     if v in ['on', 'off']:
852                         conflicting.add('QTYPE_QBOOL')
853                     if re.match(r'[-+0-9.]', v): # lazy, could be tightened
854                         conflicting.add('QTYPE_QNUM')
855             else:
856                 conflicting.add('QTYPE_QNUM')
857                 conflicting.add('QTYPE_QBOOL')
858         for qt in conflicting:
859             if qt in types_seen:
860                 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
861                                    "be distinguished from member '%s'"
862                                    % (name, key, types_seen[qt]))
863             types_seen[qt] = key
864
865
866 def check_enum(expr, info):
867     name = expr['enum']
868     members = expr['data']
869     prefix = expr.get('prefix')
870
871     if not isinstance(members, list):
872         raise QAPISemError(info,
873                            "Enum '%s' requires an array for 'data'" % name)
874     if prefix is not None and not isinstance(prefix, str):
875         raise QAPISemError(info,
876                            "Enum '%s' requires a string for 'prefix'" % name)
877
878     for member in members:
879         source = "dictionary member of enum '%s'" % name
880         check_known_keys(info, source, member, ['name'], ['if'])
881         check_if(member, info)
882         check_name(info, "Member of enum '%s'" % name, member['name'],
883                    enum_member=True)
884
885
886 def check_struct(expr, info):
887     name = expr['struct']
888     members = expr['data']
889
890     check_type(info, "'data' for struct '%s'" % name, members,
891                allow_dict=True, allow_optional=True)
892     check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
893                allow_metas=['struct'])
894
895
896 def check_known_keys(info, source, keys, required, optional):
897
898     def pprint(elems):
899         return ', '.join("'" + e + "'" for e in sorted(elems))
900
901     missing = set(required) - set(keys)
902     if missing:
903         raise QAPISemError(info, "Key%s %s %s missing from %s"
904                            % ('s' if len(missing) > 1 else '', pprint(missing),
905                               'are' if len(missing) > 1 else 'is', source))
906     allowed = set(required + optional)
907     unknown = set(keys) - allowed
908     if unknown:
909         raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s."
910                            % ('s' if len(unknown) > 1 else '', pprint(unknown),
911                               source, pprint(allowed)))
912
913
914 def check_keys(expr_elem, meta, required, optional=[]):
915     expr = expr_elem['expr']
916     info = expr_elem['info']
917     name = expr[meta]
918     if not isinstance(name, str):
919         raise QAPISemError(info, "'%s' key must have a string value" % meta)
920     required = required + [meta]
921     source = "%s '%s'" % (meta, name)
922     check_known_keys(info, source, expr.keys(), required, optional)
923     for (key, value) in expr.items():
924         if key in ['gen', 'success-response'] and value is not False:
925             raise QAPISemError(info,
926                                "'%s' of %s '%s' should only use false value"
927                                % (key, meta, name))
928         if (key in ['boxed', 'allow-oob', 'allow-preconfig']
929                 and value is not True):
930             raise QAPISemError(info,
931                                "'%s' of %s '%s' should only use true value"
932                                % (key, meta, name))
933         if key == 'if':
934             check_if(expr, info)
935
936
937 def normalize_enum(expr):
938     if isinstance(expr['data'], list):
939         expr['data'] = [m if isinstance(m, dict) else {'name': m}
940                         for m in expr['data']]
941
942
943 def normalize_members(members):
944     if isinstance(members, OrderedDict):
945         for key, arg in members.items():
946             if isinstance(arg, dict):
947                 continue
948             members[key] = {'type': arg}
949
950
951 def check_exprs(exprs):
952     global all_names
953
954     # Populate name table with names of built-in types
955     for builtin in builtin_types.keys():
956         all_names[builtin] = 'built-in'
957
958     # Learn the types and check for valid expression keys
959     for expr_elem in exprs:
960         expr = expr_elem['expr']
961         info = expr_elem['info']
962         doc = expr_elem.get('doc')
963
964         if 'include' in expr:
965             continue
966
967         if not doc and doc_required:
968             raise QAPISemError(info,
969                                "Expression missing documentation comment")
970
971         if 'enum' in expr:
972             meta = 'enum'
973             check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
974             normalize_enum(expr)
975             enum_types[expr[meta]] = expr
976         elif 'union' in expr:
977             meta = 'union'
978             check_keys(expr_elem, 'union', ['data'],
979                        ['base', 'discriminator', 'if'])
980             normalize_members(expr.get('base'))
981             normalize_members(expr['data'])
982             union_types[expr[meta]] = expr
983         elif 'alternate' in expr:
984             meta = 'alternate'
985             check_keys(expr_elem, 'alternate', ['data'], ['if'])
986             normalize_members(expr['data'])
987         elif 'struct' in expr:
988             meta = 'struct'
989             check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
990             normalize_members(expr['data'])
991             struct_types[expr[meta]] = expr
992         elif 'command' in expr:
993             meta = 'command'
994             check_keys(expr_elem, 'command', [],
995                        ['data', 'returns', 'gen', 'success-response',
996                         'boxed', 'allow-oob', 'allow-preconfig', 'if'])
997             normalize_members(expr.get('data'))
998         elif 'event' in expr:
999             meta = 'event'
1000             check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
1001             normalize_members(expr.get('data'))
1002         else:
1003             raise QAPISemError(expr_elem['info'],
1004                                "Expression is missing metatype")
1005         name = expr[meta]
1006         add_name(name, info, meta)
1007         if doc and doc.symbol != name:
1008             raise QAPISemError(info, "Definition of '%s' follows documentation"
1009                                " for '%s'" % (name, doc.symbol))
1010
1011     # Try again for hidden UnionKind enum
1012     for expr_elem in exprs:
1013         expr = expr_elem['expr']
1014
1015         if 'include' in expr:
1016             continue
1017         if 'union' in expr and not discriminator_find_enum_define(expr):
1018             name = '%sKind' % expr['union']
1019         elif 'alternate' in expr:
1020             name = '%sKind' % expr['alternate']
1021         else:
1022             continue
1023         enum_types[name] = {'enum': name}
1024         add_name(name, info, 'enum', implicit=True)
1025
1026     # Validate that exprs make sense
1027     for expr_elem in exprs:
1028         expr = expr_elem['expr']
1029         info = expr_elem['info']
1030         doc = expr_elem.get('doc')
1031
1032         if 'include' in expr:
1033             continue
1034         if 'enum' in expr:
1035             check_enum(expr, info)
1036         elif 'union' in expr:
1037             check_union(expr, info)
1038         elif 'alternate' in expr:
1039             check_alternate(expr, info)
1040         elif 'struct' in expr:
1041             check_struct(expr, info)
1042         elif 'command' in expr:
1043             check_command(expr, info)
1044         elif 'event' in expr:
1045             check_event(expr, info)
1046         else:
1047             assert False, 'unexpected meta type'
1048
1049         if doc:
1050             doc.check_expr(expr)
1051
1052     return exprs
1053
1054
1055 #
1056 # Schema compiler frontend
1057 #
1058
1059 def listify_cond(ifcond):
1060     if not ifcond:
1061         return []
1062     if not isinstance(ifcond, list):
1063         return [ifcond]
1064     return ifcond
1065
1066
1067 class QAPISchemaEntity(object):
1068     def __init__(self, name, info, doc, ifcond=None):
1069         assert name is None or isinstance(name, str)
1070         self.name = name
1071         self.module = None
1072         # For explicitly defined entities, info points to the (explicit)
1073         # definition.  For builtins (and their arrays), info is None.
1074         # For implicitly defined entities, info points to a place that
1075         # triggered the implicit definition (there may be more than one
1076         # such place).
1077         self.info = info
1078         self.doc = doc
1079         self._ifcond = ifcond  # self.ifcond is set only after .check()
1080
1081     def c_name(self):
1082         return c_name(self.name)
1083
1084     def check(self, schema):
1085         if isinstance(self._ifcond, QAPISchemaType):
1086             # inherit the condition from a type
1087             typ = self._ifcond
1088             typ.check(schema)
1089             self.ifcond = typ.ifcond
1090         else:
1091             self.ifcond = listify_cond(self._ifcond)
1092
1093     def is_implicit(self):
1094         return not self.info
1095
1096     def visit(self, visitor):
1097         pass
1098
1099
1100 class QAPISchemaVisitor(object):
1101     def visit_begin(self, schema):
1102         pass
1103
1104     def visit_end(self):
1105         pass
1106
1107     def visit_module(self, fname):
1108         pass
1109
1110     def visit_needed(self, entity):
1111         # Default to visiting everything
1112         return True
1113
1114     def visit_include(self, fname, info):
1115         pass
1116
1117     def visit_builtin_type(self, name, info, json_type):
1118         pass
1119
1120     def visit_enum_type(self, name, info, ifcond, members, prefix):
1121         pass
1122
1123     def visit_array_type(self, name, info, ifcond, element_type):
1124         pass
1125
1126     def visit_object_type(self, name, info, ifcond, base, members, variants):
1127         pass
1128
1129     def visit_object_type_flat(self, name, info, ifcond, members, variants):
1130         pass
1131
1132     def visit_alternate_type(self, name, info, ifcond, variants):
1133         pass
1134
1135     def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1136                       success_response, boxed, allow_oob, allow_preconfig):
1137         pass
1138
1139     def visit_event(self, name, info, ifcond, arg_type, boxed):
1140         pass
1141
1142
1143 class QAPISchemaInclude(QAPISchemaEntity):
1144
1145     def __init__(self, fname, info):
1146         QAPISchemaEntity.__init__(self, None, info, None)
1147         self.fname = fname
1148
1149     def visit(self, visitor):
1150         visitor.visit_include(self.fname, self.info)
1151
1152
1153 class QAPISchemaType(QAPISchemaEntity):
1154     # Return the C type for common use.
1155     # For the types we commonly box, this is a pointer type.
1156     def c_type(self):
1157         pass
1158
1159     # Return the C type to be used in a parameter list.
1160     def c_param_type(self):
1161         return self.c_type()
1162
1163     # Return the C type to be used where we suppress boxing.
1164     def c_unboxed_type(self):
1165         return self.c_type()
1166
1167     def json_type(self):
1168         pass
1169
1170     def alternate_qtype(self):
1171         json2qtype = {
1172             'null':    'QTYPE_QNULL',
1173             'string':  'QTYPE_QSTRING',
1174             'number':  'QTYPE_QNUM',
1175             'int':     'QTYPE_QNUM',
1176             'boolean': 'QTYPE_QBOOL',
1177             'object':  'QTYPE_QDICT'
1178         }
1179         return json2qtype.get(self.json_type())
1180
1181     def doc_type(self):
1182         if self.is_implicit():
1183             return None
1184         return self.name
1185
1186
1187 class QAPISchemaBuiltinType(QAPISchemaType):
1188     def __init__(self, name, json_type, c_type):
1189         QAPISchemaType.__init__(self, name, None, None)
1190         assert not c_type or isinstance(c_type, str)
1191         assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1192                              'value')
1193         self._json_type_name = json_type
1194         self._c_type_name = c_type
1195
1196     def c_name(self):
1197         return self.name
1198
1199     def c_type(self):
1200         return self._c_type_name
1201
1202     def c_param_type(self):
1203         if self.name == 'str':
1204             return 'const ' + self._c_type_name
1205         return self._c_type_name
1206
1207     def json_type(self):
1208         return self._json_type_name
1209
1210     def doc_type(self):
1211         return self.json_type()
1212
1213     def visit(self, visitor):
1214         visitor.visit_builtin_type(self.name, self.info, self.json_type())
1215
1216
1217 class QAPISchemaEnumType(QAPISchemaType):
1218     def __init__(self, name, info, doc, ifcond, members, prefix):
1219         QAPISchemaType.__init__(self, name, info, doc, ifcond)
1220         for m in members:
1221             assert isinstance(m, QAPISchemaMember)
1222             m.set_owner(name)
1223         assert prefix is None or isinstance(prefix, str)
1224         self.members = members
1225         self.prefix = prefix
1226
1227     def check(self, schema):
1228         QAPISchemaType.check(self, schema)
1229         seen = {}
1230         for m in self.members:
1231             m.check_clash(self.info, seen)
1232             if self.doc:
1233                 self.doc.connect_member(m)
1234
1235     def is_implicit(self):
1236         # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1237         return self.name.endswith('Kind') or self.name == 'QType'
1238
1239     def c_type(self):
1240         return c_name(self.name)
1241
1242     def member_names(self):
1243         return [m.name for m in self.members]
1244
1245     def json_type(self):
1246         return 'string'
1247
1248     def visit(self, visitor):
1249         visitor.visit_enum_type(self.name, self.info, self.ifcond,
1250                                 self.members, self.prefix)
1251
1252
1253 class QAPISchemaArrayType(QAPISchemaType):
1254     def __init__(self, name, info, element_type):
1255         QAPISchemaType.__init__(self, name, info, None, None)
1256         assert isinstance(element_type, str)
1257         self._element_type_name = element_type
1258         self.element_type = None
1259
1260     def check(self, schema):
1261         QAPISchemaType.check(self, schema)
1262         self.element_type = schema.lookup_type(self._element_type_name)
1263         assert self.element_type
1264         self.element_type.check(schema)
1265         self.ifcond = self.element_type.ifcond
1266
1267     def is_implicit(self):
1268         return True
1269
1270     def c_type(self):
1271         return c_name(self.name) + pointer_suffix
1272
1273     def json_type(self):
1274         return 'array'
1275
1276     def doc_type(self):
1277         elt_doc_type = self.element_type.doc_type()
1278         if not elt_doc_type:
1279             return None
1280         return 'array of ' + elt_doc_type
1281
1282     def visit(self, visitor):
1283         visitor.visit_array_type(self.name, self.info, self.ifcond,
1284                                  self.element_type)
1285
1286
1287 class QAPISchemaObjectType(QAPISchemaType):
1288     def __init__(self, name, info, doc, ifcond,
1289                  base, local_members, variants):
1290         # struct has local_members, optional base, and no variants
1291         # flat union has base, variants, and no local_members
1292         # simple union has local_members, variants, and no base
1293         QAPISchemaType.__init__(self, name, info, doc, ifcond)
1294         assert base is None or isinstance(base, str)
1295         for m in local_members:
1296             assert isinstance(m, QAPISchemaObjectTypeMember)
1297             m.set_owner(name)
1298         if variants is not None:
1299             assert isinstance(variants, QAPISchemaObjectTypeVariants)
1300             variants.set_owner(name)
1301         self._base_name = base
1302         self.base = None
1303         self.local_members = local_members
1304         self.variants = variants
1305         self.members = None
1306
1307     def check(self, schema):
1308         QAPISchemaType.check(self, schema)
1309         if self.members is False:               # check for cycles
1310             raise QAPISemError(self.info,
1311                                "Object %s contains itself" % self.name)
1312         if self.members:
1313             return
1314         self.members = False                    # mark as being checked
1315         seen = OrderedDict()
1316         if self._base_name:
1317             self.base = schema.lookup_type(self._base_name)
1318             assert isinstance(self.base, QAPISchemaObjectType)
1319             self.base.check(schema)
1320             self.base.check_clash(self.info, seen)
1321         for m in self.local_members:
1322             m.check(schema)
1323             m.check_clash(self.info, seen)
1324             if self.doc:
1325                 self.doc.connect_member(m)
1326         self.members = seen.values()
1327         if self.variants:
1328             self.variants.check(schema, seen)
1329             assert self.variants.tag_member in self.members
1330             self.variants.check_clash(self.info, seen)
1331         if self.doc:
1332             self.doc.check()
1333
1334     # Check that the members of this type do not cause duplicate JSON members,
1335     # and update seen to track the members seen so far. Report any errors
1336     # on behalf of info, which is not necessarily self.info
1337     def check_clash(self, info, seen):
1338         assert not self.variants       # not implemented
1339         for m in self.members:
1340             m.check_clash(info, seen)
1341
1342     def is_implicit(self):
1343         # See QAPISchema._make_implicit_object_type(), as well as
1344         # _def_predefineds()
1345         return self.name.startswith('q_')
1346
1347     def is_empty(self):
1348         assert self.members is not None
1349         return not self.members and not self.variants
1350
1351     def c_name(self):
1352         assert self.name != 'q_empty'
1353         return QAPISchemaType.c_name(self)
1354
1355     def c_type(self):
1356         assert not self.is_implicit()
1357         return c_name(self.name) + pointer_suffix
1358
1359     def c_unboxed_type(self):
1360         return c_name(self.name)
1361
1362     def json_type(self):
1363         return 'object'
1364
1365     def visit(self, visitor):
1366         visitor.visit_object_type(self.name, self.info, self.ifcond,
1367                                   self.base, self.local_members, self.variants)
1368         visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1369                                        self.members, self.variants)
1370
1371
1372 class QAPISchemaMember(object):
1373     role = 'member'
1374
1375     def __init__(self, name, ifcond=None):
1376         assert isinstance(name, str)
1377         self.name = name
1378         self.ifcond = listify_cond(ifcond)
1379         self.owner = None
1380
1381     def set_owner(self, name):
1382         assert not self.owner
1383         self.owner = name
1384
1385     def check_clash(self, info, seen):
1386         cname = c_name(self.name)
1387         if cname.lower() != cname and self.owner not in name_case_whitelist:
1388             raise QAPISemError(info,
1389                                "%s should not use uppercase" % self.describe())
1390         if cname in seen:
1391             raise QAPISemError(info, "%s collides with %s" %
1392                                (self.describe(), seen[cname].describe()))
1393         seen[cname] = self
1394
1395     def _pretty_owner(self):
1396         owner = self.owner
1397         if owner.startswith('q_obj_'):
1398             # See QAPISchema._make_implicit_object_type() - reverse the
1399             # mapping there to create a nice human-readable description
1400             owner = owner[6:]
1401             if owner.endswith('-arg'):
1402                 return '(parameter of %s)' % owner[:-4]
1403             elif owner.endswith('-base'):
1404                 return '(base of %s)' % owner[:-5]
1405             else:
1406                 assert owner.endswith('-wrapper')
1407                 # Unreachable and not implemented
1408                 assert False
1409         if owner.endswith('Kind'):
1410             # See QAPISchema._make_implicit_enum_type()
1411             return '(branch of %s)' % owner[:-4]
1412         return '(%s of %s)' % (self.role, owner)
1413
1414     def describe(self):
1415         return "'%s' %s" % (self.name, self._pretty_owner())
1416
1417
1418 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1419     def __init__(self, name, typ, optional, ifcond=None):
1420         QAPISchemaMember.__init__(self, name, ifcond)
1421         assert isinstance(typ, str)
1422         assert isinstance(optional, bool)
1423         self._type_name = typ
1424         self.type = None
1425         self.optional = optional
1426
1427     def check(self, schema):
1428         assert self.owner
1429         self.type = schema.lookup_type(self._type_name)
1430         assert self.type
1431
1432
1433 class QAPISchemaObjectTypeVariants(object):
1434     def __init__(self, tag_name, tag_member, variants):
1435         # Flat unions pass tag_name but not tag_member.
1436         # Simple unions and alternates pass tag_member but not tag_name.
1437         # After check(), tag_member is always set, and tag_name remains
1438         # a reliable witness of being used by a flat union.
1439         assert bool(tag_member) != bool(tag_name)
1440         assert (isinstance(tag_name, str) or
1441                 isinstance(tag_member, QAPISchemaObjectTypeMember))
1442         assert len(variants) > 0
1443         for v in variants:
1444             assert isinstance(v, QAPISchemaObjectTypeVariant)
1445         self._tag_name = tag_name
1446         self.tag_member = tag_member
1447         self.variants = variants
1448
1449     def set_owner(self, name):
1450         for v in self.variants:
1451             v.set_owner(name)
1452
1453     def check(self, schema, seen):
1454         if not self.tag_member:    # flat union
1455             self.tag_member = seen[c_name(self._tag_name)]
1456             assert self._tag_name == self.tag_member.name
1457         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1458         if self._tag_name:    # flat union
1459             # branches that are not explicitly covered get an empty type
1460             cases = set([v.name for v in self.variants])
1461             for m in self.tag_member.type.members:
1462                 if m.name not in cases:
1463                     v = QAPISchemaObjectTypeVariant(m.name, 'q_empty')
1464                     v.set_owner(self.tag_member.owner)
1465                     self.variants.append(v)
1466         for v in self.variants:
1467             v.check(schema)
1468             # Union names must match enum values; alternate names are
1469             # checked separately. Use 'seen' to tell the two apart.
1470             if seen:
1471                 assert v.name in self.tag_member.type.member_names()
1472                 assert isinstance(v.type, QAPISchemaObjectType)
1473                 v.type.check(schema)
1474
1475     def check_clash(self, info, seen):
1476         for v in self.variants:
1477             # Reset seen map for each variant, since qapi names from one
1478             # branch do not affect another branch
1479             assert isinstance(v.type, QAPISchemaObjectType)
1480             v.type.check_clash(info, dict(seen))
1481
1482
1483 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1484     role = 'branch'
1485
1486     def __init__(self, name, typ, ifcond=None):
1487         QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
1488
1489
1490 class QAPISchemaAlternateType(QAPISchemaType):
1491     def __init__(self, name, info, doc, ifcond, variants):
1492         QAPISchemaType.__init__(self, name, info, doc, ifcond)
1493         assert isinstance(variants, QAPISchemaObjectTypeVariants)
1494         assert variants.tag_member
1495         variants.set_owner(name)
1496         variants.tag_member.set_owner(self.name)
1497         self.variants = variants
1498
1499     def check(self, schema):
1500         QAPISchemaType.check(self, schema)
1501         self.variants.tag_member.check(schema)
1502         # Not calling self.variants.check_clash(), because there's nothing
1503         # to clash with
1504         self.variants.check(schema, {})
1505         # Alternate branch names have no relation to the tag enum values;
1506         # so we have to check for potential name collisions ourselves.
1507         seen = {}
1508         for v in self.variants.variants:
1509             v.check_clash(self.info, seen)
1510             if self.doc:
1511                 self.doc.connect_member(v)
1512         if self.doc:
1513             self.doc.check()
1514
1515     def c_type(self):
1516         return c_name(self.name) + pointer_suffix
1517
1518     def json_type(self):
1519         return 'value'
1520
1521     def visit(self, visitor):
1522         visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1523                                      self.variants)
1524
1525     def is_empty(self):
1526         return False
1527
1528
1529 class QAPISchemaCommand(QAPISchemaEntity):
1530     def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1531                  gen, success_response, boxed, allow_oob, allow_preconfig):
1532         QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1533         assert not arg_type or isinstance(arg_type, str)
1534         assert not ret_type or isinstance(ret_type, str)
1535         self._arg_type_name = arg_type
1536         self.arg_type = None
1537         self._ret_type_name = ret_type
1538         self.ret_type = None
1539         self.gen = gen
1540         self.success_response = success_response
1541         self.boxed = boxed
1542         self.allow_oob = allow_oob
1543         self.allow_preconfig = allow_preconfig
1544
1545     def check(self, schema):
1546         QAPISchemaEntity.check(self, schema)
1547         if self._arg_type_name:
1548             self.arg_type = schema.lookup_type(self._arg_type_name)
1549             assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1550                     isinstance(self.arg_type, QAPISchemaAlternateType))
1551             self.arg_type.check(schema)
1552             if self.boxed:
1553                 if self.arg_type.is_empty():
1554                     raise QAPISemError(self.info,
1555                                        "Cannot use 'boxed' with empty type")
1556             else:
1557                 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1558                 assert not self.arg_type.variants
1559         elif self.boxed:
1560             raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1561         if self._ret_type_name:
1562             self.ret_type = schema.lookup_type(self._ret_type_name)
1563             assert isinstance(self.ret_type, QAPISchemaType)
1564
1565     def visit(self, visitor):
1566         visitor.visit_command(self.name, self.info, self.ifcond,
1567                               self.arg_type, self.ret_type,
1568                               self.gen, self.success_response,
1569                               self.boxed, self.allow_oob,
1570                               self.allow_preconfig)
1571
1572
1573 class QAPISchemaEvent(QAPISchemaEntity):
1574     def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1575         QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1576         assert not arg_type or isinstance(arg_type, str)
1577         self._arg_type_name = arg_type
1578         self.arg_type = None
1579         self.boxed = boxed
1580
1581     def check(self, schema):
1582         QAPISchemaEntity.check(self, schema)
1583         if self._arg_type_name:
1584             self.arg_type = schema.lookup_type(self._arg_type_name)
1585             assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1586                     isinstance(self.arg_type, QAPISchemaAlternateType))
1587             self.arg_type.check(schema)
1588             if self.boxed:
1589                 if self.arg_type.is_empty():
1590                     raise QAPISemError(self.info,
1591                                        "Cannot use 'boxed' with empty type")
1592             else:
1593                 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1594                 assert not self.arg_type.variants
1595         elif self.boxed:
1596             raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1597
1598     def visit(self, visitor):
1599         visitor.visit_event(self.name, self.info, self.ifcond,
1600                             self.arg_type, self.boxed)
1601
1602
1603 class QAPISchema(object):
1604     def __init__(self, fname):
1605         self._fname = fname
1606         if sys.version_info[0] >= 3:
1607             f = open(fname, 'r', encoding='utf-8')
1608         else:
1609             f = open(fname, 'r')
1610         parser = QAPISchemaParser(f)
1611         exprs = check_exprs(parser.exprs)
1612         self.docs = parser.docs
1613         self._entity_list = []
1614         self._entity_dict = {}
1615         self._predefining = True
1616         self._def_predefineds()
1617         self._predefining = False
1618         self._def_exprs(exprs)
1619         self.check()
1620
1621     def _def_entity(self, ent):
1622         # Only the predefined types are allowed to not have info
1623         assert ent.info or self._predefining
1624         assert ent.name is None or ent.name not in self._entity_dict
1625         self._entity_list.append(ent)
1626         if ent.name is not None:
1627             self._entity_dict[ent.name] = ent
1628         if ent.info:
1629             ent.module = os.path.relpath(ent.info['file'],
1630                                          os.path.dirname(self._fname))
1631
1632     def lookup_entity(self, name, typ=None):
1633         ent = self._entity_dict.get(name)
1634         if typ and not isinstance(ent, typ):
1635             return None
1636         return ent
1637
1638     def lookup_type(self, name):
1639         return self.lookup_entity(name, QAPISchemaType)
1640
1641     def _def_include(self, expr, info, doc):
1642         include = expr['include']
1643         assert doc is None
1644         main_info = info
1645         while main_info['parent']:
1646             main_info = main_info['parent']
1647         fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1648         self._def_entity(QAPISchemaInclude(fname, info))
1649
1650     def _def_builtin_type(self, name, json_type, c_type):
1651         self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1652         # Instantiating only the arrays that are actually used would
1653         # be nice, but we can't as long as their generated code
1654         # (qapi-builtin-types.[ch]) may be shared by some other
1655         # schema.
1656         self._make_array_type(name, None)
1657
1658     def _def_predefineds(self):
1659         for t in [('str',    'string',  'char' + pointer_suffix),
1660                   ('number', 'number',  'double'),
1661                   ('int',    'int',     'int64_t'),
1662                   ('int8',   'int',     'int8_t'),
1663                   ('int16',  'int',     'int16_t'),
1664                   ('int32',  'int',     'int32_t'),
1665                   ('int64',  'int',     'int64_t'),
1666                   ('uint8',  'int',     'uint8_t'),
1667                   ('uint16', 'int',     'uint16_t'),
1668                   ('uint32', 'int',     'uint32_t'),
1669                   ('uint64', 'int',     'uint64_t'),
1670                   ('size',   'int',     'uint64_t'),
1671                   ('bool',   'boolean', 'bool'),
1672                   ('any',    'value',   'QObject' + pointer_suffix),
1673                   ('null',   'null',    'QNull' + pointer_suffix)]:
1674             self._def_builtin_type(*t)
1675         self.the_empty_object_type = QAPISchemaObjectType(
1676             'q_empty', None, None, None, None, [], None)
1677         self._def_entity(self.the_empty_object_type)
1678
1679         qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1680                   'qbool']
1681         qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
1682
1683         self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1684                                             qtype_values, 'QTYPE'))
1685
1686     def _make_enum_members(self, values):
1687         return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
1688
1689     def _make_implicit_enum_type(self, name, info, ifcond, values):
1690         # See also QAPISchemaObjectTypeMember._pretty_owner()
1691         name = name + 'Kind'   # Use namespace reserved by add_name()
1692         self._def_entity(QAPISchemaEnumType(
1693             name, info, None, ifcond, self._make_enum_members(values), None))
1694         return name
1695
1696     def _make_array_type(self, element_type, info):
1697         name = element_type + 'List'   # Use namespace reserved by add_name()
1698         if not self.lookup_type(name):
1699             self._def_entity(QAPISchemaArrayType(name, info, element_type))
1700         return name
1701
1702     def _make_implicit_object_type(self, name, info, doc, ifcond,
1703                                    role, members):
1704         if not members:
1705             return None
1706         # See also QAPISchemaObjectTypeMember._pretty_owner()
1707         name = 'q_obj_%s-%s' % (name, role)
1708         typ = self.lookup_entity(name, QAPISchemaObjectType)
1709         if typ:
1710             # The implicit object type has multiple users.  This can
1711             # happen only for simple unions' implicit wrapper types.
1712             # Its ifcond should be the disjunction of its user's
1713             # ifconds.  Not implemented.  Instead, we always pass the
1714             # wrapped type's ifcond, which is trivially the same for all
1715             # users.  It's also necessary for the wrapper to compile.
1716             # But it's not tight: the disjunction need not imply it.  We
1717             # may end up compiling useless wrapper types.
1718             # TODO kill simple unions or implement the disjunction
1719             assert ifcond == typ._ifcond # pylint: disable=protected-access
1720         else:
1721             self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1722                                                   None, members, None))
1723         return name
1724
1725     def _def_enum_type(self, expr, info, doc):
1726         name = expr['enum']
1727         data = expr['data']
1728         prefix = expr.get('prefix')
1729         ifcond = expr.get('if')
1730         self._def_entity(QAPISchemaEnumType(
1731             name, info, doc, ifcond,
1732             self._make_enum_members(data), prefix))
1733
1734     def _make_member(self, name, typ, ifcond, info):
1735         optional = False
1736         if name.startswith('*'):
1737             name = name[1:]
1738             optional = True
1739         if isinstance(typ, list):
1740             assert len(typ) == 1
1741             typ = self._make_array_type(typ[0], info)
1742         return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
1743
1744     def _make_members(self, data, info):
1745         return [self._make_member(key, value['type'], value.get('if'), info)
1746                 for (key, value) in data.items()]
1747
1748     def _def_struct_type(self, expr, info, doc):
1749         name = expr['struct']
1750         base = expr.get('base')
1751         data = expr['data']
1752         ifcond = expr.get('if')
1753         self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1754                                               self._make_members(data, info),
1755                                               None))
1756
1757     def _make_variant(self, case, typ, ifcond):
1758         return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1759
1760     def _make_simple_variant(self, case, typ, ifcond, info):
1761         if isinstance(typ, list):
1762             assert len(typ) == 1
1763             typ = self._make_array_type(typ[0], info)
1764         typ = self._make_implicit_object_type(
1765             typ, info, None, self.lookup_type(typ),
1766             'wrapper', [self._make_member('data', typ, None, info)])
1767         return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1768
1769     def _def_union_type(self, expr, info, doc):
1770         name = expr['union']
1771         data = expr['data']
1772         base = expr.get('base')
1773         ifcond = expr.get('if')
1774         tag_name = expr.get('discriminator')
1775         tag_member = None
1776         if isinstance(base, dict):
1777             base = self._make_implicit_object_type(
1778                 name, info, doc, ifcond,
1779                 'base', self._make_members(base, info))
1780         if tag_name:
1781             variants = [self._make_variant(key, value['type'], value.get('if'))
1782                         for (key, value) in data.items()]
1783             members = []
1784         else:
1785             variants = [self._make_simple_variant(key, value['type'],
1786                                                   value.get('if'), info)
1787                         for (key, value) in data.items()]
1788             enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1789             typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1790             tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1791             members = [tag_member]
1792         self._def_entity(
1793             QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1794                                  QAPISchemaObjectTypeVariants(tag_name,
1795                                                               tag_member,
1796                                                               variants)))
1797
1798     def _def_alternate_type(self, expr, info, doc):
1799         name = expr['alternate']
1800         data = expr['data']
1801         ifcond = expr.get('if')
1802         variants = [self._make_variant(key, value['type'], value.get('if'))
1803                     for (key, value) in data.items()]
1804         tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1805         self._def_entity(
1806             QAPISchemaAlternateType(name, info, doc, ifcond,
1807                                     QAPISchemaObjectTypeVariants(None,
1808                                                                  tag_member,
1809                                                                  variants)))
1810
1811     def _def_command(self, expr, info, doc):
1812         name = expr['command']
1813         data = expr.get('data')
1814         rets = expr.get('returns')
1815         gen = expr.get('gen', True)
1816         success_response = expr.get('success-response', True)
1817         boxed = expr.get('boxed', False)
1818         allow_oob = expr.get('allow-oob', False)
1819         allow_preconfig = expr.get('allow-preconfig', False)
1820         ifcond = expr.get('if')
1821         if isinstance(data, OrderedDict):
1822             data = self._make_implicit_object_type(
1823                 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1824         if isinstance(rets, list):
1825             assert len(rets) == 1
1826             rets = self._make_array_type(rets[0], info)
1827         self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1828                                            gen, success_response,
1829                                            boxed, allow_oob, allow_preconfig))
1830
1831     def _def_event(self, expr, info, doc):
1832         name = expr['event']
1833         data = expr.get('data')
1834         boxed = expr.get('boxed', False)
1835         ifcond = expr.get('if')
1836         if isinstance(data, OrderedDict):
1837             data = self._make_implicit_object_type(
1838                 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1839         self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1840
1841     def _def_exprs(self, exprs):
1842         for expr_elem in exprs:
1843             expr = expr_elem['expr']
1844             info = expr_elem['info']
1845             doc = expr_elem.get('doc')
1846             if 'enum' in expr:
1847                 self._def_enum_type(expr, info, doc)
1848             elif 'struct' in expr:
1849                 self._def_struct_type(expr, info, doc)
1850             elif 'union' in expr:
1851                 self._def_union_type(expr, info, doc)
1852             elif 'alternate' in expr:
1853                 self._def_alternate_type(expr, info, doc)
1854             elif 'command' in expr:
1855                 self._def_command(expr, info, doc)
1856             elif 'event' in expr:
1857                 self._def_event(expr, info, doc)
1858             elif 'include' in expr:
1859                 self._def_include(expr, info, doc)
1860             else:
1861                 assert False
1862
1863     def check(self):
1864         for ent in self._entity_list:
1865             ent.check(self)
1866
1867     def visit(self, visitor):
1868         visitor.visit_begin(self)
1869         module = None
1870         for entity in self._entity_list:
1871             if visitor.visit_needed(entity):
1872                 if entity.module != module:
1873                     module = entity.module
1874                     visitor.visit_module(module)
1875                 entity.visit(visitor)
1876         visitor.visit_end()
1877
1878
1879 #
1880 # Code generation helpers
1881 #
1882
1883 def camel_case(name):
1884     new_name = ''
1885     first = True
1886     for ch in name:
1887         if ch in ['_', '-']:
1888             first = True
1889         elif first:
1890             new_name += ch.upper()
1891             first = False
1892         else:
1893             new_name += ch.lower()
1894     return new_name
1895
1896
1897 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1898 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1899 # ENUM24_Name -> ENUM24_NAME
1900 def camel_to_upper(value):
1901     c_fun_str = c_name(value, False)
1902     if value.isupper():
1903         return c_fun_str
1904
1905     new_name = ''
1906     length = len(c_fun_str)
1907     for i in range(length):
1908         c = c_fun_str[i]
1909         # When c is upper and no '_' appears before, do more checks
1910         if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1911             if i < length - 1 and c_fun_str[i + 1].islower():
1912                 new_name += '_'
1913             elif c_fun_str[i - 1].isdigit():
1914                 new_name += '_'
1915         new_name += c
1916     return new_name.lstrip('_').upper()
1917
1918
1919 def c_enum_const(type_name, const_name, prefix=None):
1920     if prefix is not None:
1921         type_name = prefix
1922     return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1923
1924
1925 if hasattr(str, 'maketrans'):
1926     c_name_trans = str.maketrans('.-', '__')
1927 else:
1928     c_name_trans = string.maketrans('.-', '__')
1929
1930
1931 # Map @name to a valid C identifier.
1932 # If @protect, avoid returning certain ticklish identifiers (like
1933 # C keywords) by prepending 'q_'.
1934 #
1935 # Used for converting 'name' from a 'name':'type' qapi definition
1936 # into a generated struct member, as well as converting type names
1937 # into substrings of a generated C function name.
1938 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1939 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1940 def c_name(name, protect=True):
1941     # ANSI X3J11/88-090, 3.1.1
1942     c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1943                      'default', 'do', 'double', 'else', 'enum', 'extern',
1944                      'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1945                      'return', 'short', 'signed', 'sizeof', 'static',
1946                      'struct', 'switch', 'typedef', 'union', 'unsigned',
1947                      'void', 'volatile', 'while'])
1948     # ISO/IEC 9899:1999, 6.4.1
1949     c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1950     # ISO/IEC 9899:2011, 6.4.1
1951     c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1952                      '_Noreturn', '_Static_assert', '_Thread_local'])
1953     # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1954     # excluding _.*
1955     gcc_words = set(['asm', 'typeof'])
1956     # C++ ISO/IEC 14882:2003 2.11
1957     cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1958                      'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1959                      'namespace', 'new', 'operator', 'private', 'protected',
1960                      'public', 'reinterpret_cast', 'static_cast', 'template',
1961                      'this', 'throw', 'true', 'try', 'typeid', 'typename',
1962                      'using', 'virtual', 'wchar_t',
1963                      # alternative representations
1964                      'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1965                      'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1966     # namespace pollution:
1967     polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1968     name = name.translate(c_name_trans)
1969     if protect and (name in c89_words | c99_words | c11_words | gcc_words
1970                     | cpp_words | polluted_words):
1971         return 'q_' + name
1972     return name
1973
1974
1975 eatspace = '\033EATSPACE.'
1976 pointer_suffix = ' *' + eatspace
1977
1978
1979 def genindent(count):
1980     ret = ''
1981     for _ in range(count):
1982         ret += ' '
1983     return ret
1984
1985
1986 indent_level = 0
1987
1988
1989 def push_indent(indent_amount=4):
1990     global indent_level
1991     indent_level += indent_amount
1992
1993
1994 def pop_indent(indent_amount=4):
1995     global indent_level
1996     indent_level -= indent_amount
1997
1998
1999 # Generate @code with @kwds interpolated.
2000 # Obey indent_level, and strip eatspace.
2001 def cgen(code, **kwds):
2002     raw = code % kwds
2003     if indent_level:
2004         indent = genindent(indent_level)
2005         # re.subn() lacks flags support before Python 2.7, use re.compile()
2006         raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2007                       indent, raw)
2008         raw = raw[0]
2009     return re.sub(re.escape(eatspace) + r' *', '', raw)
2010
2011
2012 def mcgen(code, **kwds):
2013     if code[0] == '\n':
2014         code = code[1:]
2015     return cgen(code, **kwds)
2016
2017
2018 def guardname(filename):
2019     return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
2020
2021
2022 def guardstart(name):
2023     return mcgen('''
2024 #ifndef %(name)s
2025 #define %(name)s
2026
2027 ''',
2028                  name=guardname(name))
2029
2030
2031 def guardend(name):
2032     return mcgen('''
2033
2034 #endif /* %(name)s */
2035 ''',
2036                  name=guardname(name))
2037
2038
2039 def gen_if(ifcond):
2040     ret = ''
2041     for ifc in ifcond:
2042         ret += mcgen('''
2043 #if %(cond)s
2044 ''', cond=ifc)
2045     return ret
2046
2047
2048 def gen_endif(ifcond):
2049     ret = ''
2050     for ifc in reversed(ifcond):
2051         ret += mcgen('''
2052 #endif /* %(cond)s */
2053 ''', cond=ifc)
2054     return ret
2055
2056
2057 def _wrap_ifcond(ifcond, before, after):
2058     if before == after:
2059         return after   # suppress empty #if ... #endif
2060
2061     assert after.startswith(before)
2062     out = before
2063     added = after[len(before):]
2064     if added[0] == '\n':
2065         out += '\n'
2066         added = added[1:]
2067     out += gen_if(ifcond)
2068     out += added
2069     out += gen_endif(ifcond)
2070     return out
2071
2072
2073 def gen_enum_lookup(name, members, prefix=None):
2074     ret = mcgen('''
2075
2076 const QEnumLookup %(c_name)s_lookup = {
2077     .array = (const char *const[]) {
2078 ''',
2079                 c_name=c_name(name))
2080     for m in members:
2081         ret += gen_if(m.ifcond)
2082         index = c_enum_const(name, m.name, prefix)
2083         ret += mcgen('''
2084         [%(index)s] = "%(name)s",
2085 ''',
2086                      index=index, name=m.name)
2087         ret += gen_endif(m.ifcond)
2088
2089     ret += mcgen('''
2090     },
2091     .size = %(max_index)s
2092 };
2093 ''',
2094                  max_index=c_enum_const(name, '_MAX', prefix))
2095     return ret
2096
2097
2098 def gen_enum(name, members, prefix=None):
2099     # append automatically generated _MAX value
2100     enum_members = members + [QAPISchemaMember('_MAX')]
2101
2102     ret = mcgen('''
2103
2104 typedef enum %(c_name)s {
2105 ''',
2106                 c_name=c_name(name))
2107
2108     for m in enum_members:
2109         ret += gen_if(m.ifcond)
2110         ret += mcgen('''
2111     %(c_enum)s,
2112 ''',
2113                      c_enum=c_enum_const(name, m.name, prefix))
2114         ret += gen_endif(m.ifcond)
2115
2116     ret += mcgen('''
2117 } %(c_name)s;
2118 ''',
2119                  c_name=c_name(name))
2120
2121     ret += mcgen('''
2122
2123 #define %(c_name)s_str(val) \\
2124     qapi_enum_lookup(&%(c_name)s_lookup, (val))
2125
2126 extern const QEnumLookup %(c_name)s_lookup;
2127 ''',
2128                  c_name=c_name(name))
2129     return ret
2130
2131
2132 def build_params(arg_type, boxed, extra=None):
2133     ret = ''
2134     sep = ''
2135     if boxed:
2136         assert arg_type
2137         ret += '%s arg' % arg_type.c_param_type()
2138         sep = ', '
2139     elif arg_type:
2140         assert not arg_type.variants
2141         for memb in arg_type.members:
2142             ret += sep
2143             sep = ', '
2144             if memb.optional:
2145                 ret += 'bool has_%s, ' % c_name(memb.name)
2146             ret += '%s %s' % (memb.type.c_param_type(),
2147                               c_name(memb.name))
2148     if extra:
2149         ret += sep + extra
2150     return ret if ret else 'void'
2151
2152
2153 #
2154 # Accumulate and write output
2155 #
2156
2157 class QAPIGen(object):
2158
2159     def __init__(self):
2160         self._preamble = ''
2161         self._body = ''
2162
2163     def preamble_add(self, text):
2164         self._preamble += text
2165
2166     def add(self, text):
2167         self._body += text
2168
2169     def get_content(self, fname=None):
2170         return (self._top(fname) + self._preamble + self._body
2171                 + self._bottom(fname))
2172
2173     def _top(self, fname):
2174         return ''
2175
2176     def _bottom(self, fname):
2177         return ''
2178
2179     def write(self, output_dir, fname):
2180         pathname = os.path.join(output_dir, fname)
2181         dir = os.path.dirname(pathname)
2182         if dir:
2183             try:
2184                 os.makedirs(dir)
2185             except os.error as e:
2186                 if e.errno != errno.EEXIST:
2187                     raise
2188         fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2189         if sys.version_info[0] >= 3:
2190             f = open(fd, 'r+', encoding='utf-8')
2191         else:
2192             f = os.fdopen(fd, 'r+')
2193         text = self.get_content(fname)
2194         oldtext = f.read(len(text) + 1)
2195         if text != oldtext:
2196             f.seek(0)
2197             f.truncate(0)
2198             f.write(text)
2199         f.close()
2200
2201
2202 @contextmanager
2203 def ifcontext(ifcond, *args):
2204     """A 'with' statement context manager to wrap with start_if()/end_if()
2205
2206     *args: any number of QAPIGenCCode
2207
2208     Example::
2209
2210         with ifcontext(ifcond, self._genh, self._genc):
2211             modify self._genh and self._genc ...
2212
2213     Is equivalent to calling::
2214
2215         self._genh.start_if(ifcond)
2216         self._genc.start_if(ifcond)
2217         modify self._genh and self._genc ...
2218         self._genh.end_if()
2219         self._genc.end_if()
2220     """
2221     for arg in args:
2222         arg.start_if(ifcond)
2223     yield
2224     for arg in args:
2225         arg.end_if()
2226
2227
2228 class QAPIGenCCode(QAPIGen):
2229
2230     def __init__(self):
2231         QAPIGen.__init__(self)
2232         self._start_if = None
2233
2234     def start_if(self, ifcond):
2235         assert self._start_if is None
2236         self._start_if = (ifcond, self._body, self._preamble)
2237
2238     def end_if(self):
2239         assert self._start_if
2240         self._wrap_ifcond()
2241         self._start_if = None
2242
2243     def _wrap_ifcond(self):
2244         self._body = _wrap_ifcond(self._start_if[0],
2245                                   self._start_if[1], self._body)
2246         self._preamble = _wrap_ifcond(self._start_if[0],
2247                                       self._start_if[2], self._preamble)
2248
2249     def get_content(self, fname=None):
2250         assert self._start_if is None
2251         return QAPIGen.get_content(self, fname)
2252
2253
2254 class QAPIGenC(QAPIGenCCode):
2255
2256     def __init__(self, blurb, pydoc):
2257         QAPIGenCCode.__init__(self)
2258         self._blurb = blurb
2259         self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2260                                                   re.MULTILINE))
2261
2262     def _top(self, fname):
2263         return mcgen('''
2264 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2265
2266 /*
2267 %(blurb)s
2268  *
2269  * %(copyright)s
2270  *
2271  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2272  * See the COPYING.LIB file in the top-level directory.
2273  */
2274
2275 ''',
2276                      blurb=self._blurb, copyright=self._copyright)
2277
2278     def _bottom(self, fname):
2279         return mcgen('''
2280
2281 /* Dummy declaration to prevent empty .o file */
2282 char dummy_%(name)s;
2283 ''',
2284                      name=c_name(fname))
2285
2286
2287 class QAPIGenH(QAPIGenC):
2288
2289     def _top(self, fname):
2290         return QAPIGenC._top(self, fname) + guardstart(fname)
2291
2292     def _bottom(self, fname):
2293         return guardend(fname)
2294
2295
2296 class QAPIGenDoc(QAPIGen):
2297
2298     def _top(self, fname):
2299         return (QAPIGen._top(self, fname)
2300                 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2301
2302
2303 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2304
2305     def __init__(self, prefix, what, blurb, pydoc):
2306         self._prefix = prefix
2307         self._what = what
2308         self._genc = QAPIGenC(blurb, pydoc)
2309         self._genh = QAPIGenH(blurb, pydoc)
2310
2311     def write(self, output_dir):
2312         self._genc.write(output_dir, self._prefix + self._what + '.c')
2313         self._genh.write(output_dir, self._prefix + self._what + '.h')
2314
2315
2316 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2317
2318     def __init__(self, prefix, what, blurb, pydoc):
2319         self._prefix = prefix
2320         self._what = what
2321         self._blurb = blurb
2322         self._pydoc = pydoc
2323         self._module = {}
2324         self._main_module = None
2325
2326     def _module_basename(self, what, name):
2327         if name is None:
2328             return re.sub(r'-', '-builtin-', what)
2329         basename = os.path.join(os.path.dirname(name),
2330                                 self._prefix + what)
2331         if name == self._main_module:
2332             return basename
2333         return basename + '-' + os.path.splitext(os.path.basename(name))[0]
2334
2335     def _add_module(self, name, blurb):
2336         if self._main_module is None and name is not None:
2337             self._main_module = name
2338         genc = QAPIGenC(blurb, self._pydoc)
2339         genh = QAPIGenH(blurb, self._pydoc)
2340         self._module[name] = (genc, genh)
2341         self._set_module(name)
2342
2343     def _set_module(self, name):
2344         self._genc, self._genh = self._module[name]
2345
2346     def write(self, output_dir, opt_builtins=False):
2347         for name in self._module:
2348             if name is None and not opt_builtins:
2349                 continue
2350             basename = self._module_basename(self._what, name)
2351             (genc, genh) = self._module[name]
2352             genc.write(output_dir, basename + '.c')
2353             genh.write(output_dir, basename + '.h')
2354
2355     def _begin_module(self, name):
2356         pass
2357
2358     def visit_module(self, name):
2359         if name in self._module:
2360             self._set_module(name)
2361             return
2362         self._add_module(name, self._blurb)
2363         self._begin_module(name)
2364
2365     def visit_include(self, name, info):
2366         basename = self._module_basename(self._what, name)
2367         self._genh.preamble_add(mcgen('''
2368 #include "%(basename)s.h"
2369 ''',
2370                                       basename=basename))
This page took 0.166998 seconds and 4 git commands to generate.