blob: d5a113c7d6840c8d99afc61d2fda473858885df6 [file] [log] [blame]
Michael Roth0f923be2011-07-19 14:50:39 -05001#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
Eric Blakee4083112016-01-29 06:48:41 -07005# Copyright (c) 2013-2016 Red Hat Inc.
Michael Roth0f923be2011-07-19 14:50:39 -05006#
7# Authors:
8# Anthony Liguori <aliguori@us.ibm.com>
Markus Armbrusterc7a3f252013-07-27 17:41:55 +02009# Markus Armbruster <armbru@redhat.com>
Michael Roth0f923be2011-07-19 14:50:39 -050010#
Markus Armbruster678e48a2014-03-01 08:40:34 +010011# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
Michael Roth0f923be2011-07-19 14:50:39 -050013
Lluís Vilanovaa719a272014-05-07 20:46:15 +020014import re
Michael Roth0f923be2011-07-19 14:50:39 -050015from ordereddict import OrderedDict
Markus Armbruster12f8e1b2015-04-02 14:46:39 +020016import errno
Markus Armbruster2114f5a2015-04-02 13:12:21 +020017import getopt
Lluís Vilanova33aaad52014-05-02 15:52:35 +020018import os
Markus Armbruster2caba362013-07-27 17:41:56 +020019import sys
Markus Armbruster47299262015-05-14 06:50:47 -060020import string
Michael Roth0f923be2011-07-19 14:50:39 -050021
Eric Blakeb52c4b92015-05-04 09:05:00 -060022builtin_types = {
Kevin Wolf69dd62d2013-07-08 16:14:21 +020023 'str': 'QTYPE_QSTRING',
24 'int': 'QTYPE_QINT',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
27 'int8': 'QTYPE_QINT',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
Eric Blakecb17f792015-05-04 09:05:01 -060035 'size': 'QTYPE_QINT',
Eric Blake1310a3d2015-12-01 22:20:46 -070036 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070037 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020038}
39
Markus Armbrusterbc52d032017-03-15 13:56:51 +010040# Are documentation comments required?
41doc_required = False
42
Eric Blake10d4d992015-05-04 09:05:23 -060043# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010044returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060045
Eric Blake893e1f22015-12-01 22:20:57 -070046# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010047name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070048
Eric Blake4dc2e692015-05-04 09:05:17 -060049enum_types = []
50struct_types = []
51union_types = []
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'
Eric Blake44bd1272015-05-04 09:05:08 -0600570 elif find_enum(qapi_type):
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
594 return find_enum(discriminator_type)
595
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
643 name = definition['struct']
644 add_name(name, info, 'struct')
645 struct_types.append(definition)
646
Eric Blake437db252015-09-29 16:21:02 -0600647
Markus Armbruster00e4b282015-06-10 10:04:36 +0200648def find_struct(name):
649 global struct_types
650 for struct in struct_types:
651 if struct['struct'] == name:
652 return struct
653 return None
654
Eric Blake437db252015-09-29 16:21:02 -0600655
Markus Armbruster00e4b282015-06-10 10:04:36 +0200656def add_union(definition, info):
657 global union_types
658 name = definition['union']
659 add_name(name, info, 'union')
660 union_types.append(definition)
661
Eric Blake437db252015-09-29 16:21:02 -0600662
Markus Armbruster00e4b282015-06-10 10:04:36 +0200663def find_union(name):
664 global union_types
665 for union in union_types:
666 if union['union'] == name:
667 return union
668 return None
669
Eric Blake437db252015-09-29 16:21:02 -0600670
671def add_enum(name, info, enum_values=None, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200672 global enum_types
673 add_name(name, info, 'enum', implicit)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100674 enum_types.append({'enum_name': name, 'enum_values': enum_values})
Markus Armbruster00e4b282015-06-10 10:04:36 +0200675
Eric Blake437db252015-09-29 16:21:02 -0600676
Markus Armbruster00e4b282015-06-10 10:04:36 +0200677def find_enum(name):
678 global enum_types
679 for enum in enum_types:
680 if enum['enum_name'] == name:
681 return enum
682 return None
683
Markus Armbruster00e4b282015-06-10 10:04:36 +0200684
Eric Blake437db252015-09-29 16:21:02 -0600685def is_enum(name):
686 return find_enum(name) is not None
687
688
Marc-André Lureau4148c292017-01-13 15:41:25 +0100689def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600690 allow_dict=False, allow_optional=False,
691 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600692 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600693
694 if value is None:
695 return
696
Eric Blakedd883c62015-05-04 09:05:21 -0600697 # Check if array type for value is okay
698 if isinstance(value, list):
699 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100700 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600701 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100702 raise QAPISemError(info,
703 "%s: array type must contain single type name" %
704 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600705 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600706
707 # Check if type name for value is okay
708 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600709 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100710 raise QAPISemError(info, "%s uses unknown type '%s'"
711 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600712 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100713 raise QAPISemError(info, "%s cannot use %s type '%s'" %
714 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600715 return
716
Eric Blakedd883c62015-05-04 09:05:21 -0600717 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100718 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200719
720 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100721 raise QAPISemError(info,
722 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200723
724 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600725 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100726 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600727 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600728 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100729 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
730 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600731 # Todo: allow dictionaries to represent default values of
732 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100733 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200734 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600735 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600736 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600737
Eric Blake437db252015-09-29 16:21:02 -0600738
Marc-André Lureau4148c292017-01-13 15:41:25 +0100739def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600740 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600741 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600742
Eric Blakec8184082016-07-13 21:50:20 -0600743 args_meta = ['struct']
744 if boxed:
745 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100746 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600747 expr.get('data'), allow_dict=not boxed, allow_optional=True,
748 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600749 returns_meta = ['union', 'struct']
750 if name in returns_whitelist:
751 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100752 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200753 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200754 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600755
Eric Blake437db252015-09-29 16:21:02 -0600756
Marc-André Lureau4148c292017-01-13 15:41:25 +0100757def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600758 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600759 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600760
Eric Blakec8184082016-07-13 21:50:20 -0600761 meta = ['struct']
762 if boxed:
763 meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100764 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600765 expr.get('data'), allow_dict=not boxed, allow_optional=True,
766 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200767
Eric Blake437db252015-09-29 16:21:02 -0600768
Marc-André Lureau4148c292017-01-13 15:41:25 +0100769def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800770 name = expr['union']
771 base = expr.get('base')
772 discriminator = expr.get('discriminator')
773 members = expr['data']
774
Eric Blake811d04f2015-05-04 09:05:10 -0600775 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600776
777 # With no discriminator it is a simple union.
778 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600779 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600780 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600781 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100782 raise QAPISemError(info, "Simple union '%s' must not have a base" %
783 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600784
785 # Else, it's a flat union.
786 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600787 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100788 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600789 base, allow_dict=True, allow_optional=True,
790 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600791 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100792 raise QAPISemError(info, "Flat union '%s' must have a base"
793 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700794 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100795 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800796
Eric Blakec9e0a792015-05-04 09:05:22 -0600797 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600798 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100799 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600800 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700801 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800802 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100803 raise QAPISemError(info,
804 "Discriminator '%s' is not a member of base "
805 "struct '%s'"
806 % (discriminator, base))
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800807 enum_define = find_enum(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600808 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800809 # Do not allow string discriminator
810 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100811 raise QAPISemError(info,
812 "Discriminator '%s' must be of enumeration "
813 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800814
Eric Blake02a57ae2016-02-17 23:48:16 -0700815 # Check every branch; don't allow an empty union
816 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100817 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800818 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100819 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600820
Eric Blake01cfbaa2015-12-01 22:20:58 -0700821 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100822 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200823 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600824
Eric Blake44bd1272015-05-04 09:05:08 -0600825 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700826 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600827 if enum_define:
Eric Blake437db252015-09-29 16:21:02 -0600828 if key not in enum_define['enum_values']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100829 raise QAPISemError(info,
830 "Discriminator value '%s' is not found in "
831 "enum '%s'"
Markus Armbrusteref801a92017-03-15 13:57:08 +0100832 % (key, enum_define['enum_name']))
Eric Blake44bd1272015-05-04 09:05:08 -0600833
Eric Blaked0b18232016-07-13 21:50:13 -0600834 # If discriminator is user-defined, ensure all values are covered
835 if enum_define:
836 for value in enum_define['enum_values']:
837 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100838 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
839 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600840
Eric Blake437db252015-09-29 16:21:02 -0600841
Marc-André Lureau4148c292017-01-13 15:41:25 +0100842def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600843 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600844 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600845 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600846
Eric Blake02a57ae2016-02-17 23:48:16 -0700847 # Check every branch; require at least two branches
848 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100849 raise QAPISemError(info,
850 "Alternate '%s' should have at least two branches "
851 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600852 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100853 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600854
Eric Blake811d04f2015-05-04 09:05:10 -0600855 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100856 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600857 value,
858 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600859 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700860 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100861 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
862 "type '%s'" % (name, key, value))
Eric Blake811d04f2015-05-04 09:05:10 -0600863 if qtype in types_seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100864 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
865 "be distinguished from member '%s'"
866 % (name, key, types_seen[qtype]))
Eric Blake811d04f2015-05-04 09:05:10 -0600867 types_seen[qtype] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800868
Eric Blake437db252015-09-29 16:21:02 -0600869
Marc-André Lureau4148c292017-01-13 15:41:25 +0100870def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600871 name = expr['enum']
872 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100873 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600874
875 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100876 raise QAPISemError(info,
877 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100878 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100879 raise QAPISemError(info,
880 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600881 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100882 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600883 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600884
Eric Blake437db252015-09-29 16:21:02 -0600885
Marc-André Lureau4148c292017-01-13 15:41:25 +0100886def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600887 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600888 members = expr['data']
889
Marc-André Lureau4148c292017-01-13 15:41:25 +0100890 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600891 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100892 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600893 allow_metas=['struct'])
894
Eric Blake437db252015-09-29 16:21:02 -0600895
Eric Blake0545f6b2015-05-04 09:05:15 -0600896def check_keys(expr_elem, meta, required, optional=[]):
897 expr = expr_elem['expr']
898 info = expr_elem['info']
899 name = expr[meta]
900 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100901 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600902 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600903 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600904 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100905 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
906 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600907 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100908 raise QAPISemError(info,
909 "'%s' of %s '%s' should only use false value"
910 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600911 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100912 raise QAPISemError(info,
913 "'%s' of %s '%s' should only use true value"
914 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600915 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600916 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100917 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
918 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600919
Eric Blake437db252015-09-29 16:21:02 -0600920
Markus Armbruster4d076d62015-06-10 08:55:21 +0200921def check_exprs(exprs):
922 global all_names
923
Markus Armbruster79470162017-03-15 13:57:21 +0100924 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200925 for builtin in builtin_types.keys():
926 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100927
928 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200929 for expr_elem in exprs:
930 expr = expr_elem['expr']
931 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100932 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100933
Markus Armbruster79470162017-03-15 13:57:21 +0100934 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100935 raise QAPISemError(info,
936 "Expression missing documentation comment")
937
Eric Blake437db252015-09-29 16:21:02 -0600938 if 'enum' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100939 name = expr['enum']
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100940 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster79470162017-03-15 13:57:21 +0100941 add_enum(name, info, expr['data'])
Eric Blake437db252015-09-29 16:21:02 -0600942 elif 'union' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100943 name = expr['union']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200944 check_keys(expr_elem, 'union', ['data'],
945 ['base', 'discriminator'])
946 add_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600947 elif 'alternate' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100948 name = expr['alternate']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200949 check_keys(expr_elem, 'alternate', ['data'])
Markus Armbruster79470162017-03-15 13:57:21 +0100950 add_name(name, info, 'alternate')
Eric Blake437db252015-09-29 16:21:02 -0600951 elif 'struct' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100952 name = expr['struct']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200953 check_keys(expr_elem, 'struct', ['data'], ['base'])
954 add_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600955 elif 'command' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100956 name = expr['command']
Markus Armbruster4d076d62015-06-10 08:55:21 +0200957 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600958 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Markus Armbruster79470162017-03-15 13:57:21 +0100959 add_name(name, info, 'command')
Eric Blake437db252015-09-29 16:21:02 -0600960 elif 'event' in expr:
Markus Armbruster79470162017-03-15 13:57:21 +0100961 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600962 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster79470162017-03-15 13:57:21 +0100963 add_name(name, info, 'event')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200964 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100965 raise QAPISemError(expr_elem['info'],
966 "Expression is missing metatype")
Markus Armbruster79470162017-03-15 13:57:21 +0100967 if doc and doc.symbol != name:
968 raise QAPISemError(info, "Definition of '%s' follows documentation"
969 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200970
971 # Try again for hidden UnionKind enum
972 for expr_elem in exprs:
973 expr = expr_elem['expr']
Eric Blake437db252015-09-29 16:21:02 -0600974 if 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200975 if not discriminator_find_enum_define(expr):
976 add_enum('%sKind' % expr['union'], expr_elem['info'],
977 implicit=True)
Eric Blake437db252015-09-29 16:21:02 -0600978 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200979 add_enum('%sKind' % expr['alternate'], expr_elem['info'],
980 implicit=True)
981
982 # Validate that exprs make sense
983 for expr_elem in exprs:
984 expr = expr_elem['expr']
985 info = expr_elem['info']
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100986 doc = expr_elem.get('doc')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200987
Eric Blake437db252015-09-29 16:21:02 -0600988 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200989 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600990 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200991 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600992 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200993 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600994 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200995 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600996 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200997 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600998 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200999 check_event(expr, info)
1000 else:
1001 assert False, 'unexpected meta type'
1002
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001003 if doc:
1004 doc.check_expr(expr)
1005
Markus Armbrusterac882192015-09-16 13:06:05 +02001006 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -06001007
Markus Armbrusterac882192015-09-16 13:06:05 +02001008
1009#
1010# Schema compiler frontend
1011#
1012
1013class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001014 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001015 assert isinstance(name, str)
1016 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -06001017 # For explicitly defined entities, info points to the (explicit)
1018 # definition. For builtins (and their arrays), info is None.
1019 # For implicitly defined entities, info points to a place that
1020 # triggered the implicit definition (there may be more than one
1021 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +02001022 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001023 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +02001024
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001025 def c_name(self):
1026 return c_name(self.name)
1027
Markus Armbrusterac882192015-09-16 13:06:05 +02001028 def check(self, schema):
1029 pass
1030
Eric Blake49823c42015-10-12 22:22:27 -06001031 def is_implicit(self):
1032 return not self.info
1033
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001034 def visit(self, visitor):
1035 pass
1036
1037
1038class QAPISchemaVisitor(object):
1039 def visit_begin(self, schema):
1040 pass
1041
1042 def visit_end(self):
1043 pass
1044
Eric Blake25a0d9c2015-10-12 22:22:21 -06001045 def visit_needed(self, entity):
1046 # Default to visiting everything
1047 return True
1048
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001049 def visit_builtin_type(self, name, info, json_type):
1050 pass
1051
1052 def visit_enum_type(self, name, info, values, prefix):
1053 pass
1054
1055 def visit_array_type(self, name, info, element_type):
1056 pass
1057
1058 def visit_object_type(self, name, info, base, members, variants):
1059 pass
1060
Markus Armbruster39a18152015-09-16 13:06:28 +02001061 def visit_object_type_flat(self, name, info, members, variants):
1062 pass
1063
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001064 def visit_alternate_type(self, name, info, variants):
1065 pass
1066
1067 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001068 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001069 pass
1070
Eric Blake48825ca2016-07-13 21:50:19 -06001071 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001072 pass
1073
Markus Armbrusterac882192015-09-16 13:06:05 +02001074
1075class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001076 # Return the C type for common use.
1077 # For the types we commonly box, this is a pointer type.
1078 def c_type(self):
1079 pass
1080
1081 # Return the C type to be used in a parameter list.
1082 def c_param_type(self):
1083 return self.c_type()
1084
1085 # Return the C type to be used where we suppress boxing.
1086 def c_unboxed_type(self):
1087 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001088
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001089 def json_type(self):
1090 pass
1091
1092 def alternate_qtype(self):
1093 json2qtype = {
1094 'string': 'QTYPE_QSTRING',
1095 'number': 'QTYPE_QFLOAT',
1096 'int': 'QTYPE_QINT',
1097 'boolean': 'QTYPE_QBOOL',
1098 'object': 'QTYPE_QDICT'
1099 }
1100 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001101
Markus Armbruster691e0312017-03-15 13:57:14 +01001102 def doc_type(self):
1103 if self.is_implicit():
1104 return None
1105 return self.name
1106
Markus Armbrusterac882192015-09-16 13:06:05 +02001107
1108class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001109 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001110 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001111 assert not c_type or isinstance(c_type, str)
1112 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1113 'value')
1114 self._json_type_name = json_type
1115 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001116
1117 def c_name(self):
1118 return self.name
1119
Eric Blake4040d992016-03-17 16:48:28 -06001120 def c_type(self):
1121 return self._c_type_name
1122
1123 def c_param_type(self):
1124 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001125 return 'const ' + self._c_type_name
1126 return self._c_type_name
1127
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001128 def json_type(self):
1129 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001130
Markus Armbruster691e0312017-03-15 13:57:14 +01001131 def doc_type(self):
1132 return self.json_type()
1133
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001134 def visit(self, visitor):
1135 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1136
Markus Armbrusterac882192015-09-16 13:06:05 +02001137
1138class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001139 def __init__(self, name, info, doc, values, prefix):
1140 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001141 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001142 assert isinstance(v, QAPISchemaMember)
1143 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001144 assert prefix is None or isinstance(prefix, str)
1145 self.values = values
1146 self.prefix = prefix
1147
1148 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001149 seen = {}
1150 for v in self.values:
1151 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001152 if self.doc:
1153 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001154
Eric Blake99df5282015-10-12 22:22:32 -06001155 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001156 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1157 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001158
Eric Blake4040d992016-03-17 16:48:28 -06001159 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001160 return c_name(self.name)
1161
Eric Blake93bda4d2015-12-01 22:20:55 -07001162 def member_names(self):
1163 return [v.name for v in self.values]
1164
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001165 def json_type(self):
1166 return 'string'
1167
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001168 def visit(self, visitor):
1169 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001170 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001171
Markus Armbrusterac882192015-09-16 13:06:05 +02001172
1173class QAPISchemaArrayType(QAPISchemaType):
1174 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001175 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001176 assert isinstance(element_type, str)
1177 self._element_type_name = element_type
1178 self.element_type = None
1179
1180 def check(self, schema):
1181 self.element_type = schema.lookup_type(self._element_type_name)
1182 assert self.element_type
1183
Eric Blake99df5282015-10-12 22:22:32 -06001184 def is_implicit(self):
1185 return True
1186
Eric Blake4040d992016-03-17 16:48:28 -06001187 def c_type(self):
1188 return c_name(self.name) + pointer_suffix
1189
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001190 def json_type(self):
1191 return 'array'
1192
Markus Armbruster691e0312017-03-15 13:57:14 +01001193 def doc_type(self):
1194 elt_doc_type = self.element_type.doc_type()
1195 if not elt_doc_type:
1196 return None
1197 return 'array of ' + elt_doc_type
1198
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001199 def visit(self, visitor):
1200 visitor.visit_array_type(self.name, self.info, self.element_type)
1201
Markus Armbrusterac882192015-09-16 13:06:05 +02001202
1203class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001204 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001205 # struct has local_members, optional base, and no variants
1206 # flat union has base, variants, and no local_members
1207 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001208 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001209 assert base is None or isinstance(base, str)
1210 for m in local_members:
1211 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001212 m.set_owner(name)
1213 if variants is not None:
1214 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1215 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001216 self._base_name = base
1217 self.base = None
1218 self.local_members = local_members
1219 self.variants = variants
1220 self.members = None
1221
1222 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001223 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001224 raise QAPISemError(self.info,
1225 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001226 if self.members:
1227 return
1228 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001229 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001230 if self._base_name:
1231 self.base = schema.lookup_type(self._base_name)
1232 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001233 self.base.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001234 self.base.check_clash(schema, self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001235 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001236 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001237 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001238 if self.doc:
1239 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001240 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001241 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001242 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001243 assert self.variants.tag_member in self.members
Eric Blake27b60ab2015-11-18 01:52:51 -07001244 self.variants.check_clash(schema, self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001245 if self.doc:
1246 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001247
Eric Blake14f00c62016-03-03 09:16:43 -07001248 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001249 # and update seen to track the members seen so far. Report any errors
1250 # on behalf of info, which is not necessarily self.info
1251 def check_clash(self, schema, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001252 assert not self.variants # not implemented
1253 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001254 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001255
Eric Blake99df5282015-10-12 22:22:32 -06001256 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001257 # See QAPISchema._make_implicit_object_type(), as well as
1258 # _def_predefineds()
1259 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001260
Eric Blakeb6167702016-07-13 21:50:16 -06001261 def is_empty(self):
1262 assert self.members is not None
1263 return not self.members and not self.variants
1264
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001265 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001266 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001267 return QAPISchemaType.c_name(self)
1268
Eric Blake4040d992016-03-17 16:48:28 -06001269 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001270 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001271 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001272
Eric Blake4040d992016-03-17 16:48:28 -06001273 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001274 return c_name(self.name)
1275
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001276 def json_type(self):
1277 return 'object'
1278
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001279 def visit(self, visitor):
1280 visitor.visit_object_type(self.name, self.info,
1281 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001282 visitor.visit_object_type_flat(self.name, self.info,
1283 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001284
Markus Armbrusterac882192015-09-16 13:06:05 +02001285
Eric Blaked44f9ac2015-12-01 22:20:54 -07001286class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001287 role = 'member'
1288
Eric Blaked44f9ac2015-12-01 22:20:54 -07001289 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001290 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001291 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001292 self.owner = None
1293
1294 def set_owner(self, name):
1295 assert not self.owner
1296 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001297
Eric Blake27b60ab2015-11-18 01:52:51 -07001298 def check_clash(self, info, seen):
1299 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001300 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001301 raise QAPISemError(info,
1302 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001303 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001304 raise QAPISemError(info, "%s collides with %s" %
1305 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001306 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001307
Eric Blake88d4ef82015-11-18 01:52:50 -07001308 def _pretty_owner(self):
1309 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001310 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001311 # See QAPISchema._make_implicit_object_type() - reverse the
1312 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001313 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001314 if owner.endswith('-arg'):
1315 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001316 elif owner.endswith('-base'):
1317 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001318 else:
1319 assert owner.endswith('-wrapper')
1320 # Unreachable and not implemented
1321 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001322 if owner.endswith('Kind'):
1323 # See QAPISchema._make_implicit_enum_type()
1324 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001325 return '(%s of %s)' % (self.role, owner)
1326
1327 def describe(self):
1328 return "'%s' %s" % (self.name, self._pretty_owner())
1329
Markus Armbrusterac882192015-09-16 13:06:05 +02001330
Eric Blaked44f9ac2015-12-01 22:20:54 -07001331class QAPISchemaObjectTypeMember(QAPISchemaMember):
1332 def __init__(self, name, typ, optional):
1333 QAPISchemaMember.__init__(self, name)
1334 assert isinstance(typ, str)
1335 assert isinstance(optional, bool)
1336 self._type_name = typ
1337 self.type = None
1338 self.optional = optional
1339
1340 def check(self, schema):
1341 assert self.owner
1342 self.type = schema.lookup_type(self._type_name)
1343 assert self.type
1344
1345
Markus Armbrusterac882192015-09-16 13:06:05 +02001346class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001347 def __init__(self, tag_name, tag_member, variants):
1348 # Flat unions pass tag_name but not tag_member.
1349 # Simple unions and alternates pass tag_member but not tag_name.
1350 # After check(), tag_member is always set, and tag_name remains
1351 # a reliable witness of being used by a flat union.
1352 assert bool(tag_member) != bool(tag_name)
1353 assert (isinstance(tag_name, str) or
1354 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001355 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001356 for v in variants:
1357 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001358 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001359 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001360 self.variants = variants
1361
Eric Blake88d4ef82015-11-18 01:52:50 -07001362 def set_owner(self, name):
1363 for v in self.variants:
1364 v.set_owner(name)
1365
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001366 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001367 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001368 self.tag_member = seen[c_name(self._tag_name)]
1369 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001370 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1371 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001372 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001373 # Union names must match enum values; alternate names are
1374 # checked separately. Use 'seen' to tell the two apart.
1375 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001376 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001377 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001378 v.type.check(schema)
1379
Eric Blake27b60ab2015-11-18 01:52:51 -07001380 def check_clash(self, schema, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001381 for v in self.variants:
1382 # Reset seen map for each variant, since qapi names from one
1383 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001384 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blake27b60ab2015-11-18 01:52:51 -07001385 v.type.check_clash(schema, info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001386
Eric Blake437db252015-09-29 16:21:02 -06001387
Markus Armbrusterac882192015-09-16 13:06:05 +02001388class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001389 role = 'branch'
1390
Markus Armbrusterac882192015-09-16 13:06:05 +02001391 def __init__(self, name, typ):
1392 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1393
Markus Armbrusterac882192015-09-16 13:06:05 +02001394
1395class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001396 def __init__(self, name, info, doc, variants):
1397 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001398 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001399 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001400 variants.set_owner(name)
1401 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001402 self.variants = variants
1403
1404 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001405 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001406 # Not calling self.variants.check_clash(), because there's nothing
1407 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001408 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001409 # Alternate branch names have no relation to the tag enum values;
1410 # so we have to check for potential name collisions ourselves.
1411 seen = {}
1412 for v in self.variants.variants:
1413 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001414 if self.doc:
1415 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001416 if self.doc:
1417 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001418
Eric Blake4040d992016-03-17 16:48:28 -06001419 def c_type(self):
1420 return c_name(self.name) + pointer_suffix
1421
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001422 def json_type(self):
1423 return 'value'
1424
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001425 def visit(self, visitor):
1426 visitor.visit_alternate_type(self.name, self.info, self.variants)
1427
Eric Blakec8184082016-07-13 21:50:20 -06001428 def is_empty(self):
1429 return False
1430
Markus Armbrusterac882192015-09-16 13:06:05 +02001431
1432class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001433 def __init__(self, name, info, doc, arg_type, ret_type,
1434 gen, success_response, boxed):
1435 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001436 assert not arg_type or isinstance(arg_type, str)
1437 assert not ret_type or isinstance(ret_type, str)
1438 self._arg_type_name = arg_type
1439 self.arg_type = None
1440 self._ret_type_name = ret_type
1441 self.ret_type = None
1442 self.gen = gen
1443 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001444 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001445
1446 def check(self, schema):
1447 if self._arg_type_name:
1448 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001449 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1450 isinstance(self.arg_type, QAPISchemaAlternateType))
1451 self.arg_type.check(schema)
1452 if self.boxed:
1453 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001454 raise QAPISemError(self.info,
1455 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001456 else:
1457 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1458 assert not self.arg_type.variants
1459 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001460 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001461 if self._ret_type_name:
1462 self.ret_type = schema.lookup_type(self._ret_type_name)
1463 assert isinstance(self.ret_type, QAPISchemaType)
1464
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001465 def visit(self, visitor):
1466 visitor.visit_command(self.name, self.info,
1467 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001468 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001469
Markus Armbrusterac882192015-09-16 13:06:05 +02001470
1471class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001472 def __init__(self, name, info, doc, arg_type, boxed):
1473 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001474 assert not arg_type or isinstance(arg_type, str)
1475 self._arg_type_name = arg_type
1476 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001477 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001478
1479 def check(self, schema):
1480 if self._arg_type_name:
1481 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001482 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1483 isinstance(self.arg_type, QAPISchemaAlternateType))
1484 self.arg_type.check(schema)
1485 if self.boxed:
1486 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001487 raise QAPISemError(self.info,
1488 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001489 else:
1490 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1491 assert not self.arg_type.variants
1492 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001493 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001494
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001495 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001496 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001497
Markus Armbrusterac882192015-09-16 13:06:05 +02001498
1499class QAPISchema(object):
1500 def __init__(self, fname):
1501 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001502 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001503 self.exprs = check_exprs(parser.exprs)
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001504 self.docs = parser.docs
Eric Blake7618b912015-10-12 22:22:22 -06001505 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001506 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001507 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001508 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001509 self._def_exprs()
1510 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001511 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001512 print >>sys.stderr, err
1513 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001514
Markus Armbrusterac882192015-09-16 13:06:05 +02001515 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001516 # Only the predefined types are allowed to not have info
1517 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001518 assert ent.name not in self._entity_dict
1519 self._entity_dict[ent.name] = ent
1520
1521 def lookup_entity(self, name, typ=None):
1522 ent = self._entity_dict.get(name)
1523 if typ and not isinstance(ent, typ):
1524 return None
1525 return ent
1526
1527 def lookup_type(self, name):
1528 return self.lookup_entity(name, QAPISchemaType)
1529
Eric Blake861877a2016-03-17 16:48:36 -06001530 def _def_builtin_type(self, name, json_type, c_type):
1531 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001532 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1533 # qapi-types.h from a single .c, all arrays of builtins must be
1534 # declared in the first file whether or not they are used. Nicer
1535 # would be to use lazy instantiation, while figuring out how to
1536 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001537 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001538
1539 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001540 for t in [('str', 'string', 'char' + pointer_suffix),
1541 ('number', 'number', 'double'),
1542 ('int', 'int', 'int64_t'),
1543 ('int8', 'int', 'int8_t'),
1544 ('int16', 'int', 'int16_t'),
1545 ('int32', 'int', 'int32_t'),
1546 ('int64', 'int', 'int64_t'),
1547 ('uint8', 'int', 'uint8_t'),
1548 ('uint16', 'int', 'uint16_t'),
1549 ('uint32', 'int', 'uint32_t'),
1550 ('uint64', 'int', 'uint64_t'),
1551 ('size', 'int', 'uint64_t'),
1552 ('bool', 'boolean', 'bool'),
1553 ('any', 'value', 'QObject' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001554 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001555 self.the_empty_object_type = QAPISchemaObjectType(
1556 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001557 self._def_entity(self.the_empty_object_type)
Eric Blake93bda4d2015-12-01 22:20:55 -07001558 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1559 'qstring', 'qdict', 'qlist',
1560 'qfloat', 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001561 self._def_entity(QAPISchemaEnumType('QType', None, None,
1562 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001563
Eric Blake93bda4d2015-12-01 22:20:55 -07001564 def _make_enum_members(self, values):
1565 return [QAPISchemaMember(v) for v in values]
1566
Eric Blake99df5282015-10-12 22:22:32 -06001567 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001568 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001569 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001570 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001571 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001572 return name
1573
Eric Blake99df5282015-10-12 22:22:32 -06001574 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001575 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001576 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001577 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001578 return name
1579
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001580 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001581 if not members:
1582 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001583 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001584 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001585 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001586 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001587 members, None))
1588 return name
1589
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001590 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001591 name = expr['enum']
1592 data = expr['data']
1593 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001594 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001595 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001596
Eric Blake99df5282015-10-12 22:22:32 -06001597 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001598 optional = False
1599 if name.startswith('*'):
1600 name = name[1:]
1601 optional = True
1602 if isinstance(typ, list):
1603 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001604 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001605 return QAPISchemaObjectTypeMember(name, typ, optional)
1606
Eric Blake99df5282015-10-12 22:22:32 -06001607 def _make_members(self, data, info):
1608 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001609 for (key, value) in data.iteritems()]
1610
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001611 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001612 name = expr['struct']
1613 base = expr.get('base')
1614 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001615 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001616 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001617 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001618
1619 def _make_variant(self, case, typ):
1620 return QAPISchemaObjectTypeVariant(case, typ)
1621
Eric Blake99df5282015-10-12 22:22:32 -06001622 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001623 if isinstance(typ, list):
1624 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001625 typ = self._make_array_type(typ[0], info)
1626 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001627 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001628 return QAPISchemaObjectTypeVariant(case, typ)
1629
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001630 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001631 name = expr['union']
1632 data = expr['data']
1633 base = expr.get('base')
1634 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001635 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001636 if isinstance(base, dict):
1637 base = (self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001638 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001639 if tag_name:
1640 variants = [self._make_variant(key, value)
1641 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001642 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001643 else:
Eric Blake99df5282015-10-12 22:22:32 -06001644 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001645 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001646 typ = self._make_implicit_enum_type(name, info,
1647 [v.name for v in variants])
1648 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001649 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001650 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001651 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001652 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001653 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001654 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001655
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001656 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001657 name = expr['alternate']
1658 data = expr['data']
1659 variants = [self._make_variant(key, value)
1660 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001661 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001662 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001663 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001664 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001665 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001666 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001667
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001668 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001669 name = expr['command']
1670 data = expr.get('data')
1671 rets = expr.get('returns')
1672 gen = expr.get('gen', True)
1673 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001674 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001675 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001676 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001677 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001678 if isinstance(rets, list):
1679 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001680 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001681 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1682 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001683
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001684 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001685 name = expr['event']
1686 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001687 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001688 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001689 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001690 name, info, doc, 'arg', self._make_members(data, info))
1691 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001692
1693 def _def_exprs(self):
1694 for expr_elem in self.exprs:
1695 expr = expr_elem['expr']
1696 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001697 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001698 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001699 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001700 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001701 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001702 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001703 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001704 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001705 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001706 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001707 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001708 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001709 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001710 else:
1711 assert False
1712
1713 def check(self):
1714 for ent in self._entity_dict.values():
1715 ent.check(self)
1716
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001717 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001718 visitor.visit_begin(self)
1719 for (name, entity) in sorted(self._entity_dict.items()):
1720 if visitor.visit_needed(entity):
1721 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001722 visitor.visit_end()
1723
Markus Armbruster2caba362013-07-27 17:41:56 +02001724
Markus Armbruster00e4b282015-06-10 10:04:36 +02001725#
1726# Code generation helpers
1727#
1728
Michael Roth0f923be2011-07-19 14:50:39 -05001729def camel_case(name):
1730 new_name = ''
1731 first = True
1732 for ch in name:
1733 if ch in ['_', '-']:
1734 first = True
1735 elif first:
1736 new_name += ch.upper()
1737 first = False
1738 else:
1739 new_name += ch.lower()
1740 return new_name
1741
Eric Blake437db252015-09-29 16:21:02 -06001742
Markus Armbruster849bc532015-05-14 06:50:53 -06001743# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1744# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1745# ENUM24_Name -> ENUM24_NAME
1746def camel_to_upper(value):
1747 c_fun_str = c_name(value, False)
1748 if value.isupper():
1749 return c_fun_str
1750
1751 new_name = ''
1752 l = len(c_fun_str)
1753 for i in range(l):
1754 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001755 # When c is upper and no '_' appears before, do more checks
1756 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001757 if i < l - 1 and c_fun_str[i + 1].islower():
1758 new_name += '_'
1759 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001760 new_name += '_'
1761 new_name += c
1762 return new_name.lstrip('_').upper()
1763
Eric Blake437db252015-09-29 16:21:02 -06001764
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001765def c_enum_const(type_name, const_name, prefix=None):
1766 if prefix is not None:
1767 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001768 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001769
Eric Blake18df5152015-05-14 06:50:48 -06001770c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001771
Eric Blake437db252015-09-29 16:21:02 -06001772
Eric Blakec6405b52015-05-14 06:50:55 -06001773# Map @name to a valid C identifier.
1774# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001775# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001776#
1777# Used for converting 'name' from a 'name':'type' qapi definition
1778# into a generated struct member, as well as converting type names
1779# into substrings of a generated C function name.
1780# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1781# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001782def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001783 # ANSI X3J11/88-090, 3.1.1
1784 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001785 'default', 'do', 'double', 'else', 'enum', 'extern',
1786 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1787 'return', 'short', 'signed', 'sizeof', 'static',
1788 'struct', 'switch', 'typedef', 'union', 'unsigned',
1789 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001790 # ISO/IEC 9899:1999, 6.4.1
1791 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1792 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001793 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1794 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001795 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1796 # excluding _.*
1797 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001798 # C++ ISO/IEC 14882:2003 2.11
1799 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1800 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1801 'namespace', 'new', 'operator', 'private', 'protected',
1802 'public', 'reinterpret_cast', 'static_cast', 'template',
1803 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1804 'using', 'virtual', 'wchar_t',
1805 # alternative representations
1806 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1807 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001808 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001809 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001810 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001811 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1812 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001813 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001814 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001815
Amos Kong05dfb262014-06-10 19:25:53 +08001816eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001817pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001818
Eric Blake437db252015-09-29 16:21:02 -06001819
Michael Roth0f923be2011-07-19 14:50:39 -05001820def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001821 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001822 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001823 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001824 return ret
1825
1826indent_level = 0
1827
Eric Blake437db252015-09-29 16:21:02 -06001828
Michael Roth0f923be2011-07-19 14:50:39 -05001829def push_indent(indent_amount=4):
1830 global indent_level
1831 indent_level += indent_amount
1832
Eric Blake437db252015-09-29 16:21:02 -06001833
Michael Roth0f923be2011-07-19 14:50:39 -05001834def pop_indent(indent_amount=4):
1835 global indent_level
1836 indent_level -= indent_amount
1837
Eric Blake437db252015-09-29 16:21:02 -06001838
Markus Armbruster77e703b2015-06-24 19:27:32 +02001839# Generate @code with @kwds interpolated.
1840# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001841def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001842 raw = code % kwds
1843 if indent_level:
1844 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001845 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001846 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001847 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001848 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001849 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001850
Eric Blake437db252015-09-29 16:21:02 -06001851
Michael Roth0f923be2011-07-19 14:50:39 -05001852def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001853 if code[0] == '\n':
1854 code = code[1:]
1855 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001856
Michael Roth0f923be2011-07-19 14:50:39 -05001857
1858def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001859 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001860
Eric Blake437db252015-09-29 16:21:02 -06001861
Michael Rothc0afa9c2013-05-10 17:46:00 -05001862def guardstart(name):
1863 return mcgen('''
1864
1865#ifndef %(name)s
1866#define %(name)s
1867
1868''',
1869 name=guardname(name))
1870
Eric Blake437db252015-09-29 16:21:02 -06001871
Michael Rothc0afa9c2013-05-10 17:46:00 -05001872def guardend(name):
1873 return mcgen('''
1874
1875#endif /* %(name)s */
1876
1877''',
1878 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001879
Eric Blake437db252015-09-29 16:21:02 -06001880
Markus Armbrustere98859a2015-09-16 13:06:16 +02001881def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001882 ret = mcgen('''
1883
Markus Armbrustere98859a2015-09-16 13:06:16 +02001884const char *const %(c_name)s_lookup[] = {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001885''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001886 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001887 for value in values:
1888 index = c_enum_const(name, value, prefix)
1889 ret += mcgen('''
1890 [%(index)s] = "%(value)s",
1891''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001892 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001893
Eric Blake7fb1cf12015-11-18 01:52:57 -07001894 max_index = c_enum_const(name, '_MAX', prefix)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001895 ret += mcgen('''
1896 [%(max_index)s] = NULL,
1897};
1898''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001899 max_index=max_index)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001900 return ret
1901
Eric Blake437db252015-09-29 16:21:02 -06001902
Markus Armbrustere98859a2015-09-16 13:06:16 +02001903def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001904 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001905 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001906
1907 ret = mcgen('''
1908
1909typedef enum %(c_name)s {
1910''',
1911 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001912
1913 i = 0
1914 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001915 ret += mcgen('''
1916 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001917''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001918 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001919 i=i)
1920 i += 1
1921
Markus Armbrustere98859a2015-09-16 13:06:16 +02001922 ret += mcgen('''
1923} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001924''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001925 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001926
Markus Armbrustere98859a2015-09-16 13:06:16 +02001927 ret += mcgen('''
1928
1929extern const char *const %(c_name)s_lookup[];
1930''',
1931 c_name=c_name(name))
1932 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001933
Eric Blake437db252015-09-29 16:21:02 -06001934
Eric Blake48825ca2016-07-13 21:50:19 -06001935def gen_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001936 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001937 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001938 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001939 ret = ''
1940 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001941 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001942 ret += '%s arg' % arg_type.c_param_type()
1943 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001944 else:
1945 assert not arg_type.variants
1946 for memb in arg_type.members:
1947 ret += sep
1948 sep = ', '
1949 if memb.optional:
1950 ret += 'bool has_%s, ' % c_name(memb.name)
1951 ret += '%s %s' % (memb.type.c_param_type(),
1952 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001953 if extra:
1954 ret += sep + extra
1955 return ret
1956
Eric Blake1f353342015-09-29 16:21:13 -06001957
Markus Armbruster00e4b282015-06-10 10:04:36 +02001958#
1959# Common command line parsing
1960#
1961
Eric Blake437db252015-09-29 16:21:02 -06001962
Markus Armbrusteref801a92017-03-15 13:57:08 +01001963def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001964
1965 try:
1966 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001967 'chp:o:' + extra_options,
1968 ['source', 'header', 'prefix=',
1969 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001970 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001971 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001972 sys.exit(1)
1973
Markus Armbrusteref801a92017-03-15 13:57:08 +01001974 output_dir = ''
1975 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001976 do_c = False
1977 do_h = False
1978 extra_opts = []
1979
1980 for oa in opts:
1981 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001982 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001983 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001984 if match.end() != len(a):
1985 print >>sys.stderr, \
1986 "%s: 'funny character '%s' in argument of --prefix" \
1987 % (sys.argv[0], a[match.end()])
1988 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001989 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001990 elif o in ('-o', '--output-dir'):
1991 output_dir = a + '/'
1992 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001993 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001994 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001995 do_h = True
1996 else:
1997 extra_opts.append(oa)
1998
1999 if not do_c and not do_h:
2000 do_c = True
2001 do_h = True
2002
Markus Armbruster16d80f62015-04-02 13:32:16 +02002003 if len(args) != 1:
2004 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002005 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02002006 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002007
Markus Armbruster54414042015-06-09 16:22:45 +02002008 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002009
Markus Armbruster00e4b282015-06-10 10:04:36 +02002010#
2011# Generate output files with boilerplate
2012#
2013
Eric Blake437db252015-09-29 16:21:02 -06002014
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002015def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
2016 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02002017 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002018 c_file = output_dir + prefix + c_file
2019 h_file = output_dir + prefix + h_file
2020
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002021 if output_dir:
2022 try:
2023 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01002024 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002025 if e.errno != errno.EEXIST:
2026 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002027
2028 def maybe_open(really, name, opt):
2029 if really:
2030 return open(name, opt)
2031 else:
2032 import StringIO
2033 return StringIO.StringIO()
2034
2035 fdef = maybe_open(do_c, c_file, 'w')
2036 fdecl = maybe_open(do_h, h_file, 'w')
2037
2038 fdef.write(mcgen('''
2039/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2040%(comment)s
2041''',
Eric Blake437db252015-09-29 16:21:02 -06002042 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002043
2044 fdecl.write(mcgen('''
2045/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2046%(comment)s
2047#ifndef %(guard)s
2048#define %(guard)s
2049
2050''',
Eric Blake437db252015-09-29 16:21:02 -06002051 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002052
2053 return (fdef, fdecl)
2054
Eric Blake437db252015-09-29 16:21:02 -06002055
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002056def close_output(fdef, fdecl):
2057 fdecl.write('''
2058#endif
2059''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002060 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002061 fdef.close()