]> Git Repo - qemu.git/blame - scripts/qapi/common.py
qapi: Reorder check_FOO() parameters for consistency
[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 <[email protected]>
c7a3f252 9# Markus Armbruster <[email protected]>
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):
c9e0a798
EB
711 global valid_name
712 membername = name
713
714 if not isinstance(name, str):
4148c298 715 raise QAPISemError(info, "%s requires a string name" % source)
c9e0a798
EB
716 if name.startswith('*'):
717 membername = name[1:]
718 if not allow_optional:
4148c298
MAL
719 raise QAPISemError(info, "%s does not allow optional name '%s'"
720 % (source, name))
c9e0a798
EB
721 # Enum members can start with a digit, because the generated C
722 # code always prefixes it with the enum name
59a92fee
EB
723 if enum_member and membername[0].isdigit():
724 membername = 'D' + membername
7599697c
EB
725 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
726 # and 'q_obj_*' implicit type names.
9fb081e0
EB
727 if not valid_name.match(membername) or \
728 c_name(membername, False).startswith('q_'):
4148c298 729 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
638c4af9
MA
730 if not permit_upper and name.lower() != name:
731 raise QAPISemError(
732 info, "%s uses uppercase in name '%s'" % (source, name))
c9e0a798 733
437db254 734
e31fe126 735def add_name(name, info, meta):
00e4b285 736 global all_names
c9efc984 737 check_name(name, info, "'%s'" % meta, permit_upper=True)
d90675fa
MA
738 # FIXME should reject names that differ only in '_' vs. '.'
739 # vs. '-', because they're liable to clash in generated C.
00e4b285 740 if name in all_names:
4148c298
MAL
741 raise QAPISemError(info, "%s '%s' is already defined"
742 % (all_names[name], name))
e31fe126 743 if name.endswith('Kind') or name.endswith('List'):
4148c298
MAL
744 raise QAPISemError(info, "%s '%s' should not end in '%s'"
745 % (meta, name, name[-4:]))
00e4b285
MA
746 all_names[name] = meta
747
437db254 748
967c8851
MAL
749def check_if(expr, info):
750
751 def check_if_str(ifcond, info):
752 if not isinstance(ifcond, str):
753 raise QAPISemError(
754 info, "'if' condition must be a string or a list of strings")
c2c7065e
MA
755 if ifcond.strip() == '':
756 raise QAPISemError(info, "'if' condition '%s' makes no sense"
757 % ifcond)
967c8851
MAL
758
759 ifcond = expr.get('if')
760 if ifcond is None:
761 return
762 if isinstance(ifcond, list):
763 if ifcond == []:
764 raise QAPISemError(info, "'if' condition [] is useless")
765 for elt in ifcond:
766 check_if_str(elt, info)
767 else:
768 check_if_str(ifcond, info)
769
770
c9efc984 771def check_type(value, info, source,
dcca907b 772 allow_array=False, allow_dict=False, allow_metas=[]):
dd883c6f 773 global all_names
dd883c6f
EB
774
775 if value is None:
776 return
777
dd883c6f
EB
778 # Check if array type for value is okay
779 if isinstance(value, list):
780 if not allow_array:
4148c298 781 raise QAPISemError(info, "%s cannot be an array" % source)
dd883c6f 782 if len(value) != 1 or not isinstance(value[0], str):
4148c298
MAL
783 raise QAPISemError(info,
784 "%s: array type must contain single type name" %
785 source)
dd883c6f 786 value = value[0]
dd883c6f
EB
787
788 # Check if type name for value is okay
789 if isinstance(value, str):
437db254 790 if value not in all_names:
4148c298
MAL
791 raise QAPISemError(info, "%s uses unknown type '%s'"
792 % (source, value))
dd883c6f 793 if not all_names[value] in allow_metas:
4148c298
MAL
794 raise QAPISemError(info, "%s cannot use %s type '%s'" %
795 (source, all_names[value], value))
dd883c6f
EB
796 return
797
dd883c6f 798 if not allow_dict:
4148c298 799 raise QAPISemError(info, "%s should be a type name" % source)
c6b71e5a
MA
800
801 if not isinstance(value, OrderedDict):
4148c298 802 raise QAPISemError(info,
8d40738d 803 "%s should be an object or type name" % source)
c6b71e5a 804
638c4af9
MA
805 permit_upper = allow_dict in name_case_whitelist
806
c6b71e5a 807 # value is a dictionary, check that each member is okay
dd883c6f 808 for (key, arg) in value.items():
c9efc984 809 check_name(key, info, "member of %s" % source,
638c4af9 810 allow_optional=True, permit_upper=permit_upper)
5e59baf9 811 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
2ab218aa
MA
812 raise QAPISemError(
813 info, "member of %s uses reserved name '%s'" % (source, key))
c9efc984
MA
814 check_known_keys(arg, info, "member '%s' of %s" % (key, source),
815 ['type'], ['if'])
dec0012e 816 check_if(arg, info)
fe9c4dcf 817 normalize_if(arg)
c9efc984
MA
818 check_type(arg['type'], info, "member '%s' of %s" % (key, source),
819 allow_array=True,
dd883c6f 820 allow_metas=['built-in', 'union', 'alternate', 'struct',
6b5abc7d 821 'enum'])
dd883c6f 822
437db254 823
4148c298 824def check_command(expr, info):
dd883c6f 825 name = expr['command']
c818408e 826 boxed = expr.get('boxed', False)
2cbf0992 827
c818408e
EB
828 args_meta = ['struct']
829 if boxed:
b22e8658 830 args_meta += ['union']
c9efc984
MA
831 check_type(expr.get('data'), info,
832 "'data' for command '%s'" % name,
833 allow_dict=not boxed, allow_metas=args_meta)
10d4d997
EB
834 returns_meta = ['union', 'struct']
835 if name in returns_whitelist:
836 returns_meta += ['built-in', 'alternate', 'enum']
c9efc984
MA
837 check_type(expr.get('returns'), info,
838 "'returns' for command '%s'" % name,
839 allow_array=True, allow_metas=returns_meta)
dd883c6f 840
437db254 841
4148c298 842def check_event(expr, info):
4dc2e690 843 name = expr['event']
c818408e 844 boxed = expr.get('boxed', False)
4dc2e690 845
c818408e
EB
846 meta = ['struct']
847 if boxed:
b22e8658 848 meta += ['union']
c9efc984
MA
849 check_type(expr.get('data'), info,
850 "'data' for event '%s'" % name,
851 allow_dict=not boxed, allow_metas=meta)
21cd70df 852
437db254 853
ea738b21
MAL
854def enum_get_names(expr):
855 return [e['name'] for e in expr['data']]
856
857
4148c298 858def check_union(expr, info):
b86b05ed
WX
859 name = expr['union']
860 base = expr.get('base')
861 discriminator = expr.get('discriminator')
862 members = expr['data']
863
811d04fd 864 # Two types of unions, determined by discriminator.
811d04fd
EB
865
866 # With no discriminator it is a simple union.
867 if discriminator is None:
0ced9531 868 enum_values = members.keys()
437db254 869 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
44bd1276 870 if base is not None:
2ab218aa
MA
871 raise QAPISemError(
872 info, "simple union '%s' must not have a base" % name)
b86b05ed
WX
873
874 # Else, it's a flat union.
875 else:
ac4338f8 876 # The object must have a string or dictionary 'base'.
c9efc984
MA
877 check_type(base, info, "'base' for union '%s'" % name,
878 allow_dict=name, allow_metas=['struct'])
376863ef 879 if not base:
2ab218aa
MA
880 raise QAPISemError(
881 info, "flat union '%s' must have a base" % name)
14f00c6c 882 base_members = find_base_members(base)
48153745 883 assert base_members is not None
44bd1276 884
c9e0a798 885 # The value of member 'discriminator' must name a non-optional
fd41dd4e 886 # member of the base struct.
c9efc984
MA
887 check_name(discriminator, info,
888 "discriminator of flat union '%s'" % name)
87adbbff
MAL
889 discriminator_value = base_members.get(discriminator)
890 if not discriminator_value:
4148c298 891 raise QAPISemError(info,
2ab218aa 892 "discriminator '%s' is not a member of 'base'"
887a2069 893 % discriminator)
ccadd6bc 894 if discriminator_value.get('if'):
9f5e6b08
MA
895 raise QAPISemError(
896 info,
2ab218aa 897 "the discriminator '%s' for union %s must not be conditional"
887a2069 898 % (discriminator, name))
87adbbff 899 enum_define = enum_types.get(discriminator_value['type'])
5223070c
WX
900 # Do not allow string discriminator
901 if not enum_define:
2ab218aa
MA
902 raise QAPISemError(
903 info,
904 "discriminator '%s' must be of enumeration type"
905 % discriminator)
0ced9531
MA
906 enum_values = enum_get_names(enum_define)
907 allow_metas = ['struct']
908
909 if (len(enum_values) == 0):
2ab218aa 910 raise QAPISemError(info, "union '%s' has no branches" % name)
b86b05ed 911
b86b05ed 912 for (key, value) in members.items():
c9efc984 913 check_name(key, info, "member of union '%s'" % name)
c9e0a798 914
c9efc984
MA
915 check_known_keys(value, info,
916 "member '%s' of union '%s'" % (key, name),
917 ['type'], ['if'])
dec0012e 918 check_if(value, info)
fe9c4dcf 919 normalize_if(value)
01cfbaa4 920 # Each value must name a known type
c9efc984
MA
921 check_type(value['type'], info,
922 "member '%s' of union '%s'" % (key, name),
87adbbff 923 allow_array=not base, allow_metas=allow_metas)
dd883c6f 924
44bd1276 925 # If the discriminator names an enum type, then all members
61a94661 926 # of 'data' must also be members of the enum type.
0ced9531
MA
927 if discriminator is not None:
928 if key not in enum_values:
2ab218aa
MA
929 raise QAPISemError(
930 info,
931 "discriminator value '%s' is not found in enum '%s'"
932 % (key, enum_define['enum']))
44bd1276 933
437db254 934
4148c298 935def check_alternate(expr, info):
ab916fad 936 name = expr['alternate']
811d04fd 937 members = expr['data']
811d04fd
EB
938 types_seen = {}
939
f0325536 940 if len(members) == 0:
4148c298 941 raise QAPISemError(info,
2ab218aa 942 "alternate '%s' cannot have empty 'data'" % name)
811d04fd 943 for (key, value) in members.items():
c9efc984
MA
944 check_name(key, info, "member of alternate '%s'" % name)
945 check_known_keys(value, info,
87adbbff 946 "member '%s' of alternate '%s'" % (key, name),
c9efc984 947 ['type'], ['if'])
dec0012e 948 check_if(value, info)
fe9c4dcf 949 normalize_if(value)
87adbbff 950 typ = value['type']
c9e0a798 951
811d04fd 952 # Ensure alternates have no type conflicts.
c9efc984 953 check_type(typ, info, "member '%s' of alternate '%s'" % (key, name),
dd883c6f 954 allow_metas=['built-in', 'union', 'struct', 'enum'])
87adbbff 955 qtype = find_alternate_member_qtype(typ)
46534309 956 if not qtype:
2ab218aa
MA
957 raise QAPISemError(
958 info,
959 "alternate '%s' member '%s' cannot use type '%s'"
960 % (name, key, typ))
c0644771
MA
961 conflicting = set([qtype])
962 if qtype == 'QTYPE_QSTRING':
87adbbff 963 enum_expr = enum_types.get(typ)
c0644771 964 if enum_expr:
ea738b21 965 for v in enum_get_names(enum_expr):
c0644771
MA
966 if v in ['on', 'off']:
967 conflicting.add('QTYPE_QBOOL')
968 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
01b2ffce 969 conflicting.add('QTYPE_QNUM')
c0644771 970 else:
01b2ffce 971 conflicting.add('QTYPE_QNUM')
c0644771 972 conflicting.add('QTYPE_QBOOL')
c0644771 973 for qt in conflicting:
fda72ab4 974 if qt in types_seen:
2ab218aa
MA
975 raise QAPISemError(
976 info,
977 "alternate '%s' member '%s' can't be distinguished "
978 "from member '%s'"
979 % (name, key, types_seen[qt]))
c0644771 980 types_seen[qt] = key
b86b05ed 981
437db254 982
4148c298 983def check_enum(expr, info):
cf393590 984 name = expr['enum']
ea738b21 985 members = expr['data']
351d36e4 986 prefix = expr.get('prefix')
cf393590
EB
987
988 if not isinstance(members, list):
4148c298 989 raise QAPISemError(info,
2ab218aa 990 "enum '%s' requires an array for 'data'" % name)
351d36e4 991 if prefix is not None and not isinstance(prefix, str):
4148c298 992 raise QAPISemError(info,
2ab218aa 993 "enum '%s' requires a string for 'prefix'" % name)
ea738b21 994
638c4af9
MA
995 permit_upper = name in name_case_whitelist
996
cf393590 997 for member in members:
c9efc984 998 check_known_keys(member, info, "member of enum '%s'" % name,
8d40738d 999 ['name'], ['if'])
6cc32b0e 1000 check_if(member, info)
fe9c4dcf 1001 normalize_if(member)
c9efc984 1002 check_name(member['name'], info, "member of enum '%s'" % name,
638c4af9 1003 enum_member=True, permit_upper=permit_upper)
cf393590 1004
437db254 1005
4148c298 1006def check_struct(expr, info):
fd41dd4e 1007 name = expr['struct']
dd883c6f 1008 members = expr['data']
6a8c0b51 1009 features = expr.get('features')
dd883c6f 1010
c9efc984 1011 check_type(members, info, "'data' for struct '%s'" % name,
638c4af9 1012 allow_dict=name)
c9efc984 1013 check_type(expr.get('base'), info, "'base' for struct '%s'" % name,
dd883c6f
EB
1014 allow_metas=['struct'])
1015
6a8c0b51
KW
1016 if features:
1017 if not isinstance(features, list):
2ab218aa
MA
1018 raise QAPISemError(
1019 info, "struct '%s' requires an array for 'features'" % name)
6a8c0b51
KW
1020 for f in features:
1021 assert isinstance(f, dict)
c9efc984 1022 check_known_keys(f, info, "feature of struct %s" % name,
6a8c0b51
KW
1023 ['name'], ['if'])
1024
1025 check_if(f, info)
fe9c4dcf 1026 normalize_if(f)
c9efc984 1027 check_name(f['name'], info, "feature of struct %s" % name)
6a8c0b51 1028
437db254 1029
c9efc984 1030def check_known_keys(value, info, source, required, optional):
7e80d480
MAL
1031
1032 def pprint(elems):
1033 return ', '.join("'" + e + "'" for e in sorted(elems))
1034
69553976 1035 missing = set(required) - set(value)
7e80d480 1036 if missing:
2ab218aa
MA
1037 raise QAPISemError(
1038 info,
1039 "key%s %s %s missing from %s"
1040 % ('s' if len(missing) > 1 else '', pprint(missing),
1041 'are' if len(missing) > 1 else 'is', source))
7e80d480 1042 allowed = set(required + optional)
69553976 1043 unknown = set(value) - allowed
7e80d480 1044 if unknown:
2ab218aa
MA
1045 raise QAPISemError(
1046 info,
1047 "unknown key%s %s in %s\nValid keys are %s."
1048 % ('s' if len(unknown) > 1 else '', pprint(unknown),
1049 source, pprint(allowed)))
563bd35d
MAL
1050
1051
dc234189 1052def check_keys(expr, info, meta, required, optional=[]):
0545f6b8
EB
1053 name = expr[meta]
1054 if not isinstance(name, str):
4148c298 1055 raise QAPISemError(info, "'%s' key must have a string value" % meta)
437db254 1056 required = required + [meta]
563bd35d 1057 source = "%s '%s'" % (meta, name)
c9efc984 1058 check_known_keys(expr, info, source, required, optional)
0545f6b8 1059 for (key, value) in expr.items():
b736e25a 1060 if key in ['gen', 'success-response'] and value is not False:
4148c298
MAL
1061 raise QAPISemError(info,
1062 "'%s' of %s '%s' should only use false value"
1063 % (key, meta, name))
b736e25a
MA
1064 if (key in ['boxed', 'allow-oob', 'allow-preconfig']
1065 and value is not True):
4148c298
MAL
1066 raise QAPISemError(info,
1067 "'%s' of %s '%s' should only use true value"
1068 % (key, meta, name))
967c8851
MAL
1069 if key == 'if':
1070 check_if(expr, info)
0545f6b8 1071
437db254 1072
87adbbff
MAL
1073def normalize_enum(expr):
1074 if isinstance(expr['data'], list):
1075 expr['data'] = [m if isinstance(m, dict) else {'name': m}
1076 for m in expr['data']]
1077
1078
1079def normalize_members(members):
1080 if isinstance(members, OrderedDict):
1081 for key, arg in members.items():
1082 if isinstance(arg, dict):
1083 continue
1084 members[key] = {'type': arg}
1085
1086
6a8c0b51
KW
1087def normalize_features(features):
1088 if isinstance(features, list):
1089 features[:] = [f if isinstance(f, dict) else {'name': f}
1090 for f in features]
1091
1092
fe9c4dcf
MA
1093def normalize_if(expr):
1094 ifcond = expr.get('if')
1095 if isinstance(ifcond, str):
1096 expr['if'] = [ifcond]
1097
1098
4d076d67 1099def check_exprs(exprs):
4dc2e690 1100 global all_names
4dc2e690 1101
7947016d 1102 # Populate name table with names of built-in types
4d076d67
MA
1103 for builtin in builtin_types.keys():
1104 all_names[builtin] = 'built-in'
7947016d
MA
1105
1106 # Learn the types and check for valid expression keys
4d076d67
MA
1107 for expr_elem in exprs:
1108 expr = expr_elem['expr']
1109 info = expr_elem['info']
7947016d 1110 doc = expr_elem.get('doc')
3313b612 1111
97f02494
MA
1112 if 'include' in expr:
1113 continue
1114
7947016d 1115 if not doc and doc_required:
3313b612 1116 raise QAPISemError(info,
2ab218aa 1117 "definition missing documentation comment")
3313b612 1118
437db254 1119 if 'enum' in expr:
6f05345f 1120 meta = 'enum'
dc234189 1121 check_keys(expr, info, 'enum', ['data'], ['if', 'prefix'])
ea738b21 1122 normalize_enum(expr)
5f018446 1123 enum_types[expr[meta]] = expr
437db254 1124 elif 'union' in expr:
6f05345f 1125 meta = 'union'
dc234189 1126 check_keys(expr, info, 'union', ['data'],
967c8851 1127 ['base', 'discriminator', 'if'])
87adbbff
MAL
1128 normalize_members(expr.get('base'))
1129 normalize_members(expr['data'])
768562de 1130 union_types[expr[meta]] = expr
437db254 1131 elif 'alternate' in expr:
6f05345f 1132 meta = 'alternate'
dc234189 1133 check_keys(expr, info, 'alternate', ['data'], ['if'])
87adbbff 1134 normalize_members(expr['data'])
437db254 1135 elif 'struct' in expr:
6f05345f 1136 meta = 'struct'
dc234189 1137 check_keys(expr, info, 'struct', ['data'],
6a8c0b51 1138 ['base', 'if', 'features'])
87adbbff 1139 normalize_members(expr['data'])
6a8c0b51 1140 normalize_features(expr.get('features'))
ed285bf8 1141 struct_types[expr[meta]] = expr
437db254 1142 elif 'command' in expr:
6f05345f 1143 meta = 'command'
dc234189 1144 check_keys(expr, info, 'command', [],
876c6751 1145 ['data', 'returns', 'gen', 'success-response',
967c8851 1146 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
87adbbff 1147 normalize_members(expr.get('data'))
437db254 1148 elif 'event' in expr:
6f05345f 1149 meta = 'event'
dc234189 1150 check_keys(expr, info, 'event', [], ['data', 'boxed', 'if'])
87adbbff 1151 normalize_members(expr.get('data'))
4d076d67 1152 else:
2ab218aa 1153 raise QAPISemError(info, "expression is missing metatype")
fe9c4dcf 1154 normalize_if(expr)
6f05345f
MA
1155 name = expr[meta]
1156 add_name(name, info, meta)
7be6c511 1157 info.set_defn(meta, name)
7947016d 1158 if doc and doc.symbol != name:
2ab218aa
MA
1159 raise QAPISemError(
1160 info,
1161 "definition of '%s' follows documentation for '%s'"
1162 % (name, doc.symbol))
2caba36c 1163
4d076d67
MA
1164 # Validate that exprs make sense
1165 for expr_elem in exprs:
1166 expr = expr_elem['expr']
1167 info = expr_elem['info']
a9f396b0 1168 doc = expr_elem.get('doc')
268a1c5e 1169
97f02494
MA
1170 if 'include' in expr:
1171 continue
437db254 1172 if 'enum' in expr:
4d076d67 1173 check_enum(expr, info)
437db254 1174 elif 'union' in expr:
4d076d67 1175 check_union(expr, info)
437db254 1176 elif 'alternate' in expr:
4d076d67 1177 check_alternate(expr, info)
437db254 1178 elif 'struct' in expr:
4d076d67 1179 check_struct(expr, info)
437db254 1180 elif 'command' in expr:
4d076d67 1181 check_command(expr, info)
437db254 1182 elif 'event' in expr:
4d076d67
MA
1183 check_event(expr, info)
1184 else:
1185 assert False, 'unexpected meta type'
1186
a9f396b0
MA
1187 if doc:
1188 doc.check_expr(expr)
ac88219a 1189
a9f396b0 1190 return exprs
3313b612
MAL
1191
1192
ac88219a
MA
1193#
1194# Schema compiler frontend
1195#
1196
1197class QAPISchemaEntity(object):
2cbc9437 1198 def __init__(self, name, info, doc, ifcond=None):
cf40a0a5 1199 assert name is None or isinstance(name, str)
ac88219a 1200 self.name = name
f9d1743b 1201 self._module = None
99df5289
EB
1202 # For explicitly defined entities, info points to the (explicit)
1203 # definition. For builtins (and their arrays), info is None.
1204 # For implicitly defined entities, info points to a place that
1205 # triggered the implicit definition (there may be more than one
1206 # such place).
ac88219a 1207 self.info = info
069fb5b2 1208 self.doc = doc
fe9c4dcf 1209 self._ifcond = ifcond or []
f9d1743b 1210 self._checked = False
ac88219a 1211
f51d8c3d
MA
1212 def c_name(self):
1213 return c_name(self.name)
1214
ac88219a 1215 def check(self, schema):
f9d1743b 1216 assert not self._checked
56a46895 1217 if self.info:
19e950d9 1218 self._module = os.path.relpath(self.info.fname,
f9d1743b
MA
1219 os.path.dirname(schema.fname))
1220 self._checked = True
1221
1222 @property
1223 def ifcond(self):
1224 assert self._checked
1225 return self._ifcond
1226
1227 @property
1228 def module(self):
1229 assert self._checked
1230 return self._module
ac88219a 1231
49823c4b
EB
1232 def is_implicit(self):
1233 return not self.info
1234
3f7dc21b 1235 def visit(self, visitor):
56176412 1236 assert self._checked
3f7dc21b
MA
1237
1238
1239class QAPISchemaVisitor(object):
1240 def visit_begin(self, schema):
1241 pass
1242
1243 def visit_end(self):
1244 pass
1245
cf40a0a5
MA
1246 def visit_module(self, fname):
1247 pass
1248
25a0d9c9
EB
1249 def visit_needed(self, entity):
1250 # Default to visiting everything
1251 return True
1252
cf40a0a5
MA
1253 def visit_include(self, fname, info):
1254 pass
1255
3f7dc21b
MA
1256 def visit_builtin_type(self, name, info, json_type):
1257 pass
1258
1962bd39 1259 def visit_enum_type(self, name, info, ifcond, members, prefix):
3f7dc21b
MA
1260 pass
1261
fbf09a2f 1262 def visit_array_type(self, name, info, ifcond, element_type):
3f7dc21b
MA
1263 pass
1264
6a8c0b51
KW
1265 def visit_object_type(self, name, info, ifcond, base, members, variants,
1266 features):
3f7dc21b
MA
1267 pass
1268
6a8c0b51
KW
1269 def visit_object_type_flat(self, name, info, ifcond, members, variants,
1270 features):
39a18158
MA
1271 pass
1272
fbf09a2f 1273 def visit_alternate_type(self, name, info, ifcond, variants):
3f7dc21b
MA
1274 pass
1275
fbf09a2f 1276 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
d6fe3d02 1277 success_response, boxed, allow_oob, allow_preconfig):
3f7dc21b
MA
1278 pass
1279
fbf09a2f 1280 def visit_event(self, name, info, ifcond, arg_type, boxed):
3f7dc21b
MA
1281 pass
1282
ac88219a 1283
cf40a0a5
MA
1284class QAPISchemaInclude(QAPISchemaEntity):
1285
1286 def __init__(self, fname, info):
1287 QAPISchemaEntity.__init__(self, None, info, None)
1288 self.fname = fname
1289
1290 def visit(self, visitor):
56176412 1291 QAPISchemaEntity.visit(self, visitor)
cf40a0a5
MA
1292 visitor.visit_include(self.fname, self.info)
1293
1294
ac88219a 1295class QAPISchemaType(QAPISchemaEntity):
4040d995
EB
1296 # Return the C type for common use.
1297 # For the types we commonly box, this is a pointer type.
1298 def c_type(self):
1299 pass
1300
1301 # Return the C type to be used in a parameter list.
1302 def c_param_type(self):
1303 return self.c_type()
1304
1305 # Return the C type to be used where we suppress boxing.
1306 def c_unboxed_type(self):
1307 return self.c_type()
f51d8c3d 1308
f51d8c3d
MA
1309 def json_type(self):
1310 pass
1311
1312 def alternate_qtype(self):
1313 json2qtype = {
4d2d5c41 1314 'null': 'QTYPE_QNULL',
f51d8c3d 1315 'string': 'QTYPE_QSTRING',
01b2ffce
MAL
1316 'number': 'QTYPE_QNUM',
1317 'int': 'QTYPE_QNUM',
f51d8c3d
MA
1318 'boolean': 'QTYPE_QBOOL',
1319 'object': 'QTYPE_QDICT'
1320 }
1321 return json2qtype.get(self.json_type())
ac88219a 1322
691e0313
MA
1323 def doc_type(self):
1324 if self.is_implicit():
1325 return None
1326 return self.name
1327
ac88219a
MA
1328
1329class QAPISchemaBuiltinType(QAPISchemaType):
861877a0 1330 def __init__(self, name, json_type, c_type):
069fb5b2 1331 QAPISchemaType.__init__(self, name, None, None)
f51d8c3d
MA
1332 assert not c_type or isinstance(c_type, str)
1333 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1334 'value')
1335 self._json_type_name = json_type
1336 self._c_type_name = c_type
f51d8c3d
MA
1337
1338 def c_name(self):
1339 return self.name
1340
4040d995
EB
1341 def c_type(self):
1342 return self._c_type_name
1343
1344 def c_param_type(self):
1345 if self.name == 'str':
f51d8c3d
MA
1346 return 'const ' + self._c_type_name
1347 return self._c_type_name
1348
f51d8c3d
MA
1349 def json_type(self):
1350 return self._json_type_name
ac88219a 1351
691e0313
MA
1352 def doc_type(self):
1353 return self.json_type()
1354
3f7dc21b 1355 def visit(self, visitor):
56176412 1356 QAPISchemaType.visit(self, visitor)
3f7dc21b
MA
1357 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1358
ac88219a
MA
1359
1360class QAPISchemaEnumType(QAPISchemaType):
57516863 1361 def __init__(self, name, info, doc, ifcond, members, prefix):
2cbc9437 1362 QAPISchemaType.__init__(self, name, info, doc, ifcond)
57516863 1363 for m in members:
398969fe 1364 assert isinstance(m, QAPISchemaEnumMember)
57608a52 1365 m.set_defined_in(name)
ac88219a 1366 assert prefix is None or isinstance(prefix, str)
57516863 1367 self.members = members
ac88219a
MA
1368 self.prefix = prefix
1369
1370 def check(self, schema):
4fca21c1 1371 QAPISchemaType.check(self, schema)
93bda4dd 1372 seen = {}
57516863
MAL
1373 for m in self.members:
1374 m.check_clash(self.info, seen)
069fb5b2 1375 if self.doc:
57516863 1376 self.doc.connect_member(m)
ac88219a 1377
99df5289 1378 def is_implicit(self):
4636211e
MA
1379 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1380 return self.name.endswith('Kind') or self.name == 'QType'
99df5289 1381
4040d995 1382 def c_type(self):
f51d8c3d
MA
1383 return c_name(self.name)
1384
93bda4dd 1385 def member_names(self):
57516863 1386 return [m.name for m in self.members]
93bda4dd 1387
f51d8c3d
MA
1388 def json_type(self):
1389 return 'string'
1390
3f7dc21b 1391 def visit(self, visitor):
56176412 1392 QAPISchemaType.visit(self, visitor)
fbf09a2f 1393 visitor.visit_enum_type(self.name, self.info, self.ifcond,
1962bd39 1394 self.members, self.prefix)
3f7dc21b 1395
ac88219a
MA
1396
1397class QAPISchemaArrayType(QAPISchemaType):
1398 def __init__(self, name, info, element_type):
2cbc9437 1399 QAPISchemaType.__init__(self, name, info, None, None)
ac88219a
MA
1400 assert isinstance(element_type, str)
1401 self._element_type_name = element_type
1402 self.element_type = None
1403
1404 def check(self, schema):
4fca21c1 1405 QAPISchemaType.check(self, schema)
ac88219a
MA
1406 self.element_type = schema.lookup_type(self._element_type_name)
1407 assert self.element_type
0ca7b117 1408 assert not isinstance(self.element_type, QAPISchemaArrayType)
f9d1743b
MA
1409
1410 @property
1411 def ifcond(self):
1412 assert self._checked
1413 return self.element_type.ifcond
1414
1415 @property
1416 def module(self):
1417 assert self._checked
1418 return self.element_type.module
ac88219a 1419
99df5289
EB
1420 def is_implicit(self):
1421 return True
1422
4040d995
EB
1423 def c_type(self):
1424 return c_name(self.name) + pointer_suffix
1425
f51d8c3d
MA
1426 def json_type(self):
1427 return 'array'
1428
691e0313
MA
1429 def doc_type(self):
1430 elt_doc_type = self.element_type.doc_type()
1431 if not elt_doc_type:
1432 return None
1433 return 'array of ' + elt_doc_type
1434
3f7dc21b 1435 def visit(self, visitor):
56176412 1436 QAPISchemaType.visit(self, visitor)
fbf09a2f
MAL
1437 visitor.visit_array_type(self.name, self.info, self.ifcond,
1438 self.element_type)
3f7dc21b 1439
ac88219a
MA
1440
1441class QAPISchemaObjectType(QAPISchemaType):
2cbc9437 1442 def __init__(self, name, info, doc, ifcond,
6a8c0b51 1443 base, local_members, variants, features):
da34a9bd
EB
1444 # struct has local_members, optional base, and no variants
1445 # flat union has base, variants, and no local_members
1446 # simple union has local_members, variants, and no base
2cbc9437 1447 QAPISchemaType.__init__(self, name, info, doc, ifcond)
ac88219a
MA
1448 assert base is None or isinstance(base, str)
1449 for m in local_members:
1450 assert isinstance(m, QAPISchemaObjectTypeMember)
57608a52 1451 m.set_defined_in(name)
88d4ef8b
EB
1452 if variants is not None:
1453 assert isinstance(variants, QAPISchemaObjectTypeVariants)
57608a52 1454 variants.set_defined_in(name)
6a8c0b51
KW
1455 for f in features:
1456 assert isinstance(f, QAPISchemaFeature)
57608a52 1457 f.set_defined_in(name)
ac88219a
MA
1458 self._base_name = base
1459 self.base = None
1460 self.local_members = local_members
1461 self.variants = variants
1462 self.members = None
6a8c0b51 1463 self.features = features
ac88219a
MA
1464
1465 def check(self, schema):
f9d1743b
MA
1466 # This calls another type T's .check() exactly when the C
1467 # struct emitted by gen_object() contains that T's C struct
1468 # (pointers don't count).
1469 if self.members is not None:
1470 # A previous .check() completed: nothing to do
1471 return
1472 if self._checked:
1473 # Recursed: C struct contains itself
4148c298 1474 raise QAPISemError(self.info,
2ab218aa 1475 "object %s contains itself" % self.name)
f9d1743b
MA
1476
1477 QAPISchemaType.check(self, schema)
1478 assert self._checked and self.members is None
1479
23a4b2c6 1480 seen = OrderedDict()
ac88219a
MA
1481 if self._base_name:
1482 self.base = schema.lookup_type(self._base_name)
1483 assert isinstance(self.base, QAPISchemaObjectType)
ac88219a 1484 self.base.check(schema)
6bbfb12d 1485 self.base.check_clash(self.info, seen)
ac88219a 1486 for m in self.local_members:
e564e2dd 1487 m.check(schema)
27b60ab9 1488 m.check_clash(self.info, seen)
069fb5b2
MA
1489 if self.doc:
1490 self.doc.connect_member(m)
f9d1743b
MA
1491 members = seen.values()
1492
ac88219a 1493 if self.variants:
cdc5fa37 1494 self.variants.check(schema, seen)
f9d1743b 1495 assert self.variants.tag_member in members
6bbfb12d 1496 self.variants.check_clash(self.info, seen)
6a8c0b51
KW
1497
1498 # Features are in a name space separate from members
1499 seen = {}
1500 for f in self.features:
1501 f.check_clash(self.info, seen)
1502
816a57cd
MA
1503 if self.doc:
1504 self.doc.check()
ac88219a 1505
f9d1743b
MA
1506 self.members = members # mark completed
1507
14f00c6c 1508 # Check that the members of this type do not cause duplicate JSON members,
27b60ab9
EB
1509 # and update seen to track the members seen so far. Report any errors
1510 # on behalf of info, which is not necessarily self.info
6bbfb12d 1511 def check_clash(self, info, seen):
56176412 1512 assert self._checked
c2183d2e
EB
1513 assert not self.variants # not implemented
1514 for m in self.members:
27b60ab9 1515 m.check_clash(info, seen)
c2183d2e 1516
f9d1743b
MA
1517 @property
1518 def ifcond(self):
1519 assert self._checked
1520 if isinstance(self._ifcond, QAPISchemaType):
1521 # Simple union wrapper type inherits from wrapped type;
1522 # see _make_implicit_object_type()
1523 return self._ifcond.ifcond
1524 return self._ifcond
1525
99df5289 1526 def is_implicit(self):
7599697c
EB
1527 # See QAPISchema._make_implicit_object_type(), as well as
1528 # _def_predefineds()
1529 return self.name.startswith('q_')
99df5289 1530
b6167706
EB
1531 def is_empty(self):
1532 assert self.members is not None
1533 return not self.members and not self.variants
1534
f51d8c3d 1535 def c_name(self):
cd50a256 1536 assert self.name != 'q_empty'
f51d8c3d
MA
1537 return QAPISchemaType.c_name(self)
1538
4040d995 1539 def c_type(self):
49823c4b 1540 assert not self.is_implicit()
becceedc 1541 return c_name(self.name) + pointer_suffix
f51d8c3d 1542
4040d995 1543 def c_unboxed_type(self):
4040d995
EB
1544 return c_name(self.name)
1545
f51d8c3d
MA
1546 def json_type(self):
1547 return 'object'
1548
3f7dc21b 1549 def visit(self, visitor):
56176412 1550 QAPISchemaType.visit(self, visitor)
fbf09a2f 1551 visitor.visit_object_type(self.name, self.info, self.ifcond,
6a8c0b51
KW
1552 self.base, self.local_members, self.variants,
1553 self.features)
fbf09a2f 1554 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
6a8c0b51
KW
1555 self.members, self.variants,
1556 self.features)
3f7dc21b 1557
ac88219a 1558
d44f9ac8 1559class QAPISchemaMember(object):
6a8c0b51 1560 """ Represents object members, enum members and features """
88d4ef8b
EB
1561 role = 'member'
1562
6cc32b0e 1563 def __init__(self, name, ifcond=None):
ac88219a 1564 assert isinstance(name, str)
ac88219a 1565 self.name = name
fe9c4dcf 1566 self.ifcond = ifcond or []
57608a52 1567 self.defined_in = None
88d4ef8b 1568
57608a52
MA
1569 def set_defined_in(self, name):
1570 assert not self.defined_in
1571 self.defined_in = name
ac88219a 1572
27b60ab9
EB
1573 def check_clash(self, info, seen):
1574 cname = c_name(self.name)
1575 if cname in seen:
481a6bd1
MA
1576 raise QAPISemError(
1577 info,
1578 "%s collides with %s"
1579 % (self.describe(info), seen[cname].describe(info)))
27b60ab9 1580 seen[cname] = self
577de12d 1581
481a6bd1
MA
1582 def describe(self, info):
1583 role = self.role
57608a52 1584 defined_in = self.defined_in
481a6bd1
MA
1585 assert defined_in
1586
57608a52 1587 if defined_in.startswith('q_obj_'):
88d4ef8b
EB
1588 # See QAPISchema._make_implicit_object_type() - reverse the
1589 # mapping there to create a nice human-readable description
57608a52
MA
1590 defined_in = defined_in[6:]
1591 if defined_in.endswith('-arg'):
481a6bd1
MA
1592 # Implicit type created for a command's dict 'data'
1593 assert role == 'member'
1594 role = 'parameter'
57608a52 1595 elif defined_in.endswith('-base'):
481a6bd1
MA
1596 # Implicit type created for a flat union's dict 'base'
1597 role = 'base ' + role
88d4ef8b 1598 else:
481a6bd1 1599 # Implicit type created for a simple union's branch
57608a52 1600 assert defined_in.endswith('-wrapper')
88d4ef8b
EB
1601 # Unreachable and not implemented
1602 assert False
481a6bd1 1603 elif defined_in.endswith('Kind'):
93bda4dd 1604 # See QAPISchema._make_implicit_enum_type()
481a6bd1
MA
1605 # Implicit enum created for simple union's branches
1606 assert role == 'value'
1607 role = 'branch'
1608 elif defined_in != info.defn_name:
1609 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
1610 return "%s '%s'" % (role, self.name)
88d4ef8b 1611
ac88219a 1612
398969fe
MA
1613class QAPISchemaEnumMember(QAPISchemaMember):
1614 role = 'value'
1615
1616
6a8c0b51
KW
1617class QAPISchemaFeature(QAPISchemaMember):
1618 role = 'feature'
1619
1620
d44f9ac8 1621class QAPISchemaObjectTypeMember(QAPISchemaMember):
ccadd6bc
MAL
1622 def __init__(self, name, typ, optional, ifcond=None):
1623 QAPISchemaMember.__init__(self, name, ifcond)
d44f9ac8
EB
1624 assert isinstance(typ, str)
1625 assert isinstance(optional, bool)
1626 self._type_name = typ
1627 self.type = None
1628 self.optional = optional
1629
1630 def check(self, schema):
57608a52 1631 assert self.defined_in
d44f9ac8
EB
1632 self.type = schema.lookup_type(self._type_name)
1633 assert self.type
1634
1635
ac88219a 1636class QAPISchemaObjectTypeVariants(object):
46292ba7
EB
1637 def __init__(self, tag_name, tag_member, variants):
1638 # Flat unions pass tag_name but not tag_member.
1639 # Simple unions and alternates pass tag_member but not tag_name.
1640 # After check(), tag_member is always set, and tag_name remains
1641 # a reliable witness of being used by a flat union.
1642 assert bool(tag_member) != bool(tag_name)
1643 assert (isinstance(tag_name, str) or
1644 isinstance(tag_member, QAPISchemaObjectTypeMember))
ac88219a
MA
1645 for v in variants:
1646 assert isinstance(v, QAPISchemaObjectTypeVariant)
da9cb193 1647 self._tag_name = tag_name
46292ba7 1648 self.tag_member = tag_member
ac88219a
MA
1649 self.variants = variants
1650
57608a52 1651 def set_defined_in(self, name):
88d4ef8b 1652 for v in self.variants:
57608a52 1653 v.set_defined_in(name)
88d4ef8b 1654
cdc5fa37 1655 def check(self, schema, seen):
14ff8461 1656 if not self.tag_member: # flat union
da9cb193
EB
1657 self.tag_member = seen[c_name(self._tag_name)]
1658 assert self._tag_name == self.tag_member.name
ac88219a 1659 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
0ca7b117
MA
1660 assert not self.tag_member.optional
1661 assert self.tag_member.ifcond == []
800877bb
AN
1662 if self._tag_name: # flat union
1663 # branches that are not explicitly covered get an empty type
1664 cases = set([v.name for v in self.variants])
57516863
MAL
1665 for m in self.tag_member.type.members:
1666 if m.name not in cases:
ce1a1aec
MAL
1667 v = QAPISchemaObjectTypeVariant(m.name, 'q_empty',
1668 m.ifcond)
57608a52 1669 v.set_defined_in(self.tag_member.defined_in)
800877bb 1670 self.variants.append(v)
0ca7b117 1671 assert self.variants
ac88219a 1672 for v in self.variants:
10565ca9 1673 v.check(schema)
0426d53c
EB
1674 # Union names must match enum values; alternate names are
1675 # checked separately. Use 'seen' to tell the two apart.
1676 if seen:
93bda4dd 1677 assert v.name in self.tag_member.type.member_names()
0ca7b117
MA
1678 assert (isinstance(v.type, QAPISchemaObjectType)
1679 and not v.type.variants)
b807a1e1
EB
1680 v.type.check(schema)
1681
6bbfb12d 1682 def check_clash(self, info, seen):
b807a1e1
EB
1683 for v in self.variants:
1684 # Reset seen map for each variant, since qapi names from one
1685 # branch do not affect another branch
6bbfb12d 1686 v.type.check_clash(info, dict(seen))
ac88219a 1687
437db254 1688
ac88219a 1689class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
88d4ef8b
EB
1690 role = 'branch'
1691
a2724280
MAL
1692 def __init__(self, name, typ, ifcond=None):
1693 QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
ac88219a 1694
ac88219a
MA
1695
1696class QAPISchemaAlternateType(QAPISchemaType):
2cbc9437
MAL
1697 def __init__(self, name, info, doc, ifcond, variants):
1698 QAPISchemaType.__init__(self, name, info, doc, ifcond)
ac88219a 1699 assert isinstance(variants, QAPISchemaObjectTypeVariants)
da9cb193 1700 assert variants.tag_member
57608a52
MA
1701 variants.set_defined_in(name)
1702 variants.tag_member.set_defined_in(self.name)
ac88219a
MA
1703 self.variants = variants
1704
1705 def check(self, schema):
4fca21c1 1706 QAPISchemaType.check(self, schema)
e564e2dd 1707 self.variants.tag_member.check(schema)
b807a1e1
EB
1708 # Not calling self.variants.check_clash(), because there's nothing
1709 # to clash with
cdc5fa37 1710 self.variants.check(schema, {})
0426d53c
EB
1711 # Alternate branch names have no relation to the tag enum values;
1712 # so we have to check for potential name collisions ourselves.
1713 seen = {}
1714 for v in self.variants.variants:
1715 v.check_clash(self.info, seen)
0ca7b117 1716 # TODO check conflicting qtypes
069fb5b2
MA
1717 if self.doc:
1718 self.doc.connect_member(v)
816a57cd
MA
1719 if self.doc:
1720 self.doc.check()
ac88219a 1721
4040d995
EB
1722 def c_type(self):
1723 return c_name(self.name) + pointer_suffix
1724
f51d8c3d
MA
1725 def json_type(self):
1726 return 'value'
1727
3f7dc21b 1728 def visit(self, visitor):
56176412 1729 QAPISchemaType.visit(self, visitor)
fbf09a2f
MAL
1730 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1731 self.variants)
3f7dc21b 1732
ac88219a
MA
1733
1734class QAPISchemaCommand(QAPISchemaEntity):
2cbc9437 1735 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
d6fe3d02 1736 gen, success_response, boxed, allow_oob, allow_preconfig):
2cbc9437 1737 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
ac88219a
MA
1738 assert not arg_type or isinstance(arg_type, str)
1739 assert not ret_type or isinstance(ret_type, str)
1740 self._arg_type_name = arg_type
1741 self.arg_type = None
1742 self._ret_type_name = ret_type
1743 self.ret_type = None
1744 self.gen = gen
1745 self.success_response = success_response
48825ca4 1746 self.boxed = boxed
876c6751 1747 self.allow_oob = allow_oob
d6fe3d02 1748 self.allow_preconfig = allow_preconfig
ac88219a
MA
1749
1750 def check(self, schema):
4fca21c1 1751 QAPISchemaEntity.check(self, schema)
ac88219a
MA
1752 if self._arg_type_name:
1753 self.arg_type = schema.lookup_type(self._arg_type_name)
b22e8658 1754 assert isinstance(self.arg_type, QAPISchemaObjectType)
675b214b 1755 assert not self.arg_type.variants or self.boxed
c818408e 1756 elif self.boxed:
2ab218aa 1757 raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
ac88219a
MA
1758 if self._ret_type_name:
1759 self.ret_type = schema.lookup_type(self._ret_type_name)
1760 assert isinstance(self.ret_type, QAPISchemaType)
1761
3f7dc21b 1762 def visit(self, visitor):
56176412 1763 QAPISchemaEntity.visit(self, visitor)
fbf09a2f 1764 visitor.visit_command(self.name, self.info, self.ifcond,
3f7dc21b 1765 self.arg_type, self.ret_type,
876c6751 1766 self.gen, self.success_response,
d6fe3d02
IM
1767 self.boxed, self.allow_oob,
1768 self.allow_preconfig)
3f7dc21b 1769
ac88219a
MA
1770
1771class QAPISchemaEvent(QAPISchemaEntity):
2cbc9437
MAL
1772 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1773 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
ac88219a
MA
1774 assert not arg_type or isinstance(arg_type, str)
1775 self._arg_type_name = arg_type
1776 self.arg_type = None
48825ca4 1777 self.boxed = boxed
ac88219a
MA
1778
1779 def check(self, schema):
4fca21c1 1780 QAPISchemaEntity.check(self, schema)
ac88219a
MA
1781 if self._arg_type_name:
1782 self.arg_type = schema.lookup_type(self._arg_type_name)
b22e8658 1783 assert isinstance(self.arg_type, QAPISchemaObjectType)
675b214b 1784 assert not self.arg_type.variants or self.boxed
c818408e 1785 elif self.boxed:
2ab218aa 1786 raise QAPISemError(self.info, "use of 'boxed' requires 'data'")
ac88219a 1787
3f7dc21b 1788 def visit(self, visitor):
56176412 1789 QAPISchemaEntity.visit(self, visitor)
fbf09a2f
MAL
1790 visitor.visit_event(self.name, self.info, self.ifcond,
1791 self.arg_type, self.boxed)
3f7dc21b 1792
ac88219a
MA
1793
1794class QAPISchema(object):
1795 def __init__(self, fname):
56a46895 1796 self.fname = fname
de685ae5
MA
1797 if sys.version_info[0] >= 3:
1798 f = open(fname, 'r', encoding='utf-8')
1799 else:
1800 f = open(fname, 'r')
1801 parser = QAPISchemaParser(f)
181feaf3
MA
1802 exprs = check_exprs(parser.exprs)
1803 self.docs = parser.docs
8a84767c 1804 self._entity_list = []
181feaf3
MA
1805 self._entity_dict = {}
1806 self._predefining = True
1807 self._def_predefineds()
1808 self._predefining = False
1809 self._def_exprs(exprs)
1810 self.check()
ac88219a 1811
ac88219a 1812 def _def_entity(self, ent):
99df5289
EB
1813 # Only the predefined types are allowed to not have info
1814 assert ent.info or self._predefining
cf40a0a5 1815 assert ent.name is None or ent.name not in self._entity_dict
8a84767c 1816 self._entity_list.append(ent)
cf40a0a5
MA
1817 if ent.name is not None:
1818 self._entity_dict[ent.name] = ent
ac88219a
MA
1819
1820 def lookup_entity(self, name, typ=None):
1821 ent = self._entity_dict.get(name)
1822 if typ and not isinstance(ent, typ):
1823 return None
1824 return ent
1825
1826 def lookup_type(self, name):
1827 return self.lookup_entity(name, QAPISchemaType)
1828
cf40a0a5
MA
1829 def _def_include(self, expr, info, doc):
1830 include = expr['include']
1831 assert doc is None
1832 main_info = info
19e950d9
MA
1833 while main_info.parent:
1834 main_info = main_info.parent
1835 fname = os.path.relpath(include, os.path.dirname(main_info.fname))
cf40a0a5
MA
1836 self._def_entity(QAPISchemaInclude(fname, info))
1837
861877a0
EB
1838 def _def_builtin_type(self, name, json_type, c_type):
1839 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
cdb6610a
MA
1840 # Instantiating only the arrays that are actually used would
1841 # be nice, but we can't as long as their generated code
1842 # (qapi-builtin-types.[ch]) may be shared by some other
1843 # schema.
99df5289 1844 self._make_array_type(name, None)
ac88219a
MA
1845
1846 def _def_predefineds(self):
861877a0
EB
1847 for t in [('str', 'string', 'char' + pointer_suffix),
1848 ('number', 'number', 'double'),
1849 ('int', 'int', 'int64_t'),
1850 ('int8', 'int', 'int8_t'),
1851 ('int16', 'int', 'int16_t'),
1852 ('int32', 'int', 'int32_t'),
1853 ('int64', 'int', 'int64_t'),
1854 ('uint8', 'int', 'uint8_t'),
1855 ('uint16', 'int', 'uint16_t'),
1856 ('uint32', 'int', 'uint32_t'),
1857 ('uint64', 'int', 'uint64_t'),
1858 ('size', 'int', 'uint64_t'),
1859 ('bool', 'boolean', 'bool'),
4d2d5c41
MA
1860 ('any', 'value', 'QObject' + pointer_suffix),
1861 ('null', 'null', 'QNull' + pointer_suffix)]:
f51d8c3d 1862 self._def_builtin_type(*t)
069fb5b2 1863 self.the_empty_object_type = QAPISchemaObjectType(
6a8c0b51 1864 'q_empty', None, None, None, None, [], None, [])
39a18158 1865 self._def_entity(self.the_empty_object_type)
ea738b21
MAL
1866
1867 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1868 'qbool']
1869 qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
1870
2cbc9437 1871 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
069fb5b2 1872 qtype_values, 'QTYPE'))
ac88219a 1873
6a8c0b51
KW
1874 def _make_features(self, features):
1875 return [QAPISchemaFeature(f['name'], f.get('if')) for f in features]
1876
93bda4dd 1877 def _make_enum_members(self, values):
398969fe
MA
1878 return [QAPISchemaEnumMember(v['name'], v.get('if'))
1879 for v in values]
93bda4dd 1880
2cbc9437 1881 def _make_implicit_enum_type(self, name, info, ifcond, values):
481a6bd1 1882 # See also QAPISchemaObjectTypeMember.describe()
49823c4b 1883 name = name + 'Kind' # Use namespace reserved by add_name()
93bda4dd 1884 self._def_entity(QAPISchemaEnumType(
2cbc9437 1885 name, info, None, ifcond, self._make_enum_members(values), None))
ac88219a
MA
1886 return name
1887
99df5289 1888 def _make_array_type(self, element_type, info):
255960dd 1889 name = element_type + 'List' # Use namespace reserved by add_name()
ac88219a 1890 if not self.lookup_type(name):
99df5289 1891 self._def_entity(QAPISchemaArrayType(name, info, element_type))
ac88219a
MA
1892 return name
1893
2cbc9437
MAL
1894 def _make_implicit_object_type(self, name, info, doc, ifcond,
1895 role, members):
ac88219a
MA
1896 if not members:
1897 return None
481a6bd1 1898 # See also QAPISchemaObjectTypeMember.describe()
7599697c 1899 name = 'q_obj_%s-%s' % (name, role)
2cbc9437
MAL
1900 typ = self.lookup_entity(name, QAPISchemaObjectType)
1901 if typ:
1902 # The implicit object type has multiple users. This can
1903 # happen only for simple unions' implicit wrapper types.
1904 # Its ifcond should be the disjunction of its user's
1905 # ifconds. Not implemented. Instead, we always pass the
1906 # wrapped type's ifcond, which is trivially the same for all
1907 # users. It's also necessary for the wrapper to compile.
1908 # But it's not tight: the disjunction need not imply it. We
1909 # may end up compiling useless wrapper types.
1910 # TODO kill simple unions or implement the disjunction
4fca21c1 1911 assert ifcond == typ._ifcond # pylint: disable=protected-access
2cbc9437
MAL
1912 else:
1913 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
6a8c0b51 1914 None, members, None, []))
ac88219a
MA
1915 return name
1916
069fb5b2 1917 def _def_enum_type(self, expr, info, doc):
ac88219a
MA
1918 name = expr['enum']
1919 data = expr['data']
1920 prefix = expr.get('prefix')
2cbc9437 1921 ifcond = expr.get('if')
93bda4dd 1922 self._def_entity(QAPISchemaEnumType(
2cbc9437
MAL
1923 name, info, doc, ifcond,
1924 self._make_enum_members(data), prefix))
ac88219a 1925
ccadd6bc 1926 def _make_member(self, name, typ, ifcond, info):
ac88219a
MA
1927 optional = False
1928 if name.startswith('*'):
1929 name = name[1:]
1930 optional = True
1931 if isinstance(typ, list):
1932 assert len(typ) == 1
99df5289 1933 typ = self._make_array_type(typ[0], info)
ccadd6bc 1934 return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
ac88219a 1935
99df5289 1936 def _make_members(self, data, info):
ccadd6bc 1937 return [self._make_member(key, value['type'], value.get('if'), info)
2f848044 1938 for (key, value) in data.items()]
ac88219a 1939
069fb5b2 1940 def _def_struct_type(self, expr, info, doc):
ac88219a
MA
1941 name = expr['struct']
1942 base = expr.get('base')
1943 data = expr['data']
2cbc9437 1944 ifcond = expr.get('if')
6a8c0b51 1945 features = expr.get('features', [])
2cbc9437 1946 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
99df5289 1947 self._make_members(data, info),
6a8c0b51
KW
1948 None,
1949 self._make_features(features)))
ac88219a 1950
3e270dca
MAL
1951 def _make_variant(self, case, typ, ifcond):
1952 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
ac88219a 1953
a2724280 1954 def _make_simple_variant(self, case, typ, ifcond, info):
ac88219a
MA
1955 if isinstance(typ, list):
1956 assert len(typ) == 1
99df5289
EB
1957 typ = self._make_array_type(typ[0], info)
1958 typ = self._make_implicit_object_type(
4fca21c1 1959 typ, info, None, self.lookup_type(typ),
ccadd6bc 1960 'wrapper', [self._make_member('data', typ, None, info)])
a2724280 1961 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
ac88219a 1962
069fb5b2 1963 def _def_union_type(self, expr, info, doc):
ac88219a
MA
1964 name = expr['union']
1965 data = expr['data']
1966 base = expr.get('base')
2cbc9437 1967 ifcond = expr.get('if')
ac88219a 1968 tag_name = expr.get('discriminator')
46292ba7 1969 tag_member = None
ac4338f8 1970 if isinstance(base, dict):
2cbc9437
MAL
1971 base = self._make_implicit_object_type(
1972 name, info, doc, ifcond,
1973 'base', self._make_members(base, info))
ac88219a 1974 if tag_name:
3e270dca 1975 variants = [self._make_variant(key, value['type'], value.get('if'))
2f848044 1976 for (key, value) in data.items()]
da34a9bd 1977 members = []
ac88219a 1978 else:
a2724280
MAL
1979 variants = [self._make_simple_variant(key, value['type'],
1980 value.get('if'), info)
2f848044 1981 for (key, value) in data.items()]
a2724280 1982 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
ea738b21 1983 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
9d3f3494 1984 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
da34a9bd 1985 members = [tag_member]
ac88219a 1986 self._def_entity(
2cbc9437 1987 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
ac88219a 1988 QAPISchemaObjectTypeVariants(tag_name,
46292ba7 1989 tag_member,
6a8c0b51 1990 variants), []))
ac88219a 1991
069fb5b2 1992 def _def_alternate_type(self, expr, info, doc):
ac88219a
MA
1993 name = expr['alternate']
1994 data = expr['data']
2cbc9437 1995 ifcond = expr.get('if')
3e270dca 1996 variants = [self._make_variant(key, value['type'], value.get('if'))
2f848044 1997 for (key, value) in data.items()]
0426d53c 1998 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
ac88219a 1999 self._def_entity(
2cbc9437 2000 QAPISchemaAlternateType(name, info, doc, ifcond,
ac88219a 2001 QAPISchemaObjectTypeVariants(None,
46292ba7 2002 tag_member,
ac88219a 2003 variants)))
ac88219a 2004
069fb5b2 2005 def _def_command(self, expr, info, doc):
ac88219a
MA
2006 name = expr['command']
2007 data = expr.get('data')
2008 rets = expr.get('returns')
2009 gen = expr.get('gen', True)
2010 success_response = expr.get('success-response', True)
48825ca4 2011 boxed = expr.get('boxed', False)
876c6751 2012 allow_oob = expr.get('allow-oob', False)
d6fe3d02 2013 allow_preconfig = expr.get('allow-preconfig', False)
2cbc9437 2014 ifcond = expr.get('if')
ac88219a 2015 if isinstance(data, OrderedDict):
99df5289 2016 data = self._make_implicit_object_type(
2cbc9437 2017 name, info, doc, ifcond, 'arg', self._make_members(data, info))
ac88219a
MA
2018 if isinstance(rets, list):
2019 assert len(rets) == 1
99df5289 2020 rets = self._make_array_type(rets[0], info)
2cbc9437 2021 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
876c6751 2022 gen, success_response,
d6fe3d02 2023 boxed, allow_oob, allow_preconfig))
ac88219a 2024
069fb5b2 2025 def _def_event(self, expr, info, doc):
ac88219a
MA
2026 name = expr['event']
2027 data = expr.get('data')
48825ca4 2028 boxed = expr.get('boxed', False)
2cbc9437 2029 ifcond = expr.get('if')
ac88219a 2030 if isinstance(data, OrderedDict):
99df5289 2031 data = self._make_implicit_object_type(
2cbc9437
MAL
2032 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2033 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
ac88219a 2034
71a7510b
MA
2035 def _def_exprs(self, exprs):
2036 for expr_elem in exprs:
ac88219a
MA
2037 expr = expr_elem['expr']
2038 info = expr_elem['info']
069fb5b2 2039 doc = expr_elem.get('doc')
ac88219a 2040 if 'enum' in expr:
069fb5b2 2041 self._def_enum_type(expr, info, doc)
ac88219a 2042 elif 'struct' in expr:
069fb5b2 2043 self._def_struct_type(expr, info, doc)
ac88219a 2044 elif 'union' in expr:
069fb5b2 2045 self._def_union_type(expr, info, doc)
ac88219a 2046 elif 'alternate' in expr:
069fb5b2 2047 self._def_alternate_type(expr, info, doc)
ac88219a 2048 elif 'command' in expr:
069fb5b2 2049 self._def_command(expr, info, doc)
ac88219a 2050 elif 'event' in expr:
069fb5b2 2051 self._def_event(expr, info, doc)
97f02494 2052 elif 'include' in expr:
cf40a0a5 2053 self._def_include(expr, info, doc)
ac88219a
MA
2054 else:
2055 assert False
2056
2057 def check(self):
8a84767c 2058 for ent in self._entity_list:
ac88219a 2059 ent.check(self)
4d076d67 2060
3f7dc21b 2061 def visit(self, visitor):
25a0d9c9 2062 visitor.visit_begin(self)
cf40a0a5 2063 module = None
dcac6471 2064 visitor.visit_module(module)
8a84767c 2065 for entity in self._entity_list:
25a0d9c9 2066 if visitor.visit_needed(entity):
cf40a0a5
MA
2067 if entity.module != module:
2068 module = entity.module
2069 visitor.visit_module(module)
25a0d9c9 2070 entity.visit(visitor)
3f7dc21b
MA
2071 visitor.visit_end()
2072
b86b05ed 2073
00e4b285
MA
2074#
2075# Code generation helpers
2076#
2077
0f923be2
MR
2078def camel_case(name):
2079 new_name = ''
2080 first = True
2081 for ch in name:
2082 if ch in ['_', '-']:
2083 first = True
2084 elif first:
2085 new_name += ch.upper()
2086 first = False
2087 else:
2088 new_name += ch.lower()
2089 return new_name
2090
437db254 2091
849bc538
MA
2092# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2093# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2094# ENUM24_Name -> ENUM24_NAME
2095def camel_to_upper(value):
2096 c_fun_str = c_name(value, False)
2097 if value.isupper():
2098 return c_fun_str
2099
2100 new_name = ''
b736e25a
MA
2101 length = len(c_fun_str)
2102 for i in range(length):
849bc538 2103 c = c_fun_str[i]
ef801a9b
MA
2104 # When c is upper and no '_' appears before, do more checks
2105 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
b736e25a 2106 if i < length - 1 and c_fun_str[i + 1].islower():
437db254
EB
2107 new_name += '_'
2108 elif c_fun_str[i - 1].isdigit():
849bc538
MA
2109 new_name += '_'
2110 new_name += c
2111 return new_name.lstrip('_').upper()
2112
437db254 2113
351d36e4
DB
2114def c_enum_const(type_name, const_name, prefix=None):
2115 if prefix is not None:
2116 type_name = prefix
d20a580b 2117 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
849bc538 2118
b736e25a 2119
52c4272c 2120if hasattr(str, 'maketrans'):
eb815e24 2121 c_name_trans = str.maketrans('.-', '__')
52c4272c 2122else:
eb815e24 2123 c_name_trans = string.maketrans('.-', '__')
47299262 2124
437db254 2125
c6405b54
EB
2126# Map @name to a valid C identifier.
2127# If @protect, avoid returning certain ticklish identifiers (like
ef801a9b 2128# C keywords) by prepending 'q_'.
c6405b54
EB
2129#
2130# Used for converting 'name' from a 'name':'type' qapi definition
2131# into a generated struct member, as well as converting type names
2132# into substrings of a generated C function name.
2133# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2134# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
18df515e 2135def c_name(name, protect=True):
427a1a2c
BS
2136 # ANSI X3J11/88-090, 3.1.1
2137 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
437db254
EB
2138 'default', 'do', 'double', 'else', 'enum', 'extern',
2139 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2140 'return', 'short', 'signed', 'sizeof', 'static',
2141 'struct', 'switch', 'typedef', 'union', 'unsigned',
2142 'void', 'volatile', 'while'])
427a1a2c
BS
2143 # ISO/IEC 9899:1999, 6.4.1
2144 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2145 # ISO/IEC 9899:2011, 6.4.1
437db254
EB
2146 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2147 '_Noreturn', '_Static_assert', '_Thread_local'])
427a1a2c
BS
2148 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2149 # excluding _.*
2150 gcc_words = set(['asm', 'typeof'])
6f88009e
TS
2151 # C++ ISO/IEC 14882:2003 2.11
2152 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
2153 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2154 'namespace', 'new', 'operator', 'private', 'protected',
2155 'public', 'reinterpret_cast', 'static_cast', 'template',
2156 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2157 'using', 'virtual', 'wchar_t',
2158 # alternative representations
2159 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2160 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1057725f 2161 # namespace pollution:
9a801c7d 2162 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
c43567c1 2163 name = name.translate(c_name_trans)
437db254
EB
2164 if protect and (name in c89_words | c99_words | c11_words | gcc_words
2165 | cpp_words | polluted_words):
ef801a9b 2166 return 'q_' + name
c43567c1 2167 return name
0f923be2 2168
b736e25a 2169
05dfb26c 2170eatspace = '\033EATSPACE.'
d5573446 2171pointer_suffix = ' *' + eatspace
05dfb26c 2172
437db254 2173
0f923be2 2174def genindent(count):
ef801a9b 2175 ret = ''
437db254 2176 for _ in range(count):
ef801a9b 2177 ret += ' '
0f923be2
MR
2178 return ret
2179
b736e25a 2180
0f923be2
MR
2181indent_level = 0
2182
437db254 2183
0f923be2
MR
2184def push_indent(indent_amount=4):
2185 global indent_level
2186 indent_level += indent_amount
2187
437db254 2188
0f923be2
MR
2189def pop_indent(indent_amount=4):
2190 global indent_level
2191 indent_level -= indent_amount
2192
437db254 2193
77e703b8
MA
2194# Generate @code with @kwds interpolated.
2195# Obey indent_level, and strip eatspace.
0f923be2 2196def cgen(code, **kwds):
77e703b8
MA
2197 raw = code % kwds
2198 if indent_level:
2199 indent = genindent(indent_level)
2752e5be 2200 # re.subn() lacks flags support before Python 2.7, use re.compile()
485d948c
MAL
2201 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2202 indent, raw)
77e703b8 2203 raw = raw[0]
0fe675af 2204 return re.sub(re.escape(eatspace) + r' *', '', raw)
0f923be2 2205
437db254 2206
0f923be2 2207def mcgen(code, **kwds):
77e703b8
MA
2208 if code[0] == '\n':
2209 code = code[1:]
2210 return cgen(code, **kwds)
0f923be2 2211
0f923be2 2212
709395f8
MA
2213def c_fname(filename):
2214 return re.sub(r'[^A-Za-z0-9_]', '_', filename)
c0afa9c5 2215
437db254 2216
c0afa9c5
MR
2217def guardstart(name):
2218 return mcgen('''
c0afa9c5
MR
2219#ifndef %(name)s
2220#define %(name)s
2221
2222''',
709395f8 2223 name=c_fname(name).upper())
c0afa9c5 2224
437db254 2225
c0afa9c5
MR
2226def guardend(name):
2227 return mcgen('''
2228
2229#endif /* %(name)s */
c0afa9c5 2230''',
709395f8 2231 name=c_fname(name).upper())
2114f5a9 2232
437db254 2233
ded9fc28
MAL
2234def gen_if(ifcond):
2235 ret = ''
2236 for ifc in ifcond:
2237 ret += mcgen('''
2238#if %(cond)s
2239''', cond=ifc)
2240 return ret
2241
2242
2243def gen_endif(ifcond):
2244 ret = ''
2245 for ifc in reversed(ifcond):
2246 ret += mcgen('''
2247#endif /* %(cond)s */
2248''', cond=ifc)
2249 return ret
2250
2251
2252def _wrap_ifcond(ifcond, before, after):
2253 if before == after:
2254 return after # suppress empty #if ... #endif
2255
2256 assert after.startswith(before)
2257 out = before
2258 added = after[len(before):]
2259 if added[0] == '\n':
2260 out += '\n'
2261 added = added[1:]
2262 out += gen_if(ifcond)
2263 out += added
2264 out += gen_endif(ifcond)
2265 return out
2266
2267
1962bd39 2268def gen_enum_lookup(name, members, prefix=None):
efd2eaa6
MA
2269 ret = mcgen('''
2270
f7abe0ec
MAL
2271const QEnumLookup %(c_name)s_lookup = {
2272 .array = (const char *const[]) {
efd2eaa6 2273''',
e98859a9 2274 c_name=c_name(name))
1962bd39 2275 for m in members:
8ee06f61 2276 ret += gen_if(m.ifcond)
1962bd39 2277 index = c_enum_const(name, m.name, prefix)
efd2eaa6 2278 ret += mcgen('''
1962bd39 2279 [%(index)s] = "%(name)s",
efd2eaa6 2280''',
1962bd39 2281 index=index, name=m.name)
8ee06f61 2282 ret += gen_endif(m.ifcond)
efd2eaa6 2283
efd2eaa6 2284 ret += mcgen('''
f7abe0ec
MAL
2285 },
2286 .size = %(max_index)s
efd2eaa6
MA
2287};
2288''',
ebf677c8 2289 max_index=c_enum_const(name, '_MAX', prefix))
efd2eaa6
MA
2290 return ret
2291
437db254 2292
1962bd39 2293def gen_enum(name, members, prefix=None):
e98859a9 2294 # append automatically generated _MAX value
398969fe 2295 enum_members = members + [QAPISchemaEnumMember('_MAX')]
efd2eaa6 2296
e98859a9 2297 ret = mcgen('''
efd2eaa6 2298
e98859a9 2299typedef enum %(c_name)s {
efd2eaa6 2300''',
e98859a9 2301 c_name=c_name(name))
efd2eaa6 2302
1962bd39 2303 for m in enum_members:
8ee06f61 2304 ret += gen_if(m.ifcond)
e98859a9 2305 ret += mcgen('''
9c2f56e9 2306 %(c_enum)s,
efd2eaa6 2307''',
1962bd39 2308 c_enum=c_enum_const(name, m.name, prefix))
8ee06f61 2309 ret += gen_endif(m.ifcond)
efd2eaa6 2310
e98859a9
MA
2311 ret += mcgen('''
2312} %(c_name)s;
efd2eaa6 2313''',
e98859a9
MA
2314 c_name=c_name(name))
2315
2316 ret += mcgen('''
efd2eaa6 2317
5b5f825d 2318#define %(c_name)s_str(val) \\
f7abe0ec 2319 qapi_enum_lookup(&%(c_name)s_lookup, (val))
5b5f825d 2320
f7abe0ec 2321extern const QEnumLookup %(c_name)s_lookup;
e98859a9
MA
2322''',
2323 c_name=c_name(name))
2324 return ret
efd2eaa6 2325
437db254 2326
bdd2d42b 2327def build_params(arg_type, boxed, extra=None):
03b4367a
MA
2328 ret = ''
2329 sep = ''
48825ca4 2330 if boxed:
bdd2d42b 2331 assert arg_type
c818408e
EB
2332 ret += '%s arg' % arg_type.c_param_type()
2333 sep = ', '
bdd2d42b 2334 elif arg_type:
48825ca4
EB
2335 assert not arg_type.variants
2336 for memb in arg_type.members:
2337 ret += sep
2338 sep = ', '
2339 if memb.optional:
2340 ret += 'bool has_%s, ' % c_name(memb.name)
2341 ret += '%s %s' % (memb.type.c_param_type(),
2342 c_name(memb.name))
03b4367a
MA
2343 if extra:
2344 ret += sep + extra
bdd2d42b 2345 return ret if ret else 'void'
03b4367a 2346
1f353344 2347
00e4b285 2348#
47a6ea9a 2349# Accumulate and write output
00e4b285
MA
2350#
2351
47a6ea9a
MA
2352class QAPIGen(object):
2353
dddee4d7
MA
2354 def __init__(self, fname):
2355 self.fname = fname
47a6ea9a
MA
2356 self._preamble = ''
2357 self._body = ''
2358
2359 def preamble_add(self, text):
2360 self._preamble += text
2361
2362 def add(self, text):
2363 self._body += text
2364
dddee4d7
MA
2365 def get_content(self):
2366 return self._top() + self._preamble + self._body + self._bottom()
ded9fc28 2367
dddee4d7 2368 def _top(self):
47a6ea9a
MA
2369 return ''
2370
dddee4d7 2371 def _bottom(self):
47a6ea9a 2372 return ''
437db254 2373
dddee4d7
MA
2374 def write(self, output_dir):
2375 pathname = os.path.join(output_dir, self.fname)
cdb6610a
MA
2376 dir = os.path.dirname(pathname)
2377 if dir:
47a6ea9a 2378 try:
cdb6610a 2379 os.makedirs(dir)
47a6ea9a
MA
2380 except os.error as e:
2381 if e.errno != errno.EEXIST:
2382 raise
cdb6610a 2383 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
de685ae5
MA
2384 if sys.version_info[0] >= 3:
2385 f = open(fd, 'r+', encoding='utf-8')
2386 else:
2387 f = os.fdopen(fd, 'r+')
dddee4d7 2388 text = self.get_content()
907b8466
MA
2389 oldtext = f.read(len(text) + 1)
2390 if text != oldtext:
2391 f.seek(0)
2392 f.truncate(0)
2393 f.write(text)
47a6ea9a
MA
2394 f.close()
2395
2396
ded9fc28
MAL
2397@contextmanager
2398def ifcontext(ifcond, *args):
2399 """A 'with' statement context manager to wrap with start_if()/end_if()
47a6ea9a 2400
ded9fc28
MAL
2401 *args: any number of QAPIGenCCode
2402
2403 Example::
2404
2405 with ifcontext(ifcond, self._genh, self._genc):
2406 modify self._genh and self._genc ...
2407
2408 Is equivalent to calling::
2409
2410 self._genh.start_if(ifcond)
2411 self._genc.start_if(ifcond)
2412 modify self._genh and self._genc ...
2413 self._genh.end_if()
2414 self._genc.end_if()
2415 """
2416 for arg in args:
2417 arg.start_if(ifcond)
2418 yield
2419 for arg in args:
2420 arg.end_if()
2421
2422
2423class QAPIGenCCode(QAPIGen):
2424
dddee4d7
MA
2425 def __init__(self, fname):
2426 QAPIGen.__init__(self, fname)
ded9fc28
MAL
2427 self._start_if = None
2428
2429 def start_if(self, ifcond):
2430 assert self._start_if is None
2431 self._start_if = (ifcond, self._body, self._preamble)
2432
2433 def end_if(self):
2434 assert self._start_if
2435 self._wrap_ifcond()
2436 self._start_if = None
2437
2438 def _wrap_ifcond(self):
2439 self._body = _wrap_ifcond(self._start_if[0],
2440 self._start_if[1], self._body)
2441 self._preamble = _wrap_ifcond(self._start_if[0],
2442 self._start_if[2], self._preamble)
2443
dddee4d7 2444 def get_content(self):
ded9fc28 2445 assert self._start_if is None
dddee4d7 2446 return QAPIGen.get_content(self)
ded9fc28
MAL
2447
2448
2449class QAPIGenC(QAPIGenCCode):
2450
dddee4d7
MA
2451 def __init__(self, fname, blurb, pydoc):
2452 QAPIGenCCode.__init__(self, fname)
47a6ea9a
MA
2453 self._blurb = blurb
2454 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2455 re.MULTILINE))
2456
dddee4d7 2457 def _top(self):
47a6ea9a
MA
2458 return mcgen('''
2459/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
c263de3f
MA
2460
2461/*
2462%(blurb)s
5ddeec83
MA
2463 *
2464 * %(copyright)s
c263de3f
MA
2465 *
2466 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2467 * See the COPYING.LIB file in the top-level directory.
2468 */
2469
2470''',
47a6ea9a 2471 blurb=self._blurb, copyright=self._copyright)
12f8e1b9 2472
dddee4d7 2473 def _bottom(self):
252dc310 2474 return mcgen('''
5f1450f5 2475
252dc310 2476/* Dummy declaration to prevent empty .o file */
709395f8 2477char qapi_dummy_%(name)s;
252dc310 2478''',
709395f8 2479 name=c_fname(self.fname))
252dc310 2480
12f8e1b9 2481
47a6ea9a 2482class QAPIGenH(QAPIGenC):
12f8e1b9 2483
dddee4d7
MA
2484 def _top(self):
2485 return QAPIGenC._top(self) + guardstart(self.fname)
12f8e1b9 2486
dddee4d7
MA
2487 def _bottom(self):
2488 return guardend(self.fname)
12f8e1b9 2489
437db254 2490
47a6ea9a 2491class QAPIGenDoc(QAPIGen):
c263de3f 2492
dddee4d7
MA
2493 def _top(self):
2494 return (QAPIGen._top(self)
47a6ea9a 2495 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
71b3f045
MA
2496
2497
2498class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2499
2500 def __init__(self, prefix, what, blurb, pydoc):
2501 self._prefix = prefix
2502 self._what = what
dddee4d7
MA
2503 self._genc = QAPIGenC(self._prefix + self._what + '.c',
2504 blurb, pydoc)
2505 self._genh = QAPIGenH(self._prefix + self._what + '.h',
2506 blurb, pydoc)
71b3f045
MA
2507
2508 def write(self, output_dir):
dddee4d7
MA
2509 self._genc.write(output_dir)
2510 self._genh.write(output_dir)
cdb6610a
MA
2511
2512
2513class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2514
2515 def __init__(self, prefix, what, blurb, pydoc):
2516 self._prefix = prefix
2517 self._what = what
2518 self._blurb = blurb
2519 self._pydoc = pydoc
dcac6471
MA
2520 self._genc = None
2521 self._genh = None
cdb6610a 2522 self._module = {}
252dc310 2523 self._main_module = None
cdb6610a 2524
c2e196a9
MA
2525 @staticmethod
2526 def _is_user_module(name):
2527 return name and not name.startswith('./')
2528
dcac6471
MA
2529 @staticmethod
2530 def _is_builtin_module(name):
2531 return not name
2532
709395f8
MA
2533 def _module_dirname(self, what, name):
2534 if self._is_user_module(name):
2535 return os.path.dirname(name)
2536 return ''
2537
cdb6610a 2538 def _module_basename(self, what, name):
c2e196a9
MA
2539 ret = '' if self._is_builtin_module(name) else self._prefix
2540 if self._is_user_module(name):
709395f8 2541 basename = os.path.basename(name)
c2e196a9
MA
2542 ret += what
2543 if name != self._main_module:
2544 ret += '-' + os.path.splitext(basename)[0]
c2e196a9
MA
2545 else:
2546 name = name[2:] if name else 'builtin'
2547 ret += re.sub(r'-', '-' + name + '-', what)
2548 return ret
cdb6610a 2549
709395f8
MA
2550 def _module_filename(self, what, name):
2551 return os.path.join(self._module_dirname(what, name),
2552 self._module_basename(what, name))
2553
cdb6610a 2554 def _add_module(self, name, blurb):
709395f8 2555 basename = self._module_filename(self._what, name)
dddee4d7
MA
2556 genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
2557 genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
cdb6610a
MA
2558 self._module[name] = (genc, genh)
2559 self._set_module(name)
2560
c2e196a9
MA
2561 def _add_user_module(self, name, blurb):
2562 assert self._is_user_module(name)
2563 if self._main_module is None:
2564 self._main_module = name
2565 self._add_module(name, blurb)
2566
2567 def _add_system_module(self, name, blurb):
2568 self._add_module(name and './' + name, blurb)
2569
cdb6610a
MA
2570 def _set_module(self, name):
2571 self._genc, self._genh = self._module[name]
2572
252dc310 2573 def write(self, output_dir, opt_builtins=False):
cdb6610a 2574 for name in self._module:
dcac6471 2575 if self._is_builtin_module(name) and not opt_builtins:
cdb6610a 2576 continue
cdb6610a 2577 (genc, genh) = self._module[name]
dddee4d7
MA
2578 genc.write(output_dir)
2579 genh.write(output_dir)
cdb6610a 2580
dcac6471 2581 def _begin_user_module(self, name):
cdb6610a
MA
2582 pass
2583
2584 def visit_module(self, name):
252dc310
MA
2585 if name in self._module:
2586 self._set_module(name)
dcac6471
MA
2587 elif self._is_builtin_module(name):
2588 # The built-in module has not been created. No code may
2589 # be generated.
2590 self._genc = None
2591 self._genh = None
2592 else:
c2e196a9 2593 self._add_user_module(name, self._blurb)
dcac6471 2594 self._begin_user_module(name)
252dc310
MA
2595
2596 def visit_include(self, name, info):
709395f8
MA
2597 relname = os.path.relpath(self._module_filename(self._what, name),
2598 os.path.dirname(self._genh.fname))
252dc310 2599 self._genh.preamble_add(mcgen('''
709395f8 2600#include "%(relname)s.h"
252dc310 2601''',
709395f8 2602 relname=relname))
This page took 1.007595 seconds and 4 git commands to generate.