blob: 735ddaa6055e69dc0b799ca2deb3ff91703eb800 [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
Markus Armbruster5f018442017-03-15 13:57:31 +010049enum_types = {}
Eric Blake4dc2e692015-05-04 09:05:17 -060050struct_types = []
51union_types = []
Eric Blake4dc2e692015-05-04 09:05:17 -060052all_names = {}
53
Markus Armbruster00e4b282015-06-10 10:04:36 +020054#
55# Parsing the schema into expressions
56#
57
Eric Blake437db252015-09-29 16:21:02 -060058
Lluís Vilanovaa719a272014-05-07 20:46:15 +020059def error_path(parent):
Markus Armbrusteref801a92017-03-15 13:57:08 +010060 res = ''
Lluís Vilanovaa719a272014-05-07 20:46:15 +020061 while parent:
Markus Armbrusteref801a92017-03-15 13:57:08 +010062 res = ('In file included from %s:%d:\n' % (parent['file'],
Lluís Vilanovaa719a272014-05-07 20:46:15 +020063 parent['line'])) + res
64 parent = parent['parent']
65 return res
66
Eric Blake437db252015-09-29 16:21:02 -060067
Marc-André Lureau4148c292017-01-13 15:41:25 +010068class QAPIError(Exception):
69 def __init__(self, fname, line, col, incl_info, msg):
Eric Blake59b00542015-09-29 16:21:01 -060070 Exception.__init__(self)
Marc-André Lureau4148c292017-01-13 15:41:25 +010071 self.fname = fname
72 self.line = line
73 self.col = col
74 self.info = incl_info
Markus Armbruster2caba362013-07-27 17:41:56 +020075 self.msg = msg
Marc-André Lureau4148c292017-01-13 15:41:25 +010076
77 def __str__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +010078 loc = '%s:%d' % (self.fname, self.line)
Marc-André Lureau4148c292017-01-13 15:41:25 +010079 if self.col is not None:
Markus Armbrusteref801a92017-03-15 13:57:08 +010080 loc += ':%s' % self.col
81 return error_path(self.info) + '%s: %s' % (loc, self.msg)
Marc-André Lureau4148c292017-01-13 15:41:25 +010082
83
84class QAPIParseError(QAPIError):
85 def __init__(self, parser, msg):
86 col = 1
87 for ch in parser.src[parser.line_pos:parser.pos]:
Wenchao Xia515b9432014-03-04 18:44:33 -080088 if ch == '\t':
Marc-André Lureau4148c292017-01-13 15:41:25 +010089 col = (col + 7) % 8 + 1
Markus Armbruster2caba362013-07-27 17:41:56 +020090 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +010091 col += 1
92 QAPIError.__init__(self, parser.fname, parser.line, col,
93 parser.incl_info, msg)
Markus Armbruster2caba362013-07-27 17:41:56 +020094
Eric Blake437db252015-09-29 16:21:02 -060095
Marc-André Lureau4148c292017-01-13 15:41:25 +010096class QAPISemError(QAPIError):
97 def __init__(self, info, msg):
98 QAPIError.__init__(self, info['file'], info['line'], None,
99 info['parent'], msg)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800100
Eric Blake437db252015-09-29 16:21:02 -0600101
Marc-André Lureau3313b612017-01-13 15:41:29 +0100102class QAPIDoc(object):
103 class Section(object):
104 def __init__(self, name=None):
105 # optional section name (argument/member or section name)
106 self.name = name
107 # the list of lines for this section
108 self.content = []
Markus Armbrusterb116fd82017-03-15 13:57:00 +0100109 self.optional = False
Marc-André Lureau3313b612017-01-13 15:41:29 +0100110
111 def append(self, line):
112 self.content.append(line)
113
114 def __repr__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100115 return '\n'.join(self.content).strip()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100116
117 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100118 def __init__(self, name):
119 QAPIDoc.Section.__init__(self, name)
120 self.member = None
121
122 def connect(self, member):
123 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100124
125 def __init__(self, parser, info):
126 # self.parser is used to report errors with QAPIParseError. The
127 # resulting error position depends on the state of the parser.
128 # It happens to be the beginning of the comment. More or less
129 # servicable, but action at a distance.
130 self.parser = parser
131 self.info = info
132 self.symbol = None
133 self.body = QAPIDoc.Section()
134 # dict mapping parameter name to ArgSection
135 self.args = OrderedDict()
136 # a list of Section
137 self.sections = []
138 # the current section
139 self.section = self.body
Marc-André Lureau3313b612017-01-13 15:41:29 +0100140
141 def has_section(self, name):
142 """Return True if we have a section with this name."""
143 for i in self.sections:
144 if i.name == name:
145 return True
146 return False
147
148 def append(self, line):
149 """Parse a comment line and add it to the documentation."""
150 line = line[1:]
151 if not line:
152 self._append_freeform(line)
153 return
154
155 if line[0] != ' ':
156 raise QAPIParseError(self.parser, "Missing space after #")
157 line = line[1:]
158
159 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
160 # recognized, and get silently treated as ordinary text
161 if self.symbol:
162 self._append_symbol_line(line)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100163 elif not self.body.content and line.startswith('@'):
164 if not line.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100165 raise QAPIParseError(self.parser, "Line should end with :")
166 self.symbol = line[1:-1]
167 # FIXME invalid names other than the empty string aren't flagged
168 if not self.symbol:
169 raise QAPIParseError(self.parser, "Invalid name")
170 else:
171 self._append_freeform(line)
172
Markus Armbruster4ea71482017-03-15 13:57:23 +0100173 def end_comment(self):
174 self._end_section()
175
Marc-André Lureau3313b612017-01-13 15:41:29 +0100176 def _append_symbol_line(self, line):
177 name = line.split(' ', 1)[0]
178
Markus Armbrusteref801a92017-03-15 13:57:08 +0100179 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100180 line = line[len(name)+1:]
181 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100182 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100183 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100184 'Note:', 'Notes:',
185 'Example:', 'Examples:',
186 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100187 line = line[len(name)+1:]
188 self._start_section(name[:-1])
189
190 self._append_freeform(line)
191
192 def _start_args_section(self, name):
193 # FIXME invalid names other than the empty string aren't flagged
194 if not name:
195 raise QAPIParseError(self.parser, "Invalid parameter name")
196 if name in self.args:
197 raise QAPIParseError(self.parser,
198 "'%s' parameter name duplicated" % name)
199 if self.sections:
200 raise QAPIParseError(self.parser,
201 "'@%s:' can't follow '%s' section"
202 % (name, self.sections[0].name))
Markus Armbruster4ea71482017-03-15 13:57:23 +0100203 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100204 self.section = QAPIDoc.ArgSection(name)
205 self.args[name] = self.section
206
Markus Armbrusteref801a92017-03-15 13:57:08 +0100207 def _start_section(self, name=''):
208 if name in ('Returns', 'Since') and self.has_section(name):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100209 raise QAPIParseError(self.parser,
210 "Duplicated '%s' section" % name)
Markus Armbruster4ea71482017-03-15 13:57:23 +0100211 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100212 self.section = QAPIDoc.Section(name)
213 self.sections.append(self.section)
214
Markus Armbruster4ea71482017-03-15 13:57:23 +0100215 def _end_section(self):
216 if self.section:
217 contents = str(self.section)
218 if self.section.name and (not contents or contents.isspace()):
219 raise QAPIParseError(self.parser, "Empty doc section '%s'"
220 % self.section.name)
221 self.section = None
222
Marc-André Lureau3313b612017-01-13 15:41:29 +0100223 def _append_freeform(self, line):
224 in_arg = isinstance(self.section, QAPIDoc.ArgSection)
225 if (in_arg and self.section.content
226 and not self.section.content[-1]
227 and line and not line[0].isspace()):
228 self._start_section()
229 if (in_arg or not self.section.name
Markus Armbrusteref801a92017-03-15 13:57:08 +0100230 or not self.section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100231 line = line.strip()
Markus Armbruster2d433232017-03-15 13:57:22 +0100232 match = re.match(r'(@\S+:)', line)
233 if match:
234 raise QAPIParseError(self.parser,
235 "'%s' not allowed in free-form documentation"
236 % match.group(1))
Markus Armbruster1d8bda12017-03-15 13:57:06 +0100237 # TODO Drop this once the dust has settled
238 if (isinstance(self.section, QAPIDoc.ArgSection)
239 and '#optional' in line):
240 raise QAPISemError(self.info, "Please drop the #optional tag")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100241 self.section.append(line)
242
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100243 def connect_member(self, member):
244 if member.name not in self.args:
245 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100246 self.args[member.name] = QAPIDoc.ArgSection(member.name)
247 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100248
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100249 def check_expr(self, expr):
250 if self.has_section('Returns') and 'command' not in expr:
251 raise QAPISemError(self.info,
252 "'Returns:' is only valid for commands")
253
Markus Armbruster816a57c2017-03-15 13:57:26 +0100254 def check(self):
255 bogus = [name for name, section in self.args.iteritems()
256 if not section.member]
257 if bogus:
258 raise QAPISemError(
259 self.info,
260 "The following documented members are not in "
261 "the declaration: %s" % ", ".join(bogus))
262
Marc-André Lureau3313b612017-01-13 15:41:29 +0100263
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200264class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500265
Eric Blake437db252015-09-29 16:21:02 -0600266 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200267 abs_fname = os.path.abspath(fp.name)
Markus Armbruster8608d252015-06-09 18:32:29 +0200268 fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200269 self.fname = fname
Markus Armbruster54414042015-06-09 16:22:45 +0200270 previously_included.append(abs_fname)
271 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200272 self.src = fp.read()
273 if self.src == '' or self.src[-1] != '\n':
274 self.src += '\n'
275 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800276 self.line = 1
277 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200278 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100279 self.docs = []
Markus Armbrustere7823a22017-03-15 13:57:20 +0100280 self.cur_doc = None
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200281 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500282
Eric Blake437db252015-09-29 16:21:02 -0600283 while self.tok is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100284 info = {'file': fname, 'line': self.line,
285 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100286 if self.tok == '#':
Markus Armbrustere7823a22017-03-15 13:57:20 +0100287 self.reject_expr_doc()
288 self.cur_doc = self.get_doc(info)
289 self.docs.append(self.cur_doc)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100290 continue
291
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200292 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100293 if 'include' in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100294 self.reject_expr_doc()
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200295 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100296 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100297 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200298 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100299 raise QAPISemError(info,
300 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100301 self._include(include, info, os.path.dirname(abs_fname),
302 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100303 elif "pragma" in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100304 self.reject_expr_doc()
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100305 if len(expr) != 1:
306 raise QAPISemError(info, "Invalid 'pragma' directive")
307 pragma = expr['pragma']
308 if not isinstance(pragma, dict):
309 raise QAPISemError(
310 info, "Value of 'pragma' must be a dictionary")
311 for name, value in pragma.iteritems():
312 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200313 else:
314 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100315 'info': info}
Markus Armbrustere7823a22017-03-15 13:57:20 +0100316 if self.cur_doc:
317 if not self.cur_doc.symbol:
318 raise QAPISemError(
319 self.cur_doc.info,
320 "Expression documentation required")
Markus Armbrustere7823a22017-03-15 13:57:20 +0100321 expr_elem['doc'] = self.cur_doc
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200322 self.exprs.append(expr_elem)
Markus Armbrustere7823a22017-03-15 13:57:20 +0100323 self.cur_doc = None
324 self.reject_expr_doc()
325
326 def reject_expr_doc(self):
327 if self.cur_doc and self.cur_doc.symbol:
328 raise QAPISemError(
329 self.cur_doc.info,
330 "Documentation for '%s' is not followed by the definition"
331 % self.cur_doc.symbol)
Michael Roth0f923be2011-07-19 14:50:39 -0500332
Markus Armbrustere04dea82017-03-15 13:56:50 +0100333 def _include(self, include, info, base_dir, previously_included):
334 incl_abs_fname = os.path.join(base_dir, include)
335 # catch inclusion cycle
336 inf = info
337 while inf:
338 if incl_abs_fname == os.path.abspath(inf['file']):
339 raise QAPISemError(info, "Inclusion loop for %s" % include)
340 inf = inf['parent']
341
342 # skip multiple include of the same file
343 if incl_abs_fname in previously_included:
344 return
345 try:
346 fobj = open(incl_abs_fname, 'r')
347 except IOError as e:
348 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
349 exprs_include = QAPISchemaParser(fobj, previously_included, info)
350 self.exprs.extend(exprs_include.exprs)
351 self.docs.extend(exprs_include.docs)
352
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100353 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100354 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100355 if name == 'doc-required':
356 if not isinstance(value, bool):
357 raise QAPISemError(info,
358 "Pragma 'doc-required' must be boolean")
359 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100360 elif name == 'returns-whitelist':
361 if (not isinstance(value, list)
362 or any([not isinstance(elt, str) for elt in value])):
363 raise QAPISemError(info,
364 "Pragma returns-whitelist must be"
365 " a list of strings")
366 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100367 elif name == 'name-case-whitelist':
368 if (not isinstance(value, list)
369 or any([not isinstance(elt, str) for elt in value])):
370 raise QAPISemError(info,
371 "Pragma name-case-whitelist must be"
372 " a list of strings")
373 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100374 else:
375 raise QAPISemError(info, "Unknown pragma '%s'" % name)
376
Marc-André Lureau3313b612017-01-13 15:41:29 +0100377 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200378 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200379 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200380 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200381 self.cursor += 1
382 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500383
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200384 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100385 if self.src[self.cursor] == '#':
386 # Start of doc comment
387 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200388 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100389 if not skip_comment:
390 self.val = self.src[self.pos:self.cursor]
391 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100392 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200393 return
394 elif self.tok == "'":
395 string = ''
396 esc = False
397 while True:
398 ch = self.src[self.cursor]
399 self.cursor += 1
400 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100401 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200402 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600403 if ch == 'b':
404 string += '\b'
405 elif ch == 'f':
406 string += '\f'
407 elif ch == 'n':
408 string += '\n'
409 elif ch == 'r':
410 string += '\r'
411 elif ch == 't':
412 string += '\t'
413 elif ch == 'u':
414 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600415 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600416 ch = self.src[self.cursor]
417 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100418 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100419 raise QAPIParseError(self,
420 '\\u escape needs 4 '
421 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600422 value = (value << 4) + int(ch, 16)
423 # If Python 2 and 3 didn't disagree so much on
424 # how to handle Unicode, then we could allow
425 # Unicode string defaults. But most of QAPI is
426 # ASCII-only, so we aren't losing much for now.
427 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100428 raise QAPIParseError(self,
429 'For now, \\u escape '
430 'only supports non-zero '
431 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600432 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100433 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600434 string += ch
435 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100436 raise QAPIParseError(self,
437 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200438 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100439 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200440 esc = True
441 elif ch == "'":
442 self.val = string
443 return
444 else:
445 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100446 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200447 self.val = True
448 self.cursor += 3
449 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100450 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200451 self.val = False
452 self.cursor += 4
453 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100454 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200455 self.val = None
456 self.cursor += 3
457 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200458 elif self.tok == '\n':
459 if self.cursor == len(self.src):
460 self.tok = None
461 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800462 self.line += 1
463 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200464 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100465 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500466
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200467 def get_members(self):
468 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200469 if self.tok == '}':
470 self.accept()
471 return expr
472 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100473 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200474 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200475 key = self.val
476 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200477 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100478 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200479 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800480 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100481 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200482 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200483 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200484 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200485 return expr
486 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100487 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200488 self.accept()
489 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100490 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500491
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200492 def get_values(self):
493 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200494 if self.tok == ']':
495 self.accept()
496 return expr
Eric Blake437db252015-09-29 16:21:02 -0600497 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100498 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
499 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200500 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200501 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200502 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200503 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200504 return expr
505 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100506 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200507 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500508
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200509 def get_expr(self, nested):
510 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100511 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200512 if self.tok == '{':
513 self.accept()
514 expr = self.get_members()
515 elif self.tok == '[':
516 self.accept()
517 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600518 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200519 expr = self.val
520 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200521 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100522 raise QAPIParseError(self, 'Expected "{", "[" or string')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200523 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200524
Marc-André Lureau3313b612017-01-13 15:41:29 +0100525 def get_doc(self, info):
526 if self.val != '##':
527 raise QAPIParseError(self, "Junk after '##' at start of "
528 "documentation comment")
529
530 doc = QAPIDoc(self, info)
531 self.accept(False)
532 while self.tok == '#':
533 if self.val.startswith('##'):
534 # End of doc comment
535 if self.val != '##':
536 raise QAPIParseError(self, "Junk after '##' at end of "
537 "documentation comment")
Markus Armbruster4ea71482017-03-15 13:57:23 +0100538 doc.end_comment()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100539 self.accept()
540 return doc
541 else:
542 doc.append(self.val)
543 self.accept(False)
544
545 raise QAPIParseError(self, "Documentation comment must end with '##'")
546
547
Markus Armbruster00e4b282015-06-10 10:04:36 +0200548#
549# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200550# TODO fold into QAPISchema
551# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200552#
553
Eric Blake437db252015-09-29 16:21:02 -0600554
Eric Blake14f00c62016-03-03 09:16:43 -0700555def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600556 if isinstance(base, dict):
557 return base
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800558 base_struct_define = find_struct(base)
559 if not base_struct_define:
560 return None
561 return base_struct_define['data']
562
Eric Blake437db252015-09-29 16:21:02 -0600563
Eric Blake811d04f2015-05-04 09:05:10 -0600564# Return the qtype of an alternate branch, or None on error.
565def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600566 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600567 return builtin_types[qapi_type]
568 elif find_struct(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100569 return 'QTYPE_QDICT'
Markus Armbruster5f018442017-03-15 13:57:31 +0100570 elif qapi_type in enum_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100571 return 'QTYPE_QSTRING'
Eric Blake811d04f2015-05-04 09:05:10 -0600572 elif find_union(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100573 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600574 return None
575
Eric Blake437db252015-09-29 16:21:02 -0600576
Wenchao Xiabceae762014-03-06 17:08:56 -0800577# Return the discriminator enum define if discriminator is specified as an
578# enum type, otherwise return None.
579def discriminator_find_enum_define(expr):
580 base = expr.get('base')
581 discriminator = expr.get('discriminator')
582
583 if not (discriminator and base):
584 return None
585
Eric Blake14f00c62016-03-03 09:16:43 -0700586 base_members = find_base_members(base)
587 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800588 return None
589
Eric Blake14f00c62016-03-03 09:16:43 -0700590 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800591 if not discriminator_type:
592 return None
593
Markus Armbruster5f018442017-03-15 13:57:31 +0100594 return enum_types.get(discriminator_type)
Wenchao Xiabceae762014-03-06 17:08:56 -0800595
Eric Blake437db252015-09-29 16:21:02 -0600596
Eric Blake59a92fe2015-11-18 01:52:56 -0700597# Names must be letters, numbers, -, and _. They must start with letter,
598# except for downstream extensions which must start with __RFQDN_.
599# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100600valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700601 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600602
603
Marc-André Lureau4148c292017-01-13 15:41:25 +0100604def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600605 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600606 global valid_name
607 membername = name
608
609 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100610 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600611 if name.startswith('*'):
612 membername = name[1:]
613 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100614 raise QAPISemError(info, "%s does not allow optional name '%s'"
615 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600616 # Enum members can start with a digit, because the generated C
617 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700618 if enum_member and membername[0].isdigit():
619 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600620 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
621 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600622 if not valid_name.match(membername) or \
623 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100624 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600625
Eric Blake437db252015-09-29 16:21:02 -0600626
627def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200628 global all_names
629 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200630 # FIXME should reject names that differ only in '_' vs. '.'
631 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200632 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100633 raise QAPISemError(info, "%s '%s' is already defined"
634 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600635 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100636 raise QAPISemError(info, "%s '%s' should not end in '%s'"
637 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200638 all_names[name] = meta
639
Eric Blake437db252015-09-29 16:21:02 -0600640
Markus Armbruster00e4b282015-06-10 10:04:36 +0200641def add_struct(definition, info):
642 global struct_types
Markus Armbruster00e4b282015-06-10 10:04:36 +0200643 struct_types.append(definition)
644
Eric Blake437db252015-09-29 16:21:02 -0600645
Markus Armbruster00e4b282015-06-10 10:04:36 +0200646def find_struct(name):
647 global struct_types
648 for struct in struct_types:
649 if struct['struct'] == name:
650 return struct
651 return None
652
Eric Blake437db252015-09-29 16:21:02 -0600653
Markus Armbruster00e4b282015-06-10 10:04:36 +0200654def add_union(definition, info):
655 global union_types
Markus Armbruster00e4b282015-06-10 10:04:36 +0200656 union_types.append(definition)
657
Eric Blake437db252015-09-29 16:21:02 -0600658
Markus Armbruster00e4b282015-06-10 10:04:36 +0200659def find_union(name):
660 global union_types
661 for union in union_types:
662 if union['union'] == name:
663 return union
664 return None
665
Eric Blake437db252015-09-29 16:21:02 -0600666
Marc-André Lureau4148c292017-01-13 15:41:25 +0100667def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600668 allow_dict=False, allow_optional=False,
669 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600670 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600671
672 if value is None:
673 return
674
Eric Blakedd883c62015-05-04 09:05:21 -0600675 # Check if array type for value is okay
676 if isinstance(value, list):
677 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100678 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600679 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100680 raise QAPISemError(info,
681 "%s: array type must contain single type name" %
682 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600683 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600684
685 # Check if type name for value is okay
686 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600687 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100688 raise QAPISemError(info, "%s uses unknown type '%s'"
689 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600690 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100691 raise QAPISemError(info, "%s cannot use %s type '%s'" %
692 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600693 return
694
Eric Blakedd883c62015-05-04 09:05:21 -0600695 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100696 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200697
698 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100699 raise QAPISemError(info,
700 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200701
702 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600703 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100704 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600705 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600706 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100707 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
708 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600709 # Todo: allow dictionaries to represent default values of
710 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100711 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200712 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600713 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600714 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600715
Eric Blake437db252015-09-29 16:21:02 -0600716
Marc-André Lureau4148c292017-01-13 15:41:25 +0100717def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600718 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600719 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600720
Eric Blakec8184082016-07-13 21:50:20 -0600721 args_meta = ['struct']
722 if boxed:
723 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100724 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600725 expr.get('data'), allow_dict=not boxed, allow_optional=True,
726 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600727 returns_meta = ['union', 'struct']
728 if name in returns_whitelist:
729 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100730 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200731 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200732 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600733
Eric Blake437db252015-09-29 16:21:02 -0600734
Marc-André Lureau4148c292017-01-13 15:41:25 +0100735def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600736 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600737 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600738
Eric Blakec8184082016-07-13 21:50:20 -0600739 meta = ['struct']
740 if boxed:
741 meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100742 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600743 expr.get('data'), allow_dict=not boxed, allow_optional=True,
744 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200745
Eric Blake437db252015-09-29 16:21:02 -0600746
Marc-André Lureau4148c292017-01-13 15:41:25 +0100747def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800748 name = expr['union']
749 base = expr.get('base')
750 discriminator = expr.get('discriminator')
751 members = expr['data']
752
Eric Blake811d04f2015-05-04 09:05:10 -0600753 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600754
755 # With no discriminator it is a simple union.
756 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600757 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600758 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600759 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100760 raise QAPISemError(info, "Simple union '%s' must not have a base" %
761 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600762
763 # Else, it's a flat union.
764 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600765 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100766 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600767 base, allow_dict=True, allow_optional=True,
768 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600769 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100770 raise QAPISemError(info, "Flat union '%s' must have a base"
771 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700772 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100773 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800774
Eric Blakec9e0a792015-05-04 09:05:22 -0600775 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600776 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100777 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600778 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700779 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800780 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100781 raise QAPISemError(info,
782 "Discriminator '%s' is not a member of base "
783 "struct '%s'"
784 % (discriminator, base))
Markus Armbruster5f018442017-03-15 13:57:31 +0100785 enum_define = enum_types.get(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600786 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800787 # Do not allow string discriminator
788 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100789 raise QAPISemError(info,
790 "Discriminator '%s' must be of enumeration "
791 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800792
Eric Blake02a57ae2016-02-17 23:48:16 -0700793 # Check every branch; don't allow an empty union
794 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100795 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800796 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100797 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600798
Eric Blake01cfbaa2015-12-01 22:20:58 -0700799 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100800 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200801 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600802
Eric Blake44bd1272015-05-04 09:05:08 -0600803 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700804 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600805 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100806 if key not in enum_define['data']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100807 raise QAPISemError(info,
808 "Discriminator value '%s' is not found in "
809 "enum '%s'"
Markus Armbrustereda43c62017-03-15 13:57:29 +0100810 % (key, enum_define['enum']))
Eric Blake44bd1272015-05-04 09:05:08 -0600811
Eric Blaked0b18232016-07-13 21:50:13 -0600812 # If discriminator is user-defined, ensure all values are covered
813 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100814 for value in enum_define['data']:
Eric Blaked0b18232016-07-13 21:50:13 -0600815 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100816 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
817 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600818
Eric Blake437db252015-09-29 16:21:02 -0600819
Marc-André Lureau4148c292017-01-13 15:41:25 +0100820def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600821 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600822 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600823 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600824
Eric Blake02a57ae2016-02-17 23:48:16 -0700825 # Check every branch; require at least two branches
826 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100827 raise QAPISemError(info,
828 "Alternate '%s' should have at least two branches "
829 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600830 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100831 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600832
Eric Blake811d04f2015-05-04 09:05:10 -0600833 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100834 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600835 value,
836 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600837 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700838 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100839 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
840 "type '%s'" % (name, key, value))
Eric Blake811d04f2015-05-04 09:05:10 -0600841 if qtype in types_seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100842 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
843 "be distinguished from member '%s'"
844 % (name, key, types_seen[qtype]))
Eric Blake811d04f2015-05-04 09:05:10 -0600845 types_seen[qtype] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800846
Eric Blake437db252015-09-29 16:21:02 -0600847
Marc-André Lureau4148c292017-01-13 15:41:25 +0100848def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600849 name = expr['enum']
850 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100851 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600852
853 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100854 raise QAPISemError(info,
855 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100856 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100857 raise QAPISemError(info,
858 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600859 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100860 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600861 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600862
Eric Blake437db252015-09-29 16:21:02 -0600863
Marc-André Lureau4148c292017-01-13 15:41:25 +0100864def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600865 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600866 members = expr['data']
867
Marc-André Lureau4148c292017-01-13 15:41:25 +0100868 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600869 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100870 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600871 allow_metas=['struct'])
872
Eric Blake437db252015-09-29 16:21:02 -0600873
Eric Blake0545f6b2015-05-04 09:05:15 -0600874def check_keys(expr_elem, meta, required, optional=[]):
875 expr = expr_elem['expr']
876 info = expr_elem['info']
877 name = expr[meta]
878 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100879 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600880 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600881 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600882 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100883 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
884 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600885 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100886 raise QAPISemError(info,
887 "'%s' of %s '%s' should only use false value"
888 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600889 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100890 raise QAPISemError(info,
891 "'%s' of %s '%s' should only use true value"
892 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600893 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600894 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100895 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
896 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600897
Eric Blake437db252015-09-29 16:21:02 -0600898
Markus Armbruster4d076d62015-06-10 08:55:21 +0200899def check_exprs(exprs):
900 global all_names
901
Markus Armbruster79470162017-03-15 13:57:21 +0100902 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200903 for builtin in builtin_types.keys():
904 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100905
906 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200907 for expr_elem in exprs:
908 expr = expr_elem['expr']
909 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100910 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100911
Markus Armbruster79470162017-03-15 13:57:21 +0100912 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100913 raise QAPISemError(info,
914 "Expression missing documentation comment")
915
Eric Blake437db252015-09-29 16:21:02 -0600916 if 'enum' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100917 meta = 'enum'
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100918 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster5f018442017-03-15 13:57:31 +0100919 enum_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600920 elif 'union' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100921 meta = 'union'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200922 check_keys(expr_elem, 'union', ['data'],
923 ['base', 'discriminator'])
924 add_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600925 elif 'alternate' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100926 meta = 'alternate'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200927 check_keys(expr_elem, 'alternate', ['data'])
Eric Blake437db252015-09-29 16:21:02 -0600928 elif 'struct' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100929 meta = 'struct'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200930 check_keys(expr_elem, 'struct', ['data'], ['base'])
931 add_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600932 elif 'command' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100933 meta = 'command'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200934 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600935 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Eric Blake437db252015-09-29 16:21:02 -0600936 elif 'event' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100937 meta = 'event'
Eric Blakec8184082016-07-13 21:50:20 -0600938 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200939 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100940 raise QAPISemError(expr_elem['info'],
941 "Expression is missing metatype")
Markus Armbruster6f053452017-03-15 13:57:30 +0100942 name = expr[meta]
943 add_name(name, info, meta)
Markus Armbruster79470162017-03-15 13:57:21 +0100944 if doc and doc.symbol != name:
945 raise QAPISemError(info, "Definition of '%s' follows documentation"
946 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200947
948 # Try again for hidden UnionKind enum
949 for expr_elem in exprs:
950 expr = expr_elem['expr']
Markus Armbrustereda43c62017-03-15 13:57:29 +0100951 if 'union' in expr and not discriminator_find_enum_define(expr):
952 name = '%sKind' % expr['union']
Eric Blake437db252015-09-29 16:21:02 -0600953 elif 'alternate' in expr:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100954 name = '%sKind' % expr['alternate']
955 else:
956 continue
Markus Armbruster5f018442017-03-15 13:57:31 +0100957 enum_types[name] = {'enum': name}
Markus Armbruster6f053452017-03-15 13:57:30 +0100958 add_name(name, info, 'enum', implicit=True)
Markus Armbruster4d076d62015-06-10 08:55:21 +0200959
960 # Validate that exprs make sense
961 for expr_elem in exprs:
962 expr = expr_elem['expr']
963 info = expr_elem['info']
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100964 doc = expr_elem.get('doc')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200965
Eric Blake437db252015-09-29 16:21:02 -0600966 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200967 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600968 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200969 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600970 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200971 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600972 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200973 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600974 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200975 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600976 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200977 check_event(expr, info)
978 else:
979 assert False, 'unexpected meta type'
980
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100981 if doc:
982 doc.check_expr(expr)
983
Markus Armbrusterac882192015-09-16 13:06:05 +0200984 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600985
Markus Armbrusterac882192015-09-16 13:06:05 +0200986
987#
988# Schema compiler frontend
989#
990
991class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100992 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +0200993 assert isinstance(name, str)
994 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -0600995 # For explicitly defined entities, info points to the (explicit)
996 # definition. For builtins (and their arrays), info is None.
997 # For implicitly defined entities, info points to a place that
998 # triggered the implicit definition (there may be more than one
999 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +02001000 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001001 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +02001002
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001003 def c_name(self):
1004 return c_name(self.name)
1005
Markus Armbrusterac882192015-09-16 13:06:05 +02001006 def check(self, schema):
1007 pass
1008
Eric Blake49823c42015-10-12 22:22:27 -06001009 def is_implicit(self):
1010 return not self.info
1011
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001012 def visit(self, visitor):
1013 pass
1014
1015
1016class QAPISchemaVisitor(object):
1017 def visit_begin(self, schema):
1018 pass
1019
1020 def visit_end(self):
1021 pass
1022
Eric Blake25a0d9c2015-10-12 22:22:21 -06001023 def visit_needed(self, entity):
1024 # Default to visiting everything
1025 return True
1026
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001027 def visit_builtin_type(self, name, info, json_type):
1028 pass
1029
1030 def visit_enum_type(self, name, info, values, prefix):
1031 pass
1032
1033 def visit_array_type(self, name, info, element_type):
1034 pass
1035
1036 def visit_object_type(self, name, info, base, members, variants):
1037 pass
1038
Markus Armbruster39a18152015-09-16 13:06:28 +02001039 def visit_object_type_flat(self, name, info, members, variants):
1040 pass
1041
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001042 def visit_alternate_type(self, name, info, variants):
1043 pass
1044
1045 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001046 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001047 pass
1048
Eric Blake48825ca2016-07-13 21:50:19 -06001049 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001050 pass
1051
Markus Armbrusterac882192015-09-16 13:06:05 +02001052
1053class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001054 # Return the C type for common use.
1055 # For the types we commonly box, this is a pointer type.
1056 def c_type(self):
1057 pass
1058
1059 # Return the C type to be used in a parameter list.
1060 def c_param_type(self):
1061 return self.c_type()
1062
1063 # Return the C type to be used where we suppress boxing.
1064 def c_unboxed_type(self):
1065 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001066
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001067 def json_type(self):
1068 pass
1069
1070 def alternate_qtype(self):
1071 json2qtype = {
1072 'string': 'QTYPE_QSTRING',
1073 'number': 'QTYPE_QFLOAT',
1074 'int': 'QTYPE_QINT',
1075 'boolean': 'QTYPE_QBOOL',
1076 'object': 'QTYPE_QDICT'
1077 }
1078 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001079
Markus Armbruster691e0312017-03-15 13:57:14 +01001080 def doc_type(self):
1081 if self.is_implicit():
1082 return None
1083 return self.name
1084
Markus Armbrusterac882192015-09-16 13:06:05 +02001085
1086class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001087 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001088 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001089 assert not c_type or isinstance(c_type, str)
1090 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1091 'value')
1092 self._json_type_name = json_type
1093 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001094
1095 def c_name(self):
1096 return self.name
1097
Eric Blake4040d992016-03-17 16:48:28 -06001098 def c_type(self):
1099 return self._c_type_name
1100
1101 def c_param_type(self):
1102 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001103 return 'const ' + self._c_type_name
1104 return self._c_type_name
1105
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001106 def json_type(self):
1107 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001108
Markus Armbruster691e0312017-03-15 13:57:14 +01001109 def doc_type(self):
1110 return self.json_type()
1111
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001112 def visit(self, visitor):
1113 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1114
Markus Armbrusterac882192015-09-16 13:06:05 +02001115
1116class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001117 def __init__(self, name, info, doc, values, prefix):
1118 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001119 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001120 assert isinstance(v, QAPISchemaMember)
1121 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001122 assert prefix is None or isinstance(prefix, str)
1123 self.values = values
1124 self.prefix = prefix
1125
1126 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001127 seen = {}
1128 for v in self.values:
1129 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001130 if self.doc:
1131 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001132
Eric Blake99df5282015-10-12 22:22:32 -06001133 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001134 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1135 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001136
Eric Blake4040d992016-03-17 16:48:28 -06001137 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001138 return c_name(self.name)
1139
Eric Blake93bda4d2015-12-01 22:20:55 -07001140 def member_names(self):
1141 return [v.name for v in self.values]
1142
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001143 def json_type(self):
1144 return 'string'
1145
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001146 def visit(self, visitor):
1147 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001148 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001149
Markus Armbrusterac882192015-09-16 13:06:05 +02001150
1151class QAPISchemaArrayType(QAPISchemaType):
1152 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001153 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001154 assert isinstance(element_type, str)
1155 self._element_type_name = element_type
1156 self.element_type = None
1157
1158 def check(self, schema):
1159 self.element_type = schema.lookup_type(self._element_type_name)
1160 assert self.element_type
1161
Eric Blake99df5282015-10-12 22:22:32 -06001162 def is_implicit(self):
1163 return True
1164
Eric Blake4040d992016-03-17 16:48:28 -06001165 def c_type(self):
1166 return c_name(self.name) + pointer_suffix
1167
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001168 def json_type(self):
1169 return 'array'
1170
Markus Armbruster691e0312017-03-15 13:57:14 +01001171 def doc_type(self):
1172 elt_doc_type = self.element_type.doc_type()
1173 if not elt_doc_type:
1174 return None
1175 return 'array of ' + elt_doc_type
1176
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001177 def visit(self, visitor):
1178 visitor.visit_array_type(self.name, self.info, self.element_type)
1179
Markus Armbrusterac882192015-09-16 13:06:05 +02001180
1181class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001182 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001183 # struct has local_members, optional base, and no variants
1184 # flat union has base, variants, and no local_members
1185 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001186 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001187 assert base is None or isinstance(base, str)
1188 for m in local_members:
1189 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001190 m.set_owner(name)
1191 if variants is not None:
1192 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1193 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001194 self._base_name = base
1195 self.base = None
1196 self.local_members = local_members
1197 self.variants = variants
1198 self.members = None
1199
1200 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001201 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001202 raise QAPISemError(self.info,
1203 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001204 if self.members:
1205 return
1206 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001207 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001208 if self._base_name:
1209 self.base = schema.lookup_type(self._base_name)
1210 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001211 self.base.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001212 self.base.check_clash(schema, self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001213 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001214 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001215 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001216 if self.doc:
1217 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001218 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001219 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001220 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001221 assert self.variants.tag_member in self.members
Eric Blake27b60ab2015-11-18 01:52:51 -07001222 self.variants.check_clash(schema, self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001223 if self.doc:
1224 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001225
Eric Blake14f00c62016-03-03 09:16:43 -07001226 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001227 # and update seen to track the members seen so far. Report any errors
1228 # on behalf of info, which is not necessarily self.info
1229 def check_clash(self, schema, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001230 assert not self.variants # not implemented
1231 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001232 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001233
Eric Blake99df5282015-10-12 22:22:32 -06001234 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001235 # See QAPISchema._make_implicit_object_type(), as well as
1236 # _def_predefineds()
1237 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001238
Eric Blakeb6167702016-07-13 21:50:16 -06001239 def is_empty(self):
1240 assert self.members is not None
1241 return not self.members and not self.variants
1242
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001243 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001244 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001245 return QAPISchemaType.c_name(self)
1246
Eric Blake4040d992016-03-17 16:48:28 -06001247 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001248 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001249 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001250
Eric Blake4040d992016-03-17 16:48:28 -06001251 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001252 return c_name(self.name)
1253
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001254 def json_type(self):
1255 return 'object'
1256
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001257 def visit(self, visitor):
1258 visitor.visit_object_type(self.name, self.info,
1259 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001260 visitor.visit_object_type_flat(self.name, self.info,
1261 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001262
Markus Armbrusterac882192015-09-16 13:06:05 +02001263
Eric Blaked44f9ac2015-12-01 22:20:54 -07001264class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001265 role = 'member'
1266
Eric Blaked44f9ac2015-12-01 22:20:54 -07001267 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001268 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001269 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001270 self.owner = None
1271
1272 def set_owner(self, name):
1273 assert not self.owner
1274 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001275
Eric Blake27b60ab2015-11-18 01:52:51 -07001276 def check_clash(self, info, seen):
1277 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001278 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001279 raise QAPISemError(info,
1280 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001281 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001282 raise QAPISemError(info, "%s collides with %s" %
1283 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001284 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001285
Eric Blake88d4ef82015-11-18 01:52:50 -07001286 def _pretty_owner(self):
1287 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001288 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001289 # See QAPISchema._make_implicit_object_type() - reverse the
1290 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001291 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001292 if owner.endswith('-arg'):
1293 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001294 elif owner.endswith('-base'):
1295 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001296 else:
1297 assert owner.endswith('-wrapper')
1298 # Unreachable and not implemented
1299 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001300 if owner.endswith('Kind'):
1301 # See QAPISchema._make_implicit_enum_type()
1302 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001303 return '(%s of %s)' % (self.role, owner)
1304
1305 def describe(self):
1306 return "'%s' %s" % (self.name, self._pretty_owner())
1307
Markus Armbrusterac882192015-09-16 13:06:05 +02001308
Eric Blaked44f9ac2015-12-01 22:20:54 -07001309class QAPISchemaObjectTypeMember(QAPISchemaMember):
1310 def __init__(self, name, typ, optional):
1311 QAPISchemaMember.__init__(self, name)
1312 assert isinstance(typ, str)
1313 assert isinstance(optional, bool)
1314 self._type_name = typ
1315 self.type = None
1316 self.optional = optional
1317
1318 def check(self, schema):
1319 assert self.owner
1320 self.type = schema.lookup_type(self._type_name)
1321 assert self.type
1322
1323
Markus Armbrusterac882192015-09-16 13:06:05 +02001324class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001325 def __init__(self, tag_name, tag_member, variants):
1326 # Flat unions pass tag_name but not tag_member.
1327 # Simple unions and alternates pass tag_member but not tag_name.
1328 # After check(), tag_member is always set, and tag_name remains
1329 # a reliable witness of being used by a flat union.
1330 assert bool(tag_member) != bool(tag_name)
1331 assert (isinstance(tag_name, str) or
1332 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001333 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001334 for v in variants:
1335 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001336 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001337 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001338 self.variants = variants
1339
Eric Blake88d4ef82015-11-18 01:52:50 -07001340 def set_owner(self, name):
1341 for v in self.variants:
1342 v.set_owner(name)
1343
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001344 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001345 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001346 self.tag_member = seen[c_name(self._tag_name)]
1347 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001348 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1349 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001350 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001351 # Union names must match enum values; alternate names are
1352 # checked separately. Use 'seen' to tell the two apart.
1353 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001354 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001355 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001356 v.type.check(schema)
1357
Eric Blake27b60ab2015-11-18 01:52:51 -07001358 def check_clash(self, schema, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001359 for v in self.variants:
1360 # Reset seen map for each variant, since qapi names from one
1361 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001362 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blake27b60ab2015-11-18 01:52:51 -07001363 v.type.check_clash(schema, info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001364
Eric Blake437db252015-09-29 16:21:02 -06001365
Markus Armbrusterac882192015-09-16 13:06:05 +02001366class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001367 role = 'branch'
1368
Markus Armbrusterac882192015-09-16 13:06:05 +02001369 def __init__(self, name, typ):
1370 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1371
Markus Armbrusterac882192015-09-16 13:06:05 +02001372
1373class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001374 def __init__(self, name, info, doc, variants):
1375 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001376 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001377 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001378 variants.set_owner(name)
1379 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001380 self.variants = variants
1381
1382 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001383 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001384 # Not calling self.variants.check_clash(), because there's nothing
1385 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001386 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001387 # Alternate branch names have no relation to the tag enum values;
1388 # so we have to check for potential name collisions ourselves.
1389 seen = {}
1390 for v in self.variants.variants:
1391 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001392 if self.doc:
1393 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001394 if self.doc:
1395 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001396
Eric Blake4040d992016-03-17 16:48:28 -06001397 def c_type(self):
1398 return c_name(self.name) + pointer_suffix
1399
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001400 def json_type(self):
1401 return 'value'
1402
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001403 def visit(self, visitor):
1404 visitor.visit_alternate_type(self.name, self.info, self.variants)
1405
Eric Blakec8184082016-07-13 21:50:20 -06001406 def is_empty(self):
1407 return False
1408
Markus Armbrusterac882192015-09-16 13:06:05 +02001409
1410class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001411 def __init__(self, name, info, doc, arg_type, ret_type,
1412 gen, success_response, boxed):
1413 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001414 assert not arg_type or isinstance(arg_type, str)
1415 assert not ret_type or isinstance(ret_type, str)
1416 self._arg_type_name = arg_type
1417 self.arg_type = None
1418 self._ret_type_name = ret_type
1419 self.ret_type = None
1420 self.gen = gen
1421 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001422 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001423
1424 def check(self, schema):
1425 if self._arg_type_name:
1426 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001427 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1428 isinstance(self.arg_type, QAPISchemaAlternateType))
1429 self.arg_type.check(schema)
1430 if self.boxed:
1431 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001432 raise QAPISemError(self.info,
1433 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001434 else:
1435 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1436 assert not self.arg_type.variants
1437 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001438 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001439 if self._ret_type_name:
1440 self.ret_type = schema.lookup_type(self._ret_type_name)
1441 assert isinstance(self.ret_type, QAPISchemaType)
1442
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001443 def visit(self, visitor):
1444 visitor.visit_command(self.name, self.info,
1445 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001446 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001447
Markus Armbrusterac882192015-09-16 13:06:05 +02001448
1449class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001450 def __init__(self, name, info, doc, arg_type, boxed):
1451 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001452 assert not arg_type or isinstance(arg_type, str)
1453 self._arg_type_name = arg_type
1454 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001455 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001456
1457 def check(self, schema):
1458 if self._arg_type_name:
1459 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001460 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1461 isinstance(self.arg_type, QAPISchemaAlternateType))
1462 self.arg_type.check(schema)
1463 if self.boxed:
1464 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001465 raise QAPISemError(self.info,
1466 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001467 else:
1468 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1469 assert not self.arg_type.variants
1470 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001471 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001472
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001473 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001474 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001475
Markus Armbrusterac882192015-09-16 13:06:05 +02001476
1477class QAPISchema(object):
1478 def __init__(self, fname):
1479 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001480 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001481 self.exprs = check_exprs(parser.exprs)
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001482 self.docs = parser.docs
Eric Blake7618b912015-10-12 22:22:22 -06001483 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001484 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001485 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001486 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001487 self._def_exprs()
1488 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001489 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001490 print >>sys.stderr, err
1491 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001492
Markus Armbrusterac882192015-09-16 13:06:05 +02001493 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001494 # Only the predefined types are allowed to not have info
1495 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001496 assert ent.name not in self._entity_dict
1497 self._entity_dict[ent.name] = ent
1498
1499 def lookup_entity(self, name, typ=None):
1500 ent = self._entity_dict.get(name)
1501 if typ and not isinstance(ent, typ):
1502 return None
1503 return ent
1504
1505 def lookup_type(self, name):
1506 return self.lookup_entity(name, QAPISchemaType)
1507
Eric Blake861877a2016-03-17 16:48:36 -06001508 def _def_builtin_type(self, name, json_type, c_type):
1509 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001510 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1511 # qapi-types.h from a single .c, all arrays of builtins must be
1512 # declared in the first file whether or not they are used. Nicer
1513 # would be to use lazy instantiation, while figuring out how to
1514 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001515 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001516
1517 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001518 for t in [('str', 'string', 'char' + pointer_suffix),
1519 ('number', 'number', 'double'),
1520 ('int', 'int', 'int64_t'),
1521 ('int8', 'int', 'int8_t'),
1522 ('int16', 'int', 'int16_t'),
1523 ('int32', 'int', 'int32_t'),
1524 ('int64', 'int', 'int64_t'),
1525 ('uint8', 'int', 'uint8_t'),
1526 ('uint16', 'int', 'uint16_t'),
1527 ('uint32', 'int', 'uint32_t'),
1528 ('uint64', 'int', 'uint64_t'),
1529 ('size', 'int', 'uint64_t'),
1530 ('bool', 'boolean', 'bool'),
1531 ('any', 'value', 'QObject' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001532 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001533 self.the_empty_object_type = QAPISchemaObjectType(
1534 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001535 self._def_entity(self.the_empty_object_type)
Eric Blake93bda4d2015-12-01 22:20:55 -07001536 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1537 'qstring', 'qdict', 'qlist',
1538 'qfloat', 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001539 self._def_entity(QAPISchemaEnumType('QType', None, None,
1540 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001541
Eric Blake93bda4d2015-12-01 22:20:55 -07001542 def _make_enum_members(self, values):
1543 return [QAPISchemaMember(v) for v in values]
1544
Eric Blake99df5282015-10-12 22:22:32 -06001545 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001546 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001547 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001548 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001549 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001550 return name
1551
Eric Blake99df5282015-10-12 22:22:32 -06001552 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001553 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001554 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001555 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001556 return name
1557
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001558 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001559 if not members:
1560 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001561 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001562 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001563 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001564 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001565 members, None))
1566 return name
1567
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001568 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001569 name = expr['enum']
1570 data = expr['data']
1571 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001572 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001573 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001574
Eric Blake99df5282015-10-12 22:22:32 -06001575 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001576 optional = False
1577 if name.startswith('*'):
1578 name = name[1:]
1579 optional = True
1580 if isinstance(typ, list):
1581 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001582 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001583 return QAPISchemaObjectTypeMember(name, typ, optional)
1584
Eric Blake99df5282015-10-12 22:22:32 -06001585 def _make_members(self, data, info):
1586 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001587 for (key, value) in data.iteritems()]
1588
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001589 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001590 name = expr['struct']
1591 base = expr.get('base')
1592 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001593 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001594 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001595 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001596
1597 def _make_variant(self, case, typ):
1598 return QAPISchemaObjectTypeVariant(case, typ)
1599
Eric Blake99df5282015-10-12 22:22:32 -06001600 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001601 if isinstance(typ, list):
1602 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001603 typ = self._make_array_type(typ[0], info)
1604 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001605 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001606 return QAPISchemaObjectTypeVariant(case, typ)
1607
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001608 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001609 name = expr['union']
1610 data = expr['data']
1611 base = expr.get('base')
1612 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001613 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001614 if isinstance(base, dict):
1615 base = (self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001616 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001617 if tag_name:
1618 variants = [self._make_variant(key, value)
1619 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001620 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001621 else:
Eric Blake99df5282015-10-12 22:22:32 -06001622 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001623 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001624 typ = self._make_implicit_enum_type(name, info,
1625 [v.name for v in variants])
1626 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001627 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001628 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001629 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001630 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001631 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001632 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001633
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001634 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001635 name = expr['alternate']
1636 data = expr['data']
1637 variants = [self._make_variant(key, value)
1638 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001639 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001640 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001641 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001642 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001643 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001644 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001645
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001646 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001647 name = expr['command']
1648 data = expr.get('data')
1649 rets = expr.get('returns')
1650 gen = expr.get('gen', True)
1651 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001652 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001653 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001654 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001655 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001656 if isinstance(rets, list):
1657 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001658 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001659 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1660 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001661
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001662 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001663 name = expr['event']
1664 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001665 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001666 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001667 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001668 name, info, doc, 'arg', self._make_members(data, info))
1669 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001670
1671 def _def_exprs(self):
1672 for expr_elem in self.exprs:
1673 expr = expr_elem['expr']
1674 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001675 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001676 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001677 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001678 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001679 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001680 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001681 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001682 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001683 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001684 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001685 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001686 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001687 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001688 else:
1689 assert False
1690
1691 def check(self):
1692 for ent in self._entity_dict.values():
1693 ent.check(self)
1694
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001695 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001696 visitor.visit_begin(self)
1697 for (name, entity) in sorted(self._entity_dict.items()):
1698 if visitor.visit_needed(entity):
1699 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001700 visitor.visit_end()
1701
Markus Armbruster2caba362013-07-27 17:41:56 +02001702
Markus Armbruster00e4b282015-06-10 10:04:36 +02001703#
1704# Code generation helpers
1705#
1706
Michael Roth0f923be2011-07-19 14:50:39 -05001707def camel_case(name):
1708 new_name = ''
1709 first = True
1710 for ch in name:
1711 if ch in ['_', '-']:
1712 first = True
1713 elif first:
1714 new_name += ch.upper()
1715 first = False
1716 else:
1717 new_name += ch.lower()
1718 return new_name
1719
Eric Blake437db252015-09-29 16:21:02 -06001720
Markus Armbruster849bc532015-05-14 06:50:53 -06001721# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1722# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1723# ENUM24_Name -> ENUM24_NAME
1724def camel_to_upper(value):
1725 c_fun_str = c_name(value, False)
1726 if value.isupper():
1727 return c_fun_str
1728
1729 new_name = ''
1730 l = len(c_fun_str)
1731 for i in range(l):
1732 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001733 # When c is upper and no '_' appears before, do more checks
1734 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001735 if i < l - 1 and c_fun_str[i + 1].islower():
1736 new_name += '_'
1737 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001738 new_name += '_'
1739 new_name += c
1740 return new_name.lstrip('_').upper()
1741
Eric Blake437db252015-09-29 16:21:02 -06001742
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001743def c_enum_const(type_name, const_name, prefix=None):
1744 if prefix is not None:
1745 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001746 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001747
Eric Blake18df5152015-05-14 06:50:48 -06001748c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001749
Eric Blake437db252015-09-29 16:21:02 -06001750
Eric Blakec6405b52015-05-14 06:50:55 -06001751# Map @name to a valid C identifier.
1752# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001753# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001754#
1755# Used for converting 'name' from a 'name':'type' qapi definition
1756# into a generated struct member, as well as converting type names
1757# into substrings of a generated C function name.
1758# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1759# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001760def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001761 # ANSI X3J11/88-090, 3.1.1
1762 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001763 'default', 'do', 'double', 'else', 'enum', 'extern',
1764 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1765 'return', 'short', 'signed', 'sizeof', 'static',
1766 'struct', 'switch', 'typedef', 'union', 'unsigned',
1767 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001768 # ISO/IEC 9899:1999, 6.4.1
1769 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1770 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001771 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1772 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001773 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1774 # excluding _.*
1775 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001776 # C++ ISO/IEC 14882:2003 2.11
1777 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1778 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1779 'namespace', 'new', 'operator', 'private', 'protected',
1780 'public', 'reinterpret_cast', 'static_cast', 'template',
1781 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1782 'using', 'virtual', 'wchar_t',
1783 # alternative representations
1784 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1785 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001786 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001787 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001788 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001789 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1790 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001791 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001792 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001793
Amos Kong05dfb262014-06-10 19:25:53 +08001794eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001795pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001796
Eric Blake437db252015-09-29 16:21:02 -06001797
Michael Roth0f923be2011-07-19 14:50:39 -05001798def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001799 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001800 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001801 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001802 return ret
1803
1804indent_level = 0
1805
Eric Blake437db252015-09-29 16:21:02 -06001806
Michael Roth0f923be2011-07-19 14:50:39 -05001807def push_indent(indent_amount=4):
1808 global indent_level
1809 indent_level += indent_amount
1810
Eric Blake437db252015-09-29 16:21:02 -06001811
Michael Roth0f923be2011-07-19 14:50:39 -05001812def pop_indent(indent_amount=4):
1813 global indent_level
1814 indent_level -= indent_amount
1815
Eric Blake437db252015-09-29 16:21:02 -06001816
Markus Armbruster77e703b2015-06-24 19:27:32 +02001817# Generate @code with @kwds interpolated.
1818# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001819def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001820 raw = code % kwds
1821 if indent_level:
1822 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001823 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001824 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001825 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001826 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001827 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001828
Eric Blake437db252015-09-29 16:21:02 -06001829
Michael Roth0f923be2011-07-19 14:50:39 -05001830def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001831 if code[0] == '\n':
1832 code = code[1:]
1833 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001834
Michael Roth0f923be2011-07-19 14:50:39 -05001835
1836def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001837 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001838
Eric Blake437db252015-09-29 16:21:02 -06001839
Michael Rothc0afa9c2013-05-10 17:46:00 -05001840def guardstart(name):
1841 return mcgen('''
1842
1843#ifndef %(name)s
1844#define %(name)s
1845
1846''',
1847 name=guardname(name))
1848
Eric Blake437db252015-09-29 16:21:02 -06001849
Michael Rothc0afa9c2013-05-10 17:46:00 -05001850def guardend(name):
1851 return mcgen('''
1852
1853#endif /* %(name)s */
1854
1855''',
1856 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001857
Eric Blake437db252015-09-29 16:21:02 -06001858
Markus Armbrustere98859a2015-09-16 13:06:16 +02001859def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001860 ret = mcgen('''
1861
Markus Armbrustere98859a2015-09-16 13:06:16 +02001862const char *const %(c_name)s_lookup[] = {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001863''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001864 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001865 for value in values:
1866 index = c_enum_const(name, value, prefix)
1867 ret += mcgen('''
1868 [%(index)s] = "%(value)s",
1869''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001870 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001871
Eric Blake7fb1cf12015-11-18 01:52:57 -07001872 max_index = c_enum_const(name, '_MAX', prefix)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001873 ret += mcgen('''
1874 [%(max_index)s] = NULL,
1875};
1876''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001877 max_index=max_index)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001878 return ret
1879
Eric Blake437db252015-09-29 16:21:02 -06001880
Markus Armbrustere98859a2015-09-16 13:06:16 +02001881def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001882 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001883 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001884
1885 ret = mcgen('''
1886
1887typedef enum %(c_name)s {
1888''',
1889 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001890
1891 i = 0
1892 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001893 ret += mcgen('''
1894 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001895''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001896 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001897 i=i)
1898 i += 1
1899
Markus Armbrustere98859a2015-09-16 13:06:16 +02001900 ret += mcgen('''
1901} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001902''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001903 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001904
Markus Armbrustere98859a2015-09-16 13:06:16 +02001905 ret += mcgen('''
1906
1907extern const char *const %(c_name)s_lookup[];
1908''',
1909 c_name=c_name(name))
1910 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001911
Eric Blake437db252015-09-29 16:21:02 -06001912
Eric Blake48825ca2016-07-13 21:50:19 -06001913def gen_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001914 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001915 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001916 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001917 ret = ''
1918 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001919 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001920 ret += '%s arg' % arg_type.c_param_type()
1921 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001922 else:
1923 assert not arg_type.variants
1924 for memb in arg_type.members:
1925 ret += sep
1926 sep = ', '
1927 if memb.optional:
1928 ret += 'bool has_%s, ' % c_name(memb.name)
1929 ret += '%s %s' % (memb.type.c_param_type(),
1930 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001931 if extra:
1932 ret += sep + extra
1933 return ret
1934
Eric Blake1f353342015-09-29 16:21:13 -06001935
Markus Armbruster00e4b282015-06-10 10:04:36 +02001936#
1937# Common command line parsing
1938#
1939
Eric Blake437db252015-09-29 16:21:02 -06001940
Markus Armbrusteref801a92017-03-15 13:57:08 +01001941def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001942
1943 try:
1944 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001945 'chp:o:' + extra_options,
1946 ['source', 'header', 'prefix=',
1947 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001948 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001949 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001950 sys.exit(1)
1951
Markus Armbrusteref801a92017-03-15 13:57:08 +01001952 output_dir = ''
1953 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001954 do_c = False
1955 do_h = False
1956 extra_opts = []
1957
1958 for oa in opts:
1959 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001960 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001961 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001962 if match.end() != len(a):
1963 print >>sys.stderr, \
1964 "%s: 'funny character '%s' in argument of --prefix" \
1965 % (sys.argv[0], a[match.end()])
1966 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001967 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001968 elif o in ('-o', '--output-dir'):
1969 output_dir = a + '/'
1970 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001971 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001972 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001973 do_h = True
1974 else:
1975 extra_opts.append(oa)
1976
1977 if not do_c and not do_h:
1978 do_c = True
1979 do_h = True
1980
Markus Armbruster16d80f62015-04-02 13:32:16 +02001981 if len(args) != 1:
1982 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001983 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02001984 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001985
Markus Armbruster54414042015-06-09 16:22:45 +02001986 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001987
Markus Armbruster00e4b282015-06-10 10:04:36 +02001988#
1989# Generate output files with boilerplate
1990#
1991
Eric Blake437db252015-09-29 16:21:02 -06001992
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001993def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1994 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001995 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001996 c_file = output_dir + prefix + c_file
1997 h_file = output_dir + prefix + h_file
1998
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001999 if output_dir:
2000 try:
2001 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01002002 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002003 if e.errno != errno.EEXIST:
2004 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002005
2006 def maybe_open(really, name, opt):
2007 if really:
2008 return open(name, opt)
2009 else:
2010 import StringIO
2011 return StringIO.StringIO()
2012
2013 fdef = maybe_open(do_c, c_file, 'w')
2014 fdecl = maybe_open(do_h, h_file, 'w')
2015
2016 fdef.write(mcgen('''
2017/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2018%(comment)s
2019''',
Eric Blake437db252015-09-29 16:21:02 -06002020 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002021
2022 fdecl.write(mcgen('''
2023/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2024%(comment)s
2025#ifndef %(guard)s
2026#define %(guard)s
2027
2028''',
Eric Blake437db252015-09-29 16:21:02 -06002029 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002030
2031 return (fdef, fdecl)
2032
Eric Blake437db252015-09-29 16:21:02 -06002033
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002034def close_output(fdef, fdecl):
2035 fdecl.write('''
2036#endif
2037''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002038 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002039 fdef.close()