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