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