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