blob: 648355ed0e0fcd820f03133749e2cbce94de9403 [file] [log] [blame]
Michael Roth0f923be2011-07-19 14:50:39 -05001#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
Eric Blakee4083112016-01-29 06:48:41 -07005# Copyright (c) 2013-2016 Red Hat Inc.
Michael Roth0f923be2011-07-19 14:50:39 -05006#
7# Authors:
8# Anthony Liguori <aliguori@us.ibm.com>
Markus Armbrusterc7a3f252013-07-27 17:41:55 +02009# Markus Armbruster <armbru@redhat.com>
Michael Roth0f923be2011-07-19 14:50:39 -050010#
Markus Armbruster678e48a2014-03-01 08:40:34 +010011# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
Michael Roth0f923be2011-07-19 14:50:39 -050013
Lluís Vilanovaa719a272014-05-07 20:46:15 +020014import re
Michael Roth0f923be2011-07-19 14:50:39 -050015from ordereddict import OrderedDict
Markus Armbruster12f8e1b2015-04-02 14:46:39 +020016import errno
Markus Armbruster2114f5a2015-04-02 13:12:21 +020017import getopt
Lluís Vilanova33aaad52014-05-02 15:52:35 +020018import os
Markus Armbruster2caba362013-07-27 17:41:56 +020019import sys
Markus Armbruster47299262015-05-14 06:50:47 -060020import string
Michael Roth0f923be2011-07-19 14:50:39 -050021
Eric Blakeb52c4b92015-05-04 09:05:00 -060022builtin_types = {
Kevin Wolf69dd62d2013-07-08 16:14:21 +020023 'str': 'QTYPE_QSTRING',
24 'int': 'QTYPE_QINT',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
27 'int8': 'QTYPE_QINT',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
Eric Blakecb17f792015-05-04 09:05:01 -060035 'size': 'QTYPE_QINT',
Eric Blake1310a3d2015-12-01 22:20:46 -070036 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070037 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020038}
39
Markus Armbrusterbc52d032017-03-15 13:56:51 +010040# Are documentation comments required?
41doc_required = False
42
Eric Blake10d4d992015-05-04 09:05:23 -060043# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010044returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060045
Eric Blake893e1f22015-12-01 22:20:57 -070046# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010047name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070048
Eric Blake4dc2e692015-05-04 09:05:17 -060049enum_types = []
50struct_types = []
51union_types = []
52events = []
53all_names = {}
54
Markus Armbruster00e4b282015-06-10 10:04:36 +020055#
56# Parsing the schema into expressions
57#
58
Eric Blake437db252015-09-29 16:21:02 -060059
Lluís Vilanovaa719a272014-05-07 20:46:15 +020060def error_path(parent):
Markus Armbrusteref801a92017-03-15 13:57:08 +010061 res = ''
Lluís Vilanovaa719a272014-05-07 20:46:15 +020062 while parent:
Markus Armbrusteref801a92017-03-15 13:57:08 +010063 res = ('In file included from %s:%d:\n' % (parent['file'],
Lluís Vilanovaa719a272014-05-07 20:46:15 +020064 parent['line'])) + res
65 parent = parent['parent']
66 return res
67
Eric Blake437db252015-09-29 16:21:02 -060068
Marc-André Lureau4148c292017-01-13 15:41:25 +010069class QAPIError(Exception):
70 def __init__(self, fname, line, col, incl_info, msg):
Eric Blake59b00542015-09-29 16:21:01 -060071 Exception.__init__(self)
Marc-André Lureau4148c292017-01-13 15:41:25 +010072 self.fname = fname
73 self.line = line
74 self.col = col
75 self.info = incl_info
Markus Armbruster2caba362013-07-27 17:41:56 +020076 self.msg = msg
Marc-André Lureau4148c292017-01-13 15:41:25 +010077
78 def __str__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +010079 loc = '%s:%d' % (self.fname, self.line)
Marc-André Lureau4148c292017-01-13 15:41:25 +010080 if self.col is not None:
Markus Armbrusteref801a92017-03-15 13:57:08 +010081 loc += ':%s' % self.col
82 return error_path(self.info) + '%s: %s' % (loc, self.msg)
Marc-André Lureau4148c292017-01-13 15:41:25 +010083
84
85class QAPIParseError(QAPIError):
86 def __init__(self, parser, msg):
87 col = 1
88 for ch in parser.src[parser.line_pos:parser.pos]:
Wenchao Xia515b9432014-03-04 18:44:33 -080089 if ch == '\t':
Marc-André Lureau4148c292017-01-13 15:41:25 +010090 col = (col + 7) % 8 + 1
Markus Armbruster2caba362013-07-27 17:41:56 +020091 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +010092 col += 1
93 QAPIError.__init__(self, parser.fname, parser.line, col,
94 parser.incl_info, msg)
Markus Armbruster2caba362013-07-27 17:41:56 +020095
Eric Blake437db252015-09-29 16:21:02 -060096
Marc-André Lureau4148c292017-01-13 15:41:25 +010097class QAPISemError(QAPIError):
98 def __init__(self, info, msg):
99 QAPIError.__init__(self, info['file'], info['line'], None,
100 info['parent'], msg)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800101
Eric Blake437db252015-09-29 16:21:02 -0600102
Marc-André Lureau3313b612017-01-13 15:41:29 +0100103class QAPIDoc(object):
104 class Section(object):
105 def __init__(self, name=None):
106 # optional section name (argument/member or section name)
107 self.name = name
108 # the list of lines for this section
109 self.content = []
Markus Armbrusterb116fd82017-03-15 13:57:00 +0100110 self.optional = False
Marc-André Lureau3313b612017-01-13 15:41:29 +0100111
112 def append(self, line):
113 self.content.append(line)
114
115 def __repr__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100116 return '\n'.join(self.content).strip()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100117
118 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100119 def __init__(self, name):
120 QAPIDoc.Section.__init__(self, name)
121 self.member = None
122
123 def connect(self, member):
124 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100125
126 def __init__(self, parser, info):
127 # self.parser is used to report errors with QAPIParseError. The
128 # resulting error position depends on the state of the parser.
129 # It happens to be the beginning of the comment. More or less
130 # servicable, but action at a distance.
131 self.parser = parser
132 self.info = info
133 self.symbol = None
134 self.body = QAPIDoc.Section()
135 # dict mapping parameter name to ArgSection
136 self.args = OrderedDict()
137 # a list of Section
138 self.sections = []
139 # the current section
140 self.section = self.body
141 # associated expression (to be set by expression parser)
142 self.expr = None
143
144 def has_section(self, name):
145 """Return True if we have a section with this name."""
146 for i in self.sections:
147 if i.name == name:
148 return True
149 return False
150
151 def append(self, line):
152 """Parse a comment line and add it to the documentation."""
153 line = line[1:]
154 if not line:
155 self._append_freeform(line)
156 return
157
158 if line[0] != ' ':
159 raise QAPIParseError(self.parser, "Missing space after #")
160 line = line[1:]
161
162 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
163 # recognized, and get silently treated as ordinary text
164 if self.symbol:
165 self._append_symbol_line(line)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100166 elif not self.body.content and line.startswith('@'):
167 if not line.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100168 raise QAPIParseError(self.parser, "Line should end with :")
169 self.symbol = line[1:-1]
170 # FIXME invalid names other than the empty string aren't flagged
171 if not self.symbol:
172 raise QAPIParseError(self.parser, "Invalid name")
173 else:
174 self._append_freeform(line)
175
Markus Armbruster4ea71482017-03-15 13:57:23 +0100176 def end_comment(self):
177 self._end_section()
178
Marc-André Lureau3313b612017-01-13 15:41:29 +0100179 def _append_symbol_line(self, line):
180 name = line.split(' ', 1)[0]
181
Markus Armbrusteref801a92017-03-15 13:57:08 +0100182 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100183 line = line[len(name)+1:]
184 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100185 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100186 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100187 'Note:', 'Notes:',
188 'Example:', 'Examples:',
189 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100190 line = line[len(name)+1:]
191 self._start_section(name[:-1])
192
193 self._append_freeform(line)
194
195 def _start_args_section(self, name):
196 # FIXME invalid names other than the empty string aren't flagged
197 if not name:
198 raise QAPIParseError(self.parser, "Invalid parameter name")
199 if name in self.args:
200 raise QAPIParseError(self.parser,
201 "'%s' parameter name duplicated" % name)
202 if self.sections:
203 raise QAPIParseError(self.parser,
204 "'@%s:' can't follow '%s' section"
205 % (name, self.sections[0].name))
Markus Armbruster4ea71482017-03-15 13:57:23 +0100206 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100207 self.section = QAPIDoc.ArgSection(name)
208 self.args[name] = self.section
209
Markus Armbrusteref801a92017-03-15 13:57:08 +0100210 def _start_section(self, name=''):
211 if name in ('Returns', 'Since') and self.has_section(name):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100212 raise QAPIParseError(self.parser,
213 "Duplicated '%s' section" % name)
Markus Armbruster4ea71482017-03-15 13:57:23 +0100214 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100215 self.section = QAPIDoc.Section(name)
216 self.sections.append(self.section)
217
Markus Armbruster4ea71482017-03-15 13:57:23 +0100218 def _end_section(self):
219 if self.section:
220 contents = str(self.section)
221 if self.section.name and (not contents or contents.isspace()):
222 raise QAPIParseError(self.parser, "Empty doc section '%s'"
223 % self.section.name)
224 self.section = None
225
Marc-André Lureau3313b612017-01-13 15:41:29 +0100226 def _append_freeform(self, line):
227 in_arg = isinstance(self.section, QAPIDoc.ArgSection)
228 if (in_arg and self.section.content
229 and not self.section.content[-1]
230 and line and not line[0].isspace()):
231 self._start_section()
232 if (in_arg or not self.section.name
Markus Armbrusteref801a92017-03-15 13:57:08 +0100233 or not self.section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100234 line = line.strip()
Markus Armbruster2d433232017-03-15 13:57:22 +0100235 match = re.match(r'(@\S+:)', line)
236 if match:
237 raise QAPIParseError(self.parser,
238 "'%s' not allowed in free-form documentation"
239 % match.group(1))
Markus Armbruster1d8bda12017-03-15 13:57:06 +0100240 # TODO Drop this once the dust has settled
241 if (isinstance(self.section, QAPIDoc.ArgSection)
242 and '#optional' in line):
243 raise QAPISemError(self.info, "Please drop the #optional tag")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100244 self.section.append(line)
245
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100246 def connect_member(self, member):
247 if member.name not in self.args:
248 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100249 self.args[member.name] = QAPIDoc.ArgSection(member.name)
250 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100251
Marc-André Lureau3313b612017-01-13 15:41:29 +0100252
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200253class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500254
Eric Blake437db252015-09-29 16:21:02 -0600255 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200256 abs_fname = os.path.abspath(fp.name)
Markus Armbruster8608d252015-06-09 18:32:29 +0200257 fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200258 self.fname = fname
Markus Armbruster54414042015-06-09 16:22:45 +0200259 previously_included.append(abs_fname)
260 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200261 self.src = fp.read()
262 if self.src == '' or self.src[-1] != '\n':
263 self.src += '\n'
264 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800265 self.line = 1
266 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200267 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100268 self.docs = []
Markus Armbrustere7823a22017-03-15 13:57:20 +0100269 self.cur_doc = None
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200270 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500271
Eric Blake437db252015-09-29 16:21:02 -0600272 while self.tok is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100273 info = {'file': fname, 'line': self.line,
274 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100275 if self.tok == '#':
Markus Armbrustere7823a22017-03-15 13:57:20 +0100276 self.reject_expr_doc()
277 self.cur_doc = self.get_doc(info)
278 self.docs.append(self.cur_doc)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100279 continue
280
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200281 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100282 if 'include' in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100283 self.reject_expr_doc()
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200284 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100285 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100286 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200287 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100288 raise QAPISemError(info,
289 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100290 self._include(include, info, os.path.dirname(abs_fname),
291 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100292 elif "pragma" in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100293 self.reject_expr_doc()
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100294 if len(expr) != 1:
295 raise QAPISemError(info, "Invalid 'pragma' directive")
296 pragma = expr['pragma']
297 if not isinstance(pragma, dict):
298 raise QAPISemError(
299 info, "Value of 'pragma' must be a dictionary")
300 for name, value in pragma.iteritems():
301 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200302 else:
303 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100304 'info': info}
Markus Armbrustere7823a22017-03-15 13:57:20 +0100305 if self.cur_doc:
306 if not self.cur_doc.symbol:
307 raise QAPISemError(
308 self.cur_doc.info,
309 "Expression documentation required")
310 self.cur_doc.expr = expr
311 expr_elem['doc'] = self.cur_doc
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200312 self.exprs.append(expr_elem)
Markus Armbrustere7823a22017-03-15 13:57:20 +0100313 self.cur_doc = None
314 self.reject_expr_doc()
315
316 def reject_expr_doc(self):
317 if self.cur_doc and self.cur_doc.symbol:
318 raise QAPISemError(
319 self.cur_doc.info,
320 "Documentation for '%s' is not followed by the definition"
321 % self.cur_doc.symbol)
Michael Roth0f923be2011-07-19 14:50:39 -0500322
Markus Armbrustere04dea82017-03-15 13:56:50 +0100323 def _include(self, include, info, base_dir, previously_included):
324 incl_abs_fname = os.path.join(base_dir, include)
325 # catch inclusion cycle
326 inf = info
327 while inf:
328 if incl_abs_fname == os.path.abspath(inf['file']):
329 raise QAPISemError(info, "Inclusion loop for %s" % include)
330 inf = inf['parent']
331
332 # skip multiple include of the same file
333 if incl_abs_fname in previously_included:
334 return
335 try:
336 fobj = open(incl_abs_fname, 'r')
337 except IOError as e:
338 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
339 exprs_include = QAPISchemaParser(fobj, previously_included, info)
340 self.exprs.extend(exprs_include.exprs)
341 self.docs.extend(exprs_include.docs)
342
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100343 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100344 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100345 if name == 'doc-required':
346 if not isinstance(value, bool):
347 raise QAPISemError(info,
348 "Pragma 'doc-required' must be boolean")
349 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100350 elif name == 'returns-whitelist':
351 if (not isinstance(value, list)
352 or any([not isinstance(elt, str) for elt in value])):
353 raise QAPISemError(info,
354 "Pragma returns-whitelist must be"
355 " a list of strings")
356 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100357 elif name == 'name-case-whitelist':
358 if (not isinstance(value, list)
359 or any([not isinstance(elt, str) for elt in value])):
360 raise QAPISemError(info,
361 "Pragma name-case-whitelist must be"
362 " a list of strings")
363 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100364 else:
365 raise QAPISemError(info, "Unknown pragma '%s'" % name)
366
Marc-André Lureau3313b612017-01-13 15:41:29 +0100367 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200368 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200369 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200370 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200371 self.cursor += 1
372 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500373
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200374 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100375 if self.src[self.cursor] == '#':
376 # Start of doc comment
377 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200378 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100379 if not skip_comment:
380 self.val = self.src[self.pos:self.cursor]
381 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100382 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200383 return
384 elif self.tok == "'":
385 string = ''
386 esc = False
387 while True:
388 ch = self.src[self.cursor]
389 self.cursor += 1
390 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100391 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200392 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600393 if ch == 'b':
394 string += '\b'
395 elif ch == 'f':
396 string += '\f'
397 elif ch == 'n':
398 string += '\n'
399 elif ch == 'r':
400 string += '\r'
401 elif ch == 't':
402 string += '\t'
403 elif ch == 'u':
404 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600405 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600406 ch = self.src[self.cursor]
407 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100408 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100409 raise QAPIParseError(self,
410 '\\u escape needs 4 '
411 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600412 value = (value << 4) + int(ch, 16)
413 # If Python 2 and 3 didn't disagree so much on
414 # how to handle Unicode, then we could allow
415 # Unicode string defaults. But most of QAPI is
416 # ASCII-only, so we aren't losing much for now.
417 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100418 raise QAPIParseError(self,
419 'For now, \\u escape '
420 'only supports non-zero '
421 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600422 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100423 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600424 string += ch
425 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100426 raise QAPIParseError(self,
427 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200428 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100429 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200430 esc = True
431 elif ch == "'":
432 self.val = string
433 return
434 else:
435 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100436 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200437 self.val = True
438 self.cursor += 3
439 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100440 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200441 self.val = False
442 self.cursor += 4
443 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100444 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200445 self.val = None
446 self.cursor += 3
447 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200448 elif self.tok == '\n':
449 if self.cursor == len(self.src):
450 self.tok = None
451 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800452 self.line += 1
453 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200454 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100455 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500456
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200457 def get_members(self):
458 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200459 if self.tok == '}':
460 self.accept()
461 return expr
462 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100463 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200464 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200465 key = self.val
466 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200467 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100468 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200469 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800470 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100471 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200472 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200473 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200474 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200475 return expr
476 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100477 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200478 self.accept()
479 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100480 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500481
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200482 def get_values(self):
483 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200484 if self.tok == ']':
485 self.accept()
486 return expr
Eric Blake437db252015-09-29 16:21:02 -0600487 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100488 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
489 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200490 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200491 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200492 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200493 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200494 return expr
495 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100496 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200497 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500498
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200499 def get_expr(self, nested):
500 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100501 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200502 if self.tok == '{':
503 self.accept()
504 expr = self.get_members()
505 elif self.tok == '[':
506 self.accept()
507 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600508 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200509 expr = self.val
510 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200511 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100512 raise QAPIParseError(self, 'Expected "{", "[" or string')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200513 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200514
Marc-André Lureau3313b612017-01-13 15:41:29 +0100515 def get_doc(self, info):
516 if self.val != '##':
517 raise QAPIParseError(self, "Junk after '##' at start of "
518 "documentation comment")
519
520 doc = QAPIDoc(self, info)
521 self.accept(False)
522 while self.tok == '#':
523 if self.val.startswith('##'):
524 # End of doc comment
525 if self.val != '##':
526 raise QAPIParseError(self, "Junk after '##' at end of "
527 "documentation comment")
Markus Armbruster4ea71482017-03-15 13:57:23 +0100528 doc.end_comment()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100529 self.accept()
530 return doc
531 else:
532 doc.append(self.val)
533 self.accept(False)
534
535 raise QAPIParseError(self, "Documentation comment must end with '##'")
536
537
Markus Armbruster00e4b282015-06-10 10:04:36 +0200538#
539# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200540# TODO fold into QAPISchema
541# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200542#
543
Eric Blake437db252015-09-29 16:21:02 -0600544
Eric Blake14f00c62016-03-03 09:16:43 -0700545def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600546 if isinstance(base, dict):
547 return base
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800548 base_struct_define = find_struct(base)
549 if not base_struct_define:
550 return None
551 return base_struct_define['data']
552
Eric Blake437db252015-09-29 16:21:02 -0600553
Eric Blake811d04f2015-05-04 09:05:10 -0600554# Return the qtype of an alternate branch, or None on error.
555def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600556 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600557 return builtin_types[qapi_type]
558 elif find_struct(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100559 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600560 elif find_enum(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100561 return 'QTYPE_QSTRING'
Eric Blake811d04f2015-05-04 09:05:10 -0600562 elif find_union(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100563 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600564 return None
565
Eric Blake437db252015-09-29 16:21:02 -0600566
Wenchao Xiabceae762014-03-06 17:08:56 -0800567# Return the discriminator enum define if discriminator is specified as an
568# enum type, otherwise return None.
569def discriminator_find_enum_define(expr):
570 base = expr.get('base')
571 discriminator = expr.get('discriminator')
572
573 if not (discriminator and base):
574 return None
575
Eric Blake14f00c62016-03-03 09:16:43 -0700576 base_members = find_base_members(base)
577 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800578 return None
579
Eric Blake14f00c62016-03-03 09:16:43 -0700580 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800581 if not discriminator_type:
582 return None
583
584 return find_enum(discriminator_type)
585
Eric Blake437db252015-09-29 16:21:02 -0600586
Eric Blake59a92fe2015-11-18 01:52:56 -0700587# Names must be letters, numbers, -, and _. They must start with letter,
588# except for downstream extensions which must start with __RFQDN_.
589# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100590valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700591 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600592
593
Marc-André Lureau4148c292017-01-13 15:41:25 +0100594def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600595 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600596 global valid_name
597 membername = name
598
599 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100600 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600601 if name.startswith('*'):
602 membername = name[1:]
603 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100604 raise QAPISemError(info, "%s does not allow optional name '%s'"
605 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600606 # Enum members can start with a digit, because the generated C
607 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700608 if enum_member and membername[0].isdigit():
609 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600610 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
611 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600612 if not valid_name.match(membername) or \
613 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100614 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600615
Eric Blake437db252015-09-29 16:21:02 -0600616
617def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200618 global all_names
619 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200620 # FIXME should reject names that differ only in '_' vs. '.'
621 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200622 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100623 raise QAPISemError(info, "%s '%s' is already defined"
624 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600625 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100626 raise QAPISemError(info, "%s '%s' should not end in '%s'"
627 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200628 all_names[name] = meta
629
Eric Blake437db252015-09-29 16:21:02 -0600630
Markus Armbruster00e4b282015-06-10 10:04:36 +0200631def add_struct(definition, info):
632 global struct_types
633 name = definition['struct']
634 add_name(name, info, 'struct')
635 struct_types.append(definition)
636
Eric Blake437db252015-09-29 16:21:02 -0600637
Markus Armbruster00e4b282015-06-10 10:04:36 +0200638def find_struct(name):
639 global struct_types
640 for struct in struct_types:
641 if struct['struct'] == name:
642 return struct
643 return None
644
Eric Blake437db252015-09-29 16:21:02 -0600645
Markus Armbruster00e4b282015-06-10 10:04:36 +0200646def add_union(definition, info):
647 global union_types
648 name = definition['union']
649 add_name(name, info, 'union')
650 union_types.append(definition)
651
Eric Blake437db252015-09-29 16:21:02 -0600652
Markus Armbruster00e4b282015-06-10 10:04:36 +0200653def find_union(name):
654 global union_types
655 for union in union_types:
656 if union['union'] == name:
657 return union
658 return None
659
Eric Blake437db252015-09-29 16:21:02 -0600660
661def add_enum(name, info, enum_values=None, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200662 global enum_types
663 add_name(name, info, 'enum', implicit)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100664 enum_types.append({'enum_name': name, 'enum_values': enum_values})
Markus Armbruster00e4b282015-06-10 10:04:36 +0200665
Eric Blake437db252015-09-29 16:21:02 -0600666
Markus Armbruster00e4b282015-06-10 10:04:36 +0200667def find_enum(name):
668 global enum_types
669 for enum in enum_types:
670 if enum['enum_name'] == name:
671 return enum
672 return None
673
Markus Armbruster00e4b282015-06-10 10:04:36 +0200674
Eric Blake437db252015-09-29 16:21:02 -0600675def is_enum(name):
676 return find_enum(name) is not None
677
678
Marc-André Lureau4148c292017-01-13 15:41:25 +0100679def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600680 allow_dict=False, allow_optional=False,
681 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600682 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600683
684 if value is None:
685 return
686
Eric Blakedd883c62015-05-04 09:05:21 -0600687 # Check if array type for value is okay
688 if isinstance(value, list):
689 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100690 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600691 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100692 raise QAPISemError(info,
693 "%s: array type must contain single type name" %
694 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600695 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600696
697 # Check if type name for value is okay
698 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600699 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100700 raise QAPISemError(info, "%s uses unknown type '%s'"
701 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600702 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100703 raise QAPISemError(info, "%s cannot use %s type '%s'" %
704 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600705 return
706
Eric Blakedd883c62015-05-04 09:05:21 -0600707 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100708 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200709
710 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100711 raise QAPISemError(info,
712 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200713
714 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600715 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100716 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600717 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600718 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100719 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
720 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600721 # Todo: allow dictionaries to represent default values of
722 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100723 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200724 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600725 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600726 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600727
Eric Blake437db252015-09-29 16:21:02 -0600728
Marc-André Lureau4148c292017-01-13 15:41:25 +0100729def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600730 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600731 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600732
Eric Blakec8184082016-07-13 21:50:20 -0600733 args_meta = ['struct']
734 if boxed:
735 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100736 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600737 expr.get('data'), allow_dict=not boxed, allow_optional=True,
738 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600739 returns_meta = ['union', 'struct']
740 if name in returns_whitelist:
741 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100742 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200743 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200744 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600745
Eric Blake437db252015-09-29 16:21:02 -0600746
Marc-André Lureau4148c292017-01-13 15:41:25 +0100747def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600748 global events
749 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600750 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600751
Eric Blakec8184082016-07-13 21:50:20 -0600752 meta = ['struct']
753 if boxed:
754 meta += ['union', 'alternate']
Eric Blake4dc2e692015-05-04 09:05:17 -0600755 events.append(name)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100756 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600757 expr.get('data'), allow_dict=not boxed, allow_optional=True,
758 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200759
Eric Blake437db252015-09-29 16:21:02 -0600760
Marc-André Lureau4148c292017-01-13 15:41:25 +0100761def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800762 name = expr['union']
763 base = expr.get('base')
764 discriminator = expr.get('discriminator')
765 members = expr['data']
766
Eric Blake811d04f2015-05-04 09:05:10 -0600767 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600768
769 # With no discriminator it is a simple union.
770 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600771 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600772 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600773 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100774 raise QAPISemError(info, "Simple union '%s' must not have a base" %
775 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600776
777 # Else, it's a flat union.
778 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600779 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100780 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600781 base, allow_dict=True, allow_optional=True,
782 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600783 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100784 raise QAPISemError(info, "Flat union '%s' must have a base"
785 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700786 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100787 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800788
Eric Blakec9e0a792015-05-04 09:05:22 -0600789 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600790 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100791 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600792 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700793 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800794 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100795 raise QAPISemError(info,
796 "Discriminator '%s' is not a member of base "
797 "struct '%s'"
798 % (discriminator, base))
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800799 enum_define = find_enum(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600800 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800801 # Do not allow string discriminator
802 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100803 raise QAPISemError(info,
804 "Discriminator '%s' must be of enumeration "
805 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800806
Eric Blake02a57ae2016-02-17 23:48:16 -0700807 # Check every branch; don't allow an empty union
808 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100809 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800810 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100811 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600812
Eric Blake01cfbaa2015-12-01 22:20:58 -0700813 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100814 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200815 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600816
Eric Blake44bd1272015-05-04 09:05:08 -0600817 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700818 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600819 if enum_define:
Eric Blake437db252015-09-29 16:21:02 -0600820 if key not in enum_define['enum_values']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100821 raise QAPISemError(info,
822 "Discriminator value '%s' is not found in "
823 "enum '%s'"
Markus Armbrusteref801a92017-03-15 13:57:08 +0100824 % (key, enum_define['enum_name']))
Eric Blake44bd1272015-05-04 09:05:08 -0600825
Eric Blaked0b18232016-07-13 21:50:13 -0600826 # If discriminator is user-defined, ensure all values are covered
827 if enum_define:
828 for value in enum_define['enum_values']:
829 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100830 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
831 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600832
Eric Blake437db252015-09-29 16:21:02 -0600833
Marc-André Lureau4148c292017-01-13 15:41:25 +0100834def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600835 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600836 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600837 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600838
Eric Blake02a57ae2016-02-17 23:48:16 -0700839 # Check every branch; require at least two branches
840 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100841 raise QAPISemError(info,
842 "Alternate '%s' should have at least two branches "
843 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600844 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100845 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600846
Eric Blake811d04f2015-05-04 09:05:10 -0600847 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100848 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600849 value,
850 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600851 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700852 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100853 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
854 "type '%s'" % (name, key, value))
Eric Blake811d04f2015-05-04 09:05:10 -0600855 if qtype in types_seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100856 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
857 "be distinguished from member '%s'"
858 % (name, key, types_seen[qtype]))
Eric Blake811d04f2015-05-04 09:05:10 -0600859 types_seen[qtype] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800860
Eric Blake437db252015-09-29 16:21:02 -0600861
Marc-André Lureau4148c292017-01-13 15:41:25 +0100862def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600863 name = expr['enum']
864 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100865 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600866
867 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100868 raise QAPISemError(info,
869 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100870 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100871 raise QAPISemError(info,
872 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600873 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100874 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600875 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600876
Eric Blake437db252015-09-29 16:21:02 -0600877
Marc-André Lureau4148c292017-01-13 15:41:25 +0100878def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600879 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600880 members = expr['data']
881
Marc-André Lureau4148c292017-01-13 15:41:25 +0100882 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600883 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100884 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600885 allow_metas=['struct'])
886
Eric Blake437db252015-09-29 16:21:02 -0600887
Eric Blake0545f6b2015-05-04 09:05:15 -0600888def check_keys(expr_elem, meta, required, optional=[]):
889 expr = expr_elem['expr']
890 info = expr_elem['info']
891 name = expr[meta]
892 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100893 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600894 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600895 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600896 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100897 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
898 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600899 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100900 raise QAPISemError(info,
901 "'%s' of %s '%s' should only use false value"
902 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600903 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100904 raise QAPISemError(info,
905 "'%s' of %s '%s' should only use true value"
906 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600907 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600908 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100909 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
910 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600911
Eric Blake437db252015-09-29 16:21:02 -0600912
Markus Armbruster4d076d62015-06-10 08:55:21 +0200913def check_exprs(exprs):
914 global all_names
915
Markus Armbruster79470162017-03-15 13:57:21 +0100916 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200917 for builtin in builtin_types.keys():
918 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100919
920 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200921 for expr_elem in exprs:
922 expr = expr_elem['expr']
923 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100924 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100925
Markus Armbruster79470162017-03-15 13:57:21 +0100926 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100927 raise QAPISemError(info,
928 "Expression missing documentation comment")
929
Eric Blake437db252015-09-29 16:21:02 -0600930 if 'enum' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100931 name = expr['enum']
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100932 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster79470162017-03-15 13:57:21 +0100933 add_enum(name, info, expr['data'])
Eric Blake437db252015-09-29 16:21:02 -0600934 elif 'union' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100935 name = expr['union']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200936 check_keys(expr_elem, 'union', ['data'],
937 ['base', 'discriminator'])
938 add_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600939 elif 'alternate' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100940 name = expr['alternate']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200941 check_keys(expr_elem, 'alternate', ['data'])
Markus Armbruster79470162017-03-15 13:57:21 +0100942 add_name(name, info, 'alternate')
Eric Blake437db252015-09-29 16:21:02 -0600943 elif 'struct' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100944 name = expr['struct']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200945 check_keys(expr_elem, 'struct', ['data'], ['base'])
946 add_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600947 elif 'command' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100948 name = expr['command']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200949 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600950 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Markus Armbruster79470162017-03-15 13:57:21 +0100951 add_name(name, info, 'command')
Eric Blake437db252015-09-29 16:21:02 -0600952 elif 'event' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100953 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600954 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster79470162017-03-15 13:57:21 +0100955 add_name(name, info, 'event')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200956 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100957 raise QAPISemError(expr_elem['info'],
958 "Expression is missing metatype")
Markus Armbruster79470162017-03-15 13:57:21 +0100959 if doc and doc.symbol != name:
960 raise QAPISemError(info, "Definition of '%s' follows documentation"
961 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200962
963 # Try again for hidden UnionKind enum
964 for expr_elem in exprs:
965 expr = expr_elem['expr']
Eric Blake437db252015-09-29 16:21:02 -0600966 if 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200967 if not discriminator_find_enum_define(expr):
968 add_enum('%sKind' % expr['union'], expr_elem['info'],
969 implicit=True)
Eric Blake437db252015-09-29 16:21:02 -0600970 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200971 add_enum('%sKind' % expr['alternate'], expr_elem['info'],
972 implicit=True)
973
974 # Validate that exprs make sense
975 for expr_elem in exprs:
976 expr = expr_elem['expr']
977 info = expr_elem['info']
978
Eric Blake437db252015-09-29 16:21:02 -0600979 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200980 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600981 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200982 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600983 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200984 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600985 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200986 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600987 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200988 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600989 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200990 check_event(expr, info)
991 else:
992 assert False, 'unexpected meta type'
993
Markus Armbrusterac882192015-09-16 13:06:05 +0200994 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600995
Markus Armbrusterac882192015-09-16 13:06:05 +0200996
Marc-André Lureau3313b612017-01-13 15:41:29 +0100997def check_definition_doc(doc, expr, info):
998 for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
999 if i in expr:
1000 meta = i
1001 break
1002
Marc-André Lureau3313b612017-01-13 15:41:29 +01001003 if doc.has_section('Returns') and 'command' not in expr:
1004 raise QAPISemError(info, "'Returns:' is only valid for commands")
1005
1006 if meta == 'union':
1007 args = expr.get('base', [])
1008 else:
1009 args = expr.get('data', [])
1010 if isinstance(args, str):
1011 return
1012 if isinstance(args, dict):
1013 args = args.keys()
1014 assert isinstance(args, list)
1015
1016 if (meta == 'alternate'
1017 or (meta == 'union' and not expr.get('discriminator'))):
1018 args.append('type')
1019
Marc-André Lureau3313b612017-01-13 15:41:29 +01001020 doc_args = set(doc.args.keys())
1021 args = set([name.strip('*') for name in args])
1022 if not doc_args.issubset(args):
1023 raise QAPISemError(info, "The following documented members are not in "
Markus Armbrusteref801a92017-03-15 13:57:08 +01001024 "the declaration: %s" % ', '.join(doc_args - args))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001025
1026
1027def check_docs(docs):
1028 for doc in docs:
Markus Armbruster2d433232017-03-15 13:57:22 +01001029 if doc.expr:
Marc-André Lureau3313b612017-01-13 15:41:29 +01001030 check_definition_doc(doc, doc.expr, doc.info)
1031
1032 return docs
1033
1034
Markus Armbrusterac882192015-09-16 13:06:05 +02001035#
1036# Schema compiler frontend
1037#
1038
1039class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001040 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001041 assert isinstance(name, str)
1042 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -06001043 # For explicitly defined entities, info points to the (explicit)
1044 # definition. For builtins (and their arrays), info is None.
1045 # For implicitly defined entities, info points to a place that
1046 # triggered the implicit definition (there may be more than one
1047 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +02001048 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001049 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +02001050
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001051 def c_name(self):
1052 return c_name(self.name)
1053
Markus Armbrusterac882192015-09-16 13:06:05 +02001054 def check(self, schema):
1055 pass
1056
Eric Blake49823c42015-10-12 22:22:27 -06001057 def is_implicit(self):
1058 return not self.info
1059
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001060 def visit(self, visitor):
1061 pass
1062
1063
1064class QAPISchemaVisitor(object):
1065 def visit_begin(self, schema):
1066 pass
1067
1068 def visit_end(self):
1069 pass
1070
Eric Blake25a0d9c2015-10-12 22:22:21 -06001071 def visit_needed(self, entity):
1072 # Default to visiting everything
1073 return True
1074
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001075 def visit_builtin_type(self, name, info, json_type):
1076 pass
1077
1078 def visit_enum_type(self, name, info, values, prefix):
1079 pass
1080
1081 def visit_array_type(self, name, info, element_type):
1082 pass
1083
1084 def visit_object_type(self, name, info, base, members, variants):
1085 pass
1086
Markus Armbruster39a18152015-09-16 13:06:28 +02001087 def visit_object_type_flat(self, name, info, members, variants):
1088 pass
1089
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001090 def visit_alternate_type(self, name, info, variants):
1091 pass
1092
1093 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001094 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001095 pass
1096
Eric Blake48825ca2016-07-13 21:50:19 -06001097 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001098 pass
1099
Markus Armbrusterac882192015-09-16 13:06:05 +02001100
1101class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001102 # Return the C type for common use.
1103 # For the types we commonly box, this is a pointer type.
1104 def c_type(self):
1105 pass
1106
1107 # Return the C type to be used in a parameter list.
1108 def c_param_type(self):
1109 return self.c_type()
1110
1111 # Return the C type to be used where we suppress boxing.
1112 def c_unboxed_type(self):
1113 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001114
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001115 def json_type(self):
1116 pass
1117
1118 def alternate_qtype(self):
1119 json2qtype = {
1120 'string': 'QTYPE_QSTRING',
1121 'number': 'QTYPE_QFLOAT',
1122 'int': 'QTYPE_QINT',
1123 'boolean': 'QTYPE_QBOOL',
1124 'object': 'QTYPE_QDICT'
1125 }
1126 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001127
Markus Armbruster691e0312017-03-15 13:57:14 +01001128 def doc_type(self):
1129 if self.is_implicit():
1130 return None
1131 return self.name
1132
Markus Armbrusterac882192015-09-16 13:06:05 +02001133
1134class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001135 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001136 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001137 assert not c_type or isinstance(c_type, str)
1138 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1139 'value')
1140 self._json_type_name = json_type
1141 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001142
1143 def c_name(self):
1144 return self.name
1145
Eric Blake4040d992016-03-17 16:48:28 -06001146 def c_type(self):
1147 return self._c_type_name
1148
1149 def c_param_type(self):
1150 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001151 return 'const ' + self._c_type_name
1152 return self._c_type_name
1153
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001154 def json_type(self):
1155 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001156
Markus Armbruster691e0312017-03-15 13:57:14 +01001157 def doc_type(self):
1158 return self.json_type()
1159
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001160 def visit(self, visitor):
1161 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1162
Markus Armbrusterac882192015-09-16 13:06:05 +02001163
1164class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001165 def __init__(self, name, info, doc, values, prefix):
1166 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001167 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001168 assert isinstance(v, QAPISchemaMember)
1169 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001170 assert prefix is None or isinstance(prefix, str)
1171 self.values = values
1172 self.prefix = prefix
1173
1174 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001175 seen = {}
1176 for v in self.values:
1177 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001178 if self.doc:
1179 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001180
Eric Blake99df5282015-10-12 22:22:32 -06001181 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001182 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1183 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001184
Eric Blake4040d992016-03-17 16:48:28 -06001185 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001186 return c_name(self.name)
1187
Eric Blake93bda4d2015-12-01 22:20:55 -07001188 def member_names(self):
1189 return [v.name for v in self.values]
1190
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001191 def json_type(self):
1192 return 'string'
1193
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001194 def visit(self, visitor):
1195 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001196 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001197
Markus Armbrusterac882192015-09-16 13:06:05 +02001198
1199class QAPISchemaArrayType(QAPISchemaType):
1200 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001201 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001202 assert isinstance(element_type, str)
1203 self._element_type_name = element_type
1204 self.element_type = None
1205
1206 def check(self, schema):
1207 self.element_type = schema.lookup_type(self._element_type_name)
1208 assert self.element_type
1209
Eric Blake99df5282015-10-12 22:22:32 -06001210 def is_implicit(self):
1211 return True
1212
Eric Blake4040d992016-03-17 16:48:28 -06001213 def c_type(self):
1214 return c_name(self.name) + pointer_suffix
1215
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001216 def json_type(self):
1217 return 'array'
1218
Markus Armbruster691e0312017-03-15 13:57:14 +01001219 def doc_type(self):
1220 elt_doc_type = self.element_type.doc_type()
1221 if not elt_doc_type:
1222 return None
1223 return 'array of ' + elt_doc_type
1224
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001225 def visit(self, visitor):
1226 visitor.visit_array_type(self.name, self.info, self.element_type)
1227
Markus Armbrusterac882192015-09-16 13:06:05 +02001228
1229class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001230 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001231 # struct has local_members, optional base, and no variants
1232 # flat union has base, variants, and no local_members
1233 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001234 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001235 assert base is None or isinstance(base, str)
1236 for m in local_members:
1237 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001238 m.set_owner(name)
1239 if variants is not None:
1240 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1241 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001242 self._base_name = base
1243 self.base = None
1244 self.local_members = local_members
1245 self.variants = variants
1246 self.members = None
1247
1248 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001249 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001250 raise QAPISemError(self.info,
1251 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001252 if self.members:
1253 return
1254 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001255 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001256 if self._base_name:
1257 self.base = schema.lookup_type(self._base_name)
1258 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001259 self.base.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001260 self.base.check_clash(schema, self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001261 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001262 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001263 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001264 if self.doc:
1265 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001266 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001267 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001268 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001269 assert self.variants.tag_member in self.members
Eric Blake27b60ab2015-11-18 01:52:51 -07001270 self.variants.check_clash(schema, self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001271
Eric Blake14f00c62016-03-03 09:16:43 -07001272 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001273 # and update seen to track the members seen so far. Report any errors
1274 # on behalf of info, which is not necessarily self.info
1275 def check_clash(self, schema, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001276 assert not self.variants # not implemented
1277 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001278 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001279
Eric Blake99df5282015-10-12 22:22:32 -06001280 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001281 # See QAPISchema._make_implicit_object_type(), as well as
1282 # _def_predefineds()
1283 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001284
Eric Blakeb6167702016-07-13 21:50:16 -06001285 def is_empty(self):
1286 assert self.members is not None
1287 return not self.members and not self.variants
1288
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001289 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001290 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001291 return QAPISchemaType.c_name(self)
1292
Eric Blake4040d992016-03-17 16:48:28 -06001293 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001294 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001295 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001296
Eric Blake4040d992016-03-17 16:48:28 -06001297 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001298 return c_name(self.name)
1299
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001300 def json_type(self):
1301 return 'object'
1302
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001303 def visit(self, visitor):
1304 visitor.visit_object_type(self.name, self.info,
1305 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001306 visitor.visit_object_type_flat(self.name, self.info,
1307 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001308
Markus Armbrusterac882192015-09-16 13:06:05 +02001309
Eric Blaked44f9ac2015-12-01 22:20:54 -07001310class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001311 role = 'member'
1312
Eric Blaked44f9ac2015-12-01 22:20:54 -07001313 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001314 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001315 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001316 self.owner = None
1317
1318 def set_owner(self, name):
1319 assert not self.owner
1320 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001321
Eric Blake27b60ab2015-11-18 01:52:51 -07001322 def check_clash(self, info, seen):
1323 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001324 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001325 raise QAPISemError(info,
1326 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001327 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001328 raise QAPISemError(info, "%s collides with %s" %
1329 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001330 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001331
Eric Blake88d4ef82015-11-18 01:52:50 -07001332 def _pretty_owner(self):
1333 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001334 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001335 # See QAPISchema._make_implicit_object_type() - reverse the
1336 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001337 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001338 if owner.endswith('-arg'):
1339 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001340 elif owner.endswith('-base'):
1341 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001342 else:
1343 assert owner.endswith('-wrapper')
1344 # Unreachable and not implemented
1345 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001346 if owner.endswith('Kind'):
1347 # See QAPISchema._make_implicit_enum_type()
1348 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001349 return '(%s of %s)' % (self.role, owner)
1350
1351 def describe(self):
1352 return "'%s' %s" % (self.name, self._pretty_owner())
1353
Markus Armbrusterac882192015-09-16 13:06:05 +02001354
Eric Blaked44f9ac2015-12-01 22:20:54 -07001355class QAPISchemaObjectTypeMember(QAPISchemaMember):
1356 def __init__(self, name, typ, optional):
1357 QAPISchemaMember.__init__(self, name)
1358 assert isinstance(typ, str)
1359 assert isinstance(optional, bool)
1360 self._type_name = typ
1361 self.type = None
1362 self.optional = optional
1363
1364 def check(self, schema):
1365 assert self.owner
1366 self.type = schema.lookup_type(self._type_name)
1367 assert self.type
1368
1369
Markus Armbrusterac882192015-09-16 13:06:05 +02001370class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001371 def __init__(self, tag_name, tag_member, variants):
1372 # Flat unions pass tag_name but not tag_member.
1373 # Simple unions and alternates pass tag_member but not tag_name.
1374 # After check(), tag_member is always set, and tag_name remains
1375 # a reliable witness of being used by a flat union.
1376 assert bool(tag_member) != bool(tag_name)
1377 assert (isinstance(tag_name, str) or
1378 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001379 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001380 for v in variants:
1381 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001382 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001383 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001384 self.variants = variants
1385
Eric Blake88d4ef82015-11-18 01:52:50 -07001386 def set_owner(self, name):
1387 for v in self.variants:
1388 v.set_owner(name)
1389
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001390 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001391 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001392 self.tag_member = seen[c_name(self._tag_name)]
1393 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001394 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1395 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001396 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001397 # Union names must match enum values; alternate names are
1398 # checked separately. Use 'seen' to tell the two apart.
1399 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001400 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001401 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001402 v.type.check(schema)
1403
Eric Blake27b60ab2015-11-18 01:52:51 -07001404 def check_clash(self, schema, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001405 for v in self.variants:
1406 # Reset seen map for each variant, since qapi names from one
1407 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001408 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blake27b60ab2015-11-18 01:52:51 -07001409 v.type.check_clash(schema, info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001410
Eric Blake437db252015-09-29 16:21:02 -06001411
Markus Armbrusterac882192015-09-16 13:06:05 +02001412class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001413 role = 'branch'
1414
Markus Armbrusterac882192015-09-16 13:06:05 +02001415 def __init__(self, name, typ):
1416 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1417
Markus Armbrusterac882192015-09-16 13:06:05 +02001418
1419class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001420 def __init__(self, name, info, doc, variants):
1421 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001422 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001423 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001424 variants.set_owner(name)
1425 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001426 self.variants = variants
1427
1428 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001429 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001430 # Not calling self.variants.check_clash(), because there's nothing
1431 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001432 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001433 # Alternate branch names have no relation to the tag enum values;
1434 # so we have to check for potential name collisions ourselves.
1435 seen = {}
1436 for v in self.variants.variants:
1437 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001438 if self.doc:
1439 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001440
Eric Blake4040d992016-03-17 16:48:28 -06001441 def c_type(self):
1442 return c_name(self.name) + pointer_suffix
1443
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001444 def json_type(self):
1445 return 'value'
1446
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001447 def visit(self, visitor):
1448 visitor.visit_alternate_type(self.name, self.info, self.variants)
1449
Eric Blakec8184082016-07-13 21:50:20 -06001450 def is_empty(self):
1451 return False
1452
Markus Armbrusterac882192015-09-16 13:06:05 +02001453
1454class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001455 def __init__(self, name, info, doc, arg_type, ret_type,
1456 gen, success_response, boxed):
1457 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001458 assert not arg_type or isinstance(arg_type, str)
1459 assert not ret_type or isinstance(ret_type, str)
1460 self._arg_type_name = arg_type
1461 self.arg_type = None
1462 self._ret_type_name = ret_type
1463 self.ret_type = None
1464 self.gen = gen
1465 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001466 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001467
1468 def check(self, schema):
1469 if self._arg_type_name:
1470 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001471 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1472 isinstance(self.arg_type, QAPISchemaAlternateType))
1473 self.arg_type.check(schema)
1474 if self.boxed:
1475 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001476 raise QAPISemError(self.info,
1477 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001478 else:
1479 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1480 assert not self.arg_type.variants
1481 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001482 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001483 if self._ret_type_name:
1484 self.ret_type = schema.lookup_type(self._ret_type_name)
1485 assert isinstance(self.ret_type, QAPISchemaType)
1486
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001487 def visit(self, visitor):
1488 visitor.visit_command(self.name, self.info,
1489 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001490 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001491
Markus Armbrusterac882192015-09-16 13:06:05 +02001492
1493class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001494 def __init__(self, name, info, doc, arg_type, boxed):
1495 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001496 assert not arg_type or isinstance(arg_type, str)
1497 self._arg_type_name = arg_type
1498 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001499 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001500
1501 def check(self, schema):
1502 if self._arg_type_name:
1503 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001504 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1505 isinstance(self.arg_type, QAPISchemaAlternateType))
1506 self.arg_type.check(schema)
1507 if self.boxed:
1508 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001509 raise QAPISemError(self.info,
1510 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001511 else:
1512 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1513 assert not self.arg_type.variants
1514 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001515 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001516
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001517 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001518 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001519
Markus Armbrusterac882192015-09-16 13:06:05 +02001520
1521class QAPISchema(object):
1522 def __init__(self, fname):
1523 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001524 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001525 self.exprs = check_exprs(parser.exprs)
1526 self.docs = check_docs(parser.docs)
Eric Blake7618b912015-10-12 22:22:22 -06001527 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001528 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001529 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001530 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001531 self._def_exprs()
1532 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001533 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001534 print >>sys.stderr, err
1535 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001536
Markus Armbrusterac882192015-09-16 13:06:05 +02001537 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001538 # Only the predefined types are allowed to not have info
1539 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001540 assert ent.name not in self._entity_dict
1541 self._entity_dict[ent.name] = ent
1542
1543 def lookup_entity(self, name, typ=None):
1544 ent = self._entity_dict.get(name)
1545 if typ and not isinstance(ent, typ):
1546 return None
1547 return ent
1548
1549 def lookup_type(self, name):
1550 return self.lookup_entity(name, QAPISchemaType)
1551
Eric Blake861877a2016-03-17 16:48:36 -06001552 def _def_builtin_type(self, name, json_type, c_type):
1553 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001554 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1555 # qapi-types.h from a single .c, all arrays of builtins must be
1556 # declared in the first file whether or not they are used. Nicer
1557 # would be to use lazy instantiation, while figuring out how to
1558 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001559 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001560
1561 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001562 for t in [('str', 'string', 'char' + pointer_suffix),
1563 ('number', 'number', 'double'),
1564 ('int', 'int', 'int64_t'),
1565 ('int8', 'int', 'int8_t'),
1566 ('int16', 'int', 'int16_t'),
1567 ('int32', 'int', 'int32_t'),
1568 ('int64', 'int', 'int64_t'),
1569 ('uint8', 'int', 'uint8_t'),
1570 ('uint16', 'int', 'uint16_t'),
1571 ('uint32', 'int', 'uint32_t'),
1572 ('uint64', 'int', 'uint64_t'),
1573 ('size', 'int', 'uint64_t'),
1574 ('bool', 'boolean', 'bool'),
1575 ('any', 'value', 'QObject' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001576 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001577 self.the_empty_object_type = QAPISchemaObjectType(
1578 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001579 self._def_entity(self.the_empty_object_type)
Eric Blake93bda4d2015-12-01 22:20:55 -07001580 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1581 'qstring', 'qdict', 'qlist',
1582 'qfloat', 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001583 self._def_entity(QAPISchemaEnumType('QType', None, None,
1584 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001585
Eric Blake93bda4d2015-12-01 22:20:55 -07001586 def _make_enum_members(self, values):
1587 return [QAPISchemaMember(v) for v in values]
1588
Eric Blake99df5282015-10-12 22:22:32 -06001589 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001590 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001591 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001592 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001593 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001594 return name
1595
Eric Blake99df5282015-10-12 22:22:32 -06001596 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001597 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001598 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001599 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001600 return name
1601
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001602 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001603 if not members:
1604 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001605 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001606 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001607 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001608 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001609 members, None))
1610 return name
1611
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001612 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001613 name = expr['enum']
1614 data = expr['data']
1615 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001616 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001617 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001618
Eric Blake99df5282015-10-12 22:22:32 -06001619 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001620 optional = False
1621 if name.startswith('*'):
1622 name = name[1:]
1623 optional = True
1624 if isinstance(typ, list):
1625 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001626 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001627 return QAPISchemaObjectTypeMember(name, typ, optional)
1628
Eric Blake99df5282015-10-12 22:22:32 -06001629 def _make_members(self, data, info):
1630 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001631 for (key, value) in data.iteritems()]
1632
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001633 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001634 name = expr['struct']
1635 base = expr.get('base')
1636 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001637 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001638 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001639 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001640
1641 def _make_variant(self, case, typ):
1642 return QAPISchemaObjectTypeVariant(case, typ)
1643
Eric Blake99df5282015-10-12 22:22:32 -06001644 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001645 if isinstance(typ, list):
1646 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001647 typ = self._make_array_type(typ[0], info)
1648 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001649 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001650 return QAPISchemaObjectTypeVariant(case, typ)
1651
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001652 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001653 name = expr['union']
1654 data = expr['data']
1655 base = expr.get('base')
1656 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001657 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001658 if isinstance(base, dict):
1659 base = (self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001660 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001661 if tag_name:
1662 variants = [self._make_variant(key, value)
1663 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001664 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001665 else:
Eric Blake99df5282015-10-12 22:22:32 -06001666 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001667 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001668 typ = self._make_implicit_enum_type(name, info,
1669 [v.name for v in variants])
1670 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001671 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001672 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001673 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001674 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001675 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001676 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001677
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001678 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001679 name = expr['alternate']
1680 data = expr['data']
1681 variants = [self._make_variant(key, value)
1682 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001683 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001684 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001685 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001686 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001687 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001688 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001689
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001690 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001691 name = expr['command']
1692 data = expr.get('data')
1693 rets = expr.get('returns')
1694 gen = expr.get('gen', True)
1695 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001696 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001697 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001698 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001699 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001700 if isinstance(rets, list):
1701 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001702 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001703 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1704 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001705
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001706 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001707 name = expr['event']
1708 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001709 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001710 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001711 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001712 name, info, doc, 'arg', self._make_members(data, info))
1713 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001714
1715 def _def_exprs(self):
1716 for expr_elem in self.exprs:
1717 expr = expr_elem['expr']
1718 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001719 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001720 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001721 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001722 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001723 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001724 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001725 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001726 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001727 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001728 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001729 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001730 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001731 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001732 else:
1733 assert False
1734
1735 def check(self):
1736 for ent in self._entity_dict.values():
1737 ent.check(self)
1738
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001739 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001740 visitor.visit_begin(self)
1741 for (name, entity) in sorted(self._entity_dict.items()):
1742 if visitor.visit_needed(entity):
1743 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001744 visitor.visit_end()
1745
Markus Armbruster2caba362013-07-27 17:41:56 +02001746
Markus Armbruster00e4b282015-06-10 10:04:36 +02001747#
1748# Code generation helpers
1749#
1750
Michael Roth0f923be2011-07-19 14:50:39 -05001751def camel_case(name):
1752 new_name = ''
1753 first = True
1754 for ch in name:
1755 if ch in ['_', '-']:
1756 first = True
1757 elif first:
1758 new_name += ch.upper()
1759 first = False
1760 else:
1761 new_name += ch.lower()
1762 return new_name
1763
Eric Blake437db252015-09-29 16:21:02 -06001764
Markus Armbruster849bc532015-05-14 06:50:53 -06001765# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1766# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1767# ENUM24_Name -> ENUM24_NAME
1768def camel_to_upper(value):
1769 c_fun_str = c_name(value, False)
1770 if value.isupper():
1771 return c_fun_str
1772
1773 new_name = ''
1774 l = len(c_fun_str)
1775 for i in range(l):
1776 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001777 # When c is upper and no '_' appears before, do more checks
1778 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001779 if i < l - 1 and c_fun_str[i + 1].islower():
1780 new_name += '_'
1781 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001782 new_name += '_'
1783 new_name += c
1784 return new_name.lstrip('_').upper()
1785
Eric Blake437db252015-09-29 16:21:02 -06001786
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001787def c_enum_const(type_name, const_name, prefix=None):
1788 if prefix is not None:
1789 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001790 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001791
Eric Blake18df5152015-05-14 06:50:48 -06001792c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001793
Eric Blake437db252015-09-29 16:21:02 -06001794
Eric Blakec6405b52015-05-14 06:50:55 -06001795# Map @name to a valid C identifier.
1796# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001797# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001798#
1799# Used for converting 'name' from a 'name':'type' qapi definition
1800# into a generated struct member, as well as converting type names
1801# into substrings of a generated C function name.
1802# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1803# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001804def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001805 # ANSI X3J11/88-090, 3.1.1
1806 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001807 'default', 'do', 'double', 'else', 'enum', 'extern',
1808 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1809 'return', 'short', 'signed', 'sizeof', 'static',
1810 'struct', 'switch', 'typedef', 'union', 'unsigned',
1811 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001812 # ISO/IEC 9899:1999, 6.4.1
1813 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1814 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001815 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1816 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001817 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1818 # excluding _.*
1819 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001820 # C++ ISO/IEC 14882:2003 2.11
1821 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1822 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1823 'namespace', 'new', 'operator', 'private', 'protected',
1824 'public', 'reinterpret_cast', 'static_cast', 'template',
1825 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1826 'using', 'virtual', 'wchar_t',
1827 # alternative representations
1828 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1829 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001830 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001831 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001832 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001833 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1834 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001835 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001836 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001837
Amos Kong05dfb262014-06-10 19:25:53 +08001838eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001839pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001840
Eric Blake437db252015-09-29 16:21:02 -06001841
Michael Roth0f923be2011-07-19 14:50:39 -05001842def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001843 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001844 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001845 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001846 return ret
1847
1848indent_level = 0
1849
Eric Blake437db252015-09-29 16:21:02 -06001850
Michael Roth0f923be2011-07-19 14:50:39 -05001851def push_indent(indent_amount=4):
1852 global indent_level
1853 indent_level += indent_amount
1854
Eric Blake437db252015-09-29 16:21:02 -06001855
Michael Roth0f923be2011-07-19 14:50:39 -05001856def pop_indent(indent_amount=4):
1857 global indent_level
1858 indent_level -= indent_amount
1859
Eric Blake437db252015-09-29 16:21:02 -06001860
Markus Armbruster77e703b2015-06-24 19:27:32 +02001861# Generate @code with @kwds interpolated.
1862# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001863def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001864 raw = code % kwds
1865 if indent_level:
1866 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001867 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001868 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001869 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001870 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001871 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001872
Eric Blake437db252015-09-29 16:21:02 -06001873
Michael Roth0f923be2011-07-19 14:50:39 -05001874def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001875 if code[0] == '\n':
1876 code = code[1:]
1877 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001878
Michael Roth0f923be2011-07-19 14:50:39 -05001879
1880def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001881 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001882
Eric Blake437db252015-09-29 16:21:02 -06001883
Michael Rothc0afa9c2013-05-10 17:46:00 -05001884def guardstart(name):
1885 return mcgen('''
1886
1887#ifndef %(name)s
1888#define %(name)s
1889
1890''',
1891 name=guardname(name))
1892
Eric Blake437db252015-09-29 16:21:02 -06001893
Michael Rothc0afa9c2013-05-10 17:46:00 -05001894def guardend(name):
1895 return mcgen('''
1896
1897#endif /* %(name)s */
1898
1899''',
1900 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001901
Eric Blake437db252015-09-29 16:21:02 -06001902
Markus Armbrustere98859a2015-09-16 13:06:16 +02001903def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001904 ret = mcgen('''
1905
Markus Armbrustere98859a2015-09-16 13:06:16 +02001906const char *const %(c_name)s_lookup[] = {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001907''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001908 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001909 for value in values:
1910 index = c_enum_const(name, value, prefix)
1911 ret += mcgen('''
1912 [%(index)s] = "%(value)s",
1913''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001914 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001915
Eric Blake7fb1cf12015-11-18 01:52:57 -07001916 max_index = c_enum_const(name, '_MAX', prefix)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001917 ret += mcgen('''
1918 [%(max_index)s] = NULL,
1919};
1920''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001921 max_index=max_index)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001922 return ret
1923
Eric Blake437db252015-09-29 16:21:02 -06001924
Markus Armbrustere98859a2015-09-16 13:06:16 +02001925def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001926 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001927 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001928
1929 ret = mcgen('''
1930
1931typedef enum %(c_name)s {
1932''',
1933 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001934
1935 i = 0
1936 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001937 ret += mcgen('''
1938 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001939''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001940 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001941 i=i)
1942 i += 1
1943
Markus Armbrustere98859a2015-09-16 13:06:16 +02001944 ret += mcgen('''
1945} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001946''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001947 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001948
Markus Armbrustere98859a2015-09-16 13:06:16 +02001949 ret += mcgen('''
1950
1951extern const char *const %(c_name)s_lookup[];
1952''',
1953 c_name=c_name(name))
1954 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001955
Eric Blake437db252015-09-29 16:21:02 -06001956
Eric Blake48825ca2016-07-13 21:50:19 -06001957def gen_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001958 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001959 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001960 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001961 ret = ''
1962 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001963 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001964 ret += '%s arg' % arg_type.c_param_type()
1965 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001966 else:
1967 assert not arg_type.variants
1968 for memb in arg_type.members:
1969 ret += sep
1970 sep = ', '
1971 if memb.optional:
1972 ret += 'bool has_%s, ' % c_name(memb.name)
1973 ret += '%s %s' % (memb.type.c_param_type(),
1974 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001975 if extra:
1976 ret += sep + extra
1977 return ret
1978
Eric Blake1f353342015-09-29 16:21:13 -06001979
Markus Armbruster00e4b282015-06-10 10:04:36 +02001980#
1981# Common command line parsing
1982#
1983
Eric Blake437db252015-09-29 16:21:02 -06001984
Markus Armbrusteref801a92017-03-15 13:57:08 +01001985def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001986
1987 try:
1988 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001989 'chp:o:' + extra_options,
1990 ['source', 'header', 'prefix=',
1991 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001992 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001993 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001994 sys.exit(1)
1995
Markus Armbrusteref801a92017-03-15 13:57:08 +01001996 output_dir = ''
1997 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001998 do_c = False
1999 do_h = False
2000 extra_opts = []
2001
2002 for oa in opts:
2003 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01002004 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01002005 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02002006 if match.end() != len(a):
2007 print >>sys.stderr, \
2008 "%s: 'funny character '%s' in argument of --prefix" \
2009 % (sys.argv[0], a[match.end()])
2010 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02002011 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01002012 elif o in ('-o', '--output-dir'):
2013 output_dir = a + '/'
2014 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02002015 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01002016 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02002017 do_h = True
2018 else:
2019 extra_opts.append(oa)
2020
2021 if not do_c and not do_h:
2022 do_c = True
2023 do_h = True
2024
Markus Armbruster16d80f62015-04-02 13:32:16 +02002025 if len(args) != 1:
2026 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002027 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02002028 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002029
Markus Armbruster54414042015-06-09 16:22:45 +02002030 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002031
Markus Armbruster00e4b282015-06-10 10:04:36 +02002032#
2033# Generate output files with boilerplate
2034#
2035
Eric Blake437db252015-09-29 16:21:02 -06002036
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002037def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
2038 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02002039 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002040 c_file = output_dir + prefix + c_file
2041 h_file = output_dir + prefix + h_file
2042
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002043 if output_dir:
2044 try:
2045 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01002046 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002047 if e.errno != errno.EEXIST:
2048 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002049
2050 def maybe_open(really, name, opt):
2051 if really:
2052 return open(name, opt)
2053 else:
2054 import StringIO
2055 return StringIO.StringIO()
2056
2057 fdef = maybe_open(do_c, c_file, 'w')
2058 fdecl = maybe_open(do_h, h_file, 'w')
2059
2060 fdef.write(mcgen('''
2061/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2062%(comment)s
2063''',
Eric Blake437db252015-09-29 16:21:02 -06002064 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002065
2066 fdecl.write(mcgen('''
2067/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2068%(comment)s
2069#ifndef %(guard)s
2070#define %(guard)s
2071
2072''',
Eric Blake437db252015-09-29 16:21:02 -06002073 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002074
2075 return (fdef, fdecl)
2076
Eric Blake437db252015-09-29 16:21:02 -06002077
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002078def close_output(fdef, fdecl):
2079 fdecl.write('''
2080#endif
2081''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002082 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002083 fdef.close()