blob: 1aaae8e2f6113d9912ee5a7d608fa673a1dba5eb [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
Marc-André Lureau3313b612017-01-13 15:41:29 +0100141
142 def has_section(self, name):
143 """Return True if we have a section with this name."""
144 for i in self.sections:
145 if i.name == name:
146 return True
147 return False
148
149 def append(self, line):
150 """Parse a comment line and add it to the documentation."""
151 line = line[1:]
152 if not line:
153 self._append_freeform(line)
154 return
155
156 if line[0] != ' ':
157 raise QAPIParseError(self.parser, "Missing space after #")
158 line = line[1:]
159
160 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
161 # recognized, and get silently treated as ordinary text
162 if self.symbol:
163 self._append_symbol_line(line)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100164 elif not self.body.content and line.startswith('@'):
165 if not line.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100166 raise QAPIParseError(self.parser, "Line should end with :")
167 self.symbol = line[1:-1]
168 # FIXME invalid names other than the empty string aren't flagged
169 if not self.symbol:
170 raise QAPIParseError(self.parser, "Invalid name")
171 else:
172 self._append_freeform(line)
173
Markus Armbruster4ea71482017-03-15 13:57:23 +0100174 def end_comment(self):
175 self._end_section()
176
Marc-André Lureau3313b612017-01-13 15:41:29 +0100177 def _append_symbol_line(self, line):
178 name = line.split(' ', 1)[0]
179
Markus Armbrusteref801a92017-03-15 13:57:08 +0100180 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100181 line = line[len(name)+1:]
182 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100183 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100184 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100185 'Note:', 'Notes:',
186 'Example:', 'Examples:',
187 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100188 line = line[len(name)+1:]
189 self._start_section(name[:-1])
190
191 self._append_freeform(line)
192
193 def _start_args_section(self, name):
194 # FIXME invalid names other than the empty string aren't flagged
195 if not name:
196 raise QAPIParseError(self.parser, "Invalid parameter name")
197 if name in self.args:
198 raise QAPIParseError(self.parser,
199 "'%s' parameter name duplicated" % name)
200 if self.sections:
201 raise QAPIParseError(self.parser,
202 "'@%s:' can't follow '%s' section"
203 % (name, self.sections[0].name))
Markus Armbruster4ea71482017-03-15 13:57:23 +0100204 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100205 self.section = QAPIDoc.ArgSection(name)
206 self.args[name] = self.section
207
Markus Armbrusteref801a92017-03-15 13:57:08 +0100208 def _start_section(self, name=''):
209 if name in ('Returns', 'Since') and self.has_section(name):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100210 raise QAPIParseError(self.parser,
211 "Duplicated '%s' section" % name)
Markus Armbruster4ea71482017-03-15 13:57:23 +0100212 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100213 self.section = QAPIDoc.Section(name)
214 self.sections.append(self.section)
215
Markus Armbruster4ea71482017-03-15 13:57:23 +0100216 def _end_section(self):
217 if self.section:
218 contents = str(self.section)
219 if self.section.name and (not contents or contents.isspace()):
220 raise QAPIParseError(self.parser, "Empty doc section '%s'"
221 % self.section.name)
222 self.section = None
223
Marc-André Lureau3313b612017-01-13 15:41:29 +0100224 def _append_freeform(self, line):
225 in_arg = isinstance(self.section, QAPIDoc.ArgSection)
226 if (in_arg and self.section.content
227 and not self.section.content[-1]
228 and line and not line[0].isspace()):
229 self._start_section()
230 if (in_arg or not self.section.name
Markus Armbrusteref801a92017-03-15 13:57:08 +0100231 or not self.section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100232 line = line.strip()
Markus Armbruster2d433232017-03-15 13:57:22 +0100233 match = re.match(r'(@\S+:)', line)
234 if match:
235 raise QAPIParseError(self.parser,
236 "'%s' not allowed in free-form documentation"
237 % match.group(1))
Markus Armbruster1d8bda12017-03-15 13:57:06 +0100238 # TODO Drop this once the dust has settled
239 if (isinstance(self.section, QAPIDoc.ArgSection)
240 and '#optional' in line):
241 raise QAPISemError(self.info, "Please drop the #optional tag")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100242 self.section.append(line)
243
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100244 def connect_member(self, member):
245 if member.name not in self.args:
246 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100247 self.args[member.name] = QAPIDoc.ArgSection(member.name)
248 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100249
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100250 def check_expr(self, expr):
251 if self.has_section('Returns') and 'command' not in expr:
252 raise QAPISemError(self.info,
253 "'Returns:' is only valid for commands")
254
Markus Armbruster816a57c2017-03-15 13:57:26 +0100255 def check(self):
256 bogus = [name for name, section in self.args.iteritems()
257 if not section.member]
258 if bogus:
259 raise QAPISemError(
260 self.info,
261 "The following documented members are not in "
262 "the declaration: %s" % ", ".join(bogus))
263
Marc-André Lureau3313b612017-01-13 15:41:29 +0100264
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200265class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500266
Eric Blake437db252015-09-29 16:21:02 -0600267 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200268 abs_fname = os.path.abspath(fp.name)
Markus Armbruster8608d252015-06-09 18:32:29 +0200269 fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200270 self.fname = fname
Markus Armbruster54414042015-06-09 16:22:45 +0200271 previously_included.append(abs_fname)
272 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200273 self.src = fp.read()
274 if self.src == '' or self.src[-1] != '\n':
275 self.src += '\n'
276 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800277 self.line = 1
278 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200279 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100280 self.docs = []
Markus Armbrustere7823a22017-03-15 13:57:20 +0100281 self.cur_doc = None
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200282 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500283
Eric Blake437db252015-09-29 16:21:02 -0600284 while self.tok is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100285 info = {'file': fname, 'line': self.line,
286 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100287 if self.tok == '#':
Markus Armbrustere7823a22017-03-15 13:57:20 +0100288 self.reject_expr_doc()
289 self.cur_doc = self.get_doc(info)
290 self.docs.append(self.cur_doc)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100291 continue
292
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200293 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100294 if 'include' in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100295 self.reject_expr_doc()
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200296 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100297 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100298 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200299 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100300 raise QAPISemError(info,
301 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100302 self._include(include, info, os.path.dirname(abs_fname),
303 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100304 elif "pragma" in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100305 self.reject_expr_doc()
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100306 if len(expr) != 1:
307 raise QAPISemError(info, "Invalid 'pragma' directive")
308 pragma = expr['pragma']
309 if not isinstance(pragma, dict):
310 raise QAPISemError(
311 info, "Value of 'pragma' must be a dictionary")
312 for name, value in pragma.iteritems():
313 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200314 else:
315 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100316 'info': info}
Markus Armbrustere7823a22017-03-15 13:57:20 +0100317 if self.cur_doc:
318 if not self.cur_doc.symbol:
319 raise QAPISemError(
320 self.cur_doc.info,
321 "Expression documentation required")
Markus Armbrustere7823a22017-03-15 13:57:20 +0100322 expr_elem['doc'] = self.cur_doc
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200323 self.exprs.append(expr_elem)
Markus Armbrustere7823a22017-03-15 13:57:20 +0100324 self.cur_doc = None
325 self.reject_expr_doc()
326
327 def reject_expr_doc(self):
328 if self.cur_doc and self.cur_doc.symbol:
329 raise QAPISemError(
330 self.cur_doc.info,
331 "Documentation for '%s' is not followed by the definition"
332 % self.cur_doc.symbol)
Michael Roth0f923be2011-07-19 14:50:39 -0500333
Markus Armbrustere04dea82017-03-15 13:56:50 +0100334 def _include(self, include, info, base_dir, previously_included):
335 incl_abs_fname = os.path.join(base_dir, include)
336 # catch inclusion cycle
337 inf = info
338 while inf:
339 if incl_abs_fname == os.path.abspath(inf['file']):
340 raise QAPISemError(info, "Inclusion loop for %s" % include)
341 inf = inf['parent']
342
343 # skip multiple include of the same file
344 if incl_abs_fname in previously_included:
345 return
346 try:
347 fobj = open(incl_abs_fname, 'r')
348 except IOError as e:
349 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
350 exprs_include = QAPISchemaParser(fobj, previously_included, info)
351 self.exprs.extend(exprs_include.exprs)
352 self.docs.extend(exprs_include.docs)
353
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100354 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100355 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100356 if name == 'doc-required':
357 if not isinstance(value, bool):
358 raise QAPISemError(info,
359 "Pragma 'doc-required' must be boolean")
360 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100361 elif name == 'returns-whitelist':
362 if (not isinstance(value, list)
363 or any([not isinstance(elt, str) for elt in value])):
364 raise QAPISemError(info,
365 "Pragma returns-whitelist must be"
366 " a list of strings")
367 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100368 elif name == 'name-case-whitelist':
369 if (not isinstance(value, list)
370 or any([not isinstance(elt, str) for elt in value])):
371 raise QAPISemError(info,
372 "Pragma name-case-whitelist must be"
373 " a list of strings")
374 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100375 else:
376 raise QAPISemError(info, "Unknown pragma '%s'" % name)
377
Marc-André Lureau3313b612017-01-13 15:41:29 +0100378 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200379 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200380 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200381 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200382 self.cursor += 1
383 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500384
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200385 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100386 if self.src[self.cursor] == '#':
387 # Start of doc comment
388 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200389 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100390 if not skip_comment:
391 self.val = self.src[self.pos:self.cursor]
392 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100393 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200394 return
395 elif self.tok == "'":
396 string = ''
397 esc = False
398 while True:
399 ch = self.src[self.cursor]
400 self.cursor += 1
401 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100402 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200403 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600404 if ch == 'b':
405 string += '\b'
406 elif ch == 'f':
407 string += '\f'
408 elif ch == 'n':
409 string += '\n'
410 elif ch == 'r':
411 string += '\r'
412 elif ch == 't':
413 string += '\t'
414 elif ch == 'u':
415 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600416 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600417 ch = self.src[self.cursor]
418 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100419 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100420 raise QAPIParseError(self,
421 '\\u escape needs 4 '
422 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600423 value = (value << 4) + int(ch, 16)
424 # If Python 2 and 3 didn't disagree so much on
425 # how to handle Unicode, then we could allow
426 # Unicode string defaults. But most of QAPI is
427 # ASCII-only, so we aren't losing much for now.
428 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100429 raise QAPIParseError(self,
430 'For now, \\u escape '
431 'only supports non-zero '
432 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600433 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100434 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600435 string += ch
436 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100437 raise QAPIParseError(self,
438 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200439 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100440 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200441 esc = True
442 elif ch == "'":
443 self.val = string
444 return
445 else:
446 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100447 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200448 self.val = True
449 self.cursor += 3
450 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100451 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200452 self.val = False
453 self.cursor += 4
454 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100455 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200456 self.val = None
457 self.cursor += 3
458 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200459 elif self.tok == '\n':
460 if self.cursor == len(self.src):
461 self.tok = None
462 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800463 self.line += 1
464 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200465 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100466 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500467
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200468 def get_members(self):
469 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200470 if self.tok == '}':
471 self.accept()
472 return expr
473 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100474 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200475 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200476 key = self.val
477 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200478 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100479 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200480 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800481 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100482 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200483 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200484 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200485 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200486 return expr
487 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100488 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200489 self.accept()
490 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100491 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500492
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200493 def get_values(self):
494 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200495 if self.tok == ']':
496 self.accept()
497 return expr
Eric Blake437db252015-09-29 16:21:02 -0600498 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100499 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
500 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200501 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200502 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200503 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200504 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200505 return expr
506 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100507 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200508 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500509
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200510 def get_expr(self, nested):
511 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100512 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200513 if self.tok == '{':
514 self.accept()
515 expr = self.get_members()
516 elif self.tok == '[':
517 self.accept()
518 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600519 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200520 expr = self.val
521 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200522 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100523 raise QAPIParseError(self, 'Expected "{", "[" or string')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200524 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200525
Marc-André Lureau3313b612017-01-13 15:41:29 +0100526 def get_doc(self, info):
527 if self.val != '##':
528 raise QAPIParseError(self, "Junk after '##' at start of "
529 "documentation comment")
530
531 doc = QAPIDoc(self, info)
532 self.accept(False)
533 while self.tok == '#':
534 if self.val.startswith('##'):
535 # End of doc comment
536 if self.val != '##':
537 raise QAPIParseError(self, "Junk after '##' at end of "
538 "documentation comment")
Markus Armbruster4ea71482017-03-15 13:57:23 +0100539 doc.end_comment()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100540 self.accept()
541 return doc
542 else:
543 doc.append(self.val)
544 self.accept(False)
545
546 raise QAPIParseError(self, "Documentation comment must end with '##'")
547
548
Markus Armbruster00e4b282015-06-10 10:04:36 +0200549#
550# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200551# TODO fold into QAPISchema
552# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200553#
554
Eric Blake437db252015-09-29 16:21:02 -0600555
Eric Blake14f00c62016-03-03 09:16:43 -0700556def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600557 if isinstance(base, dict):
558 return base
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800559 base_struct_define = find_struct(base)
560 if not base_struct_define:
561 return None
562 return base_struct_define['data']
563
Eric Blake437db252015-09-29 16:21:02 -0600564
Eric Blake811d04f2015-05-04 09:05:10 -0600565# Return the qtype of an alternate branch, or None on error.
566def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600567 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600568 return builtin_types[qapi_type]
569 elif find_struct(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100570 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600571 elif find_enum(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100572 return 'QTYPE_QSTRING'
Eric Blake811d04f2015-05-04 09:05:10 -0600573 elif find_union(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100574 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600575 return None
576
Eric Blake437db252015-09-29 16:21:02 -0600577
Wenchao Xiabceae762014-03-06 17:08:56 -0800578# Return the discriminator enum define if discriminator is specified as an
579# enum type, otherwise return None.
580def discriminator_find_enum_define(expr):
581 base = expr.get('base')
582 discriminator = expr.get('discriminator')
583
584 if not (discriminator and base):
585 return None
586
Eric Blake14f00c62016-03-03 09:16:43 -0700587 base_members = find_base_members(base)
588 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800589 return None
590
Eric Blake14f00c62016-03-03 09:16:43 -0700591 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800592 if not discriminator_type:
593 return None
594
595 return find_enum(discriminator_type)
596
Eric Blake437db252015-09-29 16:21:02 -0600597
Eric Blake59a92fe2015-11-18 01:52:56 -0700598# Names must be letters, numbers, -, and _. They must start with letter,
599# except for downstream extensions which must start with __RFQDN_.
600# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100601valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700602 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600603
604
Marc-André Lureau4148c292017-01-13 15:41:25 +0100605def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600606 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600607 global valid_name
608 membername = name
609
610 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100611 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600612 if name.startswith('*'):
613 membername = name[1:]
614 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100615 raise QAPISemError(info, "%s does not allow optional name '%s'"
616 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600617 # Enum members can start with a digit, because the generated C
618 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700619 if enum_member and membername[0].isdigit():
620 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600621 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
622 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600623 if not valid_name.match(membername) or \
624 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100625 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600626
Eric Blake437db252015-09-29 16:21:02 -0600627
628def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200629 global all_names
630 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200631 # FIXME should reject names that differ only in '_' vs. '.'
632 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200633 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100634 raise QAPISemError(info, "%s '%s' is already defined"
635 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600636 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100637 raise QAPISemError(info, "%s '%s' should not end in '%s'"
638 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200639 all_names[name] = meta
640
Eric Blake437db252015-09-29 16:21:02 -0600641
Markus Armbruster00e4b282015-06-10 10:04:36 +0200642def add_struct(definition, info):
643 global struct_types
644 name = definition['struct']
645 add_name(name, info, 'struct')
646 struct_types.append(definition)
647
Eric Blake437db252015-09-29 16:21:02 -0600648
Markus Armbruster00e4b282015-06-10 10:04:36 +0200649def find_struct(name):
650 global struct_types
651 for struct in struct_types:
652 if struct['struct'] == name:
653 return struct
654 return None
655
Eric Blake437db252015-09-29 16:21:02 -0600656
Markus Armbruster00e4b282015-06-10 10:04:36 +0200657def add_union(definition, info):
658 global union_types
659 name = definition['union']
660 add_name(name, info, 'union')
661 union_types.append(definition)
662
Eric Blake437db252015-09-29 16:21:02 -0600663
Markus Armbruster00e4b282015-06-10 10:04:36 +0200664def find_union(name):
665 global union_types
666 for union in union_types:
667 if union['union'] == name:
668 return union
669 return None
670
Eric Blake437db252015-09-29 16:21:02 -0600671
672def add_enum(name, info, enum_values=None, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200673 global enum_types
674 add_name(name, info, 'enum', implicit)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100675 enum_types.append({'enum_name': name, 'enum_values': enum_values})
Markus Armbruster00e4b282015-06-10 10:04:36 +0200676
Eric Blake437db252015-09-29 16:21:02 -0600677
Markus Armbruster00e4b282015-06-10 10:04:36 +0200678def find_enum(name):
679 global enum_types
680 for enum in enum_types:
681 if enum['enum_name'] == name:
682 return enum
683 return None
684
Markus Armbruster00e4b282015-06-10 10:04:36 +0200685
Eric Blake437db252015-09-29 16:21:02 -0600686def is_enum(name):
687 return find_enum(name) is not None
688
689
Marc-André Lureau4148c292017-01-13 15:41:25 +0100690def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600691 allow_dict=False, allow_optional=False,
692 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600693 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600694
695 if value is None:
696 return
697
Eric Blakedd883c62015-05-04 09:05:21 -0600698 # Check if array type for value is okay
699 if isinstance(value, list):
700 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100701 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600702 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100703 raise QAPISemError(info,
704 "%s: array type must contain single type name" %
705 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600706 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600707
708 # Check if type name for value is okay
709 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600710 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100711 raise QAPISemError(info, "%s uses unknown type '%s'"
712 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600713 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100714 raise QAPISemError(info, "%s cannot use %s type '%s'" %
715 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600716 return
717
Eric Blakedd883c62015-05-04 09:05:21 -0600718 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100719 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200720
721 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100722 raise QAPISemError(info,
723 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200724
725 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600726 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100727 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600728 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600729 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100730 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
731 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600732 # Todo: allow dictionaries to represent default values of
733 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100734 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200735 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600736 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600737 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600738
Eric Blake437db252015-09-29 16:21:02 -0600739
Marc-André Lureau4148c292017-01-13 15:41:25 +0100740def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600741 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600742 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600743
Eric Blakec8184082016-07-13 21:50:20 -0600744 args_meta = ['struct']
745 if boxed:
746 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100747 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600748 expr.get('data'), allow_dict=not boxed, allow_optional=True,
749 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600750 returns_meta = ['union', 'struct']
751 if name in returns_whitelist:
752 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100753 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200754 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200755 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600756
Eric Blake437db252015-09-29 16:21:02 -0600757
Marc-André Lureau4148c292017-01-13 15:41:25 +0100758def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600759 global events
760 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600761 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600762
Eric Blakec8184082016-07-13 21:50:20 -0600763 meta = ['struct']
764 if boxed:
765 meta += ['union', 'alternate']
Eric Blake4dc2e692015-05-04 09:05:17 -0600766 events.append(name)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100767 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600768 expr.get('data'), allow_dict=not boxed, allow_optional=True,
769 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200770
Eric Blake437db252015-09-29 16:21:02 -0600771
Marc-André Lureau4148c292017-01-13 15:41:25 +0100772def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800773 name = expr['union']
774 base = expr.get('base')
775 discriminator = expr.get('discriminator')
776 members = expr['data']
777
Eric Blake811d04f2015-05-04 09:05:10 -0600778 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600779
780 # With no discriminator it is a simple union.
781 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600782 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600783 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600784 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100785 raise QAPISemError(info, "Simple union '%s' must not have a base" %
786 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600787
788 # Else, it's a flat union.
789 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600790 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100791 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600792 base, allow_dict=True, allow_optional=True,
793 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600794 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100795 raise QAPISemError(info, "Flat union '%s' must have a base"
796 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700797 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100798 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800799
Eric Blakec9e0a792015-05-04 09:05:22 -0600800 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600801 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100802 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600803 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700804 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800805 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100806 raise QAPISemError(info,
807 "Discriminator '%s' is not a member of base "
808 "struct '%s'"
809 % (discriminator, base))
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800810 enum_define = find_enum(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600811 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800812 # Do not allow string discriminator
813 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100814 raise QAPISemError(info,
815 "Discriminator '%s' must be of enumeration "
816 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800817
Eric Blake02a57ae2016-02-17 23:48:16 -0700818 # Check every branch; don't allow an empty union
819 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100820 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800821 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100822 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600823
Eric Blake01cfbaa2015-12-01 22:20:58 -0700824 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100825 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200826 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600827
Eric Blake44bd1272015-05-04 09:05:08 -0600828 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700829 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600830 if enum_define:
Eric Blake437db252015-09-29 16:21:02 -0600831 if key not in enum_define['enum_values']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100832 raise QAPISemError(info,
833 "Discriminator value '%s' is not found in "
834 "enum '%s'"
Markus Armbrusteref801a92017-03-15 13:57:08 +0100835 % (key, enum_define['enum_name']))
Eric Blake44bd1272015-05-04 09:05:08 -0600836
Eric Blaked0b18232016-07-13 21:50:13 -0600837 # If discriminator is user-defined, ensure all values are covered
838 if enum_define:
839 for value in enum_define['enum_values']:
840 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100841 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
842 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600843
Eric Blake437db252015-09-29 16:21:02 -0600844
Marc-André Lureau4148c292017-01-13 15:41:25 +0100845def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600846 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600847 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600848 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600849
Eric Blake02a57ae2016-02-17 23:48:16 -0700850 # Check every branch; require at least two branches
851 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100852 raise QAPISemError(info,
853 "Alternate '%s' should have at least two branches "
854 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600855 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100856 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600857
Eric Blake811d04f2015-05-04 09:05:10 -0600858 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100859 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600860 value,
861 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600862 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700863 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100864 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
865 "type '%s'" % (name, key, value))
Eric Blake811d04f2015-05-04 09:05:10 -0600866 if qtype in types_seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100867 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
868 "be distinguished from member '%s'"
869 % (name, key, types_seen[qtype]))
Eric Blake811d04f2015-05-04 09:05:10 -0600870 types_seen[qtype] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800871
Eric Blake437db252015-09-29 16:21:02 -0600872
Marc-André Lureau4148c292017-01-13 15:41:25 +0100873def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600874 name = expr['enum']
875 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100876 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600877
878 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100879 raise QAPISemError(info,
880 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100881 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100882 raise QAPISemError(info,
883 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600884 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100885 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600886 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600887
Eric Blake437db252015-09-29 16:21:02 -0600888
Marc-André Lureau4148c292017-01-13 15:41:25 +0100889def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600890 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600891 members = expr['data']
892
Marc-André Lureau4148c292017-01-13 15:41:25 +0100893 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600894 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100895 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600896 allow_metas=['struct'])
897
Eric Blake437db252015-09-29 16:21:02 -0600898
Eric Blake0545f6b2015-05-04 09:05:15 -0600899def check_keys(expr_elem, meta, required, optional=[]):
900 expr = expr_elem['expr']
901 info = expr_elem['info']
902 name = expr[meta]
903 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100904 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600905 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600906 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600907 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100908 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
909 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600910 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100911 raise QAPISemError(info,
912 "'%s' of %s '%s' should only use false value"
913 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600914 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100915 raise QAPISemError(info,
916 "'%s' of %s '%s' should only use true value"
917 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600918 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600919 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100920 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
921 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600922
Eric Blake437db252015-09-29 16:21:02 -0600923
Markus Armbruster4d076d62015-06-10 08:55:21 +0200924def check_exprs(exprs):
925 global all_names
926
Markus Armbruster79470162017-03-15 13:57:21 +0100927 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200928 for builtin in builtin_types.keys():
929 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100930
931 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200932 for expr_elem in exprs:
933 expr = expr_elem['expr']
934 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100935 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100936
Markus Armbruster79470162017-03-15 13:57:21 +0100937 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100938 raise QAPISemError(info,
939 "Expression missing documentation comment")
940
Eric Blake437db252015-09-29 16:21:02 -0600941 if 'enum' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100942 name = expr['enum']
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100943 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster79470162017-03-15 13:57:21 +0100944 add_enum(name, info, expr['data'])
Eric Blake437db252015-09-29 16:21:02 -0600945 elif 'union' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100946 name = expr['union']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200947 check_keys(expr_elem, 'union', ['data'],
948 ['base', 'discriminator'])
949 add_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600950 elif 'alternate' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100951 name = expr['alternate']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200952 check_keys(expr_elem, 'alternate', ['data'])
Markus Armbruster79470162017-03-15 13:57:21 +0100953 add_name(name, info, 'alternate')
Eric Blake437db252015-09-29 16:21:02 -0600954 elif 'struct' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100955 name = expr['struct']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200956 check_keys(expr_elem, 'struct', ['data'], ['base'])
957 add_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600958 elif 'command' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100959 name = expr['command']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200960 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600961 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Markus Armbruster79470162017-03-15 13:57:21 +0100962 add_name(name, info, 'command')
Eric Blake437db252015-09-29 16:21:02 -0600963 elif 'event' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100964 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600965 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster79470162017-03-15 13:57:21 +0100966 add_name(name, info, 'event')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200967 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100968 raise QAPISemError(expr_elem['info'],
969 "Expression is missing metatype")
Markus Armbruster79470162017-03-15 13:57:21 +0100970 if doc and doc.symbol != name:
971 raise QAPISemError(info, "Definition of '%s' follows documentation"
972 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200973
974 # Try again for hidden UnionKind enum
975 for expr_elem in exprs:
976 expr = expr_elem['expr']
Eric Blake437db252015-09-29 16:21:02 -0600977 if 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200978 if not discriminator_find_enum_define(expr):
979 add_enum('%sKind' % expr['union'], expr_elem['info'],
980 implicit=True)
Eric Blake437db252015-09-29 16:21:02 -0600981 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200982 add_enum('%sKind' % expr['alternate'], expr_elem['info'],
983 implicit=True)
984
985 # Validate that exprs make sense
986 for expr_elem in exprs:
987 expr = expr_elem['expr']
988 info = expr_elem['info']
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100989 doc = expr_elem.get('doc')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200990
Eric Blake437db252015-09-29 16:21:02 -0600991 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200992 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600993 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200994 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600995 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200996 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600997 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200998 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600999 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +02001000 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -06001001 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +02001002 check_event(expr, info)
1003 else:
1004 assert False, 'unexpected meta type'
1005
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001006 if doc:
1007 doc.check_expr(expr)
1008
Markus Armbrusterac882192015-09-16 13:06:05 +02001009 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -06001010
Markus Armbrusterac882192015-09-16 13:06:05 +02001011
1012#
1013# Schema compiler frontend
1014#
1015
1016class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001017 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001018 assert isinstance(name, str)
1019 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -06001020 # For explicitly defined entities, info points to the (explicit)
1021 # definition. For builtins (and their arrays), info is None.
1022 # For implicitly defined entities, info points to a place that
1023 # triggered the implicit definition (there may be more than one
1024 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +02001025 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001026 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +02001027
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001028 def c_name(self):
1029 return c_name(self.name)
1030
Markus Armbrusterac882192015-09-16 13:06:05 +02001031 def check(self, schema):
1032 pass
1033
Eric Blake49823c42015-10-12 22:22:27 -06001034 def is_implicit(self):
1035 return not self.info
1036
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001037 def visit(self, visitor):
1038 pass
1039
1040
1041class QAPISchemaVisitor(object):
1042 def visit_begin(self, schema):
1043 pass
1044
1045 def visit_end(self):
1046 pass
1047
Eric Blake25a0d9c2015-10-12 22:22:21 -06001048 def visit_needed(self, entity):
1049 # Default to visiting everything
1050 return True
1051
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001052 def visit_builtin_type(self, name, info, json_type):
1053 pass
1054
1055 def visit_enum_type(self, name, info, values, prefix):
1056 pass
1057
1058 def visit_array_type(self, name, info, element_type):
1059 pass
1060
1061 def visit_object_type(self, name, info, base, members, variants):
1062 pass
1063
Markus Armbruster39a18152015-09-16 13:06:28 +02001064 def visit_object_type_flat(self, name, info, members, variants):
1065 pass
1066
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001067 def visit_alternate_type(self, name, info, variants):
1068 pass
1069
1070 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001071 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001072 pass
1073
Eric Blake48825ca2016-07-13 21:50:19 -06001074 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001075 pass
1076
Markus Armbrusterac882192015-09-16 13:06:05 +02001077
1078class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001079 # Return the C type for common use.
1080 # For the types we commonly box, this is a pointer type.
1081 def c_type(self):
1082 pass
1083
1084 # Return the C type to be used in a parameter list.
1085 def c_param_type(self):
1086 return self.c_type()
1087
1088 # Return the C type to be used where we suppress boxing.
1089 def c_unboxed_type(self):
1090 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001091
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001092 def json_type(self):
1093 pass
1094
1095 def alternate_qtype(self):
1096 json2qtype = {
1097 'string': 'QTYPE_QSTRING',
1098 'number': 'QTYPE_QFLOAT',
1099 'int': 'QTYPE_QINT',
1100 'boolean': 'QTYPE_QBOOL',
1101 'object': 'QTYPE_QDICT'
1102 }
1103 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001104
Markus Armbruster691e0312017-03-15 13:57:14 +01001105 def doc_type(self):
1106 if self.is_implicit():
1107 return None
1108 return self.name
1109
Markus Armbrusterac882192015-09-16 13:06:05 +02001110
1111class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001112 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001113 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001114 assert not c_type or isinstance(c_type, str)
1115 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1116 'value')
1117 self._json_type_name = json_type
1118 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001119
1120 def c_name(self):
1121 return self.name
1122
Eric Blake4040d992016-03-17 16:48:28 -06001123 def c_type(self):
1124 return self._c_type_name
1125
1126 def c_param_type(self):
1127 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001128 return 'const ' + self._c_type_name
1129 return self._c_type_name
1130
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001131 def json_type(self):
1132 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001133
Markus Armbruster691e0312017-03-15 13:57:14 +01001134 def doc_type(self):
1135 return self.json_type()
1136
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001137 def visit(self, visitor):
1138 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1139
Markus Armbrusterac882192015-09-16 13:06:05 +02001140
1141class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001142 def __init__(self, name, info, doc, values, prefix):
1143 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001144 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001145 assert isinstance(v, QAPISchemaMember)
1146 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001147 assert prefix is None or isinstance(prefix, str)
1148 self.values = values
1149 self.prefix = prefix
1150
1151 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001152 seen = {}
1153 for v in self.values:
1154 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001155 if self.doc:
1156 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001157
Eric Blake99df5282015-10-12 22:22:32 -06001158 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001159 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1160 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001161
Eric Blake4040d992016-03-17 16:48:28 -06001162 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001163 return c_name(self.name)
1164
Eric Blake93bda4d2015-12-01 22:20:55 -07001165 def member_names(self):
1166 return [v.name for v in self.values]
1167
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001168 def json_type(self):
1169 return 'string'
1170
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001171 def visit(self, visitor):
1172 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001173 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001174
Markus Armbrusterac882192015-09-16 13:06:05 +02001175
1176class QAPISchemaArrayType(QAPISchemaType):
1177 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001178 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001179 assert isinstance(element_type, str)
1180 self._element_type_name = element_type
1181 self.element_type = None
1182
1183 def check(self, schema):
1184 self.element_type = schema.lookup_type(self._element_type_name)
1185 assert self.element_type
1186
Eric Blake99df5282015-10-12 22:22:32 -06001187 def is_implicit(self):
1188 return True
1189
Eric Blake4040d992016-03-17 16:48:28 -06001190 def c_type(self):
1191 return c_name(self.name) + pointer_suffix
1192
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001193 def json_type(self):
1194 return 'array'
1195
Markus Armbruster691e0312017-03-15 13:57:14 +01001196 def doc_type(self):
1197 elt_doc_type = self.element_type.doc_type()
1198 if not elt_doc_type:
1199 return None
1200 return 'array of ' + elt_doc_type
1201
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001202 def visit(self, visitor):
1203 visitor.visit_array_type(self.name, self.info, self.element_type)
1204
Markus Armbrusterac882192015-09-16 13:06:05 +02001205
1206class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001207 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001208 # struct has local_members, optional base, and no variants
1209 # flat union has base, variants, and no local_members
1210 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001211 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001212 assert base is None or isinstance(base, str)
1213 for m in local_members:
1214 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001215 m.set_owner(name)
1216 if variants is not None:
1217 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1218 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001219 self._base_name = base
1220 self.base = None
1221 self.local_members = local_members
1222 self.variants = variants
1223 self.members = None
1224
1225 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001226 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001227 raise QAPISemError(self.info,
1228 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001229 if self.members:
1230 return
1231 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001232 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001233 if self._base_name:
1234 self.base = schema.lookup_type(self._base_name)
1235 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001236 self.base.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001237 self.base.check_clash(schema, self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001238 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001239 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001240 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001241 if self.doc:
1242 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001243 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001244 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001245 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001246 assert self.variants.tag_member in self.members
Eric Blake27b60ab2015-11-18 01:52:51 -07001247 self.variants.check_clash(schema, self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001248 if self.doc:
1249 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001250
Eric Blake14f00c62016-03-03 09:16:43 -07001251 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001252 # and update seen to track the members seen so far. Report any errors
1253 # on behalf of info, which is not necessarily self.info
1254 def check_clash(self, schema, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001255 assert not self.variants # not implemented
1256 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001257 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001258
Eric Blake99df5282015-10-12 22:22:32 -06001259 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001260 # See QAPISchema._make_implicit_object_type(), as well as
1261 # _def_predefineds()
1262 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001263
Eric Blakeb6167702016-07-13 21:50:16 -06001264 def is_empty(self):
1265 assert self.members is not None
1266 return not self.members and not self.variants
1267
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001268 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001269 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001270 return QAPISchemaType.c_name(self)
1271
Eric Blake4040d992016-03-17 16:48:28 -06001272 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001273 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001274 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001275
Eric Blake4040d992016-03-17 16:48:28 -06001276 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001277 return c_name(self.name)
1278
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001279 def json_type(self):
1280 return 'object'
1281
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001282 def visit(self, visitor):
1283 visitor.visit_object_type(self.name, self.info,
1284 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001285 visitor.visit_object_type_flat(self.name, self.info,
1286 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001287
Markus Armbrusterac882192015-09-16 13:06:05 +02001288
Eric Blaked44f9ac2015-12-01 22:20:54 -07001289class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001290 role = 'member'
1291
Eric Blaked44f9ac2015-12-01 22:20:54 -07001292 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001293 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001294 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001295 self.owner = None
1296
1297 def set_owner(self, name):
1298 assert not self.owner
1299 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001300
Eric Blake27b60ab2015-11-18 01:52:51 -07001301 def check_clash(self, info, seen):
1302 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001303 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001304 raise QAPISemError(info,
1305 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001306 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001307 raise QAPISemError(info, "%s collides with %s" %
1308 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001309 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001310
Eric Blake88d4ef82015-11-18 01:52:50 -07001311 def _pretty_owner(self):
1312 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001313 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001314 # See QAPISchema._make_implicit_object_type() - reverse the
1315 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001316 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001317 if owner.endswith('-arg'):
1318 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001319 elif owner.endswith('-base'):
1320 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001321 else:
1322 assert owner.endswith('-wrapper')
1323 # Unreachable and not implemented
1324 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001325 if owner.endswith('Kind'):
1326 # See QAPISchema._make_implicit_enum_type()
1327 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001328 return '(%s of %s)' % (self.role, owner)
1329
1330 def describe(self):
1331 return "'%s' %s" % (self.name, self._pretty_owner())
1332
Markus Armbrusterac882192015-09-16 13:06:05 +02001333
Eric Blaked44f9ac2015-12-01 22:20:54 -07001334class QAPISchemaObjectTypeMember(QAPISchemaMember):
1335 def __init__(self, name, typ, optional):
1336 QAPISchemaMember.__init__(self, name)
1337 assert isinstance(typ, str)
1338 assert isinstance(optional, bool)
1339 self._type_name = typ
1340 self.type = None
1341 self.optional = optional
1342
1343 def check(self, schema):
1344 assert self.owner
1345 self.type = schema.lookup_type(self._type_name)
1346 assert self.type
1347
1348
Markus Armbrusterac882192015-09-16 13:06:05 +02001349class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001350 def __init__(self, tag_name, tag_member, variants):
1351 # Flat unions pass tag_name but not tag_member.
1352 # Simple unions and alternates pass tag_member but not tag_name.
1353 # After check(), tag_member is always set, and tag_name remains
1354 # a reliable witness of being used by a flat union.
1355 assert bool(tag_member) != bool(tag_name)
1356 assert (isinstance(tag_name, str) or
1357 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001358 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001359 for v in variants:
1360 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001361 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001362 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001363 self.variants = variants
1364
Eric Blake88d4ef82015-11-18 01:52:50 -07001365 def set_owner(self, name):
1366 for v in self.variants:
1367 v.set_owner(name)
1368
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001369 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001370 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001371 self.tag_member = seen[c_name(self._tag_name)]
1372 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001373 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1374 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001375 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001376 # Union names must match enum values; alternate names are
1377 # checked separately. Use 'seen' to tell the two apart.
1378 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001379 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001380 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001381 v.type.check(schema)
1382
Eric Blake27b60ab2015-11-18 01:52:51 -07001383 def check_clash(self, schema, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001384 for v in self.variants:
1385 # Reset seen map for each variant, since qapi names from one
1386 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001387 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blake27b60ab2015-11-18 01:52:51 -07001388 v.type.check_clash(schema, info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001389
Eric Blake437db252015-09-29 16:21:02 -06001390
Markus Armbrusterac882192015-09-16 13:06:05 +02001391class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001392 role = 'branch'
1393
Markus Armbrusterac882192015-09-16 13:06:05 +02001394 def __init__(self, name, typ):
1395 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1396
Markus Armbrusterac882192015-09-16 13:06:05 +02001397
1398class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001399 def __init__(self, name, info, doc, variants):
1400 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001401 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001402 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001403 variants.set_owner(name)
1404 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001405 self.variants = variants
1406
1407 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001408 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001409 # Not calling self.variants.check_clash(), because there's nothing
1410 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001411 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001412 # Alternate branch names have no relation to the tag enum values;
1413 # so we have to check for potential name collisions ourselves.
1414 seen = {}
1415 for v in self.variants.variants:
1416 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001417 if self.doc:
1418 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001419 if self.doc:
1420 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001421
Eric Blake4040d992016-03-17 16:48:28 -06001422 def c_type(self):
1423 return c_name(self.name) + pointer_suffix
1424
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001425 def json_type(self):
1426 return 'value'
1427
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001428 def visit(self, visitor):
1429 visitor.visit_alternate_type(self.name, self.info, self.variants)
1430
Eric Blakec8184082016-07-13 21:50:20 -06001431 def is_empty(self):
1432 return False
1433
Markus Armbrusterac882192015-09-16 13:06:05 +02001434
1435class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001436 def __init__(self, name, info, doc, arg_type, ret_type,
1437 gen, success_response, boxed):
1438 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001439 assert not arg_type or isinstance(arg_type, str)
1440 assert not ret_type or isinstance(ret_type, str)
1441 self._arg_type_name = arg_type
1442 self.arg_type = None
1443 self._ret_type_name = ret_type
1444 self.ret_type = None
1445 self.gen = gen
1446 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001447 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001448
1449 def check(self, schema):
1450 if self._arg_type_name:
1451 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001452 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1453 isinstance(self.arg_type, QAPISchemaAlternateType))
1454 self.arg_type.check(schema)
1455 if self.boxed:
1456 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001457 raise QAPISemError(self.info,
1458 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001459 else:
1460 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1461 assert not self.arg_type.variants
1462 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001463 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001464 if self._ret_type_name:
1465 self.ret_type = schema.lookup_type(self._ret_type_name)
1466 assert isinstance(self.ret_type, QAPISchemaType)
1467
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001468 def visit(self, visitor):
1469 visitor.visit_command(self.name, self.info,
1470 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001471 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001472
Markus Armbrusterac882192015-09-16 13:06:05 +02001473
1474class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001475 def __init__(self, name, info, doc, arg_type, boxed):
1476 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001477 assert not arg_type or isinstance(arg_type, str)
1478 self._arg_type_name = arg_type
1479 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001480 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001481
1482 def check(self, schema):
1483 if self._arg_type_name:
1484 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001485 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1486 isinstance(self.arg_type, QAPISchemaAlternateType))
1487 self.arg_type.check(schema)
1488 if self.boxed:
1489 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001490 raise QAPISemError(self.info,
1491 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001492 else:
1493 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1494 assert not self.arg_type.variants
1495 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001496 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001497
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001498 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001499 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001500
Markus Armbrusterac882192015-09-16 13:06:05 +02001501
1502class QAPISchema(object):
1503 def __init__(self, fname):
1504 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001505 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001506 self.exprs = check_exprs(parser.exprs)
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001507 self.docs = parser.docs
Eric Blake7618b912015-10-12 22:22:22 -06001508 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001509 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001510 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001511 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001512 self._def_exprs()
1513 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001514 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001515 print >>sys.stderr, err
1516 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001517
Markus Armbrusterac882192015-09-16 13:06:05 +02001518 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001519 # Only the predefined types are allowed to not have info
1520 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001521 assert ent.name not in self._entity_dict
1522 self._entity_dict[ent.name] = ent
1523
1524 def lookup_entity(self, name, typ=None):
1525 ent = self._entity_dict.get(name)
1526 if typ and not isinstance(ent, typ):
1527 return None
1528 return ent
1529
1530 def lookup_type(self, name):
1531 return self.lookup_entity(name, QAPISchemaType)
1532
Eric Blake861877a2016-03-17 16:48:36 -06001533 def _def_builtin_type(self, name, json_type, c_type):
1534 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001535 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1536 # qapi-types.h from a single .c, all arrays of builtins must be
1537 # declared in the first file whether or not they are used. Nicer
1538 # would be to use lazy instantiation, while figuring out how to
1539 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001540 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001541
1542 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001543 for t in [('str', 'string', 'char' + pointer_suffix),
1544 ('number', 'number', 'double'),
1545 ('int', 'int', 'int64_t'),
1546 ('int8', 'int', 'int8_t'),
1547 ('int16', 'int', 'int16_t'),
1548 ('int32', 'int', 'int32_t'),
1549 ('int64', 'int', 'int64_t'),
1550 ('uint8', 'int', 'uint8_t'),
1551 ('uint16', 'int', 'uint16_t'),
1552 ('uint32', 'int', 'uint32_t'),
1553 ('uint64', 'int', 'uint64_t'),
1554 ('size', 'int', 'uint64_t'),
1555 ('bool', 'boolean', 'bool'),
1556 ('any', 'value', 'QObject' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001557 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001558 self.the_empty_object_type = QAPISchemaObjectType(
1559 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001560 self._def_entity(self.the_empty_object_type)
Eric Blake93bda4d2015-12-01 22:20:55 -07001561 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1562 'qstring', 'qdict', 'qlist',
1563 'qfloat', 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001564 self._def_entity(QAPISchemaEnumType('QType', None, None,
1565 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001566
Eric Blake93bda4d2015-12-01 22:20:55 -07001567 def _make_enum_members(self, values):
1568 return [QAPISchemaMember(v) for v in values]
1569
Eric Blake99df5282015-10-12 22:22:32 -06001570 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001571 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001572 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001573 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001574 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001575 return name
1576
Eric Blake99df5282015-10-12 22:22:32 -06001577 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001578 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001579 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001580 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001581 return name
1582
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001583 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001584 if not members:
1585 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001586 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001587 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001588 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001589 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001590 members, None))
1591 return name
1592
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001593 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001594 name = expr['enum']
1595 data = expr['data']
1596 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001597 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001598 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001599
Eric Blake99df5282015-10-12 22:22:32 -06001600 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001601 optional = False
1602 if name.startswith('*'):
1603 name = name[1:]
1604 optional = True
1605 if isinstance(typ, list):
1606 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001607 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001608 return QAPISchemaObjectTypeMember(name, typ, optional)
1609
Eric Blake99df5282015-10-12 22:22:32 -06001610 def _make_members(self, data, info):
1611 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001612 for (key, value) in data.iteritems()]
1613
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001614 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001615 name = expr['struct']
1616 base = expr.get('base')
1617 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001618 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001619 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001620 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001621
1622 def _make_variant(self, case, typ):
1623 return QAPISchemaObjectTypeVariant(case, typ)
1624
Eric Blake99df5282015-10-12 22:22:32 -06001625 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001626 if isinstance(typ, list):
1627 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001628 typ = self._make_array_type(typ[0], info)
1629 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001630 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001631 return QAPISchemaObjectTypeVariant(case, typ)
1632
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001633 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001634 name = expr['union']
1635 data = expr['data']
1636 base = expr.get('base')
1637 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001638 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001639 if isinstance(base, dict):
1640 base = (self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001641 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001642 if tag_name:
1643 variants = [self._make_variant(key, value)
1644 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001645 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001646 else:
Eric Blake99df5282015-10-12 22:22:32 -06001647 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001648 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001649 typ = self._make_implicit_enum_type(name, info,
1650 [v.name for v in variants])
1651 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001652 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001653 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001654 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001655 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001656 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001657 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001658
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001659 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001660 name = expr['alternate']
1661 data = expr['data']
1662 variants = [self._make_variant(key, value)
1663 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001664 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001665 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001666 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001667 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001668 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001669 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001670
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001671 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001672 name = expr['command']
1673 data = expr.get('data')
1674 rets = expr.get('returns')
1675 gen = expr.get('gen', True)
1676 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001677 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001678 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001679 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001680 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001681 if isinstance(rets, list):
1682 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001683 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001684 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1685 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001686
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001687 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001688 name = expr['event']
1689 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001690 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001691 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001692 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001693 name, info, doc, 'arg', self._make_members(data, info))
1694 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001695
1696 def _def_exprs(self):
1697 for expr_elem in self.exprs:
1698 expr = expr_elem['expr']
1699 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001700 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001701 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001702 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001703 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001704 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001705 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001706 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001707 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001708 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001709 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001710 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001711 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001712 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001713 else:
1714 assert False
1715
1716 def check(self):
1717 for ent in self._entity_dict.values():
1718 ent.check(self)
1719
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001720 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001721 visitor.visit_begin(self)
1722 for (name, entity) in sorted(self._entity_dict.items()):
1723 if visitor.visit_needed(entity):
1724 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001725 visitor.visit_end()
1726
Markus Armbruster2caba362013-07-27 17:41:56 +02001727
Markus Armbruster00e4b282015-06-10 10:04:36 +02001728#
1729# Code generation helpers
1730#
1731
Michael Roth0f923be2011-07-19 14:50:39 -05001732def camel_case(name):
1733 new_name = ''
1734 first = True
1735 for ch in name:
1736 if ch in ['_', '-']:
1737 first = True
1738 elif first:
1739 new_name += ch.upper()
1740 first = False
1741 else:
1742 new_name += ch.lower()
1743 return new_name
1744
Eric Blake437db252015-09-29 16:21:02 -06001745
Markus Armbruster849bc532015-05-14 06:50:53 -06001746# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1747# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1748# ENUM24_Name -> ENUM24_NAME
1749def camel_to_upper(value):
1750 c_fun_str = c_name(value, False)
1751 if value.isupper():
1752 return c_fun_str
1753
1754 new_name = ''
1755 l = len(c_fun_str)
1756 for i in range(l):
1757 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001758 # When c is upper and no '_' appears before, do more checks
1759 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001760 if i < l - 1 and c_fun_str[i + 1].islower():
1761 new_name += '_'
1762 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001763 new_name += '_'
1764 new_name += c
1765 return new_name.lstrip('_').upper()
1766
Eric Blake437db252015-09-29 16:21:02 -06001767
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001768def c_enum_const(type_name, const_name, prefix=None):
1769 if prefix is not None:
1770 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001771 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001772
Eric Blake18df5152015-05-14 06:50:48 -06001773c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001774
Eric Blake437db252015-09-29 16:21:02 -06001775
Eric Blakec6405b52015-05-14 06:50:55 -06001776# Map @name to a valid C identifier.
1777# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001778# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001779#
1780# Used for converting 'name' from a 'name':'type' qapi definition
1781# into a generated struct member, as well as converting type names
1782# into substrings of a generated C function name.
1783# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1784# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001785def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001786 # ANSI X3J11/88-090, 3.1.1
1787 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001788 'default', 'do', 'double', 'else', 'enum', 'extern',
1789 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1790 'return', 'short', 'signed', 'sizeof', 'static',
1791 'struct', 'switch', 'typedef', 'union', 'unsigned',
1792 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001793 # ISO/IEC 9899:1999, 6.4.1
1794 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1795 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001796 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1797 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001798 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1799 # excluding _.*
1800 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001801 # C++ ISO/IEC 14882:2003 2.11
1802 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1803 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1804 'namespace', 'new', 'operator', 'private', 'protected',
1805 'public', 'reinterpret_cast', 'static_cast', 'template',
1806 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1807 'using', 'virtual', 'wchar_t',
1808 # alternative representations
1809 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1810 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001811 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001812 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001813 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001814 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1815 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001816 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001817 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001818
Amos Kong05dfb262014-06-10 19:25:53 +08001819eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001820pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001821
Eric Blake437db252015-09-29 16:21:02 -06001822
Michael Roth0f923be2011-07-19 14:50:39 -05001823def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001824 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001825 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001826 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001827 return ret
1828
1829indent_level = 0
1830
Eric Blake437db252015-09-29 16:21:02 -06001831
Michael Roth0f923be2011-07-19 14:50:39 -05001832def push_indent(indent_amount=4):
1833 global indent_level
1834 indent_level += indent_amount
1835
Eric Blake437db252015-09-29 16:21:02 -06001836
Michael Roth0f923be2011-07-19 14:50:39 -05001837def pop_indent(indent_amount=4):
1838 global indent_level
1839 indent_level -= indent_amount
1840
Eric Blake437db252015-09-29 16:21:02 -06001841
Markus Armbruster77e703b2015-06-24 19:27:32 +02001842# Generate @code with @kwds interpolated.
1843# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001844def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001845 raw = code % kwds
1846 if indent_level:
1847 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001848 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001849 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001850 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001851 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001852 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001853
Eric Blake437db252015-09-29 16:21:02 -06001854
Michael Roth0f923be2011-07-19 14:50:39 -05001855def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001856 if code[0] == '\n':
1857 code = code[1:]
1858 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001859
Michael Roth0f923be2011-07-19 14:50:39 -05001860
1861def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001862 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001863
Eric Blake437db252015-09-29 16:21:02 -06001864
Michael Rothc0afa9c2013-05-10 17:46:00 -05001865def guardstart(name):
1866 return mcgen('''
1867
1868#ifndef %(name)s
1869#define %(name)s
1870
1871''',
1872 name=guardname(name))
1873
Eric Blake437db252015-09-29 16:21:02 -06001874
Michael Rothc0afa9c2013-05-10 17:46:00 -05001875def guardend(name):
1876 return mcgen('''
1877
1878#endif /* %(name)s */
1879
1880''',
1881 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001882
Eric Blake437db252015-09-29 16:21:02 -06001883
Markus Armbrustere98859a2015-09-16 13:06:16 +02001884def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001885 ret = mcgen('''
1886
Markus Armbrustere98859a2015-09-16 13:06:16 +02001887const char *const %(c_name)s_lookup[] = {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001888''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001889 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001890 for value in values:
1891 index = c_enum_const(name, value, prefix)
1892 ret += mcgen('''
1893 [%(index)s] = "%(value)s",
1894''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001895 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001896
Eric Blake7fb1cf12015-11-18 01:52:57 -07001897 max_index = c_enum_const(name, '_MAX', prefix)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001898 ret += mcgen('''
1899 [%(max_index)s] = NULL,
1900};
1901''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001902 max_index=max_index)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001903 return ret
1904
Eric Blake437db252015-09-29 16:21:02 -06001905
Markus Armbrustere98859a2015-09-16 13:06:16 +02001906def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001907 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001908 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001909
1910 ret = mcgen('''
1911
1912typedef enum %(c_name)s {
1913''',
1914 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001915
1916 i = 0
1917 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001918 ret += mcgen('''
1919 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001920''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001921 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001922 i=i)
1923 i += 1
1924
Markus Armbrustere98859a2015-09-16 13:06:16 +02001925 ret += mcgen('''
1926} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001927''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001928 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001929
Markus Armbrustere98859a2015-09-16 13:06:16 +02001930 ret += mcgen('''
1931
1932extern const char *const %(c_name)s_lookup[];
1933''',
1934 c_name=c_name(name))
1935 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001936
Eric Blake437db252015-09-29 16:21:02 -06001937
Eric Blake48825ca2016-07-13 21:50:19 -06001938def gen_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001939 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001940 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001941 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001942 ret = ''
1943 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001944 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001945 ret += '%s arg' % arg_type.c_param_type()
1946 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001947 else:
1948 assert not arg_type.variants
1949 for memb in arg_type.members:
1950 ret += sep
1951 sep = ', '
1952 if memb.optional:
1953 ret += 'bool has_%s, ' % c_name(memb.name)
1954 ret += '%s %s' % (memb.type.c_param_type(),
1955 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001956 if extra:
1957 ret += sep + extra
1958 return ret
1959
Eric Blake1f353342015-09-29 16:21:13 -06001960
Markus Armbruster00e4b282015-06-10 10:04:36 +02001961#
1962# Common command line parsing
1963#
1964
Eric Blake437db252015-09-29 16:21:02 -06001965
Markus Armbrusteref801a92017-03-15 13:57:08 +01001966def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001967
1968 try:
1969 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001970 'chp:o:' + extra_options,
1971 ['source', 'header', 'prefix=',
1972 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001973 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001974 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001975 sys.exit(1)
1976
Markus Armbrusteref801a92017-03-15 13:57:08 +01001977 output_dir = ''
1978 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001979 do_c = False
1980 do_h = False
1981 extra_opts = []
1982
1983 for oa in opts:
1984 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001985 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001986 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001987 if match.end() != len(a):
1988 print >>sys.stderr, \
1989 "%s: 'funny character '%s' in argument of --prefix" \
1990 % (sys.argv[0], a[match.end()])
1991 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001992 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001993 elif o in ('-o', '--output-dir'):
1994 output_dir = a + '/'
1995 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001996 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001997 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001998 do_h = True
1999 else:
2000 extra_opts.append(oa)
2001
2002 if not do_c and not do_h:
2003 do_c = True
2004 do_h = True
2005
Markus Armbruster16d80f62015-04-02 13:32:16 +02002006 if len(args) != 1:
2007 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002008 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02002009 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002010
Markus Armbruster54414042015-06-09 16:22:45 +02002011 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002012
Markus Armbruster00e4b282015-06-10 10:04:36 +02002013#
2014# Generate output files with boilerplate
2015#
2016
Eric Blake437db252015-09-29 16:21:02 -06002017
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002018def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
2019 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02002020 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002021 c_file = output_dir + prefix + c_file
2022 h_file = output_dir + prefix + h_file
2023
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002024 if output_dir:
2025 try:
2026 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01002027 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002028 if e.errno != errno.EEXIST:
2029 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002030
2031 def maybe_open(really, name, opt):
2032 if really:
2033 return open(name, opt)
2034 else:
2035 import StringIO
2036 return StringIO.StringIO()
2037
2038 fdef = maybe_open(do_c, c_file, 'w')
2039 fdecl = maybe_open(do_h, h_file, 'w')
2040
2041 fdef.write(mcgen('''
2042/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2043%(comment)s
2044''',
Eric Blake437db252015-09-29 16:21:02 -06002045 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002046
2047 fdecl.write(mcgen('''
2048/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2049%(comment)s
2050#ifndef %(guard)s
2051#define %(guard)s
2052
2053''',
Eric Blake437db252015-09-29 16:21:02 -06002054 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002055
2056 return (fdef, fdecl)
2057
Eric Blake437db252015-09-29 16:21:02 -06002058
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002059def close_output(fdef, fdecl):
2060 fdecl.write('''
2061#endif
2062''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002063 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002064 fdef.close()