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