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