xref: /openbmc/qemu/scripts/qapi/common.py (revision 2cbc94376e718448699036be7f6e29ab75312b70)
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 = listify_cond(ifcond)
1025
1026    def c_name(self):
1027        return c_name(self.name)
1028
1029    def check(self, schema):
1030        pass
1031
1032    def is_implicit(self):
1033        return not self.info
1034
1035    def visit(self, visitor):
1036        pass
1037
1038
1039class QAPISchemaVisitor(object):
1040    def visit_begin(self, schema):
1041        pass
1042
1043    def visit_end(self):
1044        pass
1045
1046    def visit_module(self, fname):
1047        pass
1048
1049    def visit_needed(self, entity):
1050        # Default to visiting everything
1051        return True
1052
1053    def visit_include(self, fname, info):
1054        pass
1055
1056    def visit_builtin_type(self, name, info, json_type):
1057        pass
1058
1059    def visit_enum_type(self, name, info, values, prefix):
1060        pass
1061
1062    def visit_array_type(self, name, info, element_type):
1063        pass
1064
1065    def visit_object_type(self, name, info, base, members, variants):
1066        pass
1067
1068    def visit_object_type_flat(self, name, info, members, variants):
1069        pass
1070
1071    def visit_alternate_type(self, name, info, variants):
1072        pass
1073
1074    def visit_command(self, name, info, arg_type, ret_type, gen,
1075                      success_response, boxed, allow_oob, allow_preconfig):
1076        pass
1077
1078    def visit_event(self, name, info, arg_type, boxed):
1079        pass
1080
1081
1082class QAPISchemaInclude(QAPISchemaEntity):
1083
1084    def __init__(self, fname, info):
1085        QAPISchemaEntity.__init__(self, None, info, None)
1086        self.fname = fname
1087
1088    def visit(self, visitor):
1089        visitor.visit_include(self.fname, self.info)
1090
1091
1092class QAPISchemaType(QAPISchemaEntity):
1093    # Return the C type for common use.
1094    # For the types we commonly box, this is a pointer type.
1095    def c_type(self):
1096        pass
1097
1098    # Return the C type to be used in a parameter list.
1099    def c_param_type(self):
1100        return self.c_type()
1101
1102    # Return the C type to be used where we suppress boxing.
1103    def c_unboxed_type(self):
1104        return self.c_type()
1105
1106    def json_type(self):
1107        pass
1108
1109    def alternate_qtype(self):
1110        json2qtype = {
1111            'null':    'QTYPE_QNULL',
1112            'string':  'QTYPE_QSTRING',
1113            'number':  'QTYPE_QNUM',
1114            'int':     'QTYPE_QNUM',
1115            'boolean': 'QTYPE_QBOOL',
1116            'object':  'QTYPE_QDICT'
1117        }
1118        return json2qtype.get(self.json_type())
1119
1120    def doc_type(self):
1121        if self.is_implicit():
1122            return None
1123        return self.name
1124
1125
1126class QAPISchemaBuiltinType(QAPISchemaType):
1127    def __init__(self, name, json_type, c_type):
1128        QAPISchemaType.__init__(self, name, None, None)
1129        assert not c_type or isinstance(c_type, str)
1130        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1131                             'value')
1132        self._json_type_name = json_type
1133        self._c_type_name = c_type
1134
1135    def c_name(self):
1136        return self.name
1137
1138    def c_type(self):
1139        return self._c_type_name
1140
1141    def c_param_type(self):
1142        if self.name == 'str':
1143            return 'const ' + self._c_type_name
1144        return self._c_type_name
1145
1146    def json_type(self):
1147        return self._json_type_name
1148
1149    def doc_type(self):
1150        return self.json_type()
1151
1152    def visit(self, visitor):
1153        visitor.visit_builtin_type(self.name, self.info, self.json_type())
1154
1155
1156class QAPISchemaEnumType(QAPISchemaType):
1157    def __init__(self, name, info, doc, ifcond, values, prefix):
1158        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1159        for v in values:
1160            assert isinstance(v, QAPISchemaMember)
1161            v.set_owner(name)
1162        assert prefix is None or isinstance(prefix, str)
1163        self.values = values
1164        self.prefix = prefix
1165
1166    def check(self, schema):
1167        seen = {}
1168        for v in self.values:
1169            v.check_clash(self.info, seen)
1170            if self.doc:
1171                self.doc.connect_member(v)
1172
1173    def is_implicit(self):
1174        # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1175        return self.name.endswith('Kind') or self.name == 'QType'
1176
1177    def c_type(self):
1178        return c_name(self.name)
1179
1180    def member_names(self):
1181        return [v.name for v in self.values]
1182
1183    def json_type(self):
1184        return 'string'
1185
1186    def visit(self, visitor):
1187        visitor.visit_enum_type(self.name, self.info,
1188                                self.member_names(), self.prefix)
1189
1190
1191class QAPISchemaArrayType(QAPISchemaType):
1192    def __init__(self, name, info, element_type):
1193        QAPISchemaType.__init__(self, name, info, None, None)
1194        assert isinstance(element_type, str)
1195        self._element_type_name = element_type
1196        self.element_type = None
1197
1198    def check(self, schema):
1199        self.element_type = schema.lookup_type(self._element_type_name)
1200        assert self.element_type
1201        self.ifcond = self.element_type.ifcond
1202
1203    def is_implicit(self):
1204        return True
1205
1206    def c_type(self):
1207        return c_name(self.name) + pointer_suffix
1208
1209    def json_type(self):
1210        return 'array'
1211
1212    def doc_type(self):
1213        elt_doc_type = self.element_type.doc_type()
1214        if not elt_doc_type:
1215            return None
1216        return 'array of ' + elt_doc_type
1217
1218    def visit(self, visitor):
1219        visitor.visit_array_type(self.name, self.info, self.element_type)
1220
1221
1222class QAPISchemaObjectType(QAPISchemaType):
1223    def __init__(self, name, info, doc, ifcond,
1224                 base, local_members, variants):
1225        # struct has local_members, optional base, and no variants
1226        # flat union has base, variants, and no local_members
1227        # simple union has local_members, variants, and no base
1228        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1229        assert base is None or isinstance(base, str)
1230        for m in local_members:
1231            assert isinstance(m, QAPISchemaObjectTypeMember)
1232            m.set_owner(name)
1233        if variants is not None:
1234            assert isinstance(variants, QAPISchemaObjectTypeVariants)
1235            variants.set_owner(name)
1236        self._base_name = base
1237        self.base = None
1238        self.local_members = local_members
1239        self.variants = variants
1240        self.members = None
1241
1242    def check(self, schema):
1243        if self.members is False:               # check for cycles
1244            raise QAPISemError(self.info,
1245                               "Object %s contains itself" % self.name)
1246        if self.members:
1247            return
1248        self.members = False                    # mark as being checked
1249        seen = OrderedDict()
1250        if self._base_name:
1251            self.base = schema.lookup_type(self._base_name)
1252            assert isinstance(self.base, QAPISchemaObjectType)
1253            self.base.check(schema)
1254            self.base.check_clash(self.info, seen)
1255        for m in self.local_members:
1256            m.check(schema)
1257            m.check_clash(self.info, seen)
1258            if self.doc:
1259                self.doc.connect_member(m)
1260        self.members = seen.values()
1261        if self.variants:
1262            self.variants.check(schema, seen)
1263            assert self.variants.tag_member in self.members
1264            self.variants.check_clash(self.info, seen)
1265        if self.doc:
1266            self.doc.check()
1267
1268    # Check that the members of this type do not cause duplicate JSON members,
1269    # and update seen to track the members seen so far. Report any errors
1270    # on behalf of info, which is not necessarily self.info
1271    def check_clash(self, info, seen):
1272        assert not self.variants       # not implemented
1273        for m in self.members:
1274            m.check_clash(info, seen)
1275
1276    def is_implicit(self):
1277        # See QAPISchema._make_implicit_object_type(), as well as
1278        # _def_predefineds()
1279        return self.name.startswith('q_')
1280
1281    def is_empty(self):
1282        assert self.members is not None
1283        return not self.members and not self.variants
1284
1285    def c_name(self):
1286        assert self.name != 'q_empty'
1287        return QAPISchemaType.c_name(self)
1288
1289    def c_type(self):
1290        assert not self.is_implicit()
1291        return c_name(self.name) + pointer_suffix
1292
1293    def c_unboxed_type(self):
1294        return c_name(self.name)
1295
1296    def json_type(self):
1297        return 'object'
1298
1299    def visit(self, visitor):
1300        visitor.visit_object_type(self.name, self.info,
1301                                  self.base, self.local_members, self.variants)
1302        visitor.visit_object_type_flat(self.name, self.info,
1303                                       self.members, self.variants)
1304
1305
1306class QAPISchemaMember(object):
1307    role = 'member'
1308
1309    def __init__(self, name):
1310        assert isinstance(name, str)
1311        self.name = name
1312        self.owner = None
1313
1314    def set_owner(self, name):
1315        assert not self.owner
1316        self.owner = name
1317
1318    def check_clash(self, info, seen):
1319        cname = c_name(self.name)
1320        if cname.lower() != cname and self.owner not in name_case_whitelist:
1321            raise QAPISemError(info,
1322                               "%s should not use uppercase" % self.describe())
1323        if cname in seen:
1324            raise QAPISemError(info, "%s collides with %s" %
1325                               (self.describe(), seen[cname].describe()))
1326        seen[cname] = self
1327
1328    def _pretty_owner(self):
1329        owner = self.owner
1330        if owner.startswith('q_obj_'):
1331            # See QAPISchema._make_implicit_object_type() - reverse the
1332            # mapping there to create a nice human-readable description
1333            owner = owner[6:]
1334            if owner.endswith('-arg'):
1335                return '(parameter of %s)' % owner[:-4]
1336            elif owner.endswith('-base'):
1337                return '(base of %s)' % owner[:-5]
1338            else:
1339                assert owner.endswith('-wrapper')
1340                # Unreachable and not implemented
1341                assert False
1342        if owner.endswith('Kind'):
1343            # See QAPISchema._make_implicit_enum_type()
1344            return '(branch of %s)' % owner[:-4]
1345        return '(%s of %s)' % (self.role, owner)
1346
1347    def describe(self):
1348        return "'%s' %s" % (self.name, self._pretty_owner())
1349
1350
1351class QAPISchemaObjectTypeMember(QAPISchemaMember):
1352    def __init__(self, name, typ, optional):
1353        QAPISchemaMember.__init__(self, name)
1354        assert isinstance(typ, str)
1355        assert isinstance(optional, bool)
1356        self._type_name = typ
1357        self.type = None
1358        self.optional = optional
1359
1360    def check(self, schema):
1361        assert self.owner
1362        self.type = schema.lookup_type(self._type_name)
1363        assert self.type
1364
1365
1366class QAPISchemaObjectTypeVariants(object):
1367    def __init__(self, tag_name, tag_member, variants):
1368        # Flat unions pass tag_name but not tag_member.
1369        # Simple unions and alternates pass tag_member but not tag_name.
1370        # After check(), tag_member is always set, and tag_name remains
1371        # a reliable witness of being used by a flat union.
1372        assert bool(tag_member) != bool(tag_name)
1373        assert (isinstance(tag_name, str) or
1374                isinstance(tag_member, QAPISchemaObjectTypeMember))
1375        assert len(variants) > 0
1376        for v in variants:
1377            assert isinstance(v, QAPISchemaObjectTypeVariant)
1378        self._tag_name = tag_name
1379        self.tag_member = tag_member
1380        self.variants = variants
1381
1382    def set_owner(self, name):
1383        for v in self.variants:
1384            v.set_owner(name)
1385
1386    def check(self, schema, seen):
1387        if not self.tag_member:    # flat union
1388            self.tag_member = seen[c_name(self._tag_name)]
1389            assert self._tag_name == self.tag_member.name
1390        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1391        if self._tag_name:    # flat union
1392            # branches that are not explicitly covered get an empty type
1393            cases = set([v.name for v in self.variants])
1394            for val in self.tag_member.type.values:
1395                if val.name not in cases:
1396                    v = QAPISchemaObjectTypeVariant(val.name, 'q_empty')
1397                    v.set_owner(self.tag_member.owner)
1398                    self.variants.append(v)
1399        for v in self.variants:
1400            v.check(schema)
1401            # Union names must match enum values; alternate names are
1402            # checked separately. Use 'seen' to tell the two apart.
1403            if seen:
1404                assert v.name in self.tag_member.type.member_names()
1405                assert isinstance(v.type, QAPISchemaObjectType)
1406                v.type.check(schema)
1407
1408    def check_clash(self, info, seen):
1409        for v in self.variants:
1410            # Reset seen map for each variant, since qapi names from one
1411            # branch do not affect another branch
1412            assert isinstance(v.type, QAPISchemaObjectType)
1413            v.type.check_clash(info, dict(seen))
1414
1415
1416class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1417    role = 'branch'
1418
1419    def __init__(self, name, typ):
1420        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1421
1422
1423class QAPISchemaAlternateType(QAPISchemaType):
1424    def __init__(self, name, info, doc, ifcond, variants):
1425        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1426        assert isinstance(variants, QAPISchemaObjectTypeVariants)
1427        assert variants.tag_member
1428        variants.set_owner(name)
1429        variants.tag_member.set_owner(self.name)
1430        self.variants = variants
1431
1432    def check(self, schema):
1433        self.variants.tag_member.check(schema)
1434        # Not calling self.variants.check_clash(), because there's nothing
1435        # to clash with
1436        self.variants.check(schema, {})
1437        # Alternate branch names have no relation to the tag enum values;
1438        # so we have to check for potential name collisions ourselves.
1439        seen = {}
1440        for v in self.variants.variants:
1441            v.check_clash(self.info, seen)
1442            if self.doc:
1443                self.doc.connect_member(v)
1444        if self.doc:
1445            self.doc.check()
1446
1447    def c_type(self):
1448        return c_name(self.name) + pointer_suffix
1449
1450    def json_type(self):
1451        return 'value'
1452
1453    def visit(self, visitor):
1454        visitor.visit_alternate_type(self.name, self.info, self.variants)
1455
1456    def is_empty(self):
1457        return False
1458
1459
1460class QAPISchemaCommand(QAPISchemaEntity):
1461    def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1462                 gen, success_response, boxed, allow_oob, allow_preconfig):
1463        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1464        assert not arg_type or isinstance(arg_type, str)
1465        assert not ret_type or isinstance(ret_type, str)
1466        self._arg_type_name = arg_type
1467        self.arg_type = None
1468        self._ret_type_name = ret_type
1469        self.ret_type = None
1470        self.gen = gen
1471        self.success_response = success_response
1472        self.boxed = boxed
1473        self.allow_oob = allow_oob
1474        self.allow_preconfig = allow_preconfig
1475
1476    def check(self, schema):
1477        if self._arg_type_name:
1478            self.arg_type = schema.lookup_type(self._arg_type_name)
1479            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1480                    isinstance(self.arg_type, QAPISchemaAlternateType))
1481            self.arg_type.check(schema)
1482            if self.boxed:
1483                if self.arg_type.is_empty():
1484                    raise QAPISemError(self.info,
1485                                       "Cannot use 'boxed' with empty type")
1486            else:
1487                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1488                assert not self.arg_type.variants
1489        elif self.boxed:
1490            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1491        if self._ret_type_name:
1492            self.ret_type = schema.lookup_type(self._ret_type_name)
1493            assert isinstance(self.ret_type, QAPISchemaType)
1494
1495    def visit(self, visitor):
1496        visitor.visit_command(self.name, self.info,
1497                              self.arg_type, self.ret_type,
1498                              self.gen, self.success_response,
1499                              self.boxed, self.allow_oob,
1500                              self.allow_preconfig)
1501
1502
1503class QAPISchemaEvent(QAPISchemaEntity):
1504    def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1505        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1506        assert not arg_type or isinstance(arg_type, str)
1507        self._arg_type_name = arg_type
1508        self.arg_type = None
1509        self.boxed = boxed
1510
1511    def check(self, schema):
1512        if self._arg_type_name:
1513            self.arg_type = schema.lookup_type(self._arg_type_name)
1514            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1515                    isinstance(self.arg_type, QAPISchemaAlternateType))
1516            self.arg_type.check(schema)
1517            if self.boxed:
1518                if self.arg_type.is_empty():
1519                    raise QAPISemError(self.info,
1520                                       "Cannot use 'boxed' with empty type")
1521            else:
1522                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1523                assert not self.arg_type.variants
1524        elif self.boxed:
1525            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1526
1527    def visit(self, visitor):
1528        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
1529
1530
1531class QAPISchema(object):
1532    def __init__(self, fname):
1533        self._fname = fname
1534        if sys.version_info[0] >= 3:
1535            f = open(fname, 'r', encoding='utf-8')
1536        else:
1537            f = open(fname, 'r')
1538        parser = QAPISchemaParser(f)
1539        exprs = check_exprs(parser.exprs)
1540        self.docs = parser.docs
1541        self._entity_list = []
1542        self._entity_dict = {}
1543        self._predefining = True
1544        self._def_predefineds()
1545        self._predefining = False
1546        self._def_exprs(exprs)
1547        self.check()
1548
1549    def _def_entity(self, ent):
1550        # Only the predefined types are allowed to not have info
1551        assert ent.info or self._predefining
1552        assert ent.name is None or ent.name not in self._entity_dict
1553        self._entity_list.append(ent)
1554        if ent.name is not None:
1555            self._entity_dict[ent.name] = ent
1556        if ent.info:
1557            ent.module = os.path.relpath(ent.info['file'],
1558                                         os.path.dirname(self._fname))
1559
1560    def lookup_entity(self, name, typ=None):
1561        ent = self._entity_dict.get(name)
1562        if typ and not isinstance(ent, typ):
1563            return None
1564        return ent
1565
1566    def lookup_type(self, name):
1567        return self.lookup_entity(name, QAPISchemaType)
1568
1569    def _def_include(self, expr, info, doc):
1570        include = expr['include']
1571        assert doc is None
1572        main_info = info
1573        while main_info['parent']:
1574            main_info = main_info['parent']
1575        fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1576        self._def_entity(QAPISchemaInclude(fname, info))
1577
1578    def _def_builtin_type(self, name, json_type, c_type):
1579        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1580        # Instantiating only the arrays that are actually used would
1581        # be nice, but we can't as long as their generated code
1582        # (qapi-builtin-types.[ch]) may be shared by some other
1583        # schema.
1584        self._make_array_type(name, None)
1585
1586    def _def_predefineds(self):
1587        for t in [('str',    'string',  'char' + pointer_suffix),
1588                  ('number', 'number',  'double'),
1589                  ('int',    'int',     'int64_t'),
1590                  ('int8',   'int',     'int8_t'),
1591                  ('int16',  'int',     'int16_t'),
1592                  ('int32',  'int',     'int32_t'),
1593                  ('int64',  'int',     'int64_t'),
1594                  ('uint8',  'int',     'uint8_t'),
1595                  ('uint16', 'int',     'uint16_t'),
1596                  ('uint32', 'int',     'uint32_t'),
1597                  ('uint64', 'int',     'uint64_t'),
1598                  ('size',   'int',     'uint64_t'),
1599                  ('bool',   'boolean', 'bool'),
1600                  ('any',    'value',   'QObject' + pointer_suffix),
1601                  ('null',   'null',    'QNull' + pointer_suffix)]:
1602            self._def_builtin_type(*t)
1603        self.the_empty_object_type = QAPISchemaObjectType(
1604            'q_empty', None, None, None, None, [], None)
1605        self._def_entity(self.the_empty_object_type)
1606        qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
1607                                                'qstring', 'qdict', 'qlist',
1608                                                'qbool'])
1609        self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1610                                            qtype_values, 'QTYPE'))
1611
1612    def _make_enum_members(self, values):
1613        return [QAPISchemaMember(v) for v in values]
1614
1615    def _make_implicit_enum_type(self, name, info, ifcond, values):
1616        # See also QAPISchemaObjectTypeMember._pretty_owner()
1617        name = name + 'Kind'   # Use namespace reserved by add_name()
1618        self._def_entity(QAPISchemaEnumType(
1619            name, info, None, ifcond, self._make_enum_members(values), None))
1620        return name
1621
1622    def _make_array_type(self, element_type, info):
1623        name = element_type + 'List'   # Use namespace reserved by add_name()
1624        if not self.lookup_type(name):
1625            self._def_entity(QAPISchemaArrayType(name, info, element_type))
1626        return name
1627
1628    def _make_implicit_object_type(self, name, info, doc, ifcond,
1629                                   role, members):
1630        if not members:
1631            return None
1632        # See also QAPISchemaObjectTypeMember._pretty_owner()
1633        name = 'q_obj_%s-%s' % (name, role)
1634        typ = self.lookup_entity(name, QAPISchemaObjectType)
1635        if typ:
1636            # The implicit object type has multiple users.  This can
1637            # happen only for simple unions' implicit wrapper types.
1638            # Its ifcond should be the disjunction of its user's
1639            # ifconds.  Not implemented.  Instead, we always pass the
1640            # wrapped type's ifcond, which is trivially the same for all
1641            # users.  It's also necessary for the wrapper to compile.
1642            # But it's not tight: the disjunction need not imply it.  We
1643            # may end up compiling useless wrapper types.
1644            # TODO kill simple unions or implement the disjunction
1645            assert ifcond == typ.ifcond
1646        else:
1647            self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1648                                                  None, members, None))
1649        return name
1650
1651    def _def_enum_type(self, expr, info, doc):
1652        name = expr['enum']
1653        data = expr['data']
1654        prefix = expr.get('prefix')
1655        ifcond = expr.get('if')
1656        self._def_entity(QAPISchemaEnumType(
1657            name, info, doc, ifcond,
1658            self._make_enum_members(data), prefix))
1659
1660    def _make_member(self, name, typ, info):
1661        optional = False
1662        if name.startswith('*'):
1663            name = name[1:]
1664            optional = True
1665        if isinstance(typ, list):
1666            assert len(typ) == 1
1667            typ = self._make_array_type(typ[0], info)
1668        return QAPISchemaObjectTypeMember(name, typ, optional)
1669
1670    def _make_members(self, data, info):
1671        return [self._make_member(key, value, info)
1672                for (key, value) in data.items()]
1673
1674    def _def_struct_type(self, expr, info, doc):
1675        name = expr['struct']
1676        base = expr.get('base')
1677        data = expr['data']
1678        ifcond = expr.get('if')
1679        self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1680                                              self._make_members(data, info),
1681                                              None))
1682
1683    def _make_variant(self, case, typ):
1684        return QAPISchemaObjectTypeVariant(case, typ)
1685
1686    def _make_simple_variant(self, case, typ, info):
1687        if isinstance(typ, list):
1688            assert len(typ) == 1
1689            typ = self._make_array_type(typ[0], info)
1690        typ = self._make_implicit_object_type(
1691            typ, info, None, self.lookup_type(typ).ifcond,
1692            'wrapper', [self._make_member('data', typ, info)])
1693        return QAPISchemaObjectTypeVariant(case, typ)
1694
1695    def _def_union_type(self, expr, info, doc):
1696        name = expr['union']
1697        data = expr['data']
1698        base = expr.get('base')
1699        ifcond = expr.get('if')
1700        tag_name = expr.get('discriminator')
1701        tag_member = None
1702        if isinstance(base, dict):
1703            base = self._make_implicit_object_type(
1704                name, info, doc, ifcond,
1705                'base', self._make_members(base, info))
1706        if tag_name:
1707            variants = [self._make_variant(key, value)
1708                        for (key, value) in data.items()]
1709            members = []
1710        else:
1711            variants = [self._make_simple_variant(key, value, info)
1712                        for (key, value) in data.items()]
1713            typ = self._make_implicit_enum_type(name, info, ifcond,
1714                                                [v.name for v in variants])
1715            tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1716            members = [tag_member]
1717        self._def_entity(
1718            QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1719                                 QAPISchemaObjectTypeVariants(tag_name,
1720                                                              tag_member,
1721                                                              variants)))
1722
1723    def _def_alternate_type(self, expr, info, doc):
1724        name = expr['alternate']
1725        data = expr['data']
1726        ifcond = expr.get('if')
1727        variants = [self._make_variant(key, value)
1728                    for (key, value) in data.items()]
1729        tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1730        self._def_entity(
1731            QAPISchemaAlternateType(name, info, doc, ifcond,
1732                                    QAPISchemaObjectTypeVariants(None,
1733                                                                 tag_member,
1734                                                                 variants)))
1735
1736    def _def_command(self, expr, info, doc):
1737        name = expr['command']
1738        data = expr.get('data')
1739        rets = expr.get('returns')
1740        gen = expr.get('gen', True)
1741        success_response = expr.get('success-response', True)
1742        boxed = expr.get('boxed', False)
1743        allow_oob = expr.get('allow-oob', False)
1744        allow_preconfig = expr.get('allow-preconfig', False)
1745        ifcond = expr.get('if')
1746        if isinstance(data, OrderedDict):
1747            data = self._make_implicit_object_type(
1748                name, info, doc, ifcond, 'arg', self._make_members(data, info))
1749        if isinstance(rets, list):
1750            assert len(rets) == 1
1751            rets = self._make_array_type(rets[0], info)
1752        self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1753                                           gen, success_response,
1754                                           boxed, allow_oob, allow_preconfig))
1755
1756    def _def_event(self, expr, info, doc):
1757        name = expr['event']
1758        data = expr.get('data')
1759        boxed = expr.get('boxed', False)
1760        ifcond = expr.get('if')
1761        if isinstance(data, OrderedDict):
1762            data = self._make_implicit_object_type(
1763                name, info, doc, ifcond, 'arg', self._make_members(data, info))
1764        self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1765
1766    def _def_exprs(self, exprs):
1767        for expr_elem in exprs:
1768            expr = expr_elem['expr']
1769            info = expr_elem['info']
1770            doc = expr_elem.get('doc')
1771            if 'enum' in expr:
1772                self._def_enum_type(expr, info, doc)
1773            elif 'struct' in expr:
1774                self._def_struct_type(expr, info, doc)
1775            elif 'union' in expr:
1776                self._def_union_type(expr, info, doc)
1777            elif 'alternate' in expr:
1778                self._def_alternate_type(expr, info, doc)
1779            elif 'command' in expr:
1780                self._def_command(expr, info, doc)
1781            elif 'event' in expr:
1782                self._def_event(expr, info, doc)
1783            elif 'include' in expr:
1784                self._def_include(expr, info, doc)
1785            else:
1786                assert False
1787
1788    def check(self):
1789        for ent in self._entity_list:
1790            ent.check(self)
1791
1792    def visit(self, visitor):
1793        visitor.visit_begin(self)
1794        module = None
1795        for entity in self._entity_list:
1796            if visitor.visit_needed(entity):
1797                if entity.module != module:
1798                    module = entity.module
1799                    visitor.visit_module(module)
1800                entity.visit(visitor)
1801        visitor.visit_end()
1802
1803
1804#
1805# Code generation helpers
1806#
1807
1808def camel_case(name):
1809    new_name = ''
1810    first = True
1811    for ch in name:
1812        if ch in ['_', '-']:
1813            first = True
1814        elif first:
1815            new_name += ch.upper()
1816            first = False
1817        else:
1818            new_name += ch.lower()
1819    return new_name
1820
1821
1822# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1823# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1824# ENUM24_Name -> ENUM24_NAME
1825def camel_to_upper(value):
1826    c_fun_str = c_name(value, False)
1827    if value.isupper():
1828        return c_fun_str
1829
1830    new_name = ''
1831    l = len(c_fun_str)
1832    for i in range(l):
1833        c = c_fun_str[i]
1834        # When c is upper and no '_' appears before, do more checks
1835        if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1836            if i < l - 1 and c_fun_str[i + 1].islower():
1837                new_name += '_'
1838            elif c_fun_str[i - 1].isdigit():
1839                new_name += '_'
1840        new_name += c
1841    return new_name.lstrip('_').upper()
1842
1843
1844def c_enum_const(type_name, const_name, prefix=None):
1845    if prefix is not None:
1846        type_name = prefix
1847    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1848
1849if hasattr(str, 'maketrans'):
1850    c_name_trans = str.maketrans('.-', '__')
1851else:
1852    c_name_trans = string.maketrans('.-', '__')
1853
1854
1855# Map @name to a valid C identifier.
1856# If @protect, avoid returning certain ticklish identifiers (like
1857# C keywords) by prepending 'q_'.
1858#
1859# Used for converting 'name' from a 'name':'type' qapi definition
1860# into a generated struct member, as well as converting type names
1861# into substrings of a generated C function name.
1862# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1863# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1864def c_name(name, protect=True):
1865    # ANSI X3J11/88-090, 3.1.1
1866    c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1867                     'default', 'do', 'double', 'else', 'enum', 'extern',
1868                     'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1869                     'return', 'short', 'signed', 'sizeof', 'static',
1870                     'struct', 'switch', 'typedef', 'union', 'unsigned',
1871                     'void', 'volatile', 'while'])
1872    # ISO/IEC 9899:1999, 6.4.1
1873    c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1874    # ISO/IEC 9899:2011, 6.4.1
1875    c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1876                     '_Noreturn', '_Static_assert', '_Thread_local'])
1877    # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1878    # excluding _.*
1879    gcc_words = set(['asm', 'typeof'])
1880    # C++ ISO/IEC 14882:2003 2.11
1881    cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1882                     'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1883                     'namespace', 'new', 'operator', 'private', 'protected',
1884                     'public', 'reinterpret_cast', 'static_cast', 'template',
1885                     'this', 'throw', 'true', 'try', 'typeid', 'typename',
1886                     'using', 'virtual', 'wchar_t',
1887                     # alternative representations
1888                     'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1889                     'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1890    # namespace pollution:
1891    polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1892    name = name.translate(c_name_trans)
1893    if protect and (name in c89_words | c99_words | c11_words | gcc_words
1894                    | cpp_words | polluted_words):
1895        return 'q_' + name
1896    return name
1897
1898eatspace = '\033EATSPACE.'
1899pointer_suffix = ' *' + eatspace
1900
1901
1902def genindent(count):
1903    ret = ''
1904    for _ in range(count):
1905        ret += ' '
1906    return ret
1907
1908indent_level = 0
1909
1910
1911def push_indent(indent_amount=4):
1912    global indent_level
1913    indent_level += indent_amount
1914
1915
1916def pop_indent(indent_amount=4):
1917    global indent_level
1918    indent_level -= indent_amount
1919
1920
1921# Generate @code with @kwds interpolated.
1922# Obey indent_level, and strip eatspace.
1923def cgen(code, **kwds):
1924    raw = code % kwds
1925    if indent_level:
1926        indent = genindent(indent_level)
1927        # re.subn() lacks flags support before Python 2.7, use re.compile()
1928        raw = re.subn(re.compile(r'^.', re.MULTILINE),
1929                      indent + r'\g<0>', raw)
1930        raw = raw[0]
1931    return re.sub(re.escape(eatspace) + r' *', '', raw)
1932
1933
1934def mcgen(code, **kwds):
1935    if code[0] == '\n':
1936        code = code[1:]
1937    return cgen(code, **kwds)
1938
1939
1940def guardname(filename):
1941    return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
1942
1943
1944def guardstart(name):
1945    return mcgen('''
1946#ifndef %(name)s
1947#define %(name)s
1948
1949''',
1950                 name=guardname(name))
1951
1952
1953def guardend(name):
1954    return mcgen('''
1955
1956#endif /* %(name)s */
1957''',
1958                 name=guardname(name))
1959
1960
1961def gen_enum_lookup(name, values, prefix=None):
1962    ret = mcgen('''
1963
1964const QEnumLookup %(c_name)s_lookup = {
1965    .array = (const char *const[]) {
1966''',
1967                c_name=c_name(name))
1968    for value in values:
1969        index = c_enum_const(name, value, prefix)
1970        ret += mcgen('''
1971        [%(index)s] = "%(value)s",
1972''',
1973                     index=index, value=value)
1974
1975    ret += mcgen('''
1976    },
1977    .size = %(max_index)s
1978};
1979''',
1980                 max_index=c_enum_const(name, '_MAX', prefix))
1981    return ret
1982
1983
1984def gen_enum(name, values, prefix=None):
1985    # append automatically generated _MAX value
1986    enum_values = values + ['_MAX']
1987
1988    ret = mcgen('''
1989
1990typedef enum %(c_name)s {
1991''',
1992                c_name=c_name(name))
1993
1994    i = 0
1995    for value in enum_values:
1996        ret += mcgen('''
1997    %(c_enum)s = %(i)d,
1998''',
1999                     c_enum=c_enum_const(name, value, prefix),
2000                     i=i)
2001        i += 1
2002
2003    ret += mcgen('''
2004} %(c_name)s;
2005''',
2006                 c_name=c_name(name))
2007
2008    ret += mcgen('''
2009
2010#define %(c_name)s_str(val) \\
2011    qapi_enum_lookup(&%(c_name)s_lookup, (val))
2012
2013extern const QEnumLookup %(c_name)s_lookup;
2014''',
2015                 c_name=c_name(name))
2016    return ret
2017
2018
2019def build_params(arg_type, boxed, extra):
2020    if not arg_type:
2021        assert not boxed
2022        return extra
2023    ret = ''
2024    sep = ''
2025    if boxed:
2026        ret += '%s arg' % arg_type.c_param_type()
2027        sep = ', '
2028    else:
2029        assert not arg_type.variants
2030        for memb in arg_type.members:
2031            ret += sep
2032            sep = ', '
2033            if memb.optional:
2034                ret += 'bool has_%s, ' % c_name(memb.name)
2035            ret += '%s %s' % (memb.type.c_param_type(),
2036                              c_name(memb.name))
2037    if extra:
2038        ret += sep + extra
2039    return ret
2040
2041
2042#
2043# Accumulate and write output
2044#
2045
2046class QAPIGen(object):
2047
2048    def __init__(self):
2049        self._preamble = ''
2050        self._body = ''
2051
2052    def preamble_add(self, text):
2053        self._preamble += text
2054
2055    def add(self, text):
2056        self._body += text
2057
2058    def _top(self, fname):
2059        return ''
2060
2061    def _bottom(self, fname):
2062        return ''
2063
2064    def write(self, output_dir, fname):
2065        pathname = os.path.join(output_dir, fname)
2066        dir = os.path.dirname(pathname)
2067        if dir:
2068            try:
2069                os.makedirs(dir)
2070            except os.error as e:
2071                if e.errno != errno.EEXIST:
2072                    raise
2073        fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2074        if sys.version_info[0] >= 3:
2075            f = open(fd, 'r+', encoding='utf-8')
2076        else:
2077            f = os.fdopen(fd, 'r+')
2078        text = (self._top(fname) + self._preamble + self._body
2079                + self._bottom(fname))
2080        oldtext = f.read(len(text) + 1)
2081        if text != oldtext:
2082            f.seek(0)
2083            f.truncate(0)
2084            f.write(text)
2085        f.close()
2086
2087
2088class QAPIGenC(QAPIGen):
2089
2090    def __init__(self, blurb, pydoc):
2091        QAPIGen.__init__(self)
2092        self._blurb = blurb
2093        self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2094                                                  re.MULTILINE))
2095
2096    def _top(self, fname):
2097        return mcgen('''
2098/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2099
2100/*
2101%(blurb)s
2102 *
2103 * %(copyright)s
2104 *
2105 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2106 * See the COPYING.LIB file in the top-level directory.
2107 */
2108
2109''',
2110                     blurb=self._blurb, copyright=self._copyright)
2111
2112    def _bottom(self, fname):
2113        return mcgen('''
2114/* Dummy declaration to prevent empty .o file */
2115char dummy_%(name)s;
2116''',
2117                     name=c_name(fname))
2118
2119
2120class QAPIGenH(QAPIGenC):
2121
2122    def _top(self, fname):
2123        return QAPIGenC._top(self, fname) + guardstart(fname)
2124
2125    def _bottom(self, fname):
2126        return guardend(fname)
2127
2128
2129class QAPIGenDoc(QAPIGen):
2130
2131    def _top(self, fname):
2132        return (QAPIGen._top(self, fname)
2133                + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2134
2135
2136class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2137
2138    def __init__(self, prefix, what, blurb, pydoc):
2139        self._prefix = prefix
2140        self._what = what
2141        self._genc = QAPIGenC(blurb, pydoc)
2142        self._genh = QAPIGenH(blurb, pydoc)
2143
2144    def write(self, output_dir):
2145        self._genc.write(output_dir, self._prefix + self._what + '.c')
2146        self._genh.write(output_dir, self._prefix + self._what + '.h')
2147
2148
2149class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2150
2151    def __init__(self, prefix, what, blurb, pydoc):
2152        self._prefix = prefix
2153        self._what = what
2154        self._blurb = blurb
2155        self._pydoc = pydoc
2156        self._module = {}
2157        self._main_module = None
2158
2159    def _module_basename(self, what, name):
2160        if name is None:
2161            return re.sub(r'-', '-builtin-', what)
2162        basename = os.path.join(os.path.dirname(name),
2163                                self._prefix + what)
2164        if name == self._main_module:
2165            return basename
2166        return basename + '-' + os.path.splitext(os.path.basename(name))[0]
2167
2168    def _add_module(self, name, blurb):
2169        if self._main_module is None and name is not None:
2170            self._main_module = name
2171        genc = QAPIGenC(blurb, self._pydoc)
2172        genh = QAPIGenH(blurb, self._pydoc)
2173        self._module[name] = (genc, genh)
2174        self._set_module(name)
2175
2176    def _set_module(self, name):
2177        self._genc, self._genh = self._module[name]
2178
2179    def write(self, output_dir, opt_builtins=False):
2180        for name in self._module:
2181            if name is None and not opt_builtins:
2182                continue
2183            basename = self._module_basename(self._what, name)
2184            (genc, genh) = self._module[name]
2185            genc.write(output_dir, basename + '.c')
2186            genh.write(output_dir, basename + '.h')
2187
2188    def _begin_module(self, name):
2189        pass
2190
2191    def visit_module(self, name):
2192        if name in self._module:
2193            self._set_module(name)
2194            return
2195        self._add_module(name, self._blurb)
2196        self._begin_module(name)
2197
2198    def visit_include(self, name, info):
2199        basename = self._module_basename(self._what, name)
2200        self._genh.preamble_add(mcgen('''
2201#include "%(basename)s.h"
2202''',
2203                                      basename=basename))
2204