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