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