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