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