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