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