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