xref: /openbmc/qemu/scripts/qapi/common.py (revision 3f58cc29a8d2f01dc498ff4624a25e72448059a1)
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    args = expr.get('data')
772    boxed = expr.get('boxed', False)
773
774    if boxed and args is None:
775        raise QAPISemError(info, "'boxed': true requires 'data'")
776    check_type(args, info, "'data' for command '%s'" % name,
777               allow_dict=not boxed)
778    check_type(expr.get('returns'), info,
779               "'returns' for command '%s'" % name,
780               allow_array=True)
781
782
783def check_event(expr, info):
784    name = expr['event']
785    args = expr.get('data')
786    boxed = expr.get('boxed', False)
787
788    if boxed and args is None:
789        raise QAPISemError(info, "'boxed': true requires 'data'")
790    check_type(args, info, "'data' for event '%s'" % name,
791               allow_dict=not boxed)
792
793
794def check_union(expr, info):
795    name = expr['union']
796    base = expr.get('base')
797    discriminator = expr.get('discriminator')
798    members = expr['data']
799
800    if discriminator is None:   # simple union
801        if base is not None:
802            raise QAPISemError(
803                info, "simple union '%s' must not have a base" % name)
804    else:                       # flat union
805        check_type(base, info, "'base' for union '%s'" % name,
806                   allow_dict=name)
807        if not base:
808            raise QAPISemError(
809                info, "flat union '%s' must have a base" % name)
810        check_name_is_str(discriminator, info,
811                          "discriminator of flat union '%s'" % name)
812
813    for (key, value) in members.items():
814        check_name_str(key, info, "member of union '%s'" % name)
815        check_known_keys(value, info,
816                         "member '%s' of union '%s'" % (key, name),
817                         ['type'], ['if'])
818        check_if(value, info)
819        normalize_if(value)
820        check_type(value['type'], info,
821                   "member '%s' of union '%s'" % (key, name),
822                   allow_array=not base)
823
824
825def check_alternate(expr, info):
826    name = expr['alternate']
827    members = expr['data']
828
829    if len(members) == 0:
830        raise QAPISemError(info,
831                           "alternate '%s' cannot have empty 'data'" % name)
832    for (key, value) in members.items():
833        check_name_str(key, info, "member of alternate '%s'" % name)
834        check_known_keys(value, info,
835                         "member '%s' of alternate '%s'" % (key, name),
836                         ['type'], ['if'])
837        check_if(value, info)
838        normalize_if(value)
839        check_type(value['type'], info,
840                   "member '%s' of alternate '%s'" % (key, name))
841
842
843def check_enum(expr, info):
844    name = expr['enum']
845    members = expr['data']
846    prefix = expr.get('prefix')
847
848    if not isinstance(members, list):
849        raise QAPISemError(info,
850                           "enum '%s' requires an array for 'data'" % name)
851    if prefix is not None and not isinstance(prefix, str):
852        raise QAPISemError(info,
853                           "enum '%s' requires a string for 'prefix'" % name)
854
855    permit_upper = name in name_case_whitelist
856
857    for member in members:
858        check_known_keys(member, info, "member of enum '%s'" % name,
859                         ['name'], ['if'])
860        check_if(member, info)
861        normalize_if(member)
862        check_name(member['name'], info, "member of enum '%s'" % name,
863                   enum_member=True, permit_upper=permit_upper)
864
865
866def check_struct(expr, info):
867    name = expr['struct']
868    members = expr['data']
869    features = expr.get('features')
870
871    check_type(members, info, "'data' for struct '%s'" % name,
872               allow_dict=name)
873    check_type(expr.get('base'), info, "'base' for struct '%s'" % name)
874
875    if features:
876        if not isinstance(features, list):
877            raise QAPISemError(
878                info, "struct '%s' requires an array for 'features'" % name)
879        for f in features:
880            assert isinstance(f, dict)
881            check_known_keys(f, info, "feature of struct %s" % name,
882                             ['name'], ['if'])
883
884            check_if(f, info)
885            normalize_if(f)
886            check_name(f['name'], info, "feature of struct %s" % name)
887
888
889def check_known_keys(value, info, source, required, optional):
890
891    def pprint(elems):
892        return ', '.join("'" + e + "'" for e in sorted(elems))
893
894    missing = set(required) - set(value)
895    if missing:
896        raise QAPISemError(
897            info,
898            "key%s %s %s missing from %s"
899            % ('s' if len(missing) > 1 else '', pprint(missing),
900               'are' if len(missing) > 1 else 'is', source))
901    allowed = set(required + optional)
902    unknown = set(value) - allowed
903    if unknown:
904        raise QAPISemError(
905            info,
906            "unknown key%s %s in %s\nValid keys are %s."
907            % ('s' if len(unknown) > 1 else '', pprint(unknown),
908               source, pprint(allowed)))
909
910
911def check_keys(expr, info, meta, required, optional=[]):
912    name = expr[meta]
913    required = required + [meta]
914    source = "%s '%s'" % (meta, name)
915    check_known_keys(expr, info, source, required, optional)
916
917
918def check_flags(expr, info):
919    for key in ['gen', 'success-response']:
920        if key in expr and expr[key] is not False:
921            raise QAPISemError(
922                info, "flag '%s' may only use false value" % key)
923    for key in ['boxed', 'allow-oob', 'allow-preconfig']:
924        if key in expr and expr[key] is not True:
925            raise QAPISemError(
926                info, "flag '%s' may only use true value" % key)
927
928
929def normalize_enum(expr):
930    if isinstance(expr['data'], list):
931        expr['data'] = [m if isinstance(m, dict) else {'name': m}
932                        for m in expr['data']]
933
934
935def normalize_members(members):
936    if isinstance(members, OrderedDict):
937        for key, arg in members.items():
938            if isinstance(arg, dict):
939                continue
940            members[key] = {'type': arg}
941
942
943def normalize_features(features):
944    if isinstance(features, list):
945        features[:] = [f if isinstance(f, dict) else {'name': f}
946                       for f in features]
947
948
949def normalize_if(expr):
950    ifcond = expr.get('if')
951    if isinstance(ifcond, str):
952        expr['if'] = [ifcond]
953
954
955def check_exprs(exprs):
956    for expr_elem in exprs:
957        expr = expr_elem['expr']
958        info = expr_elem['info']
959        doc = expr_elem.get('doc')
960
961        if 'include' in expr:
962            continue
963
964        if not doc and doc_required:
965            raise QAPISemError(info,
966                               "definition missing documentation comment")
967
968        if 'enum' in expr:
969            meta = 'enum'
970        elif 'union' in expr:
971            meta = 'union'
972        elif 'alternate' in expr:
973            meta = 'alternate'
974        elif 'struct' in expr:
975            meta = 'struct'
976        elif 'command' in expr:
977            meta = 'command'
978        elif 'event' in expr:
979            meta = 'event'
980        else:
981            raise QAPISemError(info, "expression is missing metatype")
982
983        name = expr[meta]
984        check_name_is_str(name, info, "'%s'" % meta)
985        info.set_defn(meta, name)
986        check_defn_name_str(name, info, meta)
987
988        if doc and doc.symbol != name:
989            raise QAPISemError(
990                info,
991                "definition of '%s' follows documentation for '%s'"
992                % (name, doc.symbol))
993
994        if meta == 'enum':
995            check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
996            normalize_enum(expr)
997            check_enum(expr, info)
998        elif meta == 'union':
999            check_keys(expr, info, 'union', ['data'],
1000                       ['base', 'discriminator', 'if'])
1001            normalize_members(expr.get('base'))
1002            normalize_members(expr['data'])
1003            check_union(expr, info)
1004        elif meta == 'alternate':
1005            check_keys(expr, info, 'alternate', ['data'], ['if'])
1006            normalize_members(expr['data'])
1007            check_alternate(expr, info)
1008        elif meta == 'struct':
1009            check_keys(expr, info, 'struct', ['data'],
1010                       ['base', 'if', 'features'])
1011            normalize_members(expr['data'])
1012            normalize_features(expr.get('features'))
1013            check_struct(expr, info)
1014        elif meta == 'command':
1015            check_keys(expr, info, 'command', [],
1016                       ['data', 'returns', 'gen', 'success-response',
1017                        'boxed', 'allow-oob', 'allow-preconfig', 'if'])
1018            normalize_members(expr.get('data'))
1019            check_command(expr, info)
1020        elif meta == 'event':
1021            check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
1022            normalize_members(expr.get('data'))
1023            check_event(expr, info)
1024        else:
1025            assert False, 'unexpected meta type'
1026
1027        normalize_if(expr)
1028        check_if(expr, info)
1029        check_flags(expr, info)
1030
1031        if doc:
1032            doc.check_expr(expr)
1033
1034    return exprs
1035
1036
1037#
1038# Schema compiler frontend
1039# TODO catching name collisions in generated code would be nice
1040#
1041
1042class QAPISchemaEntity(object):
1043    meta = None
1044
1045    def __init__(self, name, info, doc, ifcond=None):
1046        assert name is None or isinstance(name, str)
1047        self.name = name
1048        self._module = None
1049        # For explicitly defined entities, info points to the (explicit)
1050        # definition.  For builtins (and their arrays), info is None.
1051        # For implicitly defined entities, info points to a place that
1052        # triggered the implicit definition (there may be more than one
1053        # such place).
1054        self.info = info
1055        self.doc = doc
1056        self._ifcond = ifcond or []
1057        self._checked = False
1058
1059    def c_name(self):
1060        return c_name(self.name)
1061
1062    def check(self, schema):
1063        assert not self._checked
1064        if self.info:
1065            self._module = os.path.relpath(self.info.fname,
1066                                           os.path.dirname(schema.fname))
1067        self._checked = True
1068
1069    @property
1070    def ifcond(self):
1071        assert self._checked
1072        return self._ifcond
1073
1074    @property
1075    def module(self):
1076        assert self._checked
1077        return self._module
1078
1079    def is_implicit(self):
1080        return not self.info
1081
1082    def visit(self, visitor):
1083        assert self._checked
1084
1085    def describe(self):
1086        assert self.meta
1087        return "%s '%s'" % (self.meta, self.name)
1088
1089
1090class QAPISchemaVisitor(object):
1091    def visit_begin(self, schema):
1092        pass
1093
1094    def visit_end(self):
1095        pass
1096
1097    def visit_module(self, fname):
1098        pass
1099
1100    def visit_needed(self, entity):
1101        # Default to visiting everything
1102        return True
1103
1104    def visit_include(self, fname, info):
1105        pass
1106
1107    def visit_builtin_type(self, name, info, json_type):
1108        pass
1109
1110    def visit_enum_type(self, name, info, ifcond, members, prefix):
1111        pass
1112
1113    def visit_array_type(self, name, info, ifcond, element_type):
1114        pass
1115
1116    def visit_object_type(self, name, info, ifcond, base, members, variants,
1117                          features):
1118        pass
1119
1120    def visit_object_type_flat(self, name, info, ifcond, members, variants,
1121                               features):
1122        pass
1123
1124    def visit_alternate_type(self, name, info, ifcond, variants):
1125        pass
1126
1127    def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1128                      success_response, boxed, allow_oob, allow_preconfig):
1129        pass
1130
1131    def visit_event(self, name, info, ifcond, arg_type, boxed):
1132        pass
1133
1134
1135class QAPISchemaInclude(QAPISchemaEntity):
1136
1137    def __init__(self, fname, info):
1138        QAPISchemaEntity.__init__(self, None, info, None)
1139        self.fname = fname
1140
1141    def visit(self, visitor):
1142        QAPISchemaEntity.visit(self, visitor)
1143        visitor.visit_include(self.fname, self.info)
1144
1145
1146class QAPISchemaType(QAPISchemaEntity):
1147    # Return the C type for common use.
1148    # For the types we commonly box, this is a pointer type.
1149    def c_type(self):
1150        pass
1151
1152    # Return the C type to be used in a parameter list.
1153    def c_param_type(self):
1154        return self.c_type()
1155
1156    # Return the C type to be used where we suppress boxing.
1157    def c_unboxed_type(self):
1158        return self.c_type()
1159
1160    def json_type(self):
1161        pass
1162
1163    def alternate_qtype(self):
1164        json2qtype = {
1165            'null':    'QTYPE_QNULL',
1166            'string':  'QTYPE_QSTRING',
1167            'number':  'QTYPE_QNUM',
1168            'int':     'QTYPE_QNUM',
1169            'boolean': 'QTYPE_QBOOL',
1170            'object':  'QTYPE_QDICT'
1171        }
1172        return json2qtype.get(self.json_type())
1173
1174    def doc_type(self):
1175        if self.is_implicit():
1176            return None
1177        return self.name
1178
1179    def describe(self):
1180        assert self.meta
1181        return "%s type '%s'" % (self.meta, self.name)
1182
1183
1184class QAPISchemaBuiltinType(QAPISchemaType):
1185    meta = 'built-in'
1186
1187    def __init__(self, name, json_type, c_type):
1188        QAPISchemaType.__init__(self, name, None, None)
1189        assert not c_type or isinstance(c_type, str)
1190        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1191                             'value')
1192        self._json_type_name = json_type
1193        self._c_type_name = c_type
1194
1195    def c_name(self):
1196        return self.name
1197
1198    def c_type(self):
1199        return self._c_type_name
1200
1201    def c_param_type(self):
1202        if self.name == 'str':
1203            return 'const ' + self._c_type_name
1204        return self._c_type_name
1205
1206    def json_type(self):
1207        return self._json_type_name
1208
1209    def doc_type(self):
1210        return self.json_type()
1211
1212    def visit(self, visitor):
1213        QAPISchemaType.visit(self, visitor)
1214        visitor.visit_builtin_type(self.name, self.info, self.json_type())
1215
1216
1217class QAPISchemaEnumType(QAPISchemaType):
1218    meta = 'enum'
1219
1220    def __init__(self, name, info, doc, ifcond, members, prefix):
1221        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1222        for m in members:
1223            assert isinstance(m, QAPISchemaEnumMember)
1224            m.set_defined_in(name)
1225        assert prefix is None or isinstance(prefix, str)
1226        self.members = members
1227        self.prefix = prefix
1228
1229    def check(self, schema):
1230        QAPISchemaType.check(self, schema)
1231        seen = {}
1232        for m in self.members:
1233            m.check_clash(self.info, seen)
1234            if self.doc:
1235                self.doc.connect_member(m)
1236
1237    def is_implicit(self):
1238        # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1239        return self.name.endswith('Kind') or self.name == 'QType'
1240
1241    def c_type(self):
1242        return c_name(self.name)
1243
1244    def member_names(self):
1245        return [m.name for m in self.members]
1246
1247    def json_type(self):
1248        return 'string'
1249
1250    def visit(self, visitor):
1251        QAPISchemaType.visit(self, visitor)
1252        visitor.visit_enum_type(self.name, self.info, self.ifcond,
1253                                self.members, self.prefix)
1254
1255
1256class QAPISchemaArrayType(QAPISchemaType):
1257    meta = 'array'
1258
1259    def __init__(self, name, info, element_type):
1260        QAPISchemaType.__init__(self, name, info, None, None)
1261        assert isinstance(element_type, str)
1262        self._element_type_name = element_type
1263        self.element_type = None
1264
1265    def check(self, schema):
1266        QAPISchemaType.check(self, schema)
1267        self.element_type = schema.resolve_type(
1268            self._element_type_name, self.info,
1269            self.info and self.info.defn_meta)
1270        assert not isinstance(self.element_type, QAPISchemaArrayType)
1271
1272    @property
1273    def ifcond(self):
1274        assert self._checked
1275        return self.element_type.ifcond
1276
1277    @property
1278    def module(self):
1279        assert self._checked
1280        return self.element_type.module
1281
1282    def is_implicit(self):
1283        return True
1284
1285    def c_type(self):
1286        return c_name(self.name) + pointer_suffix
1287
1288    def json_type(self):
1289        return 'array'
1290
1291    def doc_type(self):
1292        elt_doc_type = self.element_type.doc_type()
1293        if not elt_doc_type:
1294            return None
1295        return 'array of ' + elt_doc_type
1296
1297    def visit(self, visitor):
1298        QAPISchemaType.visit(self, visitor)
1299        visitor.visit_array_type(self.name, self.info, self.ifcond,
1300                                 self.element_type)
1301
1302    def describe(self):
1303        assert self.meta
1304        return "%s type ['%s']" % (self.meta, self._element_type_name)
1305
1306
1307class QAPISchemaObjectType(QAPISchemaType):
1308    def __init__(self, name, info, doc, ifcond,
1309                 base, local_members, variants, features):
1310        # struct has local_members, optional base, and no variants
1311        # flat union has base, variants, and no local_members
1312        # simple union has local_members, variants, and no base
1313        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1314        self.meta = 'union' if variants else 'struct'
1315        assert base is None or isinstance(base, str)
1316        for m in local_members:
1317            assert isinstance(m, QAPISchemaObjectTypeMember)
1318            m.set_defined_in(name)
1319        if variants is not None:
1320            assert isinstance(variants, QAPISchemaObjectTypeVariants)
1321            variants.set_defined_in(name)
1322        for f in features:
1323            assert isinstance(f, QAPISchemaFeature)
1324            f.set_defined_in(name)
1325        self._base_name = base
1326        self.base = None
1327        self.local_members = local_members
1328        self.variants = variants
1329        self.members = None
1330        self.features = features
1331
1332    def check(self, schema):
1333        # This calls another type T's .check() exactly when the C
1334        # struct emitted by gen_object() contains that T's C struct
1335        # (pointers don't count).
1336        if self.members is not None:
1337            # A previous .check() completed: nothing to do
1338            return
1339        if self._checked:
1340            # Recursed: C struct contains itself
1341            raise QAPISemError(self.info,
1342                               "object %s contains itself" % self.name)
1343
1344        QAPISchemaType.check(self, schema)
1345        assert self._checked and self.members is None
1346
1347        seen = OrderedDict()
1348        if self._base_name:
1349            self.base = schema.resolve_type(self._base_name, self.info,
1350                                            "'base'")
1351            if (not isinstance(self.base, QAPISchemaObjectType)
1352                    or self.base.variants):
1353                raise QAPISemError(
1354                    self.info,
1355                    "'base' requires a struct type, %s isn't"
1356                    % self.base.describe())
1357            self.base.check(schema)
1358            self.base.check_clash(self.info, seen)
1359        for m in self.local_members:
1360            m.check(schema)
1361            m.check_clash(self.info, seen)
1362            if self.doc:
1363                self.doc.connect_member(m)
1364        members = seen.values()
1365
1366        if self.variants:
1367            self.variants.check(schema, seen)
1368            self.variants.check_clash(self.info, seen)
1369
1370        # Features are in a name space separate from members
1371        seen = {}
1372        for f in self.features:
1373            f.check_clash(self.info, seen)
1374
1375        if self.doc:
1376            self.doc.check()
1377
1378        self.members = members  # mark completed
1379
1380    # Check that the members of this type do not cause duplicate JSON members,
1381    # and update seen to track the members seen so far. Report any errors
1382    # on behalf of info, which is not necessarily self.info
1383    def check_clash(self, info, seen):
1384        assert self._checked
1385        assert not self.variants       # not implemented
1386        for m in self.members:
1387            m.check_clash(info, seen)
1388
1389    @property
1390    def ifcond(self):
1391        assert self._checked
1392        if isinstance(self._ifcond, QAPISchemaType):
1393            # Simple union wrapper type inherits from wrapped type;
1394            # see _make_implicit_object_type()
1395            return self._ifcond.ifcond
1396        return self._ifcond
1397
1398    def is_implicit(self):
1399        # See QAPISchema._make_implicit_object_type(), as well as
1400        # _def_predefineds()
1401        return self.name.startswith('q_')
1402
1403    def is_empty(self):
1404        assert self.members is not None
1405        return not self.members and not self.variants
1406
1407    def c_name(self):
1408        assert self.name != 'q_empty'
1409        return QAPISchemaType.c_name(self)
1410
1411    def c_type(self):
1412        assert not self.is_implicit()
1413        return c_name(self.name) + pointer_suffix
1414
1415    def c_unboxed_type(self):
1416        return c_name(self.name)
1417
1418    def json_type(self):
1419        return 'object'
1420
1421    def visit(self, visitor):
1422        QAPISchemaType.visit(self, visitor)
1423        visitor.visit_object_type(self.name, self.info, self.ifcond,
1424                                  self.base, self.local_members, self.variants,
1425                                  self.features)
1426        visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1427                                       self.members, self.variants,
1428                                       self.features)
1429
1430
1431class QAPISchemaMember(object):
1432    """ Represents object members, enum members and features """
1433    role = 'member'
1434
1435    def __init__(self, name, info, ifcond=None):
1436        assert isinstance(name, str)
1437        self.name = name
1438        self.info = info
1439        self.ifcond = ifcond or []
1440        self.defined_in = None
1441
1442    def set_defined_in(self, name):
1443        assert not self.defined_in
1444        self.defined_in = name
1445
1446    def check_clash(self, info, seen):
1447        cname = c_name(self.name)
1448        if cname in seen:
1449            raise QAPISemError(
1450                info,
1451                "%s collides with %s"
1452                % (self.describe(info), seen[cname].describe(info)))
1453        seen[cname] = self
1454
1455    def describe(self, info):
1456        role = self.role
1457        defined_in = self.defined_in
1458        assert defined_in
1459
1460        if defined_in.startswith('q_obj_'):
1461            # See QAPISchema._make_implicit_object_type() - reverse the
1462            # mapping there to create a nice human-readable description
1463            defined_in = defined_in[6:]
1464            if defined_in.endswith('-arg'):
1465                # Implicit type created for a command's dict 'data'
1466                assert role == 'member'
1467                role = 'parameter'
1468            elif defined_in.endswith('-base'):
1469                # Implicit type created for a flat union's dict 'base'
1470                role = 'base ' + role
1471            else:
1472                # Implicit type created for a simple union's branch
1473                assert defined_in.endswith('-wrapper')
1474                # Unreachable and not implemented
1475                assert False
1476        elif defined_in.endswith('Kind'):
1477            # See QAPISchema._make_implicit_enum_type()
1478            # Implicit enum created for simple union's branches
1479            assert role == 'value'
1480            role = 'branch'
1481        elif defined_in != info.defn_name:
1482            return "%s '%s' of type '%s'" % (role, self.name, defined_in)
1483        return "%s '%s'" % (role, self.name)
1484
1485
1486class QAPISchemaEnumMember(QAPISchemaMember):
1487    role = 'value'
1488
1489
1490class QAPISchemaFeature(QAPISchemaMember):
1491    role = 'feature'
1492
1493
1494class QAPISchemaObjectTypeMember(QAPISchemaMember):
1495    def __init__(self, name, info, typ, optional, ifcond=None):
1496        QAPISchemaMember.__init__(self, name, info, ifcond)
1497        assert isinstance(typ, str)
1498        assert isinstance(optional, bool)
1499        self._type_name = typ
1500        self.type = None
1501        self.optional = optional
1502
1503    def check(self, schema):
1504        assert self.defined_in
1505        self.type = schema.resolve_type(self._type_name, self.info,
1506                                        self.describe)
1507
1508
1509class QAPISchemaObjectTypeVariants(object):
1510    def __init__(self, tag_name, info, tag_member, variants):
1511        # Flat unions pass tag_name but not tag_member.
1512        # Simple unions and alternates pass tag_member but not tag_name.
1513        # After check(), tag_member is always set, and tag_name remains
1514        # a reliable witness of being used by a flat union.
1515        assert bool(tag_member) != bool(tag_name)
1516        assert (isinstance(tag_name, str) or
1517                isinstance(tag_member, QAPISchemaObjectTypeMember))
1518        for v in variants:
1519            assert isinstance(v, QAPISchemaObjectTypeVariant)
1520        self._tag_name = tag_name
1521        self.info = info
1522        self.tag_member = tag_member
1523        self.variants = variants
1524
1525    def set_defined_in(self, name):
1526        for v in self.variants:
1527            v.set_defined_in(name)
1528
1529    def check(self, schema, seen):
1530        if not self.tag_member: # flat union
1531            self.tag_member = seen.get(c_name(self._tag_name))
1532            base = "'base'"
1533            # Pointing to the base type when not implicit would be
1534            # nice, but we don't know it here
1535            if not self.tag_member or self._tag_name != self.tag_member.name:
1536                raise QAPISemError(
1537                    self.info,
1538                    "discriminator '%s' is not a member of %s"
1539                    % (self._tag_name, base))
1540            # Here we do:
1541            base_type = schema.lookup_type(self.tag_member.defined_in)
1542            assert base_type
1543            if not base_type.is_implicit():
1544                base = "base type '%s'" % self.tag_member.defined_in
1545            if not isinstance(self.tag_member.type, QAPISchemaEnumType):
1546                raise QAPISemError(
1547                    self.info,
1548                    "discriminator member '%s' of %s must be of enum type"
1549                    % (self._tag_name, base))
1550            if self.tag_member.optional:
1551                raise QAPISemError(
1552                    self.info,
1553                    "discriminator member '%s' of %s must not be optional"
1554                    % (self._tag_name, base))
1555            if self.tag_member.ifcond:
1556                raise QAPISemError(
1557                    self.info,
1558                    "discriminator member '%s' of %s must not be conditional"
1559                    % (self._tag_name, base))
1560        else:                   # simple union
1561            assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1562            assert not self.tag_member.optional
1563            assert self.tag_member.ifcond == []
1564        if self._tag_name:    # flat union
1565            # branches that are not explicitly covered get an empty type
1566            cases = set([v.name for v in self.variants])
1567            for m in self.tag_member.type.members:
1568                if m.name not in cases:
1569                    v = QAPISchemaObjectTypeVariant(m.name, self.info,
1570                                                    'q_empty', m.ifcond)
1571                    v.set_defined_in(self.tag_member.defined_in)
1572                    self.variants.append(v)
1573        if not self.variants:
1574            raise QAPISemError(self.info, "union has no branches")
1575        for v in self.variants:
1576            v.check(schema)
1577            # Union names must match enum values; alternate names are
1578            # checked separately. Use 'seen' to tell the two apart.
1579            if seen:
1580                if v.name not in self.tag_member.type.member_names():
1581                    raise QAPISemError(
1582                        self.info,
1583                        "branch '%s' is not a value of %s"
1584                        % (v.name, self.tag_member.type.describe()))
1585                if (not isinstance(v.type, QAPISchemaObjectType)
1586                        or v.type.variants):
1587                    raise QAPISemError(
1588                        self.info,
1589                        "%s cannot use %s"
1590                        % (v.describe(self.info), v.type.describe()))
1591                v.type.check(schema)
1592
1593    def check_clash(self, info, seen):
1594        for v in self.variants:
1595            # Reset seen map for each variant, since qapi names from one
1596            # branch do not affect another branch
1597            v.type.check_clash(info, dict(seen))
1598
1599
1600class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1601    role = 'branch'
1602
1603    def __init__(self, name, info, typ, ifcond=None):
1604        QAPISchemaObjectTypeMember.__init__(self, name, info, typ,
1605                                            False, ifcond)
1606
1607
1608class QAPISchemaAlternateType(QAPISchemaType):
1609    meta = 'alternate'
1610
1611    def __init__(self, name, info, doc, ifcond, variants):
1612        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1613        assert isinstance(variants, QAPISchemaObjectTypeVariants)
1614        assert variants.tag_member
1615        variants.set_defined_in(name)
1616        variants.tag_member.set_defined_in(self.name)
1617        self.variants = variants
1618
1619    def check(self, schema):
1620        QAPISchemaType.check(self, schema)
1621        self.variants.tag_member.check(schema)
1622        # Not calling self.variants.check_clash(), because there's nothing
1623        # to clash with
1624        self.variants.check(schema, {})
1625        # Alternate branch names have no relation to the tag enum values;
1626        # so we have to check for potential name collisions ourselves.
1627        seen = {}
1628        types_seen = {}
1629        for v in self.variants.variants:
1630            v.check_clash(self.info, seen)
1631            qtype = v.type.alternate_qtype()
1632            if not qtype:
1633                raise QAPISemError(
1634                    self.info,
1635                    "%s cannot use %s"
1636                    % (v.describe(self.info), v.type.describe()))
1637            conflicting = set([qtype])
1638            if qtype == 'QTYPE_QSTRING':
1639                if isinstance(v.type, QAPISchemaEnumType):
1640                    for m in v.type.members:
1641                        if m.name in ['on', 'off']:
1642                            conflicting.add('QTYPE_QBOOL')
1643                        if re.match(r'[-+0-9.]', m.name):
1644                            # lazy, could be tightened
1645                            conflicting.add('QTYPE_QNUM')
1646                else:
1647                    conflicting.add('QTYPE_QNUM')
1648                    conflicting.add('QTYPE_QBOOL')
1649            for qt in conflicting:
1650                if qt in types_seen:
1651                    raise QAPISemError(
1652                        self.info,
1653                        "%s can't be distinguished from '%s'"
1654                        % (v.describe(self.info), types_seen[qt]))
1655                types_seen[qt] = v.name
1656            if self.doc:
1657                self.doc.connect_member(v)
1658        if self.doc:
1659            self.doc.check()
1660
1661    def c_type(self):
1662        return c_name(self.name) + pointer_suffix
1663
1664    def json_type(self):
1665        return 'value'
1666
1667    def visit(self, visitor):
1668        QAPISchemaType.visit(self, visitor)
1669        visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1670                                     self.variants)
1671
1672
1673class QAPISchemaCommand(QAPISchemaEntity):
1674    meta = 'command'
1675
1676    def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1677                 gen, success_response, boxed, allow_oob, allow_preconfig):
1678        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1679        assert not arg_type or isinstance(arg_type, str)
1680        assert not ret_type or isinstance(ret_type, str)
1681        self._arg_type_name = arg_type
1682        self.arg_type = None
1683        self._ret_type_name = ret_type
1684        self.ret_type = None
1685        self.gen = gen
1686        self.success_response = success_response
1687        self.boxed = boxed
1688        self.allow_oob = allow_oob
1689        self.allow_preconfig = allow_preconfig
1690
1691    def check(self, schema):
1692        QAPISchemaEntity.check(self, schema)
1693        if self._arg_type_name:
1694            self.arg_type = schema.resolve_type(
1695                self._arg_type_name, self.info, "command's 'data'")
1696            if not isinstance(self.arg_type, QAPISchemaObjectType):
1697                raise QAPISemError(
1698                    self.info,
1699                    "command's 'data' cannot take %s"
1700                    % self.arg_type.describe())
1701            if self.arg_type.variants and not self.boxed:
1702                raise QAPISemError(
1703                    self.info,
1704                    "command's 'data' can take %s only with 'boxed': true"
1705                    % self.arg_type.describe())
1706        if self._ret_type_name:
1707            self.ret_type = schema.resolve_type(
1708                self._ret_type_name, self.info, "command's 'returns'")
1709            if self.name not in returns_whitelist:
1710                if not (isinstance(self.ret_type, QAPISchemaObjectType)
1711                        or (isinstance(self.ret_type, QAPISchemaArrayType)
1712                            and isinstance(self.ret_type.element_type,
1713                                           QAPISchemaObjectType))):
1714                    raise QAPISemError(
1715                        self.info,
1716                        "command's 'returns' cannot take %s"
1717                        % self.ret_type.describe())
1718
1719    def visit(self, visitor):
1720        QAPISchemaEntity.visit(self, visitor)
1721        visitor.visit_command(self.name, self.info, self.ifcond,
1722                              self.arg_type, self.ret_type,
1723                              self.gen, self.success_response,
1724                              self.boxed, self.allow_oob,
1725                              self.allow_preconfig)
1726
1727
1728class QAPISchemaEvent(QAPISchemaEntity):
1729    meta = 'event'
1730
1731    def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1732        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1733        assert not arg_type or isinstance(arg_type, str)
1734        self._arg_type_name = arg_type
1735        self.arg_type = None
1736        self.boxed = boxed
1737
1738    def check(self, schema):
1739        QAPISchemaEntity.check(self, schema)
1740        if self._arg_type_name:
1741            self.arg_type = schema.resolve_type(
1742                self._arg_type_name, self.info, "event's 'data'")
1743            if not isinstance(self.arg_type, QAPISchemaObjectType):
1744                raise QAPISemError(
1745                    self.info,
1746                    "event's 'data' cannot take %s"
1747                    % self.arg_type.describe())
1748            if self.arg_type.variants and not self.boxed:
1749                raise QAPISemError(
1750                    self.info,
1751                    "event's 'data' can take %s only with 'boxed': true"
1752                    % self.arg_type.describe())
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