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