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