]> git.proxmox.com Git - mirror_qemu.git/blame - scripts/qapi.py
qapi: struct_types is a list used like a dict, make it one
[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
a719a27c 14import re
0f923be2 15from ordereddict import OrderedDict
12f8e1b9 16import errno
2114f5a9 17import getopt
33aaad52 18import os
2caba36c 19import sys
47299262 20import string
0f923be2 21
b52c4b9c 22builtin_types = {
69dd62df
KW
23 'str': 'QTYPE_QSTRING',
24 'int': 'QTYPE_QINT',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
27 'int8': 'QTYPE_QINT',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
cb17f79e 35 'size': 'QTYPE_QINT',
1310a3d3 36 'any': None, # any QType possible, actually
7264f5c5 37 'QType': 'QTYPE_QSTRING',
69dd62df
KW
38}
39
bc52d03f
MA
40# Are documentation comments required?
41doc_required = False
42
10d4d997 43# Whitelist of commands allowed to return a non-dictionary
1554a8fa 44returns_whitelist = []
10d4d997 45
893e1f2c 46# Whitelist of entities allowed to violate case conventions
2cfbae3c 47name_case_whitelist = []
893e1f2c 48
5f018446 49enum_types = {}
ed285bf8 50struct_types = {}
4dc2e690 51union_types = []
4dc2e690
EB
52all_names = {}
53
00e4b285
MA
54#
55# Parsing the schema into expressions
56#
57
437db254 58
a719a27c 59def error_path(parent):
ef801a9b 60 res = ''
a719a27c 61 while parent:
ef801a9b 62 res = ('In file included from %s:%d:\n' % (parent['file'],
a719a27c
LV
63 parent['line'])) + res
64 parent = parent['parent']
65 return res
66
437db254 67
4148c298
MAL
68class QAPIError(Exception):
69 def __init__(self, fname, line, col, incl_info, msg):
59b00542 70 Exception.__init__(self)
4148c298
MAL
71 self.fname = fname
72 self.line = line
73 self.col = col
74 self.info = incl_info
2caba36c 75 self.msg = msg
2caba36c
MA
76
77 def __str__(self):
ef801a9b 78 loc = '%s:%d' % (self.fname, self.line)
4148c298 79 if self.col is not None:
ef801a9b
MA
80 loc += ':%s' % self.col
81 return error_path(self.info) + '%s: %s' % (loc, self.msg)
2caba36c 82
437db254 83
4148c298
MAL
84class QAPIParseError(QAPIError):
85 def __init__(self, parser, msg):
86 col = 1
87 for ch in parser.src[parser.line_pos:parser.pos]:
88 if ch == '\t':
89 col = (col + 7) % 8 + 1
90 else:
91 col += 1
92 QAPIError.__init__(self, parser.fname, parser.line, col,
93 parser.incl_info, msg)
b86b05ed 94
4148c298
MAL
95
96class QAPISemError(QAPIError):
97 def __init__(self, info, msg):
98 QAPIError.__init__(self, info['file'], info['line'], None,
99 info['parent'], msg)
b86b05ed 100
437db254 101
3313b612
MAL
102class QAPIDoc(object):
103 class Section(object):
104 def __init__(self, name=None):
105 # optional section name (argument/member or section name)
106 self.name = name
107 # the list of lines for this section
108 self.content = []
b116fd8e 109 self.optional = False
3313b612
MAL
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:
4148c298 522 raise QAPIParseError(self, 'Expected "{", "[" or string')
c7a3f252 523 return expr
bd9927fe 524
3313b612
MAL
525 def get_doc(self, info):
526 if self.val != '##':
527 raise QAPIParseError(self, "Junk after '##' at start of "
528 "documentation comment")
529
530 doc = QAPIDoc(self, info)
531 self.accept(False)
532 while self.tok == '#':
533 if self.val.startswith('##'):
534 # End of doc comment
535 if self.val != '##':
536 raise QAPIParseError(self, "Junk after '##' at end of "
537 "documentation comment")
4ea7148e 538 doc.end_comment()
3313b612
MAL
539 self.accept()
540 return doc
541 else:
542 doc.append(self.val)
543 self.accept(False)
544
545 raise QAPIParseError(self, "Documentation comment must end with '##'")
546
547
00e4b285
MA
548#
549# Semantic analysis of schema expressions
ac88219a
MA
550# TODO fold into QAPISchema
551# TODO catching name collisions in generated code would be nice
00e4b285
MA
552#
553
437db254 554
14f00c6c 555def find_base_members(base):
ac4338f8
EB
556 if isinstance(base, dict):
557 return base
ed285bf8 558 base_struct_define = struct_types.get(base)
b86b05ed
WX
559 if not base_struct_define:
560 return None
561 return base_struct_define['data']
562
437db254 563
811d04fd
EB
564# Return the qtype of an alternate branch, or None on error.
565def find_alternate_member_qtype(qapi_type):
437db254 566 if qapi_type in builtin_types:
44bd1276 567 return builtin_types[qapi_type]
ed285bf8 568 elif qapi_type in struct_types:
ef801a9b 569 return 'QTYPE_QDICT'
5f018446 570 elif qapi_type in enum_types:
ef801a9b 571 return 'QTYPE_QSTRING'
811d04fd 572 elif find_union(qapi_type):
ef801a9b 573 return 'QTYPE_QDICT'
44bd1276
EB
574 return None
575
437db254 576
bceae769
WX
577# Return the discriminator enum define if discriminator is specified as an
578# enum type, otherwise return None.
579def discriminator_find_enum_define(expr):
580 base = expr.get('base')
581 discriminator = expr.get('discriminator')
582
583 if not (discriminator and base):
584 return None
585
14f00c6c
EB
586 base_members = find_base_members(base)
587 if not base_members:
bceae769
WX
588 return None
589
14f00c6c 590 discriminator_type = base_members.get(discriminator)
bceae769
WX
591 if not discriminator_type:
592 return None
593
5f018446 594 return enum_types.get(discriminator_type)
bceae769 595
437db254 596
59a92fee
EB
597# Names must be letters, numbers, -, and _. They must start with letter,
598# except for downstream extensions which must start with __RFQDN_.
599# Dots are only valid in the downstream extension prefix.
0fe675af 600valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
59a92fee 601 '[a-zA-Z][a-zA-Z0-9_-]*$')
437db254
EB
602
603
4148c298 604def check_name(info, source, name, allow_optional=False,
437db254 605 enum_member=False):
c9e0a798
EB
606 global valid_name
607 membername = name
608
609 if not isinstance(name, str):
4148c298 610 raise QAPISemError(info, "%s requires a string name" % source)
c9e0a798
EB
611 if name.startswith('*'):
612 membername = name[1:]
613 if not allow_optional:
4148c298
MAL
614 raise QAPISemError(info, "%s does not allow optional name '%s'"
615 % (source, name))
c9e0a798
EB
616 # Enum members can start with a digit, because the generated C
617 # code always prefixes it with the enum name
59a92fee
EB
618 if enum_member and membername[0].isdigit():
619 membername = 'D' + membername
7599697c
EB
620 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
621 # and 'q_obj_*' implicit type names.
9fb081e0
EB
622 if not valid_name.match(membername) or \
623 c_name(membername, False).startswith('q_'):
4148c298 624 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
c9e0a798 625
437db254
EB
626
627def add_name(name, info, meta, implicit=False):
00e4b285
MA
628 global all_names
629 check_name(info, "'%s'" % meta, name)
d90675fa
MA
630 # FIXME should reject names that differ only in '_' vs. '.'
631 # vs. '-', because they're liable to clash in generated C.
00e4b285 632 if name in all_names:
4148c298
MAL
633 raise QAPISemError(info, "%s '%s' is already defined"
634 % (all_names[name], name))
255960dd 635 if not implicit and (name.endswith('Kind') or name.endswith('List')):
4148c298
MAL
636 raise QAPISemError(info, "%s '%s' should not end in '%s'"
637 % (meta, name, name[-4:]))
00e4b285
MA
638 all_names[name] = meta
639
437db254 640
00e4b285
MA
641def add_union(definition, info):
642 global union_types
00e4b285
MA
643 union_types.append(definition)
644
437db254 645
00e4b285
MA
646def find_union(name):
647 global union_types
648 for union in union_types:
649 if union['union'] == name:
650 return union
651 return None
652
437db254 653
4148c298 654def check_type(info, source, value, allow_array=False,
437db254
EB
655 allow_dict=False, allow_optional=False,
656 allow_metas=[]):
dd883c6f 657 global all_names
dd883c6f
EB
658
659 if value is None:
660 return
661
dd883c6f
EB
662 # Check if array type for value is okay
663 if isinstance(value, list):
664 if not allow_array:
4148c298 665 raise QAPISemError(info, "%s cannot be an array" % source)
dd883c6f 666 if len(value) != 1 or not isinstance(value[0], str):
4148c298
MAL
667 raise QAPISemError(info,
668 "%s: array type must contain single type name" %
669 source)
dd883c6f 670 value = value[0]
dd883c6f
EB
671
672 # Check if type name for value is okay
673 if isinstance(value, str):
437db254 674 if value not in all_names:
4148c298
MAL
675 raise QAPISemError(info, "%s uses unknown type '%s'"
676 % (source, value))
dd883c6f 677 if not all_names[value] in allow_metas:
4148c298
MAL
678 raise QAPISemError(info, "%s cannot use %s type '%s'" %
679 (source, all_names[value], value))
dd883c6f
EB
680 return
681
dd883c6f 682 if not allow_dict:
4148c298 683 raise QAPISemError(info, "%s should be a type name" % source)
c6b71e5a
MA
684
685 if not isinstance(value, OrderedDict):
4148c298
MAL
686 raise QAPISemError(info,
687 "%s should be a dictionary or type name" % source)
c6b71e5a
MA
688
689 # value is a dictionary, check that each member is okay
dd883c6f 690 for (key, arg) in value.items():
4148c298 691 check_name(info, "Member of %s" % source, key,
c9e0a798 692 allow_optional=allow_optional)
5e59baf9 693 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
4148c298
MAL
694 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
695 % (source, key))
6b5abc7d
EB
696 # Todo: allow dictionaries to represent default values of
697 # an optional argument.
4148c298 698 check_type(info, "Member '%s' of %s" % (key, source), arg,
2d21291a 699 allow_array=True,
dd883c6f 700 allow_metas=['built-in', 'union', 'alternate', 'struct',
6b5abc7d 701 'enum'])
dd883c6f 702
437db254 703
4148c298 704def check_command(expr, info):
dd883c6f 705 name = expr['command']
c818408e 706 boxed = expr.get('boxed', False)
2cbf0992 707
c818408e
EB
708 args_meta = ['struct']
709 if boxed:
710 args_meta += ['union', 'alternate']
4148c298 711 check_type(info, "'data' for command '%s'" % name,
c818408e
EB
712 expr.get('data'), allow_dict=not boxed, allow_optional=True,
713 allow_metas=args_meta)
10d4d997
EB
714 returns_meta = ['union', 'struct']
715 if name in returns_whitelist:
716 returns_meta += ['built-in', 'alternate', 'enum']
4148c298 717 check_type(info, "'returns' for command '%s'" % name,
9b090d42 718 expr.get('returns'), allow_array=True,
2d21291a 719 allow_optional=True, allow_metas=returns_meta)
dd883c6f 720
437db254 721
4148c298 722def check_event(expr, info):
4dc2e690 723 name = expr['event']
c818408e 724 boxed = expr.get('boxed', False)
4dc2e690 725
c818408e
EB
726 meta = ['struct']
727 if boxed:
728 meta += ['union', 'alternate']
4148c298 729 check_type(info, "'data' for event '%s'" % name,
c818408e
EB
730 expr.get('data'), allow_dict=not boxed, allow_optional=True,
731 allow_metas=meta)
21cd70df 732
437db254 733
4148c298 734def check_union(expr, info):
b86b05ed
WX
735 name = expr['union']
736 base = expr.get('base')
737 discriminator = expr.get('discriminator')
738 members = expr['data']
739
811d04fd 740 # Two types of unions, determined by discriminator.
811d04fd
EB
741
742 # With no discriminator it is a simple union.
743 if discriminator is None:
b86b05ed 744 enum_define = None
437db254 745 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
44bd1276 746 if base is not None:
4148c298
MAL
747 raise QAPISemError(info, "Simple union '%s' must not have a base" %
748 name)
b86b05ed
WX
749
750 # Else, it's a flat union.
751 else:
ac4338f8 752 # The object must have a string or dictionary 'base'.
4148c298 753 check_type(info, "'base' for union '%s'" % name,
ac4338f8
EB
754 base, allow_dict=True, allow_optional=True,
755 allow_metas=['struct'])
376863ef 756 if not base:
4148c298
MAL
757 raise QAPISemError(info, "Flat union '%s' must have a base"
758 % name)
14f00c6c 759 base_members = find_base_members(base)
48153745 760 assert base_members is not None
44bd1276 761
c9e0a798 762 # The value of member 'discriminator' must name a non-optional
fd41dd4e 763 # member of the base struct.
4148c298 764 check_name(info, "Discriminator of flat union '%s'" % name,
c9e0a798 765 discriminator)
14f00c6c 766 discriminator_type = base_members.get(discriminator)
b86b05ed 767 if not discriminator_type:
4148c298
MAL
768 raise QAPISemError(info,
769 "Discriminator '%s' is not a member of base "
770 "struct '%s'"
771 % (discriminator, base))
5f018446 772 enum_define = enum_types.get(discriminator_type)
437db254 773 allow_metas = ['struct']
5223070c
WX
774 # Do not allow string discriminator
775 if not enum_define:
4148c298
MAL
776 raise QAPISemError(info,
777 "Discriminator '%s' must be of enumeration "
778 "type" % discriminator)
b86b05ed 779
02a57ae3
EB
780 # Check every branch; don't allow an empty union
781 if len(members) == 0:
4148c298 782 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
b86b05ed 783 for (key, value) in members.items():
4148c298 784 check_name(info, "Member of union '%s'" % name, key)
c9e0a798 785
01cfbaa4 786 # Each value must name a known type
4148c298 787 check_type(info, "Member '%s' of union '%s'" % (key, name),
f9a14273 788 value, allow_array=not base, allow_metas=allow_metas)
dd883c6f 789
44bd1276 790 # If the discriminator names an enum type, then all members
61a94661 791 # of 'data' must also be members of the enum type.
44bd1276 792 if enum_define:
eda43c68 793 if key not in enum_define['data']:
4148c298
MAL
794 raise QAPISemError(info,
795 "Discriminator value '%s' is not found in "
796 "enum '%s'"
eda43c68 797 % (key, enum_define['enum']))
44bd1276 798
d0b18239
EB
799 # If discriminator is user-defined, ensure all values are covered
800 if enum_define:
eda43c68 801 for value in enum_define['data']:
d0b18239 802 if value not in members.keys():
4148c298
MAL
803 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
804 % (name, value))
d0b18239 805
437db254 806
4148c298 807def check_alternate(expr, info):
ab916fad 808 name = expr['alternate']
811d04fd 809 members = expr['data']
811d04fd
EB
810 types_seen = {}
811
02a57ae3
EB
812 # Check every branch; require at least two branches
813 if len(members) < 2:
4148c298
MAL
814 raise QAPISemError(info,
815 "Alternate '%s' should have at least two branches "
816 "in 'data'" % name)
811d04fd 817 for (key, value) in members.items():
4148c298 818 check_name(info, "Member of alternate '%s'" % name, key)
c9e0a798 819
811d04fd 820 # Ensure alternates have no type conflicts.
4148c298 821 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
dd883c6f
EB
822 value,
823 allow_metas=['built-in', 'union', 'struct', 'enum'])
811d04fd 824 qtype = find_alternate_member_qtype(value)
46534309 825 if not qtype:
4148c298
MAL
826 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
827 "type '%s'" % (name, key, value))
811d04fd 828 if qtype in types_seen:
4148c298
MAL
829 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
830 "be distinguished from member '%s'"
831 % (name, key, types_seen[qtype]))
811d04fd 832 types_seen[qtype] = key
b86b05ed 833
437db254 834
4148c298 835def check_enum(expr, info):
cf393590
EB
836 name = expr['enum']
837 members = expr.get('data')
351d36e4 838 prefix = expr.get('prefix')
cf393590
EB
839
840 if not isinstance(members, list):
4148c298
MAL
841 raise QAPISemError(info,
842 "Enum '%s' requires an array for 'data'" % name)
351d36e4 843 if prefix is not None and not isinstance(prefix, str):
4148c298
MAL
844 raise QAPISemError(info,
845 "Enum '%s' requires a string for 'prefix'" % name)
cf393590 846 for member in members:
4148c298 847 check_name(info, "Member of enum '%s'" % name, member,
c9e0a798 848 enum_member=True)
cf393590 849
437db254 850
4148c298 851def check_struct(expr, info):
fd41dd4e 852 name = expr['struct']
dd883c6f
EB
853 members = expr['data']
854
4148c298 855 check_type(info, "'data' for struct '%s'" % name, members,
c9e0a798 856 allow_dict=True, allow_optional=True)
4148c298 857 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
dd883c6f
EB
858 allow_metas=['struct'])
859
437db254 860
0545f6b8
EB
861def check_keys(expr_elem, meta, required, optional=[]):
862 expr = expr_elem['expr']
863 info = expr_elem['info']
864 name = expr[meta]
865 if not isinstance(name, str):
4148c298 866 raise QAPISemError(info, "'%s' key must have a string value" % meta)
437db254 867 required = required + [meta]
0545f6b8 868 for (key, value) in expr.items():
437db254 869 if key not in required and key not in optional:
4148c298
MAL
870 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
871 % (key, meta, name))
437db254 872 if (key == 'gen' or key == 'success-response') and value is not False:
4148c298
MAL
873 raise QAPISemError(info,
874 "'%s' of %s '%s' should only use false value"
875 % (key, meta, name))
c818408e 876 if key == 'boxed' and value is not True:
4148c298
MAL
877 raise QAPISemError(info,
878 "'%s' of %s '%s' should only use true value"
879 % (key, meta, name))
0545f6b8 880 for key in required:
437db254 881 if key not in expr:
4148c298
MAL
882 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
883 % (key, meta, name))
0545f6b8 884
437db254 885
4d076d67 886def check_exprs(exprs):
4dc2e690 887 global all_names
4dc2e690 888
7947016d 889 # Populate name table with names of built-in types
4d076d67
MA
890 for builtin in builtin_types.keys():
891 all_names[builtin] = 'built-in'
7947016d
MA
892
893 # Learn the types and check for valid expression keys
4d076d67
MA
894 for expr_elem in exprs:
895 expr = expr_elem['expr']
896 info = expr_elem['info']
7947016d 897 doc = expr_elem.get('doc')
3313b612 898
7947016d 899 if not doc and doc_required:
3313b612
MAL
900 raise QAPISemError(info,
901 "Expression missing documentation comment")
902
437db254 903 if 'enum' in expr:
6f05345f 904 meta = 'enum'
351d36e4 905 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
5f018446 906 enum_types[expr[meta]] = expr
437db254 907 elif 'union' in expr:
6f05345f 908 meta = 'union'
4d076d67
MA
909 check_keys(expr_elem, 'union', ['data'],
910 ['base', 'discriminator'])
911 add_union(expr, info)
437db254 912 elif 'alternate' in expr:
6f05345f 913 meta = 'alternate'
4d076d67 914 check_keys(expr_elem, 'alternate', ['data'])
437db254 915 elif 'struct' in expr:
6f05345f 916 meta = 'struct'
4d076d67 917 check_keys(expr_elem, 'struct', ['data'], ['base'])
ed285bf8 918 struct_types[expr[meta]] = expr
437db254 919 elif 'command' in expr:
6f05345f 920 meta = 'command'
4d076d67 921 check_keys(expr_elem, 'command', [],
c818408e 922 ['data', 'returns', 'gen', 'success-response', 'boxed'])
437db254 923 elif 'event' in expr:
6f05345f 924 meta = 'event'
c818408e 925 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
4d076d67 926 else:
4148c298
MAL
927 raise QAPISemError(expr_elem['info'],
928 "Expression is missing metatype")
6f05345f
MA
929 name = expr[meta]
930 add_name(name, info, meta)
7947016d
MA
931 if doc and doc.symbol != name:
932 raise QAPISemError(info, "Definition of '%s' follows documentation"
933 " for '%s'" % (name, doc.symbol))
2caba36c 934
4d076d67
MA
935 # Try again for hidden UnionKind enum
936 for expr_elem in exprs:
937 expr = expr_elem['expr']
eda43c68
MA
938 if 'union' in expr and not discriminator_find_enum_define(expr):
939 name = '%sKind' % expr['union']
437db254 940 elif 'alternate' in expr:
eda43c68
MA
941 name = '%sKind' % expr['alternate']
942 else:
943 continue
5f018446 944 enum_types[name] = {'enum': name}
6f05345f 945 add_name(name, info, 'enum', implicit=True)
4d076d67
MA
946
947 # Validate that exprs make sense
948 for expr_elem in exprs:
949 expr = expr_elem['expr']
950 info = expr_elem['info']
a9f396b0 951 doc = expr_elem.get('doc')
268a1c5e 952
437db254 953 if 'enum' in expr:
4d076d67 954 check_enum(expr, info)
437db254 955 elif 'union' in expr:
4d076d67 956 check_union(expr, info)
437db254 957 elif 'alternate' in expr:
4d076d67 958 check_alternate(expr, info)
437db254 959 elif 'struct' in expr:
4d076d67 960 check_struct(expr, info)
437db254 961 elif 'command' in expr:
4d076d67 962 check_command(expr, info)
437db254 963 elif 'event' in expr:
4d076d67
MA
964 check_event(expr, info)
965 else:
966 assert False, 'unexpected meta type'
967
a9f396b0
MA
968 if doc:
969 doc.check_expr(expr)
ac88219a 970
a9f396b0 971 return exprs
3313b612
MAL
972
973
ac88219a
MA
974#
975# Schema compiler frontend
976#
977
978class QAPISchemaEntity(object):
069fb5b2 979 def __init__(self, name, info, doc):
ac88219a
MA
980 assert isinstance(name, str)
981 self.name = name
99df5289
EB
982 # For explicitly defined entities, info points to the (explicit)
983 # definition. For builtins (and their arrays), info is None.
984 # For implicitly defined entities, info points to a place that
985 # triggered the implicit definition (there may be more than one
986 # such place).
ac88219a 987 self.info = info
069fb5b2 988 self.doc = doc
ac88219a 989
f51d8c3d
MA
990 def c_name(self):
991 return c_name(self.name)
992
ac88219a
MA
993 def check(self, schema):
994 pass
995
49823c4b
EB
996 def is_implicit(self):
997 return not self.info
998
3f7dc21b
MA
999 def visit(self, visitor):
1000 pass
1001
1002
1003class QAPISchemaVisitor(object):
1004 def visit_begin(self, schema):
1005 pass
1006
1007 def visit_end(self):
1008 pass
1009
25a0d9c9
EB
1010 def visit_needed(self, entity):
1011 # Default to visiting everything
1012 return True
1013
3f7dc21b
MA
1014 def visit_builtin_type(self, name, info, json_type):
1015 pass
1016
1017 def visit_enum_type(self, name, info, values, prefix):
1018 pass
1019
1020 def visit_array_type(self, name, info, element_type):
1021 pass
1022
1023 def visit_object_type(self, name, info, base, members, variants):
1024 pass
1025
39a18158
MA
1026 def visit_object_type_flat(self, name, info, members, variants):
1027 pass
1028
3f7dc21b
MA
1029 def visit_alternate_type(self, name, info, variants):
1030 pass
1031
1032 def visit_command(self, name, info, arg_type, ret_type,
48825ca4 1033 gen, success_response, boxed):
3f7dc21b
MA
1034 pass
1035
48825ca4 1036 def visit_event(self, name, info, arg_type, boxed):
3f7dc21b
MA
1037 pass
1038
ac88219a
MA
1039
1040class QAPISchemaType(QAPISchemaEntity):
4040d995
EB
1041 # Return the C type for common use.
1042 # For the types we commonly box, this is a pointer type.
1043 def c_type(self):
1044 pass
1045
1046 # Return the C type to be used in a parameter list.
1047 def c_param_type(self):
1048 return self.c_type()
1049
1050 # Return the C type to be used where we suppress boxing.
1051 def c_unboxed_type(self):
1052 return self.c_type()
f51d8c3d 1053
f51d8c3d
MA
1054 def json_type(self):
1055 pass
1056
1057 def alternate_qtype(self):
1058 json2qtype = {
1059 'string': 'QTYPE_QSTRING',
1060 'number': 'QTYPE_QFLOAT',
1061 'int': 'QTYPE_QINT',
1062 'boolean': 'QTYPE_QBOOL',
1063 'object': 'QTYPE_QDICT'
1064 }
1065 return json2qtype.get(self.json_type())
ac88219a 1066
691e0313
MA
1067 def doc_type(self):
1068 if self.is_implicit():
1069 return None
1070 return self.name
1071
ac88219a
MA
1072
1073class QAPISchemaBuiltinType(QAPISchemaType):
861877a0 1074 def __init__(self, name, json_type, c_type):
069fb5b2 1075 QAPISchemaType.__init__(self, name, None, None)
f51d8c3d
MA
1076 assert not c_type or isinstance(c_type, str)
1077 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1078 'value')
1079 self._json_type_name = json_type
1080 self._c_type_name = c_type
f51d8c3d
MA
1081
1082 def c_name(self):
1083 return self.name
1084
4040d995
EB
1085 def c_type(self):
1086 return self._c_type_name
1087
1088 def c_param_type(self):
1089 if self.name == 'str':
f51d8c3d
MA
1090 return 'const ' + self._c_type_name
1091 return self._c_type_name
1092
f51d8c3d
MA
1093 def json_type(self):
1094 return self._json_type_name
ac88219a 1095
691e0313
MA
1096 def doc_type(self):
1097 return self.json_type()
1098
3f7dc21b
MA
1099 def visit(self, visitor):
1100 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1101
ac88219a
MA
1102
1103class QAPISchemaEnumType(QAPISchemaType):
069fb5b2
MA
1104 def __init__(self, name, info, doc, values, prefix):
1105 QAPISchemaType.__init__(self, name, info, doc)
ac88219a 1106 for v in values:
93bda4dd
EB
1107 assert isinstance(v, QAPISchemaMember)
1108 v.set_owner(name)
ac88219a
MA
1109 assert prefix is None or isinstance(prefix, str)
1110 self.values = values
1111 self.prefix = prefix
1112
1113 def check(self, schema):
93bda4dd
EB
1114 seen = {}
1115 for v in self.values:
1116 v.check_clash(self.info, seen)
069fb5b2
MA
1117 if self.doc:
1118 self.doc.connect_member(v)
ac88219a 1119
99df5289 1120 def is_implicit(self):
4636211e
MA
1121 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1122 return self.name.endswith('Kind') or self.name == 'QType'
99df5289 1123
4040d995 1124 def c_type(self):
f51d8c3d
MA
1125 return c_name(self.name)
1126
93bda4dd
EB
1127 def member_names(self):
1128 return [v.name for v in self.values]
1129
f51d8c3d
MA
1130 def json_type(self):
1131 return 'string'
1132
3f7dc21b
MA
1133 def visit(self, visitor):
1134 visitor.visit_enum_type(self.name, self.info,
93bda4dd 1135 self.member_names(), self.prefix)
3f7dc21b 1136
ac88219a
MA
1137
1138class QAPISchemaArrayType(QAPISchemaType):
1139 def __init__(self, name, info, element_type):
069fb5b2 1140 QAPISchemaType.__init__(self, name, info, None)
ac88219a
MA
1141 assert isinstance(element_type, str)
1142 self._element_type_name = element_type
1143 self.element_type = None
1144
1145 def check(self, schema):
1146 self.element_type = schema.lookup_type(self._element_type_name)
1147 assert self.element_type
1148
99df5289
EB
1149 def is_implicit(self):
1150 return True
1151
4040d995
EB
1152 def c_type(self):
1153 return c_name(self.name) + pointer_suffix
1154
f51d8c3d
MA
1155 def json_type(self):
1156 return 'array'
1157
691e0313
MA
1158 def doc_type(self):
1159 elt_doc_type = self.element_type.doc_type()
1160 if not elt_doc_type:
1161 return None
1162 return 'array of ' + elt_doc_type
1163
3f7dc21b
MA
1164 def visit(self, visitor):
1165 visitor.visit_array_type(self.name, self.info, self.element_type)
1166
ac88219a
MA
1167
1168class QAPISchemaObjectType(QAPISchemaType):
069fb5b2 1169 def __init__(self, name, info, doc, base, local_members, variants):
da34a9bd
EB
1170 # struct has local_members, optional base, and no variants
1171 # flat union has base, variants, and no local_members
1172 # simple union has local_members, variants, and no base
069fb5b2 1173 QAPISchemaType.__init__(self, name, info, doc)
ac88219a
MA
1174 assert base is None or isinstance(base, str)
1175 for m in local_members:
1176 assert isinstance(m, QAPISchemaObjectTypeMember)
88d4ef8b
EB
1177 m.set_owner(name)
1178 if variants is not None:
1179 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1180 variants.set_owner(name)
ac88219a
MA
1181 self._base_name = base
1182 self.base = None
1183 self.local_members = local_members
1184 self.variants = variants
1185 self.members = None
1186
1187 def check(self, schema):
bac5429c 1188 if self.members is False: # check for cycles
4148c298
MAL
1189 raise QAPISemError(self.info,
1190 "Object %s contains itself" % self.name)
ac88219a
MA
1191 if self.members:
1192 return
1193 self.members = False # mark as being checked
23a4b2c6 1194 seen = OrderedDict()
ac88219a
MA
1195 if self._base_name:
1196 self.base = schema.lookup_type(self._base_name)
1197 assert isinstance(self.base, QAPISchemaObjectType)
ac88219a 1198 self.base.check(schema)
27b60ab9 1199 self.base.check_clash(schema, self.info, seen)
ac88219a 1200 for m in self.local_members:
e564e2dd 1201 m.check(schema)
27b60ab9 1202 m.check_clash(self.info, seen)
069fb5b2
MA
1203 if self.doc:
1204 self.doc.connect_member(m)
14ff8461 1205 self.members = seen.values()
ac88219a 1206 if self.variants:
cdc5fa37 1207 self.variants.check(schema, seen)
14ff8461 1208 assert self.variants.tag_member in self.members
27b60ab9 1209 self.variants.check_clash(schema, self.info, seen)
816a57cd
MA
1210 if self.doc:
1211 self.doc.check()
ac88219a 1212
14f00c6c 1213 # Check that the members of this type do not cause duplicate JSON members,
27b60ab9
EB
1214 # and update seen to track the members seen so far. Report any errors
1215 # on behalf of info, which is not necessarily self.info
1216 def check_clash(self, schema, info, seen):
c2183d2e
EB
1217 assert not self.variants # not implemented
1218 for m in self.members:
27b60ab9 1219 m.check_clash(info, seen)
c2183d2e 1220
99df5289 1221 def is_implicit(self):
7599697c
EB
1222 # See QAPISchema._make_implicit_object_type(), as well as
1223 # _def_predefineds()
1224 return self.name.startswith('q_')
99df5289 1225
b6167706
EB
1226 def is_empty(self):
1227 assert self.members is not None
1228 return not self.members and not self.variants
1229
f51d8c3d 1230 def c_name(self):
cd50a256 1231 assert self.name != 'q_empty'
f51d8c3d
MA
1232 return QAPISchemaType.c_name(self)
1233
4040d995 1234 def c_type(self):
49823c4b 1235 assert not self.is_implicit()
becceedc 1236 return c_name(self.name) + pointer_suffix
f51d8c3d 1237
4040d995 1238 def c_unboxed_type(self):
4040d995
EB
1239 return c_name(self.name)
1240
f51d8c3d
MA
1241 def json_type(self):
1242 return 'object'
1243
3f7dc21b
MA
1244 def visit(self, visitor):
1245 visitor.visit_object_type(self.name, self.info,
1246 self.base, self.local_members, self.variants)
39a18158
MA
1247 visitor.visit_object_type_flat(self.name, self.info,
1248 self.members, self.variants)
3f7dc21b 1249
ac88219a 1250
d44f9ac8 1251class QAPISchemaMember(object):
88d4ef8b
EB
1252 role = 'member'
1253
d44f9ac8 1254 def __init__(self, name):
ac88219a 1255 assert isinstance(name, str)
ac88219a 1256 self.name = name
88d4ef8b
EB
1257 self.owner = None
1258
1259 def set_owner(self, name):
1260 assert not self.owner
1261 self.owner = name
ac88219a 1262
27b60ab9
EB
1263 def check_clash(self, info, seen):
1264 cname = c_name(self.name)
2cfbae3c 1265 if cname.lower() != cname and self.owner not in name_case_whitelist:
4148c298
MAL
1266 raise QAPISemError(info,
1267 "%s should not use uppercase" % self.describe())
27b60ab9 1268 if cname in seen:
4148c298
MAL
1269 raise QAPISemError(info, "%s collides with %s" %
1270 (self.describe(), seen[cname].describe()))
27b60ab9 1271 seen[cname] = self
577de12d 1272
88d4ef8b
EB
1273 def _pretty_owner(self):
1274 owner = self.owner
7599697c 1275 if owner.startswith('q_obj_'):
88d4ef8b
EB
1276 # See QAPISchema._make_implicit_object_type() - reverse the
1277 # mapping there to create a nice human-readable description
7599697c 1278 owner = owner[6:]
88d4ef8b
EB
1279 if owner.endswith('-arg'):
1280 return '(parameter of %s)' % owner[:-4]
ac4338f8
EB
1281 elif owner.endswith('-base'):
1282 return '(base of %s)' % owner[:-5]
88d4ef8b
EB
1283 else:
1284 assert owner.endswith('-wrapper')
1285 # Unreachable and not implemented
1286 assert False
93bda4dd
EB
1287 if owner.endswith('Kind'):
1288 # See QAPISchema._make_implicit_enum_type()
1289 return '(branch of %s)' % owner[:-4]
88d4ef8b
EB
1290 return '(%s of %s)' % (self.role, owner)
1291
1292 def describe(self):
1293 return "'%s' %s" % (self.name, self._pretty_owner())
1294
ac88219a 1295
d44f9ac8
EB
1296class QAPISchemaObjectTypeMember(QAPISchemaMember):
1297 def __init__(self, name, typ, optional):
1298 QAPISchemaMember.__init__(self, name)
1299 assert isinstance(typ, str)
1300 assert isinstance(optional, bool)
1301 self._type_name = typ
1302 self.type = None
1303 self.optional = optional
1304
1305 def check(self, schema):
1306 assert self.owner
1307 self.type = schema.lookup_type(self._type_name)
1308 assert self.type
1309
1310
ac88219a 1311class QAPISchemaObjectTypeVariants(object):
46292ba7
EB
1312 def __init__(self, tag_name, tag_member, variants):
1313 # Flat unions pass tag_name but not tag_member.
1314 # Simple unions and alternates pass tag_member but not tag_name.
1315 # After check(), tag_member is always set, and tag_name remains
1316 # a reliable witness of being used by a flat union.
1317 assert bool(tag_member) != bool(tag_name)
1318 assert (isinstance(tag_name, str) or
1319 isinstance(tag_member, QAPISchemaObjectTypeMember))
02a57ae3 1320 assert len(variants) > 0
ac88219a
MA
1321 for v in variants:
1322 assert isinstance(v, QAPISchemaObjectTypeVariant)
da9cb193 1323 self._tag_name = tag_name
46292ba7 1324 self.tag_member = tag_member
ac88219a
MA
1325 self.variants = variants
1326
88d4ef8b
EB
1327 def set_owner(self, name):
1328 for v in self.variants:
1329 v.set_owner(name)
1330
cdc5fa37 1331 def check(self, schema, seen):
14ff8461 1332 if not self.tag_member: # flat union
da9cb193
EB
1333 self.tag_member = seen[c_name(self._tag_name)]
1334 assert self._tag_name == self.tag_member.name
ac88219a
MA
1335 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1336 for v in self.variants:
10565ca9 1337 v.check(schema)
0426d53c
EB
1338 # Union names must match enum values; alternate names are
1339 # checked separately. Use 'seen' to tell the two apart.
1340 if seen:
93bda4dd 1341 assert v.name in self.tag_member.type.member_names()
0426d53c 1342 assert isinstance(v.type, QAPISchemaObjectType)
b807a1e1
EB
1343 v.type.check(schema)
1344
27b60ab9 1345 def check_clash(self, schema, info, seen):
b807a1e1
EB
1346 for v in self.variants:
1347 # Reset seen map for each variant, since qapi names from one
1348 # branch do not affect another branch
b807a1e1 1349 assert isinstance(v.type, QAPISchemaObjectType)
27b60ab9 1350 v.type.check_clash(schema, info, dict(seen))
ac88219a 1351
437db254 1352
ac88219a 1353class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
88d4ef8b
EB
1354 role = 'branch'
1355
ac88219a
MA
1356 def __init__(self, name, typ):
1357 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1358
ac88219a
MA
1359
1360class QAPISchemaAlternateType(QAPISchemaType):
069fb5b2
MA
1361 def __init__(self, name, info, doc, variants):
1362 QAPISchemaType.__init__(self, name, info, doc)
ac88219a 1363 assert isinstance(variants, QAPISchemaObjectTypeVariants)
da9cb193 1364 assert variants.tag_member
88d4ef8b
EB
1365 variants.set_owner(name)
1366 variants.tag_member.set_owner(self.name)
ac88219a
MA
1367 self.variants = variants
1368
1369 def check(self, schema):
e564e2dd 1370 self.variants.tag_member.check(schema)
b807a1e1
EB
1371 # Not calling self.variants.check_clash(), because there's nothing
1372 # to clash with
cdc5fa37 1373 self.variants.check(schema, {})
0426d53c
EB
1374 # Alternate branch names have no relation to the tag enum values;
1375 # so we have to check for potential name collisions ourselves.
1376 seen = {}
1377 for v in self.variants.variants:
1378 v.check_clash(self.info, seen)
069fb5b2
MA
1379 if self.doc:
1380 self.doc.connect_member(v)
816a57cd
MA
1381 if self.doc:
1382 self.doc.check()
ac88219a 1383
4040d995
EB
1384 def c_type(self):
1385 return c_name(self.name) + pointer_suffix
1386
f51d8c3d
MA
1387 def json_type(self):
1388 return 'value'
1389
3f7dc21b
MA
1390 def visit(self, visitor):
1391 visitor.visit_alternate_type(self.name, self.info, self.variants)
1392
c818408e
EB
1393 def is_empty(self):
1394 return False
1395
ac88219a
MA
1396
1397class QAPISchemaCommand(QAPISchemaEntity):
069fb5b2
MA
1398 def __init__(self, name, info, doc, arg_type, ret_type,
1399 gen, success_response, boxed):
1400 QAPISchemaEntity.__init__(self, name, info, doc)
ac88219a
MA
1401 assert not arg_type or isinstance(arg_type, str)
1402 assert not ret_type or isinstance(ret_type, str)
1403 self._arg_type_name = arg_type
1404 self.arg_type = None
1405 self._ret_type_name = ret_type
1406 self.ret_type = None
1407 self.gen = gen
1408 self.success_response = success_response
48825ca4 1409 self.boxed = boxed
ac88219a
MA
1410
1411 def check(self, schema):
1412 if self._arg_type_name:
1413 self.arg_type = schema.lookup_type(self._arg_type_name)
c818408e
EB
1414 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1415 isinstance(self.arg_type, QAPISchemaAlternateType))
1416 self.arg_type.check(schema)
1417 if self.boxed:
1418 if self.arg_type.is_empty():
4148c298
MAL
1419 raise QAPISemError(self.info,
1420 "Cannot use 'boxed' with empty type")
c818408e
EB
1421 else:
1422 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1423 assert not self.arg_type.variants
1424 elif self.boxed:
4148c298 1425 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
ac88219a
MA
1426 if self._ret_type_name:
1427 self.ret_type = schema.lookup_type(self._ret_type_name)
1428 assert isinstance(self.ret_type, QAPISchemaType)
1429
3f7dc21b
MA
1430 def visit(self, visitor):
1431 visitor.visit_command(self.name, self.info,
1432 self.arg_type, self.ret_type,
48825ca4 1433 self.gen, self.success_response, self.boxed)
3f7dc21b 1434
ac88219a
MA
1435
1436class QAPISchemaEvent(QAPISchemaEntity):
069fb5b2
MA
1437 def __init__(self, name, info, doc, arg_type, boxed):
1438 QAPISchemaEntity.__init__(self, name, info, doc)
ac88219a
MA
1439 assert not arg_type or isinstance(arg_type, str)
1440 self._arg_type_name = arg_type
1441 self.arg_type = None
48825ca4 1442 self.boxed = boxed
ac88219a
MA
1443
1444 def check(self, schema):
1445 if self._arg_type_name:
1446 self.arg_type = schema.lookup_type(self._arg_type_name)
c818408e
EB
1447 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1448 isinstance(self.arg_type, QAPISchemaAlternateType))
1449 self.arg_type.check(schema)
1450 if self.boxed:
1451 if self.arg_type.is_empty():
4148c298
MAL
1452 raise QAPISemError(self.info,
1453 "Cannot use 'boxed' with empty type")
c818408e
EB
1454 else:
1455 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1456 assert not self.arg_type.variants
1457 elif self.boxed:
4148c298 1458 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
ac88219a 1459
3f7dc21b 1460 def visit(self, visitor):
48825ca4 1461 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
3f7dc21b 1462
ac88219a
MA
1463
1464class QAPISchema(object):
1465 def __init__(self, fname):
1466 try:
ef801a9b 1467 parser = QAPISchemaParser(open(fname, 'r'))
3313b612 1468 self.exprs = check_exprs(parser.exprs)
a9f396b0 1469 self.docs = parser.docs
7618b91f 1470 self._entity_dict = {}
99df5289 1471 self._predefining = True
7618b91f 1472 self._def_predefineds()
99df5289 1473 self._predefining = False
7618b91f
EB
1474 self._def_exprs()
1475 self.check()
4148c298 1476 except QAPIError as err:
ac88219a
MA
1477 print >>sys.stderr, err
1478 exit(1)
ac88219a 1479
ac88219a 1480 def _def_entity(self, ent):
99df5289
EB
1481 # Only the predefined types are allowed to not have info
1482 assert ent.info or self._predefining
ac88219a
MA
1483 assert ent.name not in self._entity_dict
1484 self._entity_dict[ent.name] = ent
1485
1486 def lookup_entity(self, name, typ=None):
1487 ent = self._entity_dict.get(name)
1488 if typ and not isinstance(ent, typ):
1489 return None
1490 return ent
1491
1492 def lookup_type(self, name):
1493 return self.lookup_entity(name, QAPISchemaType)
1494
861877a0
EB
1495 def _def_builtin_type(self, name, json_type, c_type):
1496 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
9f08c8ec
EB
1497 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1498 # qapi-types.h from a single .c, all arrays of builtins must be
1499 # declared in the first file whether or not they are used. Nicer
1500 # would be to use lazy instantiation, while figuring out how to
1501 # avoid compilation issues with multiple qapi-types.h.
99df5289 1502 self._make_array_type(name, None)
ac88219a
MA
1503
1504 def _def_predefineds(self):
861877a0
EB
1505 for t in [('str', 'string', 'char' + pointer_suffix),
1506 ('number', 'number', 'double'),
1507 ('int', 'int', 'int64_t'),
1508 ('int8', 'int', 'int8_t'),
1509 ('int16', 'int', 'int16_t'),
1510 ('int32', 'int', 'int32_t'),
1511 ('int64', 'int', 'int64_t'),
1512 ('uint8', 'int', 'uint8_t'),
1513 ('uint16', 'int', 'uint16_t'),
1514 ('uint32', 'int', 'uint32_t'),
1515 ('uint64', 'int', 'uint64_t'),
1516 ('size', 'int', 'uint64_t'),
1517 ('bool', 'boolean', 'bool'),
1518 ('any', 'value', 'QObject' + pointer_suffix)]:
f51d8c3d 1519 self._def_builtin_type(*t)
069fb5b2
MA
1520 self.the_empty_object_type = QAPISchemaObjectType(
1521 'q_empty', None, None, None, [], None)
39a18158 1522 self._def_entity(self.the_empty_object_type)
93bda4dd
EB
1523 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1524 'qstring', 'qdict', 'qlist',
1525 'qfloat', 'qbool'])
069fb5b2
MA
1526 self._def_entity(QAPISchemaEnumType('QType', None, None,
1527 qtype_values, 'QTYPE'))
ac88219a 1528
93bda4dd
EB
1529 def _make_enum_members(self, values):
1530 return [QAPISchemaMember(v) for v in values]
1531
99df5289 1532 def _make_implicit_enum_type(self, name, info, values):
93bda4dd 1533 # See also QAPISchemaObjectTypeMember._pretty_owner()
49823c4b 1534 name = name + 'Kind' # Use namespace reserved by add_name()
93bda4dd 1535 self._def_entity(QAPISchemaEnumType(
069fb5b2 1536 name, info, None, self._make_enum_members(values), None))
ac88219a
MA
1537 return name
1538
99df5289 1539 def _make_array_type(self, element_type, info):
255960dd 1540 name = element_type + 'List' # Use namespace reserved by add_name()
ac88219a 1541 if not self.lookup_type(name):
99df5289 1542 self._def_entity(QAPISchemaArrayType(name, info, element_type))
ac88219a
MA
1543 return name
1544
069fb5b2 1545 def _make_implicit_object_type(self, name, info, doc, role, members):
ac88219a
MA
1546 if not members:
1547 return None
88d4ef8b 1548 # See also QAPISchemaObjectTypeMember._pretty_owner()
7599697c 1549 name = 'q_obj_%s-%s' % (name, role)
ac88219a 1550 if not self.lookup_entity(name, QAPISchemaObjectType):
069fb5b2 1551 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
ac88219a
MA
1552 members, None))
1553 return name
1554
069fb5b2 1555 def _def_enum_type(self, expr, info, doc):
ac88219a
MA
1556 name = expr['enum']
1557 data = expr['data']
1558 prefix = expr.get('prefix')
93bda4dd 1559 self._def_entity(QAPISchemaEnumType(
069fb5b2 1560 name, info, doc, self._make_enum_members(data), prefix))
ac88219a 1561
99df5289 1562 def _make_member(self, name, typ, info):
ac88219a
MA
1563 optional = False
1564 if name.startswith('*'):
1565 name = name[1:]
1566 optional = True
1567 if isinstance(typ, list):
1568 assert len(typ) == 1
99df5289 1569 typ = self._make_array_type(typ[0], info)
ac88219a
MA
1570 return QAPISchemaObjectTypeMember(name, typ, optional)
1571
99df5289
EB
1572 def _make_members(self, data, info):
1573 return [self._make_member(key, value, info)
ac88219a
MA
1574 for (key, value) in data.iteritems()]
1575
069fb5b2 1576 def _def_struct_type(self, expr, info, doc):
ac88219a
MA
1577 name = expr['struct']
1578 base = expr.get('base')
1579 data = expr['data']
069fb5b2 1580 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
99df5289 1581 self._make_members(data, info),
ac88219a 1582 None))
ac88219a
MA
1583
1584 def _make_variant(self, case, typ):
1585 return QAPISchemaObjectTypeVariant(case, typ)
1586
99df5289 1587 def _make_simple_variant(self, case, typ, info):
ac88219a
MA
1588 if isinstance(typ, list):
1589 assert len(typ) == 1
99df5289
EB
1590 typ = self._make_array_type(typ[0], info)
1591 typ = self._make_implicit_object_type(
069fb5b2 1592 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
ac88219a
MA
1593 return QAPISchemaObjectTypeVariant(case, typ)
1594
069fb5b2 1595 def _def_union_type(self, expr, info, doc):
ac88219a
MA
1596 name = expr['union']
1597 data = expr['data']
1598 base = expr.get('base')
1599 tag_name = expr.get('discriminator')
46292ba7 1600 tag_member = None
ac4338f8
EB
1601 if isinstance(base, dict):
1602 base = (self._make_implicit_object_type(
069fb5b2 1603 name, info, doc, 'base', self._make_members(base, info)))
ac88219a
MA
1604 if tag_name:
1605 variants = [self._make_variant(key, value)
1606 for (key, value) in data.iteritems()]
da34a9bd 1607 members = []
ac88219a 1608 else:
99df5289 1609 variants = [self._make_simple_variant(key, value, info)
ac88219a 1610 for (key, value) in data.iteritems()]
9d3f3494
EB
1611 typ = self._make_implicit_enum_type(name, info,
1612 [v.name for v in variants])
1613 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
da34a9bd 1614 members = [tag_member]
ac88219a 1615 self._def_entity(
069fb5b2 1616 QAPISchemaObjectType(name, info, doc, base, members,
ac88219a 1617 QAPISchemaObjectTypeVariants(tag_name,
46292ba7 1618 tag_member,
ac88219a 1619 variants)))
ac88219a 1620
069fb5b2 1621 def _def_alternate_type(self, expr, info, doc):
ac88219a
MA
1622 name = expr['alternate']
1623 data = expr['data']
1624 variants = [self._make_variant(key, value)
1625 for (key, value) in data.iteritems()]
0426d53c 1626 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
ac88219a 1627 self._def_entity(
069fb5b2 1628 QAPISchemaAlternateType(name, info, doc,
ac88219a 1629 QAPISchemaObjectTypeVariants(None,
46292ba7 1630 tag_member,
ac88219a 1631 variants)))
ac88219a 1632
069fb5b2 1633 def _def_command(self, expr, info, doc):
ac88219a
MA
1634 name = expr['command']
1635 data = expr.get('data')
1636 rets = expr.get('returns')
1637 gen = expr.get('gen', True)
1638 success_response = expr.get('success-response', True)
48825ca4 1639 boxed = expr.get('boxed', False)
ac88219a 1640 if isinstance(data, OrderedDict):
99df5289 1641 data = self._make_implicit_object_type(
069fb5b2 1642 name, info, doc, 'arg', self._make_members(data, info))
ac88219a
MA
1643 if isinstance(rets, list):
1644 assert len(rets) == 1
99df5289 1645 rets = self._make_array_type(rets[0], info)
069fb5b2
MA
1646 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1647 gen, success_response, boxed))
ac88219a 1648
069fb5b2 1649 def _def_event(self, expr, info, doc):
ac88219a
MA
1650 name = expr['event']
1651 data = expr.get('data')
48825ca4 1652 boxed = expr.get('boxed', False)
ac88219a 1653 if isinstance(data, OrderedDict):
99df5289 1654 data = self._make_implicit_object_type(
069fb5b2
MA
1655 name, info, doc, 'arg', self._make_members(data, info))
1656 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
ac88219a
MA
1657
1658 def _def_exprs(self):
1659 for expr_elem in self.exprs:
1660 expr = expr_elem['expr']
1661 info = expr_elem['info']
069fb5b2 1662 doc = expr_elem.get('doc')
ac88219a 1663 if 'enum' in expr:
069fb5b2 1664 self._def_enum_type(expr, info, doc)
ac88219a 1665 elif 'struct' in expr:
069fb5b2 1666 self._def_struct_type(expr, info, doc)
ac88219a 1667 elif 'union' in expr:
069fb5b2 1668 self._def_union_type(expr, info, doc)
ac88219a 1669 elif 'alternate' in expr:
069fb5b2 1670 self._def_alternate_type(expr, info, doc)
ac88219a 1671 elif 'command' in expr:
069fb5b2 1672 self._def_command(expr, info, doc)
ac88219a 1673 elif 'event' in expr:
069fb5b2 1674 self._def_event(expr, info, doc)
ac88219a
MA
1675 else:
1676 assert False
1677
1678 def check(self):
1679 for ent in self._entity_dict.values():
1680 ent.check(self)
4d076d67 1681
3f7dc21b 1682 def visit(self, visitor):
25a0d9c9
EB
1683 visitor.visit_begin(self)
1684 for (name, entity) in sorted(self._entity_dict.items()):
1685 if visitor.visit_needed(entity):
1686 entity.visit(visitor)
3f7dc21b
MA
1687 visitor.visit_end()
1688
b86b05ed 1689
00e4b285
MA
1690#
1691# Code generation helpers
1692#
1693
0f923be2
MR
1694def camel_case(name):
1695 new_name = ''
1696 first = True
1697 for ch in name:
1698 if ch in ['_', '-']:
1699 first = True
1700 elif first:
1701 new_name += ch.upper()
1702 first = False
1703 else:
1704 new_name += ch.lower()
1705 return new_name
1706
437db254 1707
849bc538
MA
1708# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1709# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1710# ENUM24_Name -> ENUM24_NAME
1711def camel_to_upper(value):
1712 c_fun_str = c_name(value, False)
1713 if value.isupper():
1714 return c_fun_str
1715
1716 new_name = ''
1717 l = len(c_fun_str)
1718 for i in range(l):
1719 c = c_fun_str[i]
ef801a9b
MA
1720 # When c is upper and no '_' appears before, do more checks
1721 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
437db254
EB
1722 if i < l - 1 and c_fun_str[i + 1].islower():
1723 new_name += '_'
1724 elif c_fun_str[i - 1].isdigit():
849bc538
MA
1725 new_name += '_'
1726 new_name += c
1727 return new_name.lstrip('_').upper()
1728
437db254 1729
351d36e4
DB
1730def c_enum_const(type_name, const_name, prefix=None):
1731 if prefix is not None:
1732 type_name = prefix
d20a580b 1733 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
849bc538 1734
18df515e 1735c_name_trans = string.maketrans('.-', '__')
47299262 1736
437db254 1737
c6405b54
EB
1738# Map @name to a valid C identifier.
1739# If @protect, avoid returning certain ticklish identifiers (like
ef801a9b 1740# C keywords) by prepending 'q_'.
c6405b54
EB
1741#
1742# Used for converting 'name' from a 'name':'type' qapi definition
1743# into a generated struct member, as well as converting type names
1744# into substrings of a generated C function name.
1745# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1746# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
18df515e 1747def c_name(name, protect=True):
427a1a2c
BS
1748 # ANSI X3J11/88-090, 3.1.1
1749 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
437db254
EB
1750 'default', 'do', 'double', 'else', 'enum', 'extern',
1751 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1752 'return', 'short', 'signed', 'sizeof', 'static',
1753 'struct', 'switch', 'typedef', 'union', 'unsigned',
1754 'void', 'volatile', 'while'])
427a1a2c
BS
1755 # ISO/IEC 9899:1999, 6.4.1
1756 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1757 # ISO/IEC 9899:2011, 6.4.1
437db254
EB
1758 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1759 '_Noreturn', '_Static_assert', '_Thread_local'])
427a1a2c
BS
1760 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1761 # excluding _.*
1762 gcc_words = set(['asm', 'typeof'])
6f88009e
TS
1763 # C++ ISO/IEC 14882:2003 2.11
1764 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1765 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1766 'namespace', 'new', 'operator', 'private', 'protected',
1767 'public', 'reinterpret_cast', 'static_cast', 'template',
1768 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1769 'using', 'virtual', 'wchar_t',
1770 # alternative representations
1771 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1772 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1057725f 1773 # namespace pollution:
86ae1911 1774 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
c43567c1 1775 name = name.translate(c_name_trans)
437db254
EB
1776 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1777 | cpp_words | polluted_words):
ef801a9b 1778 return 'q_' + name
c43567c1 1779 return name
0f923be2 1780
05dfb26c 1781eatspace = '\033EATSPACE.'
d5573446 1782pointer_suffix = ' *' + eatspace
05dfb26c 1783
437db254 1784
0f923be2 1785def genindent(count):
ef801a9b 1786 ret = ''
437db254 1787 for _ in range(count):
ef801a9b 1788 ret += ' '
0f923be2
MR
1789 return ret
1790
1791indent_level = 0
1792
437db254 1793
0f923be2
MR
1794def push_indent(indent_amount=4):
1795 global indent_level
1796 indent_level += indent_amount
1797
437db254 1798
0f923be2
MR
1799def pop_indent(indent_amount=4):
1800 global indent_level
1801 indent_level -= indent_amount
1802
437db254 1803
77e703b8
MA
1804# Generate @code with @kwds interpolated.
1805# Obey indent_level, and strip eatspace.
0f923be2 1806def cgen(code, **kwds):
77e703b8
MA
1807 raw = code % kwds
1808 if indent_level:
1809 indent = genindent(indent_level)
2752e5be 1810 # re.subn() lacks flags support before Python 2.7, use re.compile()
0fe675af 1811 raw = re.subn(re.compile(r'^.', re.MULTILINE),
2752e5be 1812 indent + r'\g<0>', raw)
77e703b8 1813 raw = raw[0]
0fe675af 1814 return re.sub(re.escape(eatspace) + r' *', '', raw)
0f923be2 1815
437db254 1816
0f923be2 1817def mcgen(code, **kwds):
77e703b8
MA
1818 if code[0] == '\n':
1819 code = code[1:]
1820 return cgen(code, **kwds)
0f923be2 1821
0f923be2
MR
1822
1823def guardname(filename):
00dfc3b2 1824 return c_name(filename, protect=False).upper()
c0afa9c5 1825
437db254 1826
c0afa9c5
MR
1827def guardstart(name):
1828 return mcgen('''
1829
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 */
1841
1842''',
1843 name=guardname(name))
2114f5a9 1844
437db254 1845
e98859a9 1846def gen_enum_lookup(name, values, prefix=None):
efd2eaa6
MA
1847 ret = mcgen('''
1848
e98859a9 1849const char *const %(c_name)s_lookup[] = {
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('''
1855 [%(index)s] = "%(value)s",
1856''',
e98859a9 1857 index=index, value=value)
efd2eaa6 1858
7fb1cf16 1859 max_index = c_enum_const(name, '_MAX', prefix)
efd2eaa6
MA
1860 ret += mcgen('''
1861 [%(max_index)s] = NULL,
1862};
1863''',
e98859a9 1864 max_index=max_index)
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
e98859a9
MA
1894extern const char *const %(c_name)s_lookup[];
1895''',
1896 c_name=c_name(name))
1897 return ret
efd2eaa6 1898
437db254 1899
48825ca4 1900def gen_params(arg_type, boxed, extra):
03b4367a 1901 if not arg_type:
c818408e 1902 assert not boxed
03b4367a 1903 return extra
03b4367a
MA
1904 ret = ''
1905 sep = ''
48825ca4 1906 if boxed:
c818408e
EB
1907 ret += '%s arg' % arg_type.c_param_type()
1908 sep = ', '
48825ca4
EB
1909 else:
1910 assert not arg_type.variants
1911 for memb in arg_type.members:
1912 ret += sep
1913 sep = ', '
1914 if memb.optional:
1915 ret += 'bool has_%s, ' % c_name(memb.name)
1916 ret += '%s %s' % (memb.type.c_param_type(),
1917 c_name(memb.name))
03b4367a
MA
1918 if extra:
1919 ret += sep + extra
1920 return ret
1921
1f353344 1922
00e4b285
MA
1923#
1924# Common command line parsing
1925#
1926
437db254 1927
ef801a9b 1928def parse_command_line(extra_options='', extra_long_options=[]):
2114f5a9
MA
1929
1930 try:
1931 opts, args = getopt.gnu_getopt(sys.argv[1:],
ef801a9b
MA
1932 'chp:o:' + extra_options,
1933 ['source', 'header', 'prefix=',
1934 'output-dir='] + extra_long_options)
291928a8 1935 except getopt.GetoptError as err:
b4540968 1936 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
2114f5a9
MA
1937 sys.exit(1)
1938
ef801a9b
MA
1939 output_dir = ''
1940 prefix = ''
2114f5a9
MA
1941 do_c = False
1942 do_h = False
1943 extra_opts = []
1944
1945 for oa in opts:
1946 o, a = oa
ef801a9b 1947 if o in ('-p', '--prefix'):
0fe675af 1948 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1cf47a15
MA
1949 if match.end() != len(a):
1950 print >>sys.stderr, \
1951 "%s: 'funny character '%s' in argument of --prefix" \
1952 % (sys.argv[0], a[match.end()])
1953 sys.exit(1)
2114f5a9 1954 prefix = a
ef801a9b
MA
1955 elif o in ('-o', '--output-dir'):
1956 output_dir = a + '/'
1957 elif o in ('-c', '--source'):
2114f5a9 1958 do_c = True
ef801a9b 1959 elif o in ('-h', '--header'):
2114f5a9
MA
1960 do_h = True
1961 else:
1962 extra_opts.append(oa)
1963
1964 if not do_c and not do_h:
1965 do_c = True
1966 do_h = True
1967
16d80f61
MA
1968 if len(args) != 1:
1969 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
b4540968 1970 sys.exit(1)
54414047 1971 fname = args[0]
b4540968 1972
54414047 1973 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
12f8e1b9 1974
00e4b285
MA
1975#
1976# Generate output files with boilerplate
1977#
1978
437db254 1979
12f8e1b9
MA
1980def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1981 c_comment, h_comment):
00dfc3b2 1982 guard = guardname(prefix + h_file)
12f8e1b9
MA
1983 c_file = output_dir + prefix + c_file
1984 h_file = output_dir + prefix + h_file
1985
c4f498fe
MA
1986 if output_dir:
1987 try:
1988 os.makedirs(output_dir)
291928a8 1989 except os.error as e:
c4f498fe
MA
1990 if e.errno != errno.EEXIST:
1991 raise
12f8e1b9
MA
1992
1993 def maybe_open(really, name, opt):
1994 if really:
1995 return open(name, opt)
1996 else:
1997 import StringIO
1998 return StringIO.StringIO()
1999
2000 fdef = maybe_open(do_c, c_file, 'w')
2001 fdecl = maybe_open(do_h, h_file, 'w')
2002
2003 fdef.write(mcgen('''
2004/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2005%(comment)s
2006''',
437db254 2007 comment=c_comment))
12f8e1b9
MA
2008
2009 fdecl.write(mcgen('''
2010/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2011%(comment)s
2012#ifndef %(guard)s
2013#define %(guard)s
2014
2015''',
437db254 2016 comment=h_comment, guard=guard))
12f8e1b9
MA
2017
2018 return (fdef, fdecl)
2019
437db254 2020
12f8e1b9
MA
2021def close_output(fdef, fdecl):
2022 fdecl.write('''
2023#endif
2024''')
12f8e1b9 2025 fdecl.close()
12f8e1b9 2026 fdef.close()