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