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