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