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