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