blob: d19300d657c069693e627d7273e1264bd577c5f1 [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
Markus Armbruster12f8e1b2015-04-02 14:46:39 +020014import errno
Markus Armbruster2114f5a2015-04-02 13:12:21 +020015import getopt
Lluís Vilanova33aaad52014-05-02 15:52:35 +020016import os
Markus Armbrusterc2613942017-03-15 13:57:35 +010017import re
Markus Armbruster47299262015-05-14 06:50:47 -060018import string
Markus Armbrusterc2613942017-03-15 13:57:35 +010019import sys
20from ordereddict import OrderedDict
Michael Roth0f923be2011-07-19 14:50:39 -050021
Eric Blakeb52c4b92015-05-04 09:05:00 -060022builtin_types = {
Kevin Wolf69dd62d2013-07-08 16:14:21 +020023 'str': 'QTYPE_QSTRING',
24 'int': 'QTYPE_QINT',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
27 'int8': 'QTYPE_QINT',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
Eric Blakecb17f792015-05-04 09:05:01 -060035 'size': 'QTYPE_QINT',
Eric Blake1310a3d2015-12-01 22:20:46 -070036 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070037 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020038}
39
Markus Armbrusterbc52d032017-03-15 13:56:51 +010040# Are documentation comments required?
41doc_required = False
42
Eric Blake10d4d992015-05-04 09:05:23 -060043# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010044returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060045
Eric Blake893e1f22015-12-01 22:20:57 -070046# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010047name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070048
Markus Armbruster5f018442017-03-15 13:57:31 +010049enum_types = {}
Markus Armbrustered285bf2017-03-15 13:57:32 +010050struct_types = {}
Markus Armbruster768562d2017-03-15 13:57:33 +010051union_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
Markus Armbrustered285bf2017-03-15 13:57:32 +0100558 base_struct_define = struct_types.get(base)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800559 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]
Markus Armbrustered285bf2017-03-15 13:57:32 +0100568 elif qapi_type in struct_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100569 return 'QTYPE_QDICT'
Markus Armbruster5f018442017-03-15 13:57:31 +0100570 elif qapi_type in enum_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100571 return 'QTYPE_QSTRING'
Markus Armbruster768562d2017-03-15 13:57:33 +0100572 elif qapi_type in union_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100573 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600574 return None
575
Eric Blake437db252015-09-29 16:21:02 -0600576
Wenchao Xiabceae762014-03-06 17:08:56 -0800577# Return the discriminator enum define if discriminator is specified as an
578# enum type, otherwise return None.
579def discriminator_find_enum_define(expr):
580 base = expr.get('base')
581 discriminator = expr.get('discriminator')
582
583 if not (discriminator and base):
584 return None
585
Eric Blake14f00c62016-03-03 09:16:43 -0700586 base_members = find_base_members(base)
587 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800588 return None
589
Eric Blake14f00c62016-03-03 09:16:43 -0700590 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800591 if not discriminator_type:
592 return None
593
Markus Armbruster5f018442017-03-15 13:57:31 +0100594 return enum_types.get(discriminator_type)
Wenchao Xiabceae762014-03-06 17:08:56 -0800595
Eric Blake437db252015-09-29 16:21:02 -0600596
Eric Blake59a92fe2015-11-18 01:52:56 -0700597# Names must be letters, numbers, -, and _. They must start with letter,
598# except for downstream extensions which must start with __RFQDN_.
599# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100600valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700601 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600602
603
Marc-André Lureau4148c292017-01-13 15:41:25 +0100604def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600605 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600606 global valid_name
607 membername = name
608
609 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100610 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600611 if name.startswith('*'):
612 membername = name[1:]
613 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100614 raise QAPISemError(info, "%s does not allow optional name '%s'"
615 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600616 # Enum members can start with a digit, because the generated C
617 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700618 if enum_member and membername[0].isdigit():
619 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600620 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
621 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600622 if not valid_name.match(membername) or \
623 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100624 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600625
Eric Blake437db252015-09-29 16:21:02 -0600626
627def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200628 global all_names
629 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200630 # FIXME should reject names that differ only in '_' vs. '.'
631 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200632 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100633 raise QAPISemError(info, "%s '%s' is already defined"
634 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600635 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100636 raise QAPISemError(info, "%s '%s' should not end in '%s'"
637 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200638 all_names[name] = meta
639
Eric Blake437db252015-09-29 16:21:02 -0600640
Marc-André Lureau4148c292017-01-13 15:41:25 +0100641def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600642 allow_dict=False, allow_optional=False,
643 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600644 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600645
646 if value is None:
647 return
648
Eric Blakedd883c62015-05-04 09:05:21 -0600649 # Check if array type for value is okay
650 if isinstance(value, list):
651 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100652 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600653 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100654 raise QAPISemError(info,
655 "%s: array type must contain single type name" %
656 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600657 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600658
659 # Check if type name for value is okay
660 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600661 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100662 raise QAPISemError(info, "%s uses unknown type '%s'"
663 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600664 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100665 raise QAPISemError(info, "%s cannot use %s type '%s'" %
666 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600667 return
668
Eric Blakedd883c62015-05-04 09:05:21 -0600669 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100670 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200671
672 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100673 raise QAPISemError(info,
674 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200675
676 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600677 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100678 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600679 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600680 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100681 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
682 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600683 # Todo: allow dictionaries to represent default values of
684 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100685 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200686 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600687 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600688 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600689
Eric Blake437db252015-09-29 16:21:02 -0600690
Marc-André Lureau4148c292017-01-13 15:41:25 +0100691def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600692 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600693 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600694
Eric Blakec8184082016-07-13 21:50:20 -0600695 args_meta = ['struct']
696 if boxed:
697 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100698 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600699 expr.get('data'), allow_dict=not boxed, allow_optional=True,
700 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600701 returns_meta = ['union', 'struct']
702 if name in returns_whitelist:
703 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100704 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200705 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200706 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600707
Eric Blake437db252015-09-29 16:21:02 -0600708
Marc-André Lureau4148c292017-01-13 15:41:25 +0100709def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600710 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600711 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600712
Eric Blakec8184082016-07-13 21:50:20 -0600713 meta = ['struct']
714 if boxed:
715 meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100716 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600717 expr.get('data'), allow_dict=not boxed, allow_optional=True,
718 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200719
Eric Blake437db252015-09-29 16:21:02 -0600720
Marc-André Lureau4148c292017-01-13 15:41:25 +0100721def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800722 name = expr['union']
723 base = expr.get('base')
724 discriminator = expr.get('discriminator')
725 members = expr['data']
726
Eric Blake811d04f2015-05-04 09:05:10 -0600727 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600728
729 # With no discriminator it is a simple union.
730 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600731 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600732 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600733 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100734 raise QAPISemError(info, "Simple union '%s' must not have a base" %
735 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600736
737 # Else, it's a flat union.
738 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600739 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100740 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600741 base, allow_dict=True, allow_optional=True,
742 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600743 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100744 raise QAPISemError(info, "Flat union '%s' must have a base"
745 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700746 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100747 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800748
Eric Blakec9e0a792015-05-04 09:05:22 -0600749 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600750 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100751 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600752 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700753 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800754 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100755 raise QAPISemError(info,
756 "Discriminator '%s' is not a member of base "
757 "struct '%s'"
758 % (discriminator, base))
Markus Armbruster5f018442017-03-15 13:57:31 +0100759 enum_define = enum_types.get(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600760 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800761 # Do not allow string discriminator
762 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100763 raise QAPISemError(info,
764 "Discriminator '%s' must be of enumeration "
765 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800766
Eric Blake02a57ae2016-02-17 23:48:16 -0700767 # Check every branch; don't allow an empty union
768 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100769 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800770 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100771 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600772
Eric Blake01cfbaa2015-12-01 22:20:58 -0700773 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100774 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200775 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600776
Eric Blake44bd1272015-05-04 09:05:08 -0600777 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700778 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600779 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100780 if key not in enum_define['data']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100781 raise QAPISemError(info,
782 "Discriminator value '%s' is not found in "
783 "enum '%s'"
Markus Armbrustereda43c62017-03-15 13:57:29 +0100784 % (key, enum_define['enum']))
Eric Blake44bd1272015-05-04 09:05:08 -0600785
Eric Blaked0b18232016-07-13 21:50:13 -0600786 # If discriminator is user-defined, ensure all values are covered
787 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100788 for value in enum_define['data']:
Eric Blaked0b18232016-07-13 21:50:13 -0600789 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100790 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
791 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600792
Eric Blake437db252015-09-29 16:21:02 -0600793
Marc-André Lureau4148c292017-01-13 15:41:25 +0100794def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600795 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600796 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600797 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600798
Eric Blake02a57ae2016-02-17 23:48:16 -0700799 # Check every branch; require at least two branches
800 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100801 raise QAPISemError(info,
802 "Alternate '%s' should have at least two branches "
803 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600804 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100805 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600806
Eric Blake811d04f2015-05-04 09:05:10 -0600807 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100808 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600809 value,
810 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600811 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700812 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100813 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
814 "type '%s'" % (name, key, value))
Eric Blake811d04f2015-05-04 09:05:10 -0600815 if qtype in types_seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100816 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
817 "be distinguished from member '%s'"
818 % (name, key, types_seen[qtype]))
Eric Blake811d04f2015-05-04 09:05:10 -0600819 types_seen[qtype] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800820
Eric Blake437db252015-09-29 16:21:02 -0600821
Marc-André Lureau4148c292017-01-13 15:41:25 +0100822def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600823 name = expr['enum']
824 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100825 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600826
827 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100828 raise QAPISemError(info,
829 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100830 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100831 raise QAPISemError(info,
832 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600833 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100834 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600835 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600836
Eric Blake437db252015-09-29 16:21:02 -0600837
Marc-André Lureau4148c292017-01-13 15:41:25 +0100838def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600839 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600840 members = expr['data']
841
Marc-André Lureau4148c292017-01-13 15:41:25 +0100842 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600843 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100844 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600845 allow_metas=['struct'])
846
Eric Blake437db252015-09-29 16:21:02 -0600847
Eric Blake0545f6b2015-05-04 09:05:15 -0600848def check_keys(expr_elem, meta, required, optional=[]):
849 expr = expr_elem['expr']
850 info = expr_elem['info']
851 name = expr[meta]
852 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100853 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600854 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600855 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600856 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100857 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
858 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600859 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100860 raise QAPISemError(info,
861 "'%s' of %s '%s' should only use false value"
862 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600863 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100864 raise QAPISemError(info,
865 "'%s' of %s '%s' should only use true value"
866 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600867 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600868 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100869 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
870 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600871
Eric Blake437db252015-09-29 16:21:02 -0600872
Markus Armbruster4d076d62015-06-10 08:55:21 +0200873def check_exprs(exprs):
874 global all_names
875
Markus Armbruster79470162017-03-15 13:57:21 +0100876 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200877 for builtin in builtin_types.keys():
878 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100879
880 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200881 for expr_elem in exprs:
882 expr = expr_elem['expr']
883 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100884 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100885
Markus Armbruster79470162017-03-15 13:57:21 +0100886 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100887 raise QAPISemError(info,
888 "Expression missing documentation comment")
889
Eric Blake437db252015-09-29 16:21:02 -0600890 if 'enum' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100891 meta = 'enum'
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100892 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster5f018442017-03-15 13:57:31 +0100893 enum_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600894 elif 'union' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100895 meta = 'union'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200896 check_keys(expr_elem, 'union', ['data'],
897 ['base', 'discriminator'])
Markus Armbruster768562d2017-03-15 13:57:33 +0100898 union_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600899 elif 'alternate' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100900 meta = 'alternate'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200901 check_keys(expr_elem, 'alternate', ['data'])
Eric Blake437db252015-09-29 16:21:02 -0600902 elif 'struct' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100903 meta = 'struct'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200904 check_keys(expr_elem, 'struct', ['data'], ['base'])
Markus Armbrustered285bf2017-03-15 13:57:32 +0100905 struct_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600906 elif 'command' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100907 meta = 'command'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200908 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600909 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Eric Blake437db252015-09-29 16:21:02 -0600910 elif 'event' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100911 meta = 'event'
Eric Blakec8184082016-07-13 21:50:20 -0600912 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200913 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100914 raise QAPISemError(expr_elem['info'],
915 "Expression is missing metatype")
Markus Armbruster6f053452017-03-15 13:57:30 +0100916 name = expr[meta]
917 add_name(name, info, meta)
Markus Armbruster79470162017-03-15 13:57:21 +0100918 if doc and doc.symbol != name:
919 raise QAPISemError(info, "Definition of '%s' follows documentation"
920 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200921
922 # Try again for hidden UnionKind enum
923 for expr_elem in exprs:
924 expr = expr_elem['expr']
Markus Armbrustereda43c62017-03-15 13:57:29 +0100925 if 'union' in expr and not discriminator_find_enum_define(expr):
926 name = '%sKind' % expr['union']
Eric Blake437db252015-09-29 16:21:02 -0600927 elif 'alternate' in expr:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100928 name = '%sKind' % expr['alternate']
929 else:
930 continue
Markus Armbruster5f018442017-03-15 13:57:31 +0100931 enum_types[name] = {'enum': name}
Markus Armbruster6f053452017-03-15 13:57:30 +0100932 add_name(name, info, 'enum', implicit=True)
Markus Armbruster4d076d62015-06-10 08:55:21 +0200933
934 # Validate that exprs make sense
935 for expr_elem in exprs:
936 expr = expr_elem['expr']
937 info = expr_elem['info']
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100938 doc = expr_elem.get('doc')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200939
Eric Blake437db252015-09-29 16:21:02 -0600940 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200941 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600942 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200943 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600944 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200945 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600946 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200947 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600948 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200949 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600950 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200951 check_event(expr, info)
952 else:
953 assert False, 'unexpected meta type'
954
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100955 if doc:
956 doc.check_expr(expr)
957
Markus Armbrusterac882192015-09-16 13:06:05 +0200958 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600959
Markus Armbrusterac882192015-09-16 13:06:05 +0200960
961#
962# Schema compiler frontend
963#
964
965class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100966 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +0200967 assert isinstance(name, str)
968 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -0600969 # For explicitly defined entities, info points to the (explicit)
970 # definition. For builtins (and their arrays), info is None.
971 # For implicitly defined entities, info points to a place that
972 # triggered the implicit definition (there may be more than one
973 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +0200974 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100975 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +0200976
Markus Armbrusterf51d8c32015-09-16 13:06:06 +0200977 def c_name(self):
978 return c_name(self.name)
979
Markus Armbrusterac882192015-09-16 13:06:05 +0200980 def check(self, schema):
981 pass
982
Eric Blake49823c42015-10-12 22:22:27 -0600983 def is_implicit(self):
984 return not self.info
985
Markus Armbruster3f7dc212015-09-16 13:06:07 +0200986 def visit(self, visitor):
987 pass
988
989
990class QAPISchemaVisitor(object):
991 def visit_begin(self, schema):
992 pass
993
994 def visit_end(self):
995 pass
996
Eric Blake25a0d9c2015-10-12 22:22:21 -0600997 def visit_needed(self, entity):
998 # Default to visiting everything
999 return True
1000
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001001 def visit_builtin_type(self, name, info, json_type):
1002 pass
1003
1004 def visit_enum_type(self, name, info, values, prefix):
1005 pass
1006
1007 def visit_array_type(self, name, info, element_type):
1008 pass
1009
1010 def visit_object_type(self, name, info, base, members, variants):
1011 pass
1012
Markus Armbruster39a18152015-09-16 13:06:28 +02001013 def visit_object_type_flat(self, name, info, members, variants):
1014 pass
1015
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001016 def visit_alternate_type(self, name, info, variants):
1017 pass
1018
1019 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001020 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001021 pass
1022
Eric Blake48825ca2016-07-13 21:50:19 -06001023 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001024 pass
1025
Markus Armbrusterac882192015-09-16 13:06:05 +02001026
1027class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001028 # Return the C type for common use.
1029 # For the types we commonly box, this is a pointer type.
1030 def c_type(self):
1031 pass
1032
1033 # Return the C type to be used in a parameter list.
1034 def c_param_type(self):
1035 return self.c_type()
1036
1037 # Return the C type to be used where we suppress boxing.
1038 def c_unboxed_type(self):
1039 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001040
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001041 def json_type(self):
1042 pass
1043
1044 def alternate_qtype(self):
1045 json2qtype = {
1046 'string': 'QTYPE_QSTRING',
1047 'number': 'QTYPE_QFLOAT',
1048 'int': 'QTYPE_QINT',
1049 'boolean': 'QTYPE_QBOOL',
1050 'object': 'QTYPE_QDICT'
1051 }
1052 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001053
Markus Armbruster691e0312017-03-15 13:57:14 +01001054 def doc_type(self):
1055 if self.is_implicit():
1056 return None
1057 return self.name
1058
Markus Armbrusterac882192015-09-16 13:06:05 +02001059
1060class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001061 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001062 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001063 assert not c_type or isinstance(c_type, str)
1064 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1065 'value')
1066 self._json_type_name = json_type
1067 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001068
1069 def c_name(self):
1070 return self.name
1071
Eric Blake4040d992016-03-17 16:48:28 -06001072 def c_type(self):
1073 return self._c_type_name
1074
1075 def c_param_type(self):
1076 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001077 return 'const ' + self._c_type_name
1078 return self._c_type_name
1079
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001080 def json_type(self):
1081 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001082
Markus Armbruster691e0312017-03-15 13:57:14 +01001083 def doc_type(self):
1084 return self.json_type()
1085
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001086 def visit(self, visitor):
1087 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1088
Markus Armbrusterac882192015-09-16 13:06:05 +02001089
1090class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001091 def __init__(self, name, info, doc, values, prefix):
1092 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001093 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001094 assert isinstance(v, QAPISchemaMember)
1095 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001096 assert prefix is None or isinstance(prefix, str)
1097 self.values = values
1098 self.prefix = prefix
1099
1100 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001101 seen = {}
1102 for v in self.values:
1103 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001104 if self.doc:
1105 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001106
Eric Blake99df5282015-10-12 22:22:32 -06001107 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001108 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1109 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001110
Eric Blake4040d992016-03-17 16:48:28 -06001111 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001112 return c_name(self.name)
1113
Eric Blake93bda4d2015-12-01 22:20:55 -07001114 def member_names(self):
1115 return [v.name for v in self.values]
1116
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001117 def json_type(self):
1118 return 'string'
1119
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001120 def visit(self, visitor):
1121 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001122 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001123
Markus Armbrusterac882192015-09-16 13:06:05 +02001124
1125class QAPISchemaArrayType(QAPISchemaType):
1126 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001127 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001128 assert isinstance(element_type, str)
1129 self._element_type_name = element_type
1130 self.element_type = None
1131
1132 def check(self, schema):
1133 self.element_type = schema.lookup_type(self._element_type_name)
1134 assert self.element_type
1135
Eric Blake99df5282015-10-12 22:22:32 -06001136 def is_implicit(self):
1137 return True
1138
Eric Blake4040d992016-03-17 16:48:28 -06001139 def c_type(self):
1140 return c_name(self.name) + pointer_suffix
1141
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001142 def json_type(self):
1143 return 'array'
1144
Markus Armbruster691e0312017-03-15 13:57:14 +01001145 def doc_type(self):
1146 elt_doc_type = self.element_type.doc_type()
1147 if not elt_doc_type:
1148 return None
1149 return 'array of ' + elt_doc_type
1150
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001151 def visit(self, visitor):
1152 visitor.visit_array_type(self.name, self.info, self.element_type)
1153
Markus Armbrusterac882192015-09-16 13:06:05 +02001154
1155class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001156 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001157 # struct has local_members, optional base, and no variants
1158 # flat union has base, variants, and no local_members
1159 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001160 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001161 assert base is None or isinstance(base, str)
1162 for m in local_members:
1163 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001164 m.set_owner(name)
1165 if variants is not None:
1166 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1167 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001168 self._base_name = base
1169 self.base = None
1170 self.local_members = local_members
1171 self.variants = variants
1172 self.members = None
1173
1174 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001175 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001176 raise QAPISemError(self.info,
1177 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001178 if self.members:
1179 return
1180 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001181 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001182 if self._base_name:
1183 self.base = schema.lookup_type(self._base_name)
1184 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001185 self.base.check(schema)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001186 self.base.check_clash(self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001187 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001188 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001189 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001190 if self.doc:
1191 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001192 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001193 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001194 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001195 assert self.variants.tag_member in self.members
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001196 self.variants.check_clash(self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001197 if self.doc:
1198 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001199
Eric Blake14f00c62016-03-03 09:16:43 -07001200 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001201 # and update seen to track the members seen so far. Report any errors
1202 # on behalf of info, which is not necessarily self.info
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001203 def check_clash(self, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001204 assert not self.variants # not implemented
1205 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001206 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001207
Eric Blake99df5282015-10-12 22:22:32 -06001208 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001209 # See QAPISchema._make_implicit_object_type(), as well as
1210 # _def_predefineds()
1211 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001212
Eric Blakeb6167702016-07-13 21:50:16 -06001213 def is_empty(self):
1214 assert self.members is not None
1215 return not self.members and not self.variants
1216
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001217 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001218 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001219 return QAPISchemaType.c_name(self)
1220
Eric Blake4040d992016-03-17 16:48:28 -06001221 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001222 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001223 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001224
Eric Blake4040d992016-03-17 16:48:28 -06001225 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001226 return c_name(self.name)
1227
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001228 def json_type(self):
1229 return 'object'
1230
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001231 def visit(self, visitor):
1232 visitor.visit_object_type(self.name, self.info,
1233 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001234 visitor.visit_object_type_flat(self.name, self.info,
1235 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001236
Markus Armbrusterac882192015-09-16 13:06:05 +02001237
Eric Blaked44f9ac2015-12-01 22:20:54 -07001238class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001239 role = 'member'
1240
Eric Blaked44f9ac2015-12-01 22:20:54 -07001241 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001242 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001243 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001244 self.owner = None
1245
1246 def set_owner(self, name):
1247 assert not self.owner
1248 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001249
Eric Blake27b60ab2015-11-18 01:52:51 -07001250 def check_clash(self, info, seen):
1251 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001252 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001253 raise QAPISemError(info,
1254 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001255 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001256 raise QAPISemError(info, "%s collides with %s" %
1257 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001258 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001259
Eric Blake88d4ef82015-11-18 01:52:50 -07001260 def _pretty_owner(self):
1261 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001262 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001263 # See QAPISchema._make_implicit_object_type() - reverse the
1264 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001265 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001266 if owner.endswith('-arg'):
1267 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001268 elif owner.endswith('-base'):
1269 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001270 else:
1271 assert owner.endswith('-wrapper')
1272 # Unreachable and not implemented
1273 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001274 if owner.endswith('Kind'):
1275 # See QAPISchema._make_implicit_enum_type()
1276 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001277 return '(%s of %s)' % (self.role, owner)
1278
1279 def describe(self):
1280 return "'%s' %s" % (self.name, self._pretty_owner())
1281
Markus Armbrusterac882192015-09-16 13:06:05 +02001282
Eric Blaked44f9ac2015-12-01 22:20:54 -07001283class QAPISchemaObjectTypeMember(QAPISchemaMember):
1284 def __init__(self, name, typ, optional):
1285 QAPISchemaMember.__init__(self, name)
1286 assert isinstance(typ, str)
1287 assert isinstance(optional, bool)
1288 self._type_name = typ
1289 self.type = None
1290 self.optional = optional
1291
1292 def check(self, schema):
1293 assert self.owner
1294 self.type = schema.lookup_type(self._type_name)
1295 assert self.type
1296
1297
Markus Armbrusterac882192015-09-16 13:06:05 +02001298class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001299 def __init__(self, tag_name, tag_member, variants):
1300 # Flat unions pass tag_name but not tag_member.
1301 # Simple unions and alternates pass tag_member but not tag_name.
1302 # After check(), tag_member is always set, and tag_name remains
1303 # a reliable witness of being used by a flat union.
1304 assert bool(tag_member) != bool(tag_name)
1305 assert (isinstance(tag_name, str) or
1306 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001307 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001308 for v in variants:
1309 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001310 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001311 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001312 self.variants = variants
1313
Eric Blake88d4ef82015-11-18 01:52:50 -07001314 def set_owner(self, name):
1315 for v in self.variants:
1316 v.set_owner(name)
1317
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001318 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001319 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001320 self.tag_member = seen[c_name(self._tag_name)]
1321 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001322 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1323 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001324 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001325 # Union names must match enum values; alternate names are
1326 # checked separately. Use 'seen' to tell the two apart.
1327 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001328 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001329 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001330 v.type.check(schema)
1331
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001332 def check_clash(self, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001333 for v in self.variants:
1334 # Reset seen map for each variant, since qapi names from one
1335 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001336 assert isinstance(v.type, QAPISchemaObjectType)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001337 v.type.check_clash(info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001338
Eric Blake437db252015-09-29 16:21:02 -06001339
Markus Armbrusterac882192015-09-16 13:06:05 +02001340class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001341 role = 'branch'
1342
Markus Armbrusterac882192015-09-16 13:06:05 +02001343 def __init__(self, name, typ):
1344 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1345
Markus Armbrusterac882192015-09-16 13:06:05 +02001346
1347class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001348 def __init__(self, name, info, doc, variants):
1349 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001350 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001351 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001352 variants.set_owner(name)
1353 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001354 self.variants = variants
1355
1356 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001357 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001358 # Not calling self.variants.check_clash(), because there's nothing
1359 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001360 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001361 # Alternate branch names have no relation to the tag enum values;
1362 # so we have to check for potential name collisions ourselves.
1363 seen = {}
1364 for v in self.variants.variants:
1365 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001366 if self.doc:
1367 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001368 if self.doc:
1369 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001370
Eric Blake4040d992016-03-17 16:48:28 -06001371 def c_type(self):
1372 return c_name(self.name) + pointer_suffix
1373
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001374 def json_type(self):
1375 return 'value'
1376
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001377 def visit(self, visitor):
1378 visitor.visit_alternate_type(self.name, self.info, self.variants)
1379
Eric Blakec8184082016-07-13 21:50:20 -06001380 def is_empty(self):
1381 return False
1382
Markus Armbrusterac882192015-09-16 13:06:05 +02001383
1384class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001385 def __init__(self, name, info, doc, arg_type, ret_type,
1386 gen, success_response, boxed):
1387 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001388 assert not arg_type or isinstance(arg_type, str)
1389 assert not ret_type or isinstance(ret_type, str)
1390 self._arg_type_name = arg_type
1391 self.arg_type = None
1392 self._ret_type_name = ret_type
1393 self.ret_type = None
1394 self.gen = gen
1395 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001396 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001397
1398 def check(self, schema):
1399 if self._arg_type_name:
1400 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001401 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1402 isinstance(self.arg_type, QAPISchemaAlternateType))
1403 self.arg_type.check(schema)
1404 if self.boxed:
1405 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001406 raise QAPISemError(self.info,
1407 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001408 else:
1409 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1410 assert not self.arg_type.variants
1411 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001412 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001413 if self._ret_type_name:
1414 self.ret_type = schema.lookup_type(self._ret_type_name)
1415 assert isinstance(self.ret_type, QAPISchemaType)
1416
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001417 def visit(self, visitor):
1418 visitor.visit_command(self.name, self.info,
1419 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001420 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001421
Markus Armbrusterac882192015-09-16 13:06:05 +02001422
1423class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001424 def __init__(self, name, info, doc, arg_type, boxed):
1425 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001426 assert not arg_type or isinstance(arg_type, str)
1427 self._arg_type_name = arg_type
1428 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001429 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001430
1431 def check(self, schema):
1432 if self._arg_type_name:
1433 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001434 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1435 isinstance(self.arg_type, QAPISchemaAlternateType))
1436 self.arg_type.check(schema)
1437 if self.boxed:
1438 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001439 raise QAPISemError(self.info,
1440 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001441 else:
1442 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1443 assert not self.arg_type.variants
1444 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001445 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001446
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001447 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001448 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001449
Markus Armbrusterac882192015-09-16 13:06:05 +02001450
1451class QAPISchema(object):
1452 def __init__(self, fname):
1453 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001454 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001455 self.exprs = check_exprs(parser.exprs)
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001456 self.docs = parser.docs
Eric Blake7618b912015-10-12 22:22:22 -06001457 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001458 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001459 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001460 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001461 self._def_exprs()
1462 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001463 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001464 print >>sys.stderr, err
1465 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001466
Markus Armbrusterac882192015-09-16 13:06:05 +02001467 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001468 # Only the predefined types are allowed to not have info
1469 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001470 assert ent.name not in self._entity_dict
1471 self._entity_dict[ent.name] = ent
1472
1473 def lookup_entity(self, name, typ=None):
1474 ent = self._entity_dict.get(name)
1475 if typ and not isinstance(ent, typ):
1476 return None
1477 return ent
1478
1479 def lookup_type(self, name):
1480 return self.lookup_entity(name, QAPISchemaType)
1481
Eric Blake861877a2016-03-17 16:48:36 -06001482 def _def_builtin_type(self, name, json_type, c_type):
1483 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001484 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1485 # qapi-types.h from a single .c, all arrays of builtins must be
1486 # declared in the first file whether or not they are used. Nicer
1487 # would be to use lazy instantiation, while figuring out how to
1488 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001489 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001490
1491 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001492 for t in [('str', 'string', 'char' + pointer_suffix),
1493 ('number', 'number', 'double'),
1494 ('int', 'int', 'int64_t'),
1495 ('int8', 'int', 'int8_t'),
1496 ('int16', 'int', 'int16_t'),
1497 ('int32', 'int', 'int32_t'),
1498 ('int64', 'int', 'int64_t'),
1499 ('uint8', 'int', 'uint8_t'),
1500 ('uint16', 'int', 'uint16_t'),
1501 ('uint32', 'int', 'uint32_t'),
1502 ('uint64', 'int', 'uint64_t'),
1503 ('size', 'int', 'uint64_t'),
1504 ('bool', 'boolean', 'bool'),
1505 ('any', 'value', 'QObject' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001506 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001507 self.the_empty_object_type = QAPISchemaObjectType(
1508 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001509 self._def_entity(self.the_empty_object_type)
Eric Blake93bda4d2015-12-01 22:20:55 -07001510 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1511 'qstring', 'qdict', 'qlist',
1512 'qfloat', 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001513 self._def_entity(QAPISchemaEnumType('QType', None, None,
1514 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001515
Eric Blake93bda4d2015-12-01 22:20:55 -07001516 def _make_enum_members(self, values):
1517 return [QAPISchemaMember(v) for v in values]
1518
Eric Blake99df5282015-10-12 22:22:32 -06001519 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001520 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001521 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001522 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001523 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001524 return name
1525
Eric Blake99df5282015-10-12 22:22:32 -06001526 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001527 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001528 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001529 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001530 return name
1531
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001532 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001533 if not members:
1534 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001535 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001536 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001537 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001538 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001539 members, None))
1540 return name
1541
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001542 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001543 name = expr['enum']
1544 data = expr['data']
1545 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001546 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001547 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001548
Eric Blake99df5282015-10-12 22:22:32 -06001549 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001550 optional = False
1551 if name.startswith('*'):
1552 name = name[1:]
1553 optional = True
1554 if isinstance(typ, list):
1555 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001556 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001557 return QAPISchemaObjectTypeMember(name, typ, optional)
1558
Eric Blake99df5282015-10-12 22:22:32 -06001559 def _make_members(self, data, info):
1560 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001561 for (key, value) in data.iteritems()]
1562
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001563 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001564 name = expr['struct']
1565 base = expr.get('base')
1566 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001567 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001568 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001569 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001570
1571 def _make_variant(self, case, typ):
1572 return QAPISchemaObjectTypeVariant(case, typ)
1573
Eric Blake99df5282015-10-12 22:22:32 -06001574 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001575 if isinstance(typ, list):
1576 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001577 typ = self._make_array_type(typ[0], info)
1578 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001579 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001580 return QAPISchemaObjectTypeVariant(case, typ)
1581
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001582 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001583 name = expr['union']
1584 data = expr['data']
1585 base = expr.get('base')
1586 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001587 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001588 if isinstance(base, dict):
1589 base = (self._make_implicit_object_type(
Markus Armbrusterc2613942017-03-15 13:57:35 +01001590 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001591 if tag_name:
1592 variants = [self._make_variant(key, value)
1593 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001594 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001595 else:
Eric Blake99df5282015-10-12 22:22:32 -06001596 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001597 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001598 typ = self._make_implicit_enum_type(name, info,
1599 [v.name for v in variants])
1600 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001601 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001602 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001603 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001604 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001605 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001606 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001607
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001608 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001609 name = expr['alternate']
1610 data = expr['data']
1611 variants = [self._make_variant(key, value)
1612 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001613 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001614 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001615 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001616 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001617 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001618 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001619
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001620 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001621 name = expr['command']
1622 data = expr.get('data')
1623 rets = expr.get('returns')
1624 gen = expr.get('gen', True)
1625 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001626 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001627 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001628 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001629 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001630 if isinstance(rets, list):
1631 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001632 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001633 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1634 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001635
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001636 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001637 name = expr['event']
1638 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001639 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001640 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001641 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001642 name, info, doc, 'arg', self._make_members(data, info))
1643 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001644
1645 def _def_exprs(self):
1646 for expr_elem in self.exprs:
1647 expr = expr_elem['expr']
1648 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001649 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001650 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001651 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001652 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001653 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001654 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001655 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001656 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001657 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001658 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001659 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001660 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001661 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001662 else:
1663 assert False
1664
1665 def check(self):
1666 for ent in self._entity_dict.values():
1667 ent.check(self)
1668
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001669 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001670 visitor.visit_begin(self)
1671 for (name, entity) in sorted(self._entity_dict.items()):
1672 if visitor.visit_needed(entity):
1673 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001674 visitor.visit_end()
1675
Markus Armbruster2caba362013-07-27 17:41:56 +02001676
Markus Armbruster00e4b282015-06-10 10:04:36 +02001677#
1678# Code generation helpers
1679#
1680
Michael Roth0f923be2011-07-19 14:50:39 -05001681def camel_case(name):
1682 new_name = ''
1683 first = True
1684 for ch in name:
1685 if ch in ['_', '-']:
1686 first = True
1687 elif first:
1688 new_name += ch.upper()
1689 first = False
1690 else:
1691 new_name += ch.lower()
1692 return new_name
1693
Eric Blake437db252015-09-29 16:21:02 -06001694
Markus Armbruster849bc532015-05-14 06:50:53 -06001695# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1696# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1697# ENUM24_Name -> ENUM24_NAME
1698def camel_to_upper(value):
1699 c_fun_str = c_name(value, False)
1700 if value.isupper():
1701 return c_fun_str
1702
1703 new_name = ''
1704 l = len(c_fun_str)
1705 for i in range(l):
1706 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001707 # When c is upper and no '_' appears before, do more checks
1708 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001709 if i < l - 1 and c_fun_str[i + 1].islower():
1710 new_name += '_'
1711 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001712 new_name += '_'
1713 new_name += c
1714 return new_name.lstrip('_').upper()
1715
Eric Blake437db252015-09-29 16:21:02 -06001716
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001717def c_enum_const(type_name, const_name, prefix=None):
1718 if prefix is not None:
1719 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001720 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001721
Eric Blake18df5152015-05-14 06:50:48 -06001722c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001723
Eric Blake437db252015-09-29 16:21:02 -06001724
Eric Blakec6405b52015-05-14 06:50:55 -06001725# Map @name to a valid C identifier.
1726# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001727# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001728#
1729# Used for converting 'name' from a 'name':'type' qapi definition
1730# into a generated struct member, as well as converting type names
1731# into substrings of a generated C function name.
1732# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1733# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001734def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001735 # ANSI X3J11/88-090, 3.1.1
1736 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001737 'default', 'do', 'double', 'else', 'enum', 'extern',
1738 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1739 'return', 'short', 'signed', 'sizeof', 'static',
1740 'struct', 'switch', 'typedef', 'union', 'unsigned',
1741 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001742 # ISO/IEC 9899:1999, 6.4.1
1743 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1744 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001745 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1746 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001747 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1748 # excluding _.*
1749 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001750 # C++ ISO/IEC 14882:2003 2.11
1751 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1752 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1753 'namespace', 'new', 'operator', 'private', 'protected',
1754 'public', 'reinterpret_cast', 'static_cast', 'template',
1755 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1756 'using', 'virtual', 'wchar_t',
1757 # alternative representations
1758 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1759 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001760 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001761 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001762 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001763 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1764 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001765 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001766 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001767
Amos Kong05dfb262014-06-10 19:25:53 +08001768eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001769pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001770
Eric Blake437db252015-09-29 16:21:02 -06001771
Michael Roth0f923be2011-07-19 14:50:39 -05001772def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001773 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001774 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001775 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001776 return ret
1777
1778indent_level = 0
1779
Eric Blake437db252015-09-29 16:21:02 -06001780
Michael Roth0f923be2011-07-19 14:50:39 -05001781def push_indent(indent_amount=4):
1782 global indent_level
1783 indent_level += indent_amount
1784
Eric Blake437db252015-09-29 16:21:02 -06001785
Michael Roth0f923be2011-07-19 14:50:39 -05001786def pop_indent(indent_amount=4):
1787 global indent_level
1788 indent_level -= indent_amount
1789
Eric Blake437db252015-09-29 16:21:02 -06001790
Markus Armbruster77e703b2015-06-24 19:27:32 +02001791# Generate @code with @kwds interpolated.
1792# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001793def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001794 raw = code % kwds
1795 if indent_level:
1796 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001797 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001798 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001799 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001800 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001801 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001802
Eric Blake437db252015-09-29 16:21:02 -06001803
Michael Roth0f923be2011-07-19 14:50:39 -05001804def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001805 if code[0] == '\n':
1806 code = code[1:]
1807 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001808
Michael Roth0f923be2011-07-19 14:50:39 -05001809
1810def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001811 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001812
Eric Blake437db252015-09-29 16:21:02 -06001813
Michael Rothc0afa9c2013-05-10 17:46:00 -05001814def guardstart(name):
1815 return mcgen('''
1816
1817#ifndef %(name)s
1818#define %(name)s
1819
1820''',
1821 name=guardname(name))
1822
Eric Blake437db252015-09-29 16:21:02 -06001823
Michael Rothc0afa9c2013-05-10 17:46:00 -05001824def guardend(name):
1825 return mcgen('''
1826
1827#endif /* %(name)s */
1828
1829''',
1830 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001831
Eric Blake437db252015-09-29 16:21:02 -06001832
Markus Armbrustere98859a2015-09-16 13:06:16 +02001833def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001834 ret = mcgen('''
1835
Markus Armbrustere98859a2015-09-16 13:06:16 +02001836const char *const %(c_name)s_lookup[] = {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001837''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001838 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001839 for value in values:
1840 index = c_enum_const(name, value, prefix)
1841 ret += mcgen('''
1842 [%(index)s] = "%(value)s",
1843''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001844 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001845
Eric Blake7fb1cf12015-11-18 01:52:57 -07001846 max_index = c_enum_const(name, '_MAX', prefix)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001847 ret += mcgen('''
1848 [%(max_index)s] = NULL,
1849};
1850''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001851 max_index=max_index)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001852 return ret
1853
Eric Blake437db252015-09-29 16:21:02 -06001854
Markus Armbrustere98859a2015-09-16 13:06:16 +02001855def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001856 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001857 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001858
1859 ret = mcgen('''
1860
1861typedef enum %(c_name)s {
1862''',
1863 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001864
1865 i = 0
1866 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001867 ret += mcgen('''
1868 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001869''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001870 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001871 i=i)
1872 i += 1
1873
Markus Armbrustere98859a2015-09-16 13:06:16 +02001874 ret += mcgen('''
1875} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001876''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001877 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001878
Markus Armbrustere98859a2015-09-16 13:06:16 +02001879 ret += mcgen('''
1880
1881extern const char *const %(c_name)s_lookup[];
1882''',
1883 c_name=c_name(name))
1884 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001885
Eric Blake437db252015-09-29 16:21:02 -06001886
Eric Blake48825ca2016-07-13 21:50:19 -06001887def gen_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001888 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001889 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001890 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001891 ret = ''
1892 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001893 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001894 ret += '%s arg' % arg_type.c_param_type()
1895 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001896 else:
1897 assert not arg_type.variants
1898 for memb in arg_type.members:
1899 ret += sep
1900 sep = ', '
1901 if memb.optional:
1902 ret += 'bool has_%s, ' % c_name(memb.name)
1903 ret += '%s %s' % (memb.type.c_param_type(),
1904 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001905 if extra:
1906 ret += sep + extra
1907 return ret
1908
Eric Blake1f353342015-09-29 16:21:13 -06001909
Markus Armbruster00e4b282015-06-10 10:04:36 +02001910#
1911# Common command line parsing
1912#
1913
Eric Blake437db252015-09-29 16:21:02 -06001914
Markus Armbrusteref801a92017-03-15 13:57:08 +01001915def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001916
1917 try:
1918 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001919 'chp:o:' + extra_options,
1920 ['source', 'header', 'prefix=',
1921 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001922 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001923 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001924 sys.exit(1)
1925
Markus Armbrusteref801a92017-03-15 13:57:08 +01001926 output_dir = ''
1927 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001928 do_c = False
1929 do_h = False
1930 extra_opts = []
1931
1932 for oa in opts:
1933 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001934 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001935 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001936 if match.end() != len(a):
1937 print >>sys.stderr, \
1938 "%s: 'funny character '%s' in argument of --prefix" \
1939 % (sys.argv[0], a[match.end()])
1940 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001941 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001942 elif o in ('-o', '--output-dir'):
1943 output_dir = a + '/'
1944 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001945 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001946 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001947 do_h = True
1948 else:
1949 extra_opts.append(oa)
1950
1951 if not do_c and not do_h:
1952 do_c = True
1953 do_h = True
1954
Markus Armbruster16d80f62015-04-02 13:32:16 +02001955 if len(args) != 1:
1956 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001957 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02001958 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001959
Markus Armbruster54414042015-06-09 16:22:45 +02001960 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001961
Markus Armbruster00e4b282015-06-10 10:04:36 +02001962#
1963# Generate output files with boilerplate
1964#
1965
Eric Blake437db252015-09-29 16:21:02 -06001966
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001967def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1968 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001969 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001970 c_file = output_dir + prefix + c_file
1971 h_file = output_dir + prefix + h_file
1972
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001973 if output_dir:
1974 try:
1975 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01001976 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001977 if e.errno != errno.EEXIST:
1978 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001979
1980 def maybe_open(really, name, opt):
1981 if really:
1982 return open(name, opt)
1983 else:
1984 import StringIO
1985 return StringIO.StringIO()
1986
1987 fdef = maybe_open(do_c, c_file, 'w')
1988 fdecl = maybe_open(do_h, h_file, 'w')
1989
1990 fdef.write(mcgen('''
1991/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1992%(comment)s
1993''',
Eric Blake437db252015-09-29 16:21:02 -06001994 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001995
1996 fdecl.write(mcgen('''
1997/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1998%(comment)s
1999#ifndef %(guard)s
2000#define %(guard)s
2001
2002''',
Eric Blake437db252015-09-29 16:21:02 -06002003 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002004
2005 return (fdef, fdecl)
2006
Eric Blake437db252015-09-29 16:21:02 -06002007
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002008def close_output(fdef, fdecl):
2009 fdecl.write('''
2010#endif
2011''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002012 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002013 fdef.close()