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