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