blob: ca9926b7969c7b40a83a2760e110315912a87b54 [file] [log] [blame]
Michael Roth0f923be2011-07-19 14:50:39 -05001#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
Eric Blakee4083112016-01-29 06:48:41 -07005# Copyright (c) 2013-2016 Red Hat Inc.
Michael Roth0f923be2011-07-19 14:50:39 -05006#
7# Authors:
8# Anthony Liguori <aliguori@us.ibm.com>
Markus Armbrusterc7a3f252013-07-27 17:41:55 +02009# Markus Armbruster <armbru@redhat.com>
Michael Roth0f923be2011-07-19 14:50:39 -050010#
Markus Armbruster678e48a2014-03-01 08:40:34 +010011# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
Michael Roth0f923be2011-07-19 14:50:39 -050013
Lluís Vilanovaa719a272014-05-07 20:46:15 +020014import re
Michael Roth0f923be2011-07-19 14:50:39 -050015from ordereddict import OrderedDict
Markus Armbruster12f8e1b2015-04-02 14:46:39 +020016import errno
Markus Armbruster2114f5a2015-04-02 13:12:21 +020017import getopt
Lluís Vilanova33aaad52014-05-02 15:52:35 +020018import os
Markus Armbruster2caba362013-07-27 17:41:56 +020019import sys
Markus Armbruster47299262015-05-14 06:50:47 -060020import string
Michael Roth0f923be2011-07-19 14:50:39 -050021
Eric Blakeb52c4b92015-05-04 09:05:00 -060022builtin_types = {
Kevin Wolf69dd62d2013-07-08 16:14:21 +020023 'str': 'QTYPE_QSTRING',
24 'int': 'QTYPE_QINT',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
27 'int8': 'QTYPE_QINT',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
Eric Blakecb17f792015-05-04 09:05:01 -060035 'size': 'QTYPE_QINT',
Eric Blake1310a3d2015-12-01 22:20:46 -070036 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070037 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020038}
39
Markus Armbrusterbc52d032017-03-15 13:56:51 +010040# Are documentation comments required?
41doc_required = False
42
Eric Blake10d4d992015-05-04 09:05:23 -060043# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010044returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060045
Eric Blake893e1f22015-12-01 22:20:57 -070046# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010047name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070048
Eric Blake4dc2e692015-05-04 09:05:17 -060049enum_types = []
50struct_types = []
51union_types = []
52events = []
53all_names = {}
54
Markus Armbruster00e4b282015-06-10 10:04:36 +020055#
56# Parsing the schema into expressions
57#
58
Eric Blake437db252015-09-29 16:21:02 -060059
Lluís Vilanovaa719a272014-05-07 20:46:15 +020060def error_path(parent):
Markus Armbrusteref801a92017-03-15 13:57:08 +010061 res = ''
Lluís Vilanovaa719a272014-05-07 20:46:15 +020062 while parent:
Markus Armbrusteref801a92017-03-15 13:57:08 +010063 res = ('In file included from %s:%d:\n' % (parent['file'],
Lluís Vilanovaa719a272014-05-07 20:46:15 +020064 parent['line'])) + res
65 parent = parent['parent']
66 return res
67
Eric Blake437db252015-09-29 16:21:02 -060068
Marc-André Lureau4148c292017-01-13 15:41:25 +010069class QAPIError(Exception):
70 def __init__(self, fname, line, col, incl_info, msg):
Eric Blake59b00542015-09-29 16:21:01 -060071 Exception.__init__(self)
Marc-André Lureau4148c292017-01-13 15:41:25 +010072 self.fname = fname
73 self.line = line
74 self.col = col
75 self.info = incl_info
Markus Armbruster2caba362013-07-27 17:41:56 +020076 self.msg = msg
Marc-André Lureau4148c292017-01-13 15:41:25 +010077
78 def __str__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +010079 loc = '%s:%d' % (self.fname, self.line)
Marc-André Lureau4148c292017-01-13 15:41:25 +010080 if self.col is not None:
Markus Armbrusteref801a92017-03-15 13:57:08 +010081 loc += ':%s' % self.col
82 return error_path(self.info) + '%s: %s' % (loc, self.msg)
Marc-André Lureau4148c292017-01-13 15:41:25 +010083
84
85class QAPIParseError(QAPIError):
86 def __init__(self, parser, msg):
87 col = 1
88 for ch in parser.src[parser.line_pos:parser.pos]:
Wenchao Xia515b9432014-03-04 18:44:33 -080089 if ch == '\t':
Marc-André Lureau4148c292017-01-13 15:41:25 +010090 col = (col + 7) % 8 + 1
Markus Armbruster2caba362013-07-27 17:41:56 +020091 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +010092 col += 1
93 QAPIError.__init__(self, parser.fname, parser.line, col,
94 parser.incl_info, msg)
Markus Armbruster2caba362013-07-27 17:41:56 +020095
Eric Blake437db252015-09-29 16:21:02 -060096
Marc-André Lureau4148c292017-01-13 15:41:25 +010097class QAPISemError(QAPIError):
98 def __init__(self, info, msg):
99 QAPIError.__init__(self, info['file'], info['line'], None,
100 info['parent'], msg)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800101
Eric Blake437db252015-09-29 16:21:02 -0600102
Marc-André Lureau3313b612017-01-13 15:41:29 +0100103class QAPIDoc(object):
104 class Section(object):
105 def __init__(self, name=None):
106 # optional section name (argument/member or section name)
107 self.name = name
108 # the list of lines for this section
109 self.content = []
Markus Armbrusterb116fd82017-03-15 13:57:00 +0100110 self.optional = False
Marc-André Lureau3313b612017-01-13 15:41:29 +0100111
112 def append(self, line):
113 self.content.append(line)
114
115 def __repr__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100116 return '\n'.join(self.content).strip()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100117
118 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100119 def __init__(self, name):
120 QAPIDoc.Section.__init__(self, name)
121 self.member = None
122
123 def connect(self, member):
124 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100125
126 def __init__(self, parser, info):
127 # self.parser is used to report errors with QAPIParseError. The
128 # resulting error position depends on the state of the parser.
129 # It happens to be the beginning of the comment. More or less
130 # servicable, but action at a distance.
131 self.parser = parser
132 self.info = info
133 self.symbol = None
134 self.body = QAPIDoc.Section()
135 # dict mapping parameter name to ArgSection
136 self.args = OrderedDict()
137 # a list of Section
138 self.sections = []
139 # the current section
140 self.section = self.body
141 # associated expression (to be set by expression parser)
142 self.expr = None
143
144 def has_section(self, name):
145 """Return True if we have a section with this name."""
146 for i in self.sections:
147 if i.name == name:
148 return True
149 return False
150
151 def append(self, line):
152 """Parse a comment line and add it to the documentation."""
153 line = line[1:]
154 if not line:
155 self._append_freeform(line)
156 return
157
158 if line[0] != ' ':
159 raise QAPIParseError(self.parser, "Missing space after #")
160 line = line[1:]
161
162 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
163 # recognized, and get silently treated as ordinary text
164 if self.symbol:
165 self._append_symbol_line(line)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100166 elif not self.body.content and line.startswith('@'):
167 if not line.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100168 raise QAPIParseError(self.parser, "Line should end with :")
169 self.symbol = line[1:-1]
170 # FIXME invalid names other than the empty string aren't flagged
171 if not self.symbol:
172 raise QAPIParseError(self.parser, "Invalid name")
173 else:
174 self._append_freeform(line)
175
Markus Armbruster4ea71482017-03-15 13:57:23 +0100176 def end_comment(self):
177 self._end_section()
178
Marc-André Lureau3313b612017-01-13 15:41:29 +0100179 def _append_symbol_line(self, line):
180 name = line.split(' ', 1)[0]
181
Markus Armbrusteref801a92017-03-15 13:57:08 +0100182 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100183 line = line[len(name)+1:]
184 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100185 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100186 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100187 'Note:', 'Notes:',
188 'Example:', 'Examples:',
189 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100190 line = line[len(name)+1:]
191 self._start_section(name[:-1])
192
193 self._append_freeform(line)
194
195 def _start_args_section(self, name):
196 # FIXME invalid names other than the empty string aren't flagged
197 if not name:
198 raise QAPIParseError(self.parser, "Invalid parameter name")
199 if name in self.args:
200 raise QAPIParseError(self.parser,
201 "'%s' parameter name duplicated" % name)
202 if self.sections:
203 raise QAPIParseError(self.parser,
204 "'@%s:' can't follow '%s' section"
205 % (name, self.sections[0].name))
Markus Armbruster4ea71482017-03-15 13:57:23 +0100206 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100207 self.section = QAPIDoc.ArgSection(name)
208 self.args[name] = self.section
209
Markus Armbrusteref801a92017-03-15 13:57:08 +0100210 def _start_section(self, name=''):
211 if name in ('Returns', 'Since') and self.has_section(name):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100212 raise QAPIParseError(self.parser,
213 "Duplicated '%s' section" % name)
Markus Armbruster4ea71482017-03-15 13:57:23 +0100214 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100215 self.section = QAPIDoc.Section(name)
216 self.sections.append(self.section)
217
Markus Armbruster4ea71482017-03-15 13:57:23 +0100218 def _end_section(self):
219 if self.section:
220 contents = str(self.section)
221 if self.section.name and (not contents or contents.isspace()):
222 raise QAPIParseError(self.parser, "Empty doc section '%s'"
223 % self.section.name)
224 self.section = None
225
Marc-André Lureau3313b612017-01-13 15:41:29 +0100226 def _append_freeform(self, line):
227 in_arg = isinstance(self.section, QAPIDoc.ArgSection)
228 if (in_arg and self.section.content
229 and not self.section.content[-1]
230 and line and not line[0].isspace()):
231 self._start_section()
232 if (in_arg or not self.section.name
Markus Armbrusteref801a92017-03-15 13:57:08 +0100233 or not self.section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100234 line = line.strip()
Markus Armbruster2d433232017-03-15 13:57:22 +0100235 match = re.match(r'(@\S+:)', line)
236 if match:
237 raise QAPIParseError(self.parser,
238 "'%s' not allowed in free-form documentation"
239 % match.group(1))
Markus Armbruster1d8bda12017-03-15 13:57:06 +0100240 # TODO Drop this once the dust has settled
241 if (isinstance(self.section, QAPIDoc.ArgSection)
242 and '#optional' in line):
243 raise QAPISemError(self.info, "Please drop the #optional tag")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100244 self.section.append(line)
245
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100246 def connect_member(self, member):
247 if member.name not in self.args:
248 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100249 self.args[member.name] = QAPIDoc.ArgSection(member.name)
250 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100251
Markus Armbruster816a57c2017-03-15 13:57:26 +0100252 def check(self):
253 bogus = [name for name, section in self.args.iteritems()
254 if not section.member]
255 if bogus:
256 raise QAPISemError(
257 self.info,
258 "The following documented members are not in "
259 "the declaration: %s" % ", ".join(bogus))
260
Marc-André Lureau3313b612017-01-13 15:41:29 +0100261
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200262class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500263
Eric Blake437db252015-09-29 16:21:02 -0600264 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200265 abs_fname = os.path.abspath(fp.name)
Markus Armbruster8608d252015-06-09 18:32:29 +0200266 fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200267 self.fname = fname
Markus Armbruster54414042015-06-09 16:22:45 +0200268 previously_included.append(abs_fname)
269 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200270 self.src = fp.read()
271 if self.src == '' or self.src[-1] != '\n':
272 self.src += '\n'
273 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800274 self.line = 1
275 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200276 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100277 self.docs = []
Markus Armbrustere7823a22017-03-15 13:57:20 +0100278 self.cur_doc = None
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200279 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500280
Eric Blake437db252015-09-29 16:21:02 -0600281 while self.tok is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100282 info = {'file': fname, 'line': self.line,
283 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100284 if self.tok == '#':
Markus Armbrustere7823a22017-03-15 13:57:20 +0100285 self.reject_expr_doc()
286 self.cur_doc = self.get_doc(info)
287 self.docs.append(self.cur_doc)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100288 continue
289
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200290 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100291 if 'include' in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100292 self.reject_expr_doc()
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200293 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100294 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100295 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200296 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100297 raise QAPISemError(info,
298 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100299 self._include(include, info, os.path.dirname(abs_fname),
300 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100301 elif "pragma" in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100302 self.reject_expr_doc()
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100303 if len(expr) != 1:
304 raise QAPISemError(info, "Invalid 'pragma' directive")
305 pragma = expr['pragma']
306 if not isinstance(pragma, dict):
307 raise QAPISemError(
308 info, "Value of 'pragma' must be a dictionary")
309 for name, value in pragma.iteritems():
310 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200311 else:
312 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100313 'info': info}
Markus Armbrustere7823a22017-03-15 13:57:20 +0100314 if self.cur_doc:
315 if not self.cur_doc.symbol:
316 raise QAPISemError(
317 self.cur_doc.info,
318 "Expression documentation required")
319 self.cur_doc.expr = expr
320 expr_elem['doc'] = self.cur_doc
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200321 self.exprs.append(expr_elem)
Markus Armbrustere7823a22017-03-15 13:57:20 +0100322 self.cur_doc = None
323 self.reject_expr_doc()
324
325 def reject_expr_doc(self):
326 if self.cur_doc and self.cur_doc.symbol:
327 raise QAPISemError(
328 self.cur_doc.info,
329 "Documentation for '%s' is not followed by the definition"
330 % self.cur_doc.symbol)
Michael Roth0f923be2011-07-19 14:50:39 -0500331
Markus Armbrustere04dea82017-03-15 13:56:50 +0100332 def _include(self, include, info, base_dir, previously_included):
333 incl_abs_fname = os.path.join(base_dir, include)
334 # catch inclusion cycle
335 inf = info
336 while inf:
337 if incl_abs_fname == os.path.abspath(inf['file']):
338 raise QAPISemError(info, "Inclusion loop for %s" % include)
339 inf = inf['parent']
340
341 # skip multiple include of the same file
342 if incl_abs_fname in previously_included:
343 return
344 try:
345 fobj = open(incl_abs_fname, 'r')
346 except IOError as e:
347 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
348 exprs_include = QAPISchemaParser(fobj, previously_included, info)
349 self.exprs.extend(exprs_include.exprs)
350 self.docs.extend(exprs_include.docs)
351
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100352 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100353 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100354 if name == 'doc-required':
355 if not isinstance(value, bool):
356 raise QAPISemError(info,
357 "Pragma 'doc-required' must be boolean")
358 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100359 elif name == 'returns-whitelist':
360 if (not isinstance(value, list)
361 or any([not isinstance(elt, str) for elt in value])):
362 raise QAPISemError(info,
363 "Pragma returns-whitelist must be"
364 " a list of strings")
365 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100366 elif name == 'name-case-whitelist':
367 if (not isinstance(value, list)
368 or any([not isinstance(elt, str) for elt in value])):
369 raise QAPISemError(info,
370 "Pragma name-case-whitelist must be"
371 " a list of strings")
372 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100373 else:
374 raise QAPISemError(info, "Unknown pragma '%s'" % name)
375
Marc-André Lureau3313b612017-01-13 15:41:29 +0100376 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200377 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200378 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200379 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200380 self.cursor += 1
381 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500382
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200383 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100384 if self.src[self.cursor] == '#':
385 # Start of doc comment
386 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200387 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100388 if not skip_comment:
389 self.val = self.src[self.pos:self.cursor]
390 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100391 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200392 return
393 elif self.tok == "'":
394 string = ''
395 esc = False
396 while True:
397 ch = self.src[self.cursor]
398 self.cursor += 1
399 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100400 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200401 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600402 if ch == 'b':
403 string += '\b'
404 elif ch == 'f':
405 string += '\f'
406 elif ch == 'n':
407 string += '\n'
408 elif ch == 'r':
409 string += '\r'
410 elif ch == 't':
411 string += '\t'
412 elif ch == 'u':
413 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600414 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600415 ch = self.src[self.cursor]
416 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100417 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100418 raise QAPIParseError(self,
419 '\\u escape needs 4 '
420 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600421 value = (value << 4) + int(ch, 16)
422 # If Python 2 and 3 didn't disagree so much on
423 # how to handle Unicode, then we could allow
424 # Unicode string defaults. But most of QAPI is
425 # ASCII-only, so we aren't losing much for now.
426 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100427 raise QAPIParseError(self,
428 'For now, \\u escape '
429 'only supports non-zero '
430 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600431 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100432 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600433 string += ch
434 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100435 raise QAPIParseError(self,
436 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200437 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100438 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200439 esc = True
440 elif ch == "'":
441 self.val = string
442 return
443 else:
444 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100445 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200446 self.val = True
447 self.cursor += 3
448 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100449 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200450 self.val = False
451 self.cursor += 4
452 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100453 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200454 self.val = None
455 self.cursor += 3
456 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200457 elif self.tok == '\n':
458 if self.cursor == len(self.src):
459 self.tok = None
460 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800461 self.line += 1
462 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200463 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100464 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500465
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200466 def get_members(self):
467 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200468 if self.tok == '}':
469 self.accept()
470 return expr
471 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100472 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200473 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200474 key = self.val
475 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200476 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100477 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200478 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800479 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100480 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200481 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200482 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200483 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200484 return expr
485 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100486 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200487 self.accept()
488 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100489 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500490
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200491 def get_values(self):
492 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200493 if self.tok == ']':
494 self.accept()
495 return expr
Eric Blake437db252015-09-29 16:21:02 -0600496 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100497 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
498 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200499 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200500 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200501 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200502 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200503 return expr
504 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100505 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200506 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500507
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200508 def get_expr(self, nested):
509 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100510 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200511 if self.tok == '{':
512 self.accept()
513 expr = self.get_members()
514 elif self.tok == '[':
515 self.accept()
516 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600517 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200518 expr = self.val
519 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200520 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100521 raise QAPIParseError(self, 'Expected "{", "[" or string')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200522 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200523
Marc-André Lureau3313b612017-01-13 15:41:29 +0100524 def get_doc(self, info):
525 if self.val != '##':
526 raise QAPIParseError(self, "Junk after '##' at start of "
527 "documentation comment")
528
529 doc = QAPIDoc(self, info)
530 self.accept(False)
531 while self.tok == '#':
532 if self.val.startswith('##'):
533 # End of doc comment
534 if self.val != '##':
535 raise QAPIParseError(self, "Junk after '##' at end of "
536 "documentation comment")
Markus Armbruster4ea71482017-03-15 13:57:23 +0100537 doc.end_comment()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100538 self.accept()
539 return doc
540 else:
541 doc.append(self.val)
542 self.accept(False)
543
544 raise QAPIParseError(self, "Documentation comment must end with '##'")
545
546
Markus Armbruster00e4b282015-06-10 10:04:36 +0200547#
548# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200549# TODO fold into QAPISchema
550# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200551#
552
Eric Blake437db252015-09-29 16:21:02 -0600553
Eric Blake14f00c62016-03-03 09:16:43 -0700554def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600555 if isinstance(base, dict):
556 return base
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800557 base_struct_define = find_struct(base)
558 if not base_struct_define:
559 return None
560 return base_struct_define['data']
561
Eric Blake437db252015-09-29 16:21:02 -0600562
Eric Blake811d04f2015-05-04 09:05:10 -0600563# Return the qtype of an alternate branch, or None on error.
564def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600565 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600566 return builtin_types[qapi_type]
567 elif find_struct(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100568 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600569 elif find_enum(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100570 return 'QTYPE_QSTRING'
Eric Blake811d04f2015-05-04 09:05:10 -0600571 elif find_union(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100572 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600573 return None
574
Eric Blake437db252015-09-29 16:21:02 -0600575
Wenchao Xiabceae762014-03-06 17:08:56 -0800576# Return the discriminator enum define if discriminator is specified as an
577# enum type, otherwise return None.
578def discriminator_find_enum_define(expr):
579 base = expr.get('base')
580 discriminator = expr.get('discriminator')
581
582 if not (discriminator and base):
583 return None
584
Eric Blake14f00c62016-03-03 09:16:43 -0700585 base_members = find_base_members(base)
586 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800587 return None
588
Eric Blake14f00c62016-03-03 09:16:43 -0700589 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800590 if not discriminator_type:
591 return None
592
593 return find_enum(discriminator_type)
594
Eric Blake437db252015-09-29 16:21:02 -0600595
Eric Blake59a92fe2015-11-18 01:52:56 -0700596# Names must be letters, numbers, -, and _. They must start with letter,
597# except for downstream extensions which must start with __RFQDN_.
598# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100599valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700600 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600601
602
Marc-André Lureau4148c292017-01-13 15:41:25 +0100603def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600604 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600605 global valid_name
606 membername = name
607
608 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100609 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600610 if name.startswith('*'):
611 membername = name[1:]
612 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100613 raise QAPISemError(info, "%s does not allow optional name '%s'"
614 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600615 # Enum members can start with a digit, because the generated C
616 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700617 if enum_member and membername[0].isdigit():
618 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600619 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
620 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600621 if not valid_name.match(membername) or \
622 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100623 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600624
Eric Blake437db252015-09-29 16:21:02 -0600625
626def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200627 global all_names
628 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200629 # FIXME should reject names that differ only in '_' vs. '.'
630 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200631 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100632 raise QAPISemError(info, "%s '%s' is already defined"
633 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600634 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100635 raise QAPISemError(info, "%s '%s' should not end in '%s'"
636 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200637 all_names[name] = meta
638
Eric Blake437db252015-09-29 16:21:02 -0600639
Markus Armbruster00e4b282015-06-10 10:04:36 +0200640def add_struct(definition, info):
641 global struct_types
642 name = definition['struct']
643 add_name(name, info, 'struct')
644 struct_types.append(definition)
645
Eric Blake437db252015-09-29 16:21:02 -0600646
Markus Armbruster00e4b282015-06-10 10:04:36 +0200647def find_struct(name):
648 global struct_types
649 for struct in struct_types:
650 if struct['struct'] == name:
651 return struct
652 return None
653
Eric Blake437db252015-09-29 16:21:02 -0600654
Markus Armbruster00e4b282015-06-10 10:04:36 +0200655def add_union(definition, info):
656 global union_types
657 name = definition['union']
658 add_name(name, info, 'union')
659 union_types.append(definition)
660
Eric Blake437db252015-09-29 16:21:02 -0600661
Markus Armbruster00e4b282015-06-10 10:04:36 +0200662def find_union(name):
663 global union_types
664 for union in union_types:
665 if union['union'] == name:
666 return union
667 return None
668
Eric Blake437db252015-09-29 16:21:02 -0600669
670def add_enum(name, info, enum_values=None, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200671 global enum_types
672 add_name(name, info, 'enum', implicit)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100673 enum_types.append({'enum_name': name, 'enum_values': enum_values})
Markus Armbruster00e4b282015-06-10 10:04:36 +0200674
Eric Blake437db252015-09-29 16:21:02 -0600675
Markus Armbruster00e4b282015-06-10 10:04:36 +0200676def find_enum(name):
677 global enum_types
678 for enum in enum_types:
679 if enum['enum_name'] == name:
680 return enum
681 return None
682
Markus Armbruster00e4b282015-06-10 10:04:36 +0200683
Eric Blake437db252015-09-29 16:21:02 -0600684def is_enum(name):
685 return find_enum(name) is not None
686
687
Marc-André Lureau4148c292017-01-13 15:41:25 +0100688def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600689 allow_dict=False, allow_optional=False,
690 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600691 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600692
693 if value is None:
694 return
695
Eric Blakedd883c62015-05-04 09:05:21 -0600696 # Check if array type for value is okay
697 if isinstance(value, list):
698 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100699 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600700 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100701 raise QAPISemError(info,
702 "%s: array type must contain single type name" %
703 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600704 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600705
706 # Check if type name for value is okay
707 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600708 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100709 raise QAPISemError(info, "%s uses unknown type '%s'"
710 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600711 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100712 raise QAPISemError(info, "%s cannot use %s type '%s'" %
713 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600714 return
715
Eric Blakedd883c62015-05-04 09:05:21 -0600716 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100717 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200718
719 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100720 raise QAPISemError(info,
721 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200722
723 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600724 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100725 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600726 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600727 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100728 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
729 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600730 # Todo: allow dictionaries to represent default values of
731 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100732 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200733 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600734 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600735 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600736
Eric Blake437db252015-09-29 16:21:02 -0600737
Marc-André Lureau4148c292017-01-13 15:41:25 +0100738def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600739 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600740 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600741
Eric Blakec8184082016-07-13 21:50:20 -0600742 args_meta = ['struct']
743 if boxed:
744 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100745 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600746 expr.get('data'), allow_dict=not boxed, allow_optional=True,
747 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600748 returns_meta = ['union', 'struct']
749 if name in returns_whitelist:
750 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100751 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200752 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200753 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600754
Eric Blake437db252015-09-29 16:21:02 -0600755
Marc-André Lureau4148c292017-01-13 15:41:25 +0100756def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600757 global events
758 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600759 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600760
Eric Blakec8184082016-07-13 21:50:20 -0600761 meta = ['struct']
762 if boxed:
763 meta += ['union', 'alternate']
Eric Blake4dc2e692015-05-04 09:05:17 -0600764 events.append(name)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100765 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600766 expr.get('data'), allow_dict=not boxed, allow_optional=True,
767 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200768
Eric Blake437db252015-09-29 16:21:02 -0600769
Marc-André Lureau4148c292017-01-13 15:41:25 +0100770def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800771 name = expr['union']
772 base = expr.get('base')
773 discriminator = expr.get('discriminator')
774 members = expr['data']
775
Eric Blake811d04f2015-05-04 09:05:10 -0600776 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600777
778 # With no discriminator it is a simple union.
779 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600780 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600781 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600782 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100783 raise QAPISemError(info, "Simple union '%s' must not have a base" %
784 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600785
786 # Else, it's a flat union.
787 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600788 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100789 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600790 base, allow_dict=True, allow_optional=True,
791 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600792 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100793 raise QAPISemError(info, "Flat union '%s' must have a base"
794 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700795 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100796 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800797
Eric Blakec9e0a792015-05-04 09:05:22 -0600798 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600799 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100800 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600801 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700802 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800803 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100804 raise QAPISemError(info,
805 "Discriminator '%s' is not a member of base "
806 "struct '%s'"
807 % (discriminator, base))
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800808 enum_define = find_enum(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600809 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800810 # Do not allow string discriminator
811 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100812 raise QAPISemError(info,
813 "Discriminator '%s' must be of enumeration "
814 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800815
Eric Blake02a57ae2016-02-17 23:48:16 -0700816 # Check every branch; don't allow an empty union
817 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100818 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800819 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100820 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600821
Eric Blake01cfbaa2015-12-01 22:20:58 -0700822 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100823 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200824 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600825
Eric Blake44bd1272015-05-04 09:05:08 -0600826 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700827 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600828 if enum_define:
Eric Blake437db252015-09-29 16:21:02 -0600829 if key not in enum_define['enum_values']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100830 raise QAPISemError(info,
831 "Discriminator value '%s' is not found in "
832 "enum '%s'"
Markus Armbrusteref801a92017-03-15 13:57:08 +0100833 % (key, enum_define['enum_name']))
Eric Blake44bd1272015-05-04 09:05:08 -0600834
Eric Blaked0b18232016-07-13 21:50:13 -0600835 # If discriminator is user-defined, ensure all values are covered
836 if enum_define:
837 for value in enum_define['enum_values']:
838 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100839 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
840 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600841
Eric Blake437db252015-09-29 16:21:02 -0600842
Marc-André Lureau4148c292017-01-13 15:41:25 +0100843def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600844 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600845 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600846 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600847
Eric Blake02a57ae2016-02-17 23:48:16 -0700848 # Check every branch; require at least two branches
849 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100850 raise QAPISemError(info,
851 "Alternate '%s' should have at least two branches "
852 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600853 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100854 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600855
Eric Blake811d04f2015-05-04 09:05:10 -0600856 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100857 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600858 value,
859 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600860 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700861 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100862 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
863 "type '%s'" % (name, key, value))
Eric Blake811d04f2015-05-04 09:05:10 -0600864 if qtype in types_seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100865 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
866 "be distinguished from member '%s'"
867 % (name, key, types_seen[qtype]))
Eric Blake811d04f2015-05-04 09:05:10 -0600868 types_seen[qtype] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800869
Eric Blake437db252015-09-29 16:21:02 -0600870
Marc-André Lureau4148c292017-01-13 15:41:25 +0100871def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600872 name = expr['enum']
873 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100874 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600875
876 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100877 raise QAPISemError(info,
878 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100879 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100880 raise QAPISemError(info,
881 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600882 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100883 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600884 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600885
Eric Blake437db252015-09-29 16:21:02 -0600886
Marc-André Lureau4148c292017-01-13 15:41:25 +0100887def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600888 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600889 members = expr['data']
890
Marc-André Lureau4148c292017-01-13 15:41:25 +0100891 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600892 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100893 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600894 allow_metas=['struct'])
895
Eric Blake437db252015-09-29 16:21:02 -0600896
Eric Blake0545f6b2015-05-04 09:05:15 -0600897def check_keys(expr_elem, meta, required, optional=[]):
898 expr = expr_elem['expr']
899 info = expr_elem['info']
900 name = expr[meta]
901 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100902 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600903 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600904 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600905 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100906 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
907 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600908 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100909 raise QAPISemError(info,
910 "'%s' of %s '%s' should only use false value"
911 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600912 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100913 raise QAPISemError(info,
914 "'%s' of %s '%s' should only use true value"
915 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600916 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600917 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100918 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
919 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600920
Eric Blake437db252015-09-29 16:21:02 -0600921
Markus Armbruster4d076d62015-06-10 08:55:21 +0200922def check_exprs(exprs):
923 global all_names
924
Markus Armbruster79470162017-03-15 13:57:21 +0100925 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200926 for builtin in builtin_types.keys():
927 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100928
929 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200930 for expr_elem in exprs:
931 expr = expr_elem['expr']
932 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100933 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100934
Markus Armbruster79470162017-03-15 13:57:21 +0100935 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100936 raise QAPISemError(info,
937 "Expression missing documentation comment")
938
Eric Blake437db252015-09-29 16:21:02 -0600939 if 'enum' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100940 name = expr['enum']
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100941 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster79470162017-03-15 13:57:21 +0100942 add_enum(name, info, expr['data'])
Eric Blake437db252015-09-29 16:21:02 -0600943 elif 'union' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100944 name = expr['union']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200945 check_keys(expr_elem, 'union', ['data'],
946 ['base', 'discriminator'])
947 add_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600948 elif 'alternate' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100949 name = expr['alternate']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200950 check_keys(expr_elem, 'alternate', ['data'])
Markus Armbruster79470162017-03-15 13:57:21 +0100951 add_name(name, info, 'alternate')
Eric Blake437db252015-09-29 16:21:02 -0600952 elif 'struct' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100953 name = expr['struct']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200954 check_keys(expr_elem, 'struct', ['data'], ['base'])
955 add_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600956 elif 'command' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100957 name = expr['command']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200958 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600959 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Markus Armbruster79470162017-03-15 13:57:21 +0100960 add_name(name, info, 'command')
Eric Blake437db252015-09-29 16:21:02 -0600961 elif 'event' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100962 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600963 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster79470162017-03-15 13:57:21 +0100964 add_name(name, info, 'event')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200965 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100966 raise QAPISemError(expr_elem['info'],
967 "Expression is missing metatype")
Markus Armbruster79470162017-03-15 13:57:21 +0100968 if doc and doc.symbol != name:
969 raise QAPISemError(info, "Definition of '%s' follows documentation"
970 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200971
972 # Try again for hidden UnionKind enum
973 for expr_elem in exprs:
974 expr = expr_elem['expr']
Eric Blake437db252015-09-29 16:21:02 -0600975 if 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200976 if not discriminator_find_enum_define(expr):
977 add_enum('%sKind' % expr['union'], expr_elem['info'],
978 implicit=True)
Eric Blake437db252015-09-29 16:21:02 -0600979 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200980 add_enum('%sKind' % expr['alternate'], expr_elem['info'],
981 implicit=True)
982
983 # Validate that exprs make sense
984 for expr_elem in exprs:
985 expr = expr_elem['expr']
986 info = expr_elem['info']
987
Eric Blake437db252015-09-29 16:21:02 -0600988 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200989 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600990 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200991 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600992 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200993 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600994 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200995 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600996 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200997 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600998 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200999 check_event(expr, info)
1000 else:
1001 assert False, 'unexpected meta type'
1002
Markus Armbrusterac882192015-09-16 13:06:05 +02001003 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -06001004
Markus Armbrusterac882192015-09-16 13:06:05 +02001005
Marc-André Lureau3313b612017-01-13 15:41:29 +01001006def check_definition_doc(doc, expr, info):
Marc-André Lureau3313b612017-01-13 15:41:29 +01001007 if doc.has_section('Returns') and 'command' not in expr:
1008 raise QAPISemError(info, "'Returns:' is only valid for commands")
1009
Marc-André Lureau3313b612017-01-13 15:41:29 +01001010
1011def check_docs(docs):
1012 for doc in docs:
Markus Armbruster2d433232017-03-15 13:57:22 +01001013 if doc.expr:
Marc-André Lureau3313b612017-01-13 15:41:29 +01001014 check_definition_doc(doc, doc.expr, doc.info)
1015
1016 return docs
1017
1018
Markus Armbrusterac882192015-09-16 13:06:05 +02001019#
1020# Schema compiler frontend
1021#
1022
1023class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001024 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001025 assert isinstance(name, str)
1026 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -06001027 # For explicitly defined entities, info points to the (explicit)
1028 # definition. For builtins (and their arrays), info is None.
1029 # For implicitly defined entities, info points to a place that
1030 # triggered the implicit definition (there may be more than one
1031 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +02001032 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001033 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +02001034
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001035 def c_name(self):
1036 return c_name(self.name)
1037
Markus Armbrusterac882192015-09-16 13:06:05 +02001038 def check(self, schema):
1039 pass
1040
Eric Blake49823c42015-10-12 22:22:27 -06001041 def is_implicit(self):
1042 return not self.info
1043
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001044 def visit(self, visitor):
1045 pass
1046
1047
1048class QAPISchemaVisitor(object):
1049 def visit_begin(self, schema):
1050 pass
1051
1052 def visit_end(self):
1053 pass
1054
Eric Blake25a0d9c2015-10-12 22:22:21 -06001055 def visit_needed(self, entity):
1056 # Default to visiting everything
1057 return True
1058
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001059 def visit_builtin_type(self, name, info, json_type):
1060 pass
1061
1062 def visit_enum_type(self, name, info, values, prefix):
1063 pass
1064
1065 def visit_array_type(self, name, info, element_type):
1066 pass
1067
1068 def visit_object_type(self, name, info, base, members, variants):
1069 pass
1070
Markus Armbruster39a18152015-09-16 13:06:28 +02001071 def visit_object_type_flat(self, name, info, members, variants):
1072 pass
1073
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001074 def visit_alternate_type(self, name, info, variants):
1075 pass
1076
1077 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001078 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001079 pass
1080
Eric Blake48825ca2016-07-13 21:50:19 -06001081 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001082 pass
1083
Markus Armbrusterac882192015-09-16 13:06:05 +02001084
1085class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001086 # Return the C type for common use.
1087 # For the types we commonly box, this is a pointer type.
1088 def c_type(self):
1089 pass
1090
1091 # Return the C type to be used in a parameter list.
1092 def c_param_type(self):
1093 return self.c_type()
1094
1095 # Return the C type to be used where we suppress boxing.
1096 def c_unboxed_type(self):
1097 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001098
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001099 def json_type(self):
1100 pass
1101
1102 def alternate_qtype(self):
1103 json2qtype = {
1104 'string': 'QTYPE_QSTRING',
1105 'number': 'QTYPE_QFLOAT',
1106 'int': 'QTYPE_QINT',
1107 'boolean': 'QTYPE_QBOOL',
1108 'object': 'QTYPE_QDICT'
1109 }
1110 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001111
Markus Armbruster691e0312017-03-15 13:57:14 +01001112 def doc_type(self):
1113 if self.is_implicit():
1114 return None
1115 return self.name
1116
Markus Armbrusterac882192015-09-16 13:06:05 +02001117
1118class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001119 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001120 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001121 assert not c_type or isinstance(c_type, str)
1122 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1123 'value')
1124 self._json_type_name = json_type
1125 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001126
1127 def c_name(self):
1128 return self.name
1129
Eric Blake4040d992016-03-17 16:48:28 -06001130 def c_type(self):
1131 return self._c_type_name
1132
1133 def c_param_type(self):
1134 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001135 return 'const ' + self._c_type_name
1136 return self._c_type_name
1137
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001138 def json_type(self):
1139 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001140
Markus Armbruster691e0312017-03-15 13:57:14 +01001141 def doc_type(self):
1142 return self.json_type()
1143
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001144 def visit(self, visitor):
1145 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1146
Markus Armbrusterac882192015-09-16 13:06:05 +02001147
1148class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001149 def __init__(self, name, info, doc, values, prefix):
1150 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001151 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001152 assert isinstance(v, QAPISchemaMember)
1153 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001154 assert prefix is None or isinstance(prefix, str)
1155 self.values = values
1156 self.prefix = prefix
1157
1158 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001159 seen = {}
1160 for v in self.values:
1161 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001162 if self.doc:
1163 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001164
Eric Blake99df5282015-10-12 22:22:32 -06001165 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001166 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1167 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001168
Eric Blake4040d992016-03-17 16:48:28 -06001169 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001170 return c_name(self.name)
1171
Eric Blake93bda4d2015-12-01 22:20:55 -07001172 def member_names(self):
1173 return [v.name for v in self.values]
1174
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001175 def json_type(self):
1176 return 'string'
1177
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001178 def visit(self, visitor):
1179 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001180 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001181
Markus Armbrusterac882192015-09-16 13:06:05 +02001182
1183class QAPISchemaArrayType(QAPISchemaType):
1184 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001185 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001186 assert isinstance(element_type, str)
1187 self._element_type_name = element_type
1188 self.element_type = None
1189
1190 def check(self, schema):
1191 self.element_type = schema.lookup_type(self._element_type_name)
1192 assert self.element_type
1193
Eric Blake99df5282015-10-12 22:22:32 -06001194 def is_implicit(self):
1195 return True
1196
Eric Blake4040d992016-03-17 16:48:28 -06001197 def c_type(self):
1198 return c_name(self.name) + pointer_suffix
1199
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001200 def json_type(self):
1201 return 'array'
1202
Markus Armbruster691e0312017-03-15 13:57:14 +01001203 def doc_type(self):
1204 elt_doc_type = self.element_type.doc_type()
1205 if not elt_doc_type:
1206 return None
1207 return 'array of ' + elt_doc_type
1208
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001209 def visit(self, visitor):
1210 visitor.visit_array_type(self.name, self.info, self.element_type)
1211
Markus Armbrusterac882192015-09-16 13:06:05 +02001212
1213class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001214 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001215 # struct has local_members, optional base, and no variants
1216 # flat union has base, variants, and no local_members
1217 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001218 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001219 assert base is None or isinstance(base, str)
1220 for m in local_members:
1221 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001222 m.set_owner(name)
1223 if variants is not None:
1224 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1225 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001226 self._base_name = base
1227 self.base = None
1228 self.local_members = local_members
1229 self.variants = variants
1230 self.members = None
1231
1232 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001233 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001234 raise QAPISemError(self.info,
1235 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001236 if self.members:
1237 return
1238 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001239 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001240 if self._base_name:
1241 self.base = schema.lookup_type(self._base_name)
1242 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001243 self.base.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001244 self.base.check_clash(schema, self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001245 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001246 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001247 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001248 if self.doc:
1249 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001250 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001251 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001252 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001253 assert self.variants.tag_member in self.members
Eric Blake27b60ab2015-11-18 01:52:51 -07001254 self.variants.check_clash(schema, self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001255 if self.doc:
1256 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001257
Eric Blake14f00c62016-03-03 09:16:43 -07001258 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001259 # and update seen to track the members seen so far. Report any errors
1260 # on behalf of info, which is not necessarily self.info
1261 def check_clash(self, schema, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001262 assert not self.variants # not implemented
1263 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001264 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001265
Eric Blake99df5282015-10-12 22:22:32 -06001266 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001267 # See QAPISchema._make_implicit_object_type(), as well as
1268 # _def_predefineds()
1269 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001270
Eric Blakeb6167702016-07-13 21:50:16 -06001271 def is_empty(self):
1272 assert self.members is not None
1273 return not self.members and not self.variants
1274
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001275 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001276 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001277 return QAPISchemaType.c_name(self)
1278
Eric Blake4040d992016-03-17 16:48:28 -06001279 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001280 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001281 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001282
Eric Blake4040d992016-03-17 16:48:28 -06001283 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001284 return c_name(self.name)
1285
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001286 def json_type(self):
1287 return 'object'
1288
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001289 def visit(self, visitor):
1290 visitor.visit_object_type(self.name, self.info,
1291 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001292 visitor.visit_object_type_flat(self.name, self.info,
1293 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001294
Markus Armbrusterac882192015-09-16 13:06:05 +02001295
Eric Blaked44f9ac2015-12-01 22:20:54 -07001296class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001297 role = 'member'
1298
Eric Blaked44f9ac2015-12-01 22:20:54 -07001299 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001300 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001301 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001302 self.owner = None
1303
1304 def set_owner(self, name):
1305 assert not self.owner
1306 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001307
Eric Blake27b60ab2015-11-18 01:52:51 -07001308 def check_clash(self, info, seen):
1309 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001310 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001311 raise QAPISemError(info,
1312 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001313 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001314 raise QAPISemError(info, "%s collides with %s" %
1315 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001316 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001317
Eric Blake88d4ef82015-11-18 01:52:50 -07001318 def _pretty_owner(self):
1319 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001320 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001321 # See QAPISchema._make_implicit_object_type() - reverse the
1322 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001323 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001324 if owner.endswith('-arg'):
1325 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001326 elif owner.endswith('-base'):
1327 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001328 else:
1329 assert owner.endswith('-wrapper')
1330 # Unreachable and not implemented
1331 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001332 if owner.endswith('Kind'):
1333 # See QAPISchema._make_implicit_enum_type()
1334 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001335 return '(%s of %s)' % (self.role, owner)
1336
1337 def describe(self):
1338 return "'%s' %s" % (self.name, self._pretty_owner())
1339
Markus Armbrusterac882192015-09-16 13:06:05 +02001340
Eric Blaked44f9ac2015-12-01 22:20:54 -07001341class QAPISchemaObjectTypeMember(QAPISchemaMember):
1342 def __init__(self, name, typ, optional):
1343 QAPISchemaMember.__init__(self, name)
1344 assert isinstance(typ, str)
1345 assert isinstance(optional, bool)
1346 self._type_name = typ
1347 self.type = None
1348 self.optional = optional
1349
1350 def check(self, schema):
1351 assert self.owner
1352 self.type = schema.lookup_type(self._type_name)
1353 assert self.type
1354
1355
Markus Armbrusterac882192015-09-16 13:06:05 +02001356class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001357 def __init__(self, tag_name, tag_member, variants):
1358 # Flat unions pass tag_name but not tag_member.
1359 # Simple unions and alternates pass tag_member but not tag_name.
1360 # After check(), tag_member is always set, and tag_name remains
1361 # a reliable witness of being used by a flat union.
1362 assert bool(tag_member) != bool(tag_name)
1363 assert (isinstance(tag_name, str) or
1364 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001365 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001366 for v in variants:
1367 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001368 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001369 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001370 self.variants = variants
1371
Eric Blake88d4ef82015-11-18 01:52:50 -07001372 def set_owner(self, name):
1373 for v in self.variants:
1374 v.set_owner(name)
1375
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001376 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001377 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001378 self.tag_member = seen[c_name(self._tag_name)]
1379 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001380 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1381 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001382 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001383 # Union names must match enum values; alternate names are
1384 # checked separately. Use 'seen' to tell the two apart.
1385 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001386 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001387 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001388 v.type.check(schema)
1389
Eric Blake27b60ab2015-11-18 01:52:51 -07001390 def check_clash(self, schema, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001391 for v in self.variants:
1392 # Reset seen map for each variant, since qapi names from one
1393 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001394 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blake27b60ab2015-11-18 01:52:51 -07001395 v.type.check_clash(schema, info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001396
Eric Blake437db252015-09-29 16:21:02 -06001397
Markus Armbrusterac882192015-09-16 13:06:05 +02001398class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001399 role = 'branch'
1400
Markus Armbrusterac882192015-09-16 13:06:05 +02001401 def __init__(self, name, typ):
1402 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1403
Markus Armbrusterac882192015-09-16 13:06:05 +02001404
1405class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001406 def __init__(self, name, info, doc, variants):
1407 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001408 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001409 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001410 variants.set_owner(name)
1411 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001412 self.variants = variants
1413
1414 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001415 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001416 # Not calling self.variants.check_clash(), because there's nothing
1417 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001418 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001419 # Alternate branch names have no relation to the tag enum values;
1420 # so we have to check for potential name collisions ourselves.
1421 seen = {}
1422 for v in self.variants.variants:
1423 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001424 if self.doc:
1425 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001426 if self.doc:
1427 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001428
Eric Blake4040d992016-03-17 16:48:28 -06001429 def c_type(self):
1430 return c_name(self.name) + pointer_suffix
1431
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001432 def json_type(self):
1433 return 'value'
1434
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001435 def visit(self, visitor):
1436 visitor.visit_alternate_type(self.name, self.info, self.variants)
1437
Eric Blakec8184082016-07-13 21:50:20 -06001438 def is_empty(self):
1439 return False
1440
Markus Armbrusterac882192015-09-16 13:06:05 +02001441
1442class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001443 def __init__(self, name, info, doc, arg_type, ret_type,
1444 gen, success_response, boxed):
1445 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001446 assert not arg_type or isinstance(arg_type, str)
1447 assert not ret_type or isinstance(ret_type, str)
1448 self._arg_type_name = arg_type
1449 self.arg_type = None
1450 self._ret_type_name = ret_type
1451 self.ret_type = None
1452 self.gen = gen
1453 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001454 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001455
1456 def check(self, schema):
1457 if self._arg_type_name:
1458 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001459 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1460 isinstance(self.arg_type, QAPISchemaAlternateType))
1461 self.arg_type.check(schema)
1462 if self.boxed:
1463 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001464 raise QAPISemError(self.info,
1465 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001466 else:
1467 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1468 assert not self.arg_type.variants
1469 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001470 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001471 if self._ret_type_name:
1472 self.ret_type = schema.lookup_type(self._ret_type_name)
1473 assert isinstance(self.ret_type, QAPISchemaType)
1474
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001475 def visit(self, visitor):
1476 visitor.visit_command(self.name, self.info,
1477 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001478 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001479
Markus Armbrusterac882192015-09-16 13:06:05 +02001480
1481class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001482 def __init__(self, name, info, doc, arg_type, boxed):
1483 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001484 assert not arg_type or isinstance(arg_type, str)
1485 self._arg_type_name = arg_type
1486 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001487 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001488
1489 def check(self, schema):
1490 if self._arg_type_name:
1491 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001492 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1493 isinstance(self.arg_type, QAPISchemaAlternateType))
1494 self.arg_type.check(schema)
1495 if self.boxed:
1496 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001497 raise QAPISemError(self.info,
1498 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001499 else:
1500 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1501 assert not self.arg_type.variants
1502 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001503 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001504
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001505 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001506 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001507
Markus Armbrusterac882192015-09-16 13:06:05 +02001508
1509class QAPISchema(object):
1510 def __init__(self, fname):
1511 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001512 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001513 self.exprs = check_exprs(parser.exprs)
1514 self.docs = check_docs(parser.docs)
Eric Blake7618b912015-10-12 22:22:22 -06001515 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001516 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001517 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001518 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001519 self._def_exprs()
1520 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001521 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001522 print >>sys.stderr, err
1523 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001524
Markus Armbrusterac882192015-09-16 13:06:05 +02001525 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001526 # Only the predefined types are allowed to not have info
1527 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001528 assert ent.name not in self._entity_dict
1529 self._entity_dict[ent.name] = ent
1530
1531 def lookup_entity(self, name, typ=None):
1532 ent = self._entity_dict.get(name)
1533 if typ and not isinstance(ent, typ):
1534 return None
1535 return ent
1536
1537 def lookup_type(self, name):
1538 return self.lookup_entity(name, QAPISchemaType)
1539
Eric Blake861877a2016-03-17 16:48:36 -06001540 def _def_builtin_type(self, name, json_type, c_type):
1541 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001542 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1543 # qapi-types.h from a single .c, all arrays of builtins must be
1544 # declared in the first file whether or not they are used. Nicer
1545 # would be to use lazy instantiation, while figuring out how to
1546 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001547 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001548
1549 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001550 for t in [('str', 'string', 'char' + pointer_suffix),
1551 ('number', 'number', 'double'),
1552 ('int', 'int', 'int64_t'),
1553 ('int8', 'int', 'int8_t'),
1554 ('int16', 'int', 'int16_t'),
1555 ('int32', 'int', 'int32_t'),
1556 ('int64', 'int', 'int64_t'),
1557 ('uint8', 'int', 'uint8_t'),
1558 ('uint16', 'int', 'uint16_t'),
1559 ('uint32', 'int', 'uint32_t'),
1560 ('uint64', 'int', 'uint64_t'),
1561 ('size', 'int', 'uint64_t'),
1562 ('bool', 'boolean', 'bool'),
1563 ('any', 'value', 'QObject' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001564 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001565 self.the_empty_object_type = QAPISchemaObjectType(
1566 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001567 self._def_entity(self.the_empty_object_type)
Eric Blake93bda4d2015-12-01 22:20:55 -07001568 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1569 'qstring', 'qdict', 'qlist',
1570 'qfloat', 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001571 self._def_entity(QAPISchemaEnumType('QType', None, None,
1572 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001573
Eric Blake93bda4d2015-12-01 22:20:55 -07001574 def _make_enum_members(self, values):
1575 return [QAPISchemaMember(v) for v in values]
1576
Eric Blake99df5282015-10-12 22:22:32 -06001577 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001578 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001579 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001580 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001581 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001582 return name
1583
Eric Blake99df5282015-10-12 22:22:32 -06001584 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001585 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001586 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001587 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001588 return name
1589
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001590 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001591 if not members:
1592 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001593 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001594 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001595 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001596 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001597 members, None))
1598 return name
1599
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001600 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001601 name = expr['enum']
1602 data = expr['data']
1603 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001604 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001605 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001606
Eric Blake99df5282015-10-12 22:22:32 -06001607 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001608 optional = False
1609 if name.startswith('*'):
1610 name = name[1:]
1611 optional = True
1612 if isinstance(typ, list):
1613 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001614 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001615 return QAPISchemaObjectTypeMember(name, typ, optional)
1616
Eric Blake99df5282015-10-12 22:22:32 -06001617 def _make_members(self, data, info):
1618 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001619 for (key, value) in data.iteritems()]
1620
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001621 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001622 name = expr['struct']
1623 base = expr.get('base')
1624 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001625 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001626 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001627 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001628
1629 def _make_variant(self, case, typ):
1630 return QAPISchemaObjectTypeVariant(case, typ)
1631
Eric Blake99df5282015-10-12 22:22:32 -06001632 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001633 if isinstance(typ, list):
1634 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001635 typ = self._make_array_type(typ[0], info)
1636 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001637 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001638 return QAPISchemaObjectTypeVariant(case, typ)
1639
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001640 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001641 name = expr['union']
1642 data = expr['data']
1643 base = expr.get('base')
1644 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001645 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001646 if isinstance(base, dict):
1647 base = (self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001648 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001649 if tag_name:
1650 variants = [self._make_variant(key, value)
1651 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001652 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001653 else:
Eric Blake99df5282015-10-12 22:22:32 -06001654 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001655 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001656 typ = self._make_implicit_enum_type(name, info,
1657 [v.name for v in variants])
1658 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001659 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001660 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001661 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001662 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001663 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001664 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001665
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001666 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001667 name = expr['alternate']
1668 data = expr['data']
1669 variants = [self._make_variant(key, value)
1670 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001671 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001672 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001673 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001674 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001675 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001676 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001677
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001678 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001679 name = expr['command']
1680 data = expr.get('data')
1681 rets = expr.get('returns')
1682 gen = expr.get('gen', True)
1683 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001684 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001685 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001686 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001687 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001688 if isinstance(rets, list):
1689 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001690 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001691 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1692 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001693
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001694 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001695 name = expr['event']
1696 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001697 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001698 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001699 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001700 name, info, doc, 'arg', self._make_members(data, info))
1701 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001702
1703 def _def_exprs(self):
1704 for expr_elem in self.exprs:
1705 expr = expr_elem['expr']
1706 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001707 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001708 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001709 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001710 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001711 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001712 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001713 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001714 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001715 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001716 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001717 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001718 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001719 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001720 else:
1721 assert False
1722
1723 def check(self):
1724 for ent in self._entity_dict.values():
1725 ent.check(self)
1726
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001727 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001728 visitor.visit_begin(self)
1729 for (name, entity) in sorted(self._entity_dict.items()):
1730 if visitor.visit_needed(entity):
1731 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001732 visitor.visit_end()
1733
Markus Armbruster2caba362013-07-27 17:41:56 +02001734
Markus Armbruster00e4b282015-06-10 10:04:36 +02001735#
1736# Code generation helpers
1737#
1738
Michael Roth0f923be2011-07-19 14:50:39 -05001739def camel_case(name):
1740 new_name = ''
1741 first = True
1742 for ch in name:
1743 if ch in ['_', '-']:
1744 first = True
1745 elif first:
1746 new_name += ch.upper()
1747 first = False
1748 else:
1749 new_name += ch.lower()
1750 return new_name
1751
Eric Blake437db252015-09-29 16:21:02 -06001752
Markus Armbruster849bc532015-05-14 06:50:53 -06001753# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1754# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1755# ENUM24_Name -> ENUM24_NAME
1756def camel_to_upper(value):
1757 c_fun_str = c_name(value, False)
1758 if value.isupper():
1759 return c_fun_str
1760
1761 new_name = ''
1762 l = len(c_fun_str)
1763 for i in range(l):
1764 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001765 # When c is upper and no '_' appears before, do more checks
1766 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001767 if i < l - 1 and c_fun_str[i + 1].islower():
1768 new_name += '_'
1769 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001770 new_name += '_'
1771 new_name += c
1772 return new_name.lstrip('_').upper()
1773
Eric Blake437db252015-09-29 16:21:02 -06001774
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001775def c_enum_const(type_name, const_name, prefix=None):
1776 if prefix is not None:
1777 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001778 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001779
Eric Blake18df5152015-05-14 06:50:48 -06001780c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001781
Eric Blake437db252015-09-29 16:21:02 -06001782
Eric Blakec6405b52015-05-14 06:50:55 -06001783# Map @name to a valid C identifier.
1784# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001785# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001786#
1787# Used for converting 'name' from a 'name':'type' qapi definition
1788# into a generated struct member, as well as converting type names
1789# into substrings of a generated C function name.
1790# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1791# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001792def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001793 # ANSI X3J11/88-090, 3.1.1
1794 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001795 'default', 'do', 'double', 'else', 'enum', 'extern',
1796 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1797 'return', 'short', 'signed', 'sizeof', 'static',
1798 'struct', 'switch', 'typedef', 'union', 'unsigned',
1799 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001800 # ISO/IEC 9899:1999, 6.4.1
1801 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1802 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001803 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1804 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001805 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1806 # excluding _.*
1807 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001808 # C++ ISO/IEC 14882:2003 2.11
1809 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1810 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1811 'namespace', 'new', 'operator', 'private', 'protected',
1812 'public', 'reinterpret_cast', 'static_cast', 'template',
1813 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1814 'using', 'virtual', 'wchar_t',
1815 # alternative representations
1816 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1817 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001818 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001819 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001820 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001821 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1822 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001823 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001824 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001825
Amos Kong05dfb262014-06-10 19:25:53 +08001826eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001827pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001828
Eric Blake437db252015-09-29 16:21:02 -06001829
Michael Roth0f923be2011-07-19 14:50:39 -05001830def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001831 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001832 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001833 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001834 return ret
1835
1836indent_level = 0
1837
Eric Blake437db252015-09-29 16:21:02 -06001838
Michael Roth0f923be2011-07-19 14:50:39 -05001839def push_indent(indent_amount=4):
1840 global indent_level
1841 indent_level += indent_amount
1842
Eric Blake437db252015-09-29 16:21:02 -06001843
Michael Roth0f923be2011-07-19 14:50:39 -05001844def pop_indent(indent_amount=4):
1845 global indent_level
1846 indent_level -= indent_amount
1847
Eric Blake437db252015-09-29 16:21:02 -06001848
Markus Armbruster77e703b2015-06-24 19:27:32 +02001849# Generate @code with @kwds interpolated.
1850# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001851def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001852 raw = code % kwds
1853 if indent_level:
1854 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001855 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001856 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001857 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001858 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001859 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001860
Eric Blake437db252015-09-29 16:21:02 -06001861
Michael Roth0f923be2011-07-19 14:50:39 -05001862def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001863 if code[0] == '\n':
1864 code = code[1:]
1865 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001866
Michael Roth0f923be2011-07-19 14:50:39 -05001867
1868def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001869 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001870
Eric Blake437db252015-09-29 16:21:02 -06001871
Michael Rothc0afa9c2013-05-10 17:46:00 -05001872def guardstart(name):
1873 return mcgen('''
1874
1875#ifndef %(name)s
1876#define %(name)s
1877
1878''',
1879 name=guardname(name))
1880
Eric Blake437db252015-09-29 16:21:02 -06001881
Michael Rothc0afa9c2013-05-10 17:46:00 -05001882def guardend(name):
1883 return mcgen('''
1884
1885#endif /* %(name)s */
1886
1887''',
1888 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001889
Eric Blake437db252015-09-29 16:21:02 -06001890
Markus Armbrustere98859a2015-09-16 13:06:16 +02001891def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001892 ret = mcgen('''
1893
Markus Armbrustere98859a2015-09-16 13:06:16 +02001894const char *const %(c_name)s_lookup[] = {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001895''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001896 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001897 for value in values:
1898 index = c_enum_const(name, value, prefix)
1899 ret += mcgen('''
1900 [%(index)s] = "%(value)s",
1901''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001902 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001903
Eric Blake7fb1cf12015-11-18 01:52:57 -07001904 max_index = c_enum_const(name, '_MAX', prefix)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001905 ret += mcgen('''
1906 [%(max_index)s] = NULL,
1907};
1908''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001909 max_index=max_index)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001910 return ret
1911
Eric Blake437db252015-09-29 16:21:02 -06001912
Markus Armbrustere98859a2015-09-16 13:06:16 +02001913def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001914 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001915 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001916
1917 ret = mcgen('''
1918
1919typedef enum %(c_name)s {
1920''',
1921 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001922
1923 i = 0
1924 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001925 ret += mcgen('''
1926 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001927''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001928 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001929 i=i)
1930 i += 1
1931
Markus Armbrustere98859a2015-09-16 13:06:16 +02001932 ret += mcgen('''
1933} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001934''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001935 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001936
Markus Armbrustere98859a2015-09-16 13:06:16 +02001937 ret += mcgen('''
1938
1939extern const char *const %(c_name)s_lookup[];
1940''',
1941 c_name=c_name(name))
1942 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001943
Eric Blake437db252015-09-29 16:21:02 -06001944
Eric Blake48825ca2016-07-13 21:50:19 -06001945def gen_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001946 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001947 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001948 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001949 ret = ''
1950 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001951 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001952 ret += '%s arg' % arg_type.c_param_type()
1953 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001954 else:
1955 assert not arg_type.variants
1956 for memb in arg_type.members:
1957 ret += sep
1958 sep = ', '
1959 if memb.optional:
1960 ret += 'bool has_%s, ' % c_name(memb.name)
1961 ret += '%s %s' % (memb.type.c_param_type(),
1962 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001963 if extra:
1964 ret += sep + extra
1965 return ret
1966
Eric Blake1f353342015-09-29 16:21:13 -06001967
Markus Armbruster00e4b282015-06-10 10:04:36 +02001968#
1969# Common command line parsing
1970#
1971
Eric Blake437db252015-09-29 16:21:02 -06001972
Markus Armbrusteref801a92017-03-15 13:57:08 +01001973def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001974
1975 try:
1976 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001977 'chp:o:' + extra_options,
1978 ['source', 'header', 'prefix=',
1979 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001980 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001981 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001982 sys.exit(1)
1983
Markus Armbrusteref801a92017-03-15 13:57:08 +01001984 output_dir = ''
1985 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001986 do_c = False
1987 do_h = False
1988 extra_opts = []
1989
1990 for oa in opts:
1991 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001992 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001993 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001994 if match.end() != len(a):
1995 print >>sys.stderr, \
1996 "%s: 'funny character '%s' in argument of --prefix" \
1997 % (sys.argv[0], a[match.end()])
1998 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001999 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01002000 elif o in ('-o', '--output-dir'):
2001 output_dir = a + '/'
2002 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02002003 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01002004 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02002005 do_h = True
2006 else:
2007 extra_opts.append(oa)
2008
2009 if not do_c and not do_h:
2010 do_c = True
2011 do_h = True
2012
Markus Armbruster16d80f62015-04-02 13:32:16 +02002013 if len(args) != 1:
2014 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002015 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02002016 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002017
Markus Armbruster54414042015-06-09 16:22:45 +02002018 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002019
Markus Armbruster00e4b282015-06-10 10:04:36 +02002020#
2021# Generate output files with boilerplate
2022#
2023
Eric Blake437db252015-09-29 16:21:02 -06002024
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002025def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
2026 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02002027 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002028 c_file = output_dir + prefix + c_file
2029 h_file = output_dir + prefix + h_file
2030
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002031 if output_dir:
2032 try:
2033 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01002034 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002035 if e.errno != errno.EEXIST:
2036 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002037
2038 def maybe_open(really, name, opt):
2039 if really:
2040 return open(name, opt)
2041 else:
2042 import StringIO
2043 return StringIO.StringIO()
2044
2045 fdef = maybe_open(do_c, c_file, 'w')
2046 fdecl = maybe_open(do_h, h_file, 'w')
2047
2048 fdef.write(mcgen('''
2049/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2050%(comment)s
2051''',
Eric Blake437db252015-09-29 16:21:02 -06002052 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002053
2054 fdecl.write(mcgen('''
2055/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2056%(comment)s
2057#ifndef %(guard)s
2058#define %(guard)s
2059
2060''',
Eric Blake437db252015-09-29 16:21:02 -06002061 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002062
2063 return (fdef, fdecl)
2064
Eric Blake437db252015-09-29 16:21:02 -06002065
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002066def close_output(fdef, fdecl):
2067 fdecl.write('''
2068#endif
2069''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002070 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002071 fdef.close()