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