]> Git Repo - qemu.git/blame - scripts/qapi/common.py
qapi: add 'ifcond' to visitor methods
[qemu.git] / scripts / qapi / common.py
CommitLineData
0f923be2
MR
1#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
47a6ea9a 5# Copyright (c) 2013-2018 Red Hat Inc.
0f923be2
MR
6#
7# Authors:
8# Anthony Liguori <[email protected]>
c7a3f252 9# Markus Armbruster <[email protected]>
0f923be2 10#
678e48a2
MA
11# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
0f923be2 13
ef9d9108 14from __future__ import print_function
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
fbf09a2f 1065 def visit_enum_type(self, name, info, ifcond, values, prefix):
3f7dc21b
MA
1066 pass
1067
fbf09a2f 1068 def visit_array_type(self, name, info, ifcond, element_type):
3f7dc21b
MA
1069 pass
1070
fbf09a2f 1071 def visit_object_type(self, name, info, ifcond, base, members, variants):
3f7dc21b
MA
1072 pass
1073
fbf09a2f 1074 def visit_object_type_flat(self, name, info, ifcond, members, variants):
39a18158
MA
1075 pass
1076
fbf09a2f 1077 def visit_alternate_type(self, name, info, ifcond, variants):
3f7dc21b
MA
1078 pass
1079
fbf09a2f 1080 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
d6fe3d02 1081 success_response, boxed, allow_oob, allow_preconfig):
3f7dc21b
MA
1082 pass
1083
fbf09a2f 1084 def visit_event(self, name, info, ifcond, 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 1193 def visit(self, visitor):
fbf09a2f 1194 visitor.visit_enum_type(self.name, self.info, self.ifcond,
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 1227 def visit(self, visitor):
fbf09a2f
MAL
1228 visitor.visit_array_type(self.name, self.info, self.ifcond,
1229 self.element_type)
3f7dc21b 1230
ac88219a
MA
1231
1232class QAPISchemaObjectType(QAPISchemaType):
2cbc9437
MAL
1233 def __init__(self, name, info, doc, ifcond,
1234 base, local_members, variants):
da34a9bd
EB
1235 # struct has local_members, optional base, and no variants
1236 # flat union has base, variants, and no local_members
1237 # simple union has local_members, variants, and no base
2cbc9437 1238 QAPISchemaType.__init__(self, name, info, doc, ifcond)
ac88219a
MA
1239 assert base is None or isinstance(base, str)
1240 for m in local_members:
1241 assert isinstance(m, QAPISchemaObjectTypeMember)
88d4ef8b
EB
1242 m.set_owner(name)
1243 if variants is not None:
1244 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1245 variants.set_owner(name)
ac88219a
MA
1246 self._base_name = base
1247 self.base = None
1248 self.local_members = local_members
1249 self.variants = variants
1250 self.members = None
1251
1252 def check(self, schema):
4fca21c1 1253 QAPISchemaType.check(self, schema)
bac5429c 1254 if self.members is False: # check for cycles
4148c298
MAL
1255 raise QAPISemError(self.info,
1256 "Object %s contains itself" % self.name)
ac88219a
MA
1257 if self.members:
1258 return
1259 self.members = False # mark as being checked
23a4b2c6 1260 seen = OrderedDict()
ac88219a
MA
1261 if self._base_name:
1262 self.base = schema.lookup_type(self._base_name)
1263 assert isinstance(self.base, QAPISchemaObjectType)
ac88219a 1264 self.base.check(schema)
6bbfb12d 1265 self.base.check_clash(self.info, seen)
ac88219a 1266 for m in self.local_members:
e564e2dd 1267 m.check(schema)
27b60ab9 1268 m.check_clash(self.info, seen)
069fb5b2
MA
1269 if self.doc:
1270 self.doc.connect_member(m)
14ff8461 1271 self.members = seen.values()
ac88219a 1272 if self.variants:
cdc5fa37 1273 self.variants.check(schema, seen)
14ff8461 1274 assert self.variants.tag_member in self.members
6bbfb12d 1275 self.variants.check_clash(self.info, seen)
816a57cd
MA
1276 if self.doc:
1277 self.doc.check()
ac88219a 1278
14f00c6c 1279 # Check that the members of this type do not cause duplicate JSON members,
27b60ab9
EB
1280 # and update seen to track the members seen so far. Report any errors
1281 # on behalf of info, which is not necessarily self.info
6bbfb12d 1282 def check_clash(self, info, seen):
c2183d2e
EB
1283 assert not self.variants # not implemented
1284 for m in self.members:
27b60ab9 1285 m.check_clash(info, seen)
c2183d2e 1286
99df5289 1287 def is_implicit(self):
7599697c
EB
1288 # See QAPISchema._make_implicit_object_type(), as well as
1289 # _def_predefineds()
1290 return self.name.startswith('q_')
99df5289 1291
b6167706
EB
1292 def is_empty(self):
1293 assert self.members is not None
1294 return not self.members and not self.variants
1295
f51d8c3d 1296 def c_name(self):
cd50a256 1297 assert self.name != 'q_empty'
f51d8c3d
MA
1298 return QAPISchemaType.c_name(self)
1299
4040d995 1300 def c_type(self):
49823c4b 1301 assert not self.is_implicit()
becceedc 1302 return c_name(self.name) + pointer_suffix
f51d8c3d 1303
4040d995 1304 def c_unboxed_type(self):
4040d995
EB
1305 return c_name(self.name)
1306
f51d8c3d
MA
1307 def json_type(self):
1308 return 'object'
1309
3f7dc21b 1310 def visit(self, visitor):
fbf09a2f 1311 visitor.visit_object_type(self.name, self.info, self.ifcond,
3f7dc21b 1312 self.base, self.local_members, self.variants)
fbf09a2f 1313 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
39a18158 1314 self.members, self.variants)
3f7dc21b 1315
ac88219a 1316
d44f9ac8 1317class QAPISchemaMember(object):
88d4ef8b
EB
1318 role = 'member'
1319
d44f9ac8 1320 def __init__(self, name):
ac88219a 1321 assert isinstance(name, str)
ac88219a 1322 self.name = name
88d4ef8b
EB
1323 self.owner = None
1324
1325 def set_owner(self, name):
1326 assert not self.owner
1327 self.owner = name
ac88219a 1328
27b60ab9
EB
1329 def check_clash(self, info, seen):
1330 cname = c_name(self.name)
2cfbae3c 1331 if cname.lower() != cname and self.owner not in name_case_whitelist:
4148c298
MAL
1332 raise QAPISemError(info,
1333 "%s should not use uppercase" % self.describe())
27b60ab9 1334 if cname in seen:
4148c298
MAL
1335 raise QAPISemError(info, "%s collides with %s" %
1336 (self.describe(), seen[cname].describe()))
27b60ab9 1337 seen[cname] = self
577de12d 1338
88d4ef8b
EB
1339 def _pretty_owner(self):
1340 owner = self.owner
7599697c 1341 if owner.startswith('q_obj_'):
88d4ef8b
EB
1342 # See QAPISchema._make_implicit_object_type() - reverse the
1343 # mapping there to create a nice human-readable description
7599697c 1344 owner = owner[6:]
88d4ef8b
EB
1345 if owner.endswith('-arg'):
1346 return '(parameter of %s)' % owner[:-4]
ac4338f8
EB
1347 elif owner.endswith('-base'):
1348 return '(base of %s)' % owner[:-5]
88d4ef8b
EB
1349 else:
1350 assert owner.endswith('-wrapper')
1351 # Unreachable and not implemented
1352 assert False
93bda4dd
EB
1353 if owner.endswith('Kind'):
1354 # See QAPISchema._make_implicit_enum_type()
1355 return '(branch of %s)' % owner[:-4]
88d4ef8b
EB
1356 return '(%s of %s)' % (self.role, owner)
1357
1358 def describe(self):
1359 return "'%s' %s" % (self.name, self._pretty_owner())
1360
ac88219a 1361
d44f9ac8
EB
1362class QAPISchemaObjectTypeMember(QAPISchemaMember):
1363 def __init__(self, name, typ, optional):
1364 QAPISchemaMember.__init__(self, name)
1365 assert isinstance(typ, str)
1366 assert isinstance(optional, bool)
1367 self._type_name = typ
1368 self.type = None
1369 self.optional = optional
1370
1371 def check(self, schema):
1372 assert self.owner
1373 self.type = schema.lookup_type(self._type_name)
1374 assert self.type
1375
1376
ac88219a 1377class QAPISchemaObjectTypeVariants(object):
46292ba7
EB
1378 def __init__(self, tag_name, tag_member, variants):
1379 # Flat unions pass tag_name but not tag_member.
1380 # Simple unions and alternates pass tag_member but not tag_name.
1381 # After check(), tag_member is always set, and tag_name remains
1382 # a reliable witness of being used by a flat union.
1383 assert bool(tag_member) != bool(tag_name)
1384 assert (isinstance(tag_name, str) or
1385 isinstance(tag_member, QAPISchemaObjectTypeMember))
02a57ae3 1386 assert len(variants) > 0
ac88219a
MA
1387 for v in variants:
1388 assert isinstance(v, QAPISchemaObjectTypeVariant)
da9cb193 1389 self._tag_name = tag_name
46292ba7 1390 self.tag_member = tag_member
ac88219a
MA
1391 self.variants = variants
1392
88d4ef8b
EB
1393 def set_owner(self, name):
1394 for v in self.variants:
1395 v.set_owner(name)
1396
cdc5fa37 1397 def check(self, schema, seen):
14ff8461 1398 if not self.tag_member: # flat union
da9cb193
EB
1399 self.tag_member = seen[c_name(self._tag_name)]
1400 assert self._tag_name == self.tag_member.name
ac88219a 1401 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
800877bb
AN
1402 if self._tag_name: # flat union
1403 # branches that are not explicitly covered get an empty type
1404 cases = set([v.name for v in self.variants])
1405 for val in self.tag_member.type.values:
1406 if val.name not in cases:
1407 v = QAPISchemaObjectTypeVariant(val.name, 'q_empty')
1408 v.set_owner(self.tag_member.owner)
1409 self.variants.append(v)
ac88219a 1410 for v in self.variants:
10565ca9 1411 v.check(schema)
0426d53c
EB
1412 # Union names must match enum values; alternate names are
1413 # checked separately. Use 'seen' to tell the two apart.
1414 if seen:
93bda4dd 1415 assert v.name in self.tag_member.type.member_names()
0426d53c 1416 assert isinstance(v.type, QAPISchemaObjectType)
b807a1e1
EB
1417 v.type.check(schema)
1418
6bbfb12d 1419 def check_clash(self, info, seen):
b807a1e1
EB
1420 for v in self.variants:
1421 # Reset seen map for each variant, since qapi names from one
1422 # branch do not affect another branch
b807a1e1 1423 assert isinstance(v.type, QAPISchemaObjectType)
6bbfb12d 1424 v.type.check_clash(info, dict(seen))
ac88219a 1425
437db254 1426
ac88219a 1427class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
88d4ef8b
EB
1428 role = 'branch'
1429
ac88219a
MA
1430 def __init__(self, name, typ):
1431 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1432
ac88219a
MA
1433
1434class QAPISchemaAlternateType(QAPISchemaType):
2cbc9437
MAL
1435 def __init__(self, name, info, doc, ifcond, variants):
1436 QAPISchemaType.__init__(self, name, info, doc, ifcond)
ac88219a 1437 assert isinstance(variants, QAPISchemaObjectTypeVariants)
da9cb193 1438 assert variants.tag_member
88d4ef8b
EB
1439 variants.set_owner(name)
1440 variants.tag_member.set_owner(self.name)
ac88219a
MA
1441 self.variants = variants
1442
1443 def check(self, schema):
4fca21c1 1444 QAPISchemaType.check(self, schema)
e564e2dd 1445 self.variants.tag_member.check(schema)
b807a1e1
EB
1446 # Not calling self.variants.check_clash(), because there's nothing
1447 # to clash with
cdc5fa37 1448 self.variants.check(schema, {})
0426d53c
EB
1449 # Alternate branch names have no relation to the tag enum values;
1450 # so we have to check for potential name collisions ourselves.
1451 seen = {}
1452 for v in self.variants.variants:
1453 v.check_clash(self.info, seen)
069fb5b2
MA
1454 if self.doc:
1455 self.doc.connect_member(v)
816a57cd
MA
1456 if self.doc:
1457 self.doc.check()
ac88219a 1458
4040d995
EB
1459 def c_type(self):
1460 return c_name(self.name) + pointer_suffix
1461
f51d8c3d
MA
1462 def json_type(self):
1463 return 'value'
1464
3f7dc21b 1465 def visit(self, visitor):
fbf09a2f
MAL
1466 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1467 self.variants)
3f7dc21b 1468
c818408e
EB
1469 def is_empty(self):
1470 return False
1471
ac88219a
MA
1472
1473class QAPISchemaCommand(QAPISchemaEntity):
2cbc9437 1474 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
d6fe3d02 1475 gen, success_response, boxed, allow_oob, allow_preconfig):
2cbc9437 1476 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
ac88219a
MA
1477 assert not arg_type or isinstance(arg_type, str)
1478 assert not ret_type or isinstance(ret_type, str)
1479 self._arg_type_name = arg_type
1480 self.arg_type = None
1481 self._ret_type_name = ret_type
1482 self.ret_type = None
1483 self.gen = gen
1484 self.success_response = success_response
48825ca4 1485 self.boxed = boxed
876c6751 1486 self.allow_oob = allow_oob
d6fe3d02 1487 self.allow_preconfig = allow_preconfig
ac88219a
MA
1488
1489 def check(self, schema):
4fca21c1 1490 QAPISchemaEntity.check(self, schema)
ac88219a
MA
1491 if self._arg_type_name:
1492 self.arg_type = schema.lookup_type(self._arg_type_name)
c818408e
EB
1493 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1494 isinstance(self.arg_type, QAPISchemaAlternateType))
1495 self.arg_type.check(schema)
1496 if self.boxed:
1497 if self.arg_type.is_empty():
4148c298
MAL
1498 raise QAPISemError(self.info,
1499 "Cannot use 'boxed' with empty type")
c818408e
EB
1500 else:
1501 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1502 assert not self.arg_type.variants
1503 elif self.boxed:
4148c298 1504 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
ac88219a
MA
1505 if self._ret_type_name:
1506 self.ret_type = schema.lookup_type(self._ret_type_name)
1507 assert isinstance(self.ret_type, QAPISchemaType)
1508
3f7dc21b 1509 def visit(self, visitor):
fbf09a2f 1510 visitor.visit_command(self.name, self.info, self.ifcond,
3f7dc21b 1511 self.arg_type, self.ret_type,
876c6751 1512 self.gen, self.success_response,
d6fe3d02
IM
1513 self.boxed, self.allow_oob,
1514 self.allow_preconfig)
3f7dc21b 1515
ac88219a
MA
1516
1517class QAPISchemaEvent(QAPISchemaEntity):
2cbc9437
MAL
1518 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1519 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
ac88219a
MA
1520 assert not arg_type or isinstance(arg_type, str)
1521 self._arg_type_name = arg_type
1522 self.arg_type = None
48825ca4 1523 self.boxed = boxed
ac88219a
MA
1524
1525 def check(self, schema):
4fca21c1 1526 QAPISchemaEntity.check(self, schema)
ac88219a
MA
1527 if self._arg_type_name:
1528 self.arg_type = schema.lookup_type(self._arg_type_name)
c818408e
EB
1529 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1530 isinstance(self.arg_type, QAPISchemaAlternateType))
1531 self.arg_type.check(schema)
1532 if self.boxed:
1533 if self.arg_type.is_empty():
4148c298
MAL
1534 raise QAPISemError(self.info,
1535 "Cannot use 'boxed' with empty type")
c818408e
EB
1536 else:
1537 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1538 assert not self.arg_type.variants
1539 elif self.boxed:
4148c298 1540 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
ac88219a 1541
3f7dc21b 1542 def visit(self, visitor):
fbf09a2f
MAL
1543 visitor.visit_event(self.name, self.info, self.ifcond,
1544 self.arg_type, self.boxed)
3f7dc21b 1545
ac88219a
MA
1546
1547class QAPISchema(object):
1548 def __init__(self, fname):
cf40a0a5 1549 self._fname = fname
de685ae5
MA
1550 if sys.version_info[0] >= 3:
1551 f = open(fname, 'r', encoding='utf-8')
1552 else:
1553 f = open(fname, 'r')
1554 parser = QAPISchemaParser(f)
181feaf3
MA
1555 exprs = check_exprs(parser.exprs)
1556 self.docs = parser.docs
8a84767c 1557 self._entity_list = []
181feaf3
MA
1558 self._entity_dict = {}
1559 self._predefining = True
1560 self._def_predefineds()
1561 self._predefining = False
1562 self._def_exprs(exprs)
1563 self.check()
ac88219a 1564
ac88219a 1565 def _def_entity(self, ent):
99df5289
EB
1566 # Only the predefined types are allowed to not have info
1567 assert ent.info or self._predefining
cf40a0a5 1568 assert ent.name is None or ent.name not in self._entity_dict
8a84767c 1569 self._entity_list.append(ent)
cf40a0a5
MA
1570 if ent.name is not None:
1571 self._entity_dict[ent.name] = ent
1572 if ent.info:
1573 ent.module = os.path.relpath(ent.info['file'],
1574 os.path.dirname(self._fname))
ac88219a
MA
1575
1576 def lookup_entity(self, name, typ=None):
1577 ent = self._entity_dict.get(name)
1578 if typ and not isinstance(ent, typ):
1579 return None
1580 return ent
1581
1582 def lookup_type(self, name):
1583 return self.lookup_entity(name, QAPISchemaType)
1584
cf40a0a5
MA
1585 def _def_include(self, expr, info, doc):
1586 include = expr['include']
1587 assert doc is None
1588 main_info = info
1589 while main_info['parent']:
1590 main_info = main_info['parent']
1591 fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1592 self._def_entity(QAPISchemaInclude(fname, info))
1593
861877a0
EB
1594 def _def_builtin_type(self, name, json_type, c_type):
1595 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
cdb6610a
MA
1596 # Instantiating only the arrays that are actually used would
1597 # be nice, but we can't as long as their generated code
1598 # (qapi-builtin-types.[ch]) may be shared by some other
1599 # schema.
99df5289 1600 self._make_array_type(name, None)
ac88219a
MA
1601
1602 def _def_predefineds(self):
861877a0
EB
1603 for t in [('str', 'string', 'char' + pointer_suffix),
1604 ('number', 'number', 'double'),
1605 ('int', 'int', 'int64_t'),
1606 ('int8', 'int', 'int8_t'),
1607 ('int16', 'int', 'int16_t'),
1608 ('int32', 'int', 'int32_t'),
1609 ('int64', 'int', 'int64_t'),
1610 ('uint8', 'int', 'uint8_t'),
1611 ('uint16', 'int', 'uint16_t'),
1612 ('uint32', 'int', 'uint32_t'),
1613 ('uint64', 'int', 'uint64_t'),
1614 ('size', 'int', 'uint64_t'),
1615 ('bool', 'boolean', 'bool'),
4d2d5c41
MA
1616 ('any', 'value', 'QObject' + pointer_suffix),
1617 ('null', 'null', 'QNull' + pointer_suffix)]:
f51d8c3d 1618 self._def_builtin_type(*t)
069fb5b2 1619 self.the_empty_object_type = QAPISchemaObjectType(
2cbc9437 1620 'q_empty', None, None, None, None, [], None)
39a18158 1621 self._def_entity(self.the_empty_object_type)
01b2ffce 1622 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
93bda4dd 1623 'qstring', 'qdict', 'qlist',
01b2ffce 1624 'qbool'])
2cbc9437 1625 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
069fb5b2 1626 qtype_values, 'QTYPE'))
ac88219a 1627
93bda4dd
EB
1628 def _make_enum_members(self, values):
1629 return [QAPISchemaMember(v) for v in values]
1630
2cbc9437 1631 def _make_implicit_enum_type(self, name, info, ifcond, values):
93bda4dd 1632 # See also QAPISchemaObjectTypeMember._pretty_owner()
49823c4b 1633 name = name + 'Kind' # Use namespace reserved by add_name()
93bda4dd 1634 self._def_entity(QAPISchemaEnumType(
2cbc9437 1635 name, info, None, ifcond, self._make_enum_members(values), None))
ac88219a
MA
1636 return name
1637
99df5289 1638 def _make_array_type(self, element_type, info):
255960dd 1639 name = element_type + 'List' # Use namespace reserved by add_name()
ac88219a 1640 if not self.lookup_type(name):
99df5289 1641 self._def_entity(QAPISchemaArrayType(name, info, element_type))
ac88219a
MA
1642 return name
1643
2cbc9437
MAL
1644 def _make_implicit_object_type(self, name, info, doc, ifcond,
1645 role, members):
ac88219a
MA
1646 if not members:
1647 return None
88d4ef8b 1648 # See also QAPISchemaObjectTypeMember._pretty_owner()
7599697c 1649 name = 'q_obj_%s-%s' % (name, role)
2cbc9437
MAL
1650 typ = self.lookup_entity(name, QAPISchemaObjectType)
1651 if typ:
1652 # The implicit object type has multiple users. This can
1653 # happen only for simple unions' implicit wrapper types.
1654 # Its ifcond should be the disjunction of its user's
1655 # ifconds. Not implemented. Instead, we always pass the
1656 # wrapped type's ifcond, which is trivially the same for all
1657 # users. It's also necessary for the wrapper to compile.
1658 # But it's not tight: the disjunction need not imply it. We
1659 # may end up compiling useless wrapper types.
1660 # TODO kill simple unions or implement the disjunction
4fca21c1 1661 assert ifcond == typ._ifcond # pylint: disable=protected-access
2cbc9437
MAL
1662 else:
1663 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1664 None, members, None))
ac88219a
MA
1665 return name
1666
069fb5b2 1667 def _def_enum_type(self, expr, info, doc):
ac88219a
MA
1668 name = expr['enum']
1669 data = expr['data']
1670 prefix = expr.get('prefix')
2cbc9437 1671 ifcond = expr.get('if')
93bda4dd 1672 self._def_entity(QAPISchemaEnumType(
2cbc9437
MAL
1673 name, info, doc, ifcond,
1674 self._make_enum_members(data), prefix))
ac88219a 1675
99df5289 1676 def _make_member(self, name, typ, info):
ac88219a
MA
1677 optional = False
1678 if name.startswith('*'):
1679 name = name[1:]
1680 optional = True
1681 if isinstance(typ, list):
1682 assert len(typ) == 1
99df5289 1683 typ = self._make_array_type(typ[0], info)
ac88219a
MA
1684 return QAPISchemaObjectTypeMember(name, typ, optional)
1685
99df5289
EB
1686 def _make_members(self, data, info):
1687 return [self._make_member(key, value, info)
2f848044 1688 for (key, value) in data.items()]
ac88219a 1689
069fb5b2 1690 def _def_struct_type(self, expr, info, doc):
ac88219a
MA
1691 name = expr['struct']
1692 base = expr.get('base')
1693 data = expr['data']
2cbc9437
MAL
1694 ifcond = expr.get('if')
1695 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
99df5289 1696 self._make_members(data, info),
ac88219a 1697 None))
ac88219a
MA
1698
1699 def _make_variant(self, case, typ):
1700 return QAPISchemaObjectTypeVariant(case, typ)
1701
99df5289 1702 def _make_simple_variant(self, case, typ, info):
ac88219a
MA
1703 if isinstance(typ, list):
1704 assert len(typ) == 1
99df5289
EB
1705 typ = self._make_array_type(typ[0], info)
1706 typ = self._make_implicit_object_type(
4fca21c1 1707 typ, info, None, self.lookup_type(typ),
2cbc9437 1708 'wrapper', [self._make_member('data', typ, info)])
ac88219a
MA
1709 return QAPISchemaObjectTypeVariant(case, typ)
1710
069fb5b2 1711 def _def_union_type(self, expr, info, doc):
ac88219a
MA
1712 name = expr['union']
1713 data = expr['data']
1714 base = expr.get('base')
2cbc9437 1715 ifcond = expr.get('if')
ac88219a 1716 tag_name = expr.get('discriminator')
46292ba7 1717 tag_member = None
ac4338f8 1718 if isinstance(base, dict):
2cbc9437
MAL
1719 base = self._make_implicit_object_type(
1720 name, info, doc, ifcond,
1721 'base', self._make_members(base, info))
ac88219a
MA
1722 if tag_name:
1723 variants = [self._make_variant(key, value)
2f848044 1724 for (key, value) in data.items()]
da34a9bd 1725 members = []
ac88219a 1726 else:
99df5289 1727 variants = [self._make_simple_variant(key, value, info)
2f848044 1728 for (key, value) in data.items()]
2cbc9437 1729 typ = self._make_implicit_enum_type(name, info, ifcond,
9d3f3494
EB
1730 [v.name for v in variants])
1731 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
da34a9bd 1732 members = [tag_member]
ac88219a 1733 self._def_entity(
2cbc9437 1734 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
ac88219a 1735 QAPISchemaObjectTypeVariants(tag_name,
46292ba7 1736 tag_member,
ac88219a 1737 variants)))
ac88219a 1738
069fb5b2 1739 def _def_alternate_type(self, expr, info, doc):
ac88219a
MA
1740 name = expr['alternate']
1741 data = expr['data']
2cbc9437 1742 ifcond = expr.get('if')
ac88219a 1743 variants = [self._make_variant(key, value)
2f848044 1744 for (key, value) in data.items()]
0426d53c 1745 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
ac88219a 1746 self._def_entity(
2cbc9437 1747 QAPISchemaAlternateType(name, info, doc, ifcond,
ac88219a 1748 QAPISchemaObjectTypeVariants(None,
46292ba7 1749 tag_member,
ac88219a 1750 variants)))
ac88219a 1751
069fb5b2 1752 def _def_command(self, expr, info, doc):
ac88219a
MA
1753 name = expr['command']
1754 data = expr.get('data')
1755 rets = expr.get('returns')
1756 gen = expr.get('gen', True)
1757 success_response = expr.get('success-response', True)
48825ca4 1758 boxed = expr.get('boxed', False)
876c6751 1759 allow_oob = expr.get('allow-oob', False)
d6fe3d02 1760 allow_preconfig = expr.get('allow-preconfig', False)
2cbc9437 1761 ifcond = expr.get('if')
ac88219a 1762 if isinstance(data, OrderedDict):
99df5289 1763 data = self._make_implicit_object_type(
2cbc9437 1764 name, info, doc, ifcond, 'arg', self._make_members(data, info))
ac88219a
MA
1765 if isinstance(rets, list):
1766 assert len(rets) == 1
99df5289 1767 rets = self._make_array_type(rets[0], info)
2cbc9437 1768 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
876c6751 1769 gen, success_response,
d6fe3d02 1770 boxed, allow_oob, allow_preconfig))
ac88219a 1771
069fb5b2 1772 def _def_event(self, expr, info, doc):
ac88219a
MA
1773 name = expr['event']
1774 data = expr.get('data')
48825ca4 1775 boxed = expr.get('boxed', False)
2cbc9437 1776 ifcond = expr.get('if')
ac88219a 1777 if isinstance(data, OrderedDict):
99df5289 1778 data = self._make_implicit_object_type(
2cbc9437
MAL
1779 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1780 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
ac88219a 1781
71a7510b
MA
1782 def _def_exprs(self, exprs):
1783 for expr_elem in exprs:
ac88219a
MA
1784 expr = expr_elem['expr']
1785 info = expr_elem['info']
069fb5b2 1786 doc = expr_elem.get('doc')
ac88219a 1787 if 'enum' in expr:
069fb5b2 1788 self._def_enum_type(expr, info, doc)
ac88219a 1789 elif 'struct' in expr:
069fb5b2 1790 self._def_struct_type(expr, info, doc)
ac88219a 1791 elif 'union' in expr:
069fb5b2 1792 self._def_union_type(expr, info, doc)
ac88219a 1793 elif 'alternate' in expr:
069fb5b2 1794 self._def_alternate_type(expr, info, doc)
ac88219a 1795 elif 'command' in expr:
069fb5b2 1796 self._def_command(expr, info, doc)
ac88219a 1797 elif 'event' in expr:
069fb5b2 1798 self._def_event(expr, info, doc)
97f02494 1799 elif 'include' in expr:
cf40a0a5 1800 self._def_include(expr, info, doc)
ac88219a
MA
1801 else:
1802 assert False
1803
1804 def check(self):
8a84767c 1805 for ent in self._entity_list:
ac88219a 1806 ent.check(self)
4d076d67 1807
3f7dc21b 1808 def visit(self, visitor):
25a0d9c9 1809 visitor.visit_begin(self)
cf40a0a5 1810 module = None
8a84767c 1811 for entity in self._entity_list:
25a0d9c9 1812 if visitor.visit_needed(entity):
cf40a0a5
MA
1813 if entity.module != module:
1814 module = entity.module
1815 visitor.visit_module(module)
25a0d9c9 1816 entity.visit(visitor)
3f7dc21b
MA
1817 visitor.visit_end()
1818
b86b05ed 1819
00e4b285
MA
1820#
1821# Code generation helpers
1822#
1823
0f923be2
MR
1824def camel_case(name):
1825 new_name = ''
1826 first = True
1827 for ch in name:
1828 if ch in ['_', '-']:
1829 first = True
1830 elif first:
1831 new_name += ch.upper()
1832 first = False
1833 else:
1834 new_name += ch.lower()
1835 return new_name
1836
437db254 1837
849bc538
MA
1838# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1839# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1840# ENUM24_Name -> ENUM24_NAME
1841def camel_to_upper(value):
1842 c_fun_str = c_name(value, False)
1843 if value.isupper():
1844 return c_fun_str
1845
1846 new_name = ''
1847 l = len(c_fun_str)
1848 for i in range(l):
1849 c = c_fun_str[i]
ef801a9b
MA
1850 # When c is upper and no '_' appears before, do more checks
1851 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
437db254
EB
1852 if i < l - 1 and c_fun_str[i + 1].islower():
1853 new_name += '_'
1854 elif c_fun_str[i - 1].isdigit():
849bc538
MA
1855 new_name += '_'
1856 new_name += c
1857 return new_name.lstrip('_').upper()
1858
437db254 1859
351d36e4
DB
1860def c_enum_const(type_name, const_name, prefix=None):
1861 if prefix is not None:
1862 type_name = prefix
d20a580b 1863 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
849bc538 1864
52c4272c 1865if hasattr(str, 'maketrans'):
eb815e24 1866 c_name_trans = str.maketrans('.-', '__')
52c4272c 1867else:
eb815e24 1868 c_name_trans = string.maketrans('.-', '__')
47299262 1869
437db254 1870
c6405b54
EB
1871# Map @name to a valid C identifier.
1872# If @protect, avoid returning certain ticklish identifiers (like
ef801a9b 1873# C keywords) by prepending 'q_'.
c6405b54
EB
1874#
1875# Used for converting 'name' from a 'name':'type' qapi definition
1876# into a generated struct member, as well as converting type names
1877# into substrings of a generated C function name.
1878# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1879# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
18df515e 1880def c_name(name, protect=True):
427a1a2c
BS
1881 # ANSI X3J11/88-090, 3.1.1
1882 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
437db254
EB
1883 'default', 'do', 'double', 'else', 'enum', 'extern',
1884 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1885 'return', 'short', 'signed', 'sizeof', 'static',
1886 'struct', 'switch', 'typedef', 'union', 'unsigned',
1887 'void', 'volatile', 'while'])
427a1a2c
BS
1888 # ISO/IEC 9899:1999, 6.4.1
1889 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1890 # ISO/IEC 9899:2011, 6.4.1
437db254
EB
1891 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1892 '_Noreturn', '_Static_assert', '_Thread_local'])
427a1a2c
BS
1893 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1894 # excluding _.*
1895 gcc_words = set(['asm', 'typeof'])
6f88009e
TS
1896 # C++ ISO/IEC 14882:2003 2.11
1897 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1898 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1899 'namespace', 'new', 'operator', 'private', 'protected',
1900 'public', 'reinterpret_cast', 'static_cast', 'template',
1901 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1902 'using', 'virtual', 'wchar_t',
1903 # alternative representations
1904 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1905 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1057725f 1906 # namespace pollution:
9a801c7d 1907 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
c43567c1 1908 name = name.translate(c_name_trans)
437db254
EB
1909 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1910 | cpp_words | polluted_words):
ef801a9b 1911 return 'q_' + name
c43567c1 1912 return name
0f923be2 1913
05dfb26c 1914eatspace = '\033EATSPACE.'
d5573446 1915pointer_suffix = ' *' + eatspace
05dfb26c 1916
437db254 1917
0f923be2 1918def genindent(count):
ef801a9b 1919 ret = ''
437db254 1920 for _ in range(count):
ef801a9b 1921 ret += ' '
0f923be2
MR
1922 return ret
1923
1924indent_level = 0
1925
437db254 1926
0f923be2
MR
1927def push_indent(indent_amount=4):
1928 global indent_level
1929 indent_level += indent_amount
1930
437db254 1931
0f923be2
MR
1932def pop_indent(indent_amount=4):
1933 global indent_level
1934 indent_level -= indent_amount
1935
437db254 1936
77e703b8
MA
1937# Generate @code with @kwds interpolated.
1938# Obey indent_level, and strip eatspace.
0f923be2 1939def cgen(code, **kwds):
77e703b8
MA
1940 raw = code % kwds
1941 if indent_level:
1942 indent = genindent(indent_level)
2752e5be 1943 # re.subn() lacks flags support before Python 2.7, use re.compile()
0fe675af 1944 raw = re.subn(re.compile(r'^.', re.MULTILINE),
2752e5be 1945 indent + r'\g<0>', raw)
77e703b8 1946 raw = raw[0]
0fe675af 1947 return re.sub(re.escape(eatspace) + r' *', '', raw)
0f923be2 1948
437db254 1949
0f923be2 1950def mcgen(code, **kwds):
77e703b8
MA
1951 if code[0] == '\n':
1952 code = code[1:]
1953 return cgen(code, **kwds)
0f923be2 1954
0f923be2
MR
1955
1956def guardname(filename):
f9c14639 1957 return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
c0afa9c5 1958
437db254 1959
c0afa9c5
MR
1960def guardstart(name):
1961 return mcgen('''
c0afa9c5
MR
1962#ifndef %(name)s
1963#define %(name)s
1964
1965''',
1966 name=guardname(name))
1967
437db254 1968
c0afa9c5
MR
1969def guardend(name):
1970 return mcgen('''
1971
1972#endif /* %(name)s */
c0afa9c5
MR
1973''',
1974 name=guardname(name))
2114f5a9 1975
437db254 1976
e98859a9 1977def gen_enum_lookup(name, values, prefix=None):
efd2eaa6
MA
1978 ret = mcgen('''
1979
f7abe0ec
MAL
1980const QEnumLookup %(c_name)s_lookup = {
1981 .array = (const char *const[]) {
efd2eaa6 1982''',
e98859a9 1983 c_name=c_name(name))
efd2eaa6
MA
1984 for value in values:
1985 index = c_enum_const(name, value, prefix)
1986 ret += mcgen('''
f7abe0ec 1987 [%(index)s] = "%(value)s",
efd2eaa6 1988''',
e98859a9 1989 index=index, value=value)
efd2eaa6 1990
efd2eaa6 1991 ret += mcgen('''
f7abe0ec
MAL
1992 },
1993 .size = %(max_index)s
efd2eaa6
MA
1994};
1995''',
ebf677c8 1996 max_index=c_enum_const(name, '_MAX', prefix))
efd2eaa6
MA
1997 return ret
1998
437db254 1999
e98859a9
MA
2000def gen_enum(name, values, prefix=None):
2001 # append automatically generated _MAX value
7fb1cf16 2002 enum_values = values + ['_MAX']
efd2eaa6 2003
e98859a9 2004 ret = mcgen('''
efd2eaa6 2005
e98859a9 2006typedef enum %(c_name)s {
efd2eaa6 2007''',
e98859a9 2008 c_name=c_name(name))
efd2eaa6
MA
2009
2010 i = 0
2011 for value in enum_values:
e98859a9
MA
2012 ret += mcgen('''
2013 %(c_enum)s = %(i)d,
efd2eaa6 2014''',
e98859a9 2015 c_enum=c_enum_const(name, value, prefix),
efd2eaa6
MA
2016 i=i)
2017 i += 1
2018
e98859a9
MA
2019 ret += mcgen('''
2020} %(c_name)s;
efd2eaa6 2021''',
e98859a9
MA
2022 c_name=c_name(name))
2023
2024 ret += mcgen('''
efd2eaa6 2025
5b5f825d 2026#define %(c_name)s_str(val) \\
f7abe0ec 2027 qapi_enum_lookup(&%(c_name)s_lookup, (val))
5b5f825d 2028
f7abe0ec 2029extern const QEnumLookup %(c_name)s_lookup;
e98859a9
MA
2030''',
2031 c_name=c_name(name))
2032 return ret
efd2eaa6 2033
437db254 2034
086ee7a6 2035def build_params(arg_type, boxed, extra):
03b4367a 2036 if not arg_type:
c818408e 2037 assert not boxed
03b4367a 2038 return extra
03b4367a
MA
2039 ret = ''
2040 sep = ''
48825ca4 2041 if boxed:
c818408e
EB
2042 ret += '%s arg' % arg_type.c_param_type()
2043 sep = ', '
48825ca4
EB
2044 else:
2045 assert not arg_type.variants
2046 for memb in arg_type.members:
2047 ret += sep
2048 sep = ', '
2049 if memb.optional:
2050 ret += 'bool has_%s, ' % c_name(memb.name)
2051 ret += '%s %s' % (memb.type.c_param_type(),
2052 c_name(memb.name))
03b4367a
MA
2053 if extra:
2054 ret += sep + extra
2055 return ret
2056
1f353344 2057
00e4b285 2058#
47a6ea9a 2059# Accumulate and write output
00e4b285
MA
2060#
2061
47a6ea9a
MA
2062class QAPIGen(object):
2063
2064 def __init__(self):
2065 self._preamble = ''
2066 self._body = ''
2067
2068 def preamble_add(self, text):
2069 self._preamble += text
2070
2071 def add(self, text):
2072 self._body += text
2073
2074 def _top(self, fname):
2075 return ''
2076
2077 def _bottom(self, fname):
2078 return ''
437db254 2079
47a6ea9a 2080 def write(self, output_dir, fname):
cdb6610a
MA
2081 pathname = os.path.join(output_dir, fname)
2082 dir = os.path.dirname(pathname)
2083 if dir:
47a6ea9a 2084 try:
cdb6610a 2085 os.makedirs(dir)
47a6ea9a
MA
2086 except os.error as e:
2087 if e.errno != errno.EEXIST:
2088 raise
cdb6610a 2089 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
de685ae5
MA
2090 if sys.version_info[0] >= 3:
2091 f = open(fd, 'r+', encoding='utf-8')
2092 else:
2093 f = os.fdopen(fd, 'r+')
907b8466 2094 text = (self._top(fname) + self._preamble + self._body
47a6ea9a 2095 + self._bottom(fname))
907b8466
MA
2096 oldtext = f.read(len(text) + 1)
2097 if text != oldtext:
2098 f.seek(0)
2099 f.truncate(0)
2100 f.write(text)
47a6ea9a
MA
2101 f.close()
2102
2103
2104class QAPIGenC(QAPIGen):
2105
2106 def __init__(self, blurb, pydoc):
2107 QAPIGen.__init__(self)
2108 self._blurb = blurb
2109 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2110 re.MULTILINE))
2111
2112 def _top(self, fname):
2113 return mcgen('''
2114/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
c263de3f
MA
2115
2116/*
2117%(blurb)s
5ddeec83
MA
2118 *
2119 * %(copyright)s
c263de3f
MA
2120 *
2121 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2122 * See the COPYING.LIB file in the top-level directory.
2123 */
2124
2125''',
47a6ea9a 2126 blurb=self._blurb, copyright=self._copyright)
12f8e1b9 2127
252dc310
MA
2128 def _bottom(self, fname):
2129 return mcgen('''
2130/* Dummy declaration to prevent empty .o file */
2131char dummy_%(name)s;
2132''',
2133 name=c_name(fname))
2134
12f8e1b9 2135
47a6ea9a 2136class QAPIGenH(QAPIGenC):
12f8e1b9 2137
47a6ea9a
MA
2138 def _top(self, fname):
2139 return QAPIGenC._top(self, fname) + guardstart(fname)
12f8e1b9 2140
47a6ea9a
MA
2141 def _bottom(self, fname):
2142 return guardend(fname)
12f8e1b9 2143
437db254 2144
47a6ea9a 2145class QAPIGenDoc(QAPIGen):
c263de3f 2146
47a6ea9a
MA
2147 def _top(self, fname):
2148 return (QAPIGen._top(self, fname)
2149 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
71b3f045
MA
2150
2151
2152class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2153
2154 def __init__(self, prefix, what, blurb, pydoc):
2155 self._prefix = prefix
2156 self._what = what
2157 self._genc = QAPIGenC(blurb, pydoc)
2158 self._genh = QAPIGenH(blurb, pydoc)
2159
2160 def write(self, output_dir):
2161 self._genc.write(output_dir, self._prefix + self._what + '.c')
2162 self._genh.write(output_dir, self._prefix + self._what + '.h')
cdb6610a
MA
2163
2164
2165class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2166
2167 def __init__(self, prefix, what, blurb, pydoc):
2168 self._prefix = prefix
2169 self._what = what
2170 self._blurb = blurb
2171 self._pydoc = pydoc
2172 self._module = {}
252dc310 2173 self._main_module = None
cdb6610a
MA
2174
2175 def _module_basename(self, what, name):
2176 if name is None:
2177 return re.sub(r'-', '-builtin-', what)
252dc310
MA
2178 basename = os.path.join(os.path.dirname(name),
2179 self._prefix + what)
2180 if name == self._main_module:
2181 return basename
2182 return basename + '-' + os.path.splitext(os.path.basename(name))[0]
cdb6610a
MA
2183
2184 def _add_module(self, name, blurb):
252dc310
MA
2185 if self._main_module is None and name is not None:
2186 self._main_module = name
cdb6610a
MA
2187 genc = QAPIGenC(blurb, self._pydoc)
2188 genh = QAPIGenH(blurb, self._pydoc)
2189 self._module[name] = (genc, genh)
2190 self._set_module(name)
2191
2192 def _set_module(self, name):
2193 self._genc, self._genh = self._module[name]
2194
252dc310 2195 def write(self, output_dir, opt_builtins=False):
cdb6610a
MA
2196 for name in self._module:
2197 if name is None and not opt_builtins:
2198 continue
2199 basename = self._module_basename(self._what, name)
2200 (genc, genh) = self._module[name]
2201 genc.write(output_dir, basename + '.c')
2202 genh.write(output_dir, basename + '.h')
2203
2204 def _begin_module(self, name):
2205 pass
2206
2207 def visit_module(self, name):
252dc310
MA
2208 if name in self._module:
2209 self._set_module(name)
cdb6610a
MA
2210 return
2211 self._add_module(name, self._blurb)
2212 self._begin_module(name)
252dc310
MA
2213
2214 def visit_include(self, name, info):
2215 basename = self._module_basename(self._what, name)
2216 self._genh.preamble_add(mcgen('''
2217#include "%(basename)s.h"
2218''',
2219 basename=basename))
This page took 0.861356 seconds and 4 git commands to generate.