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