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