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