blob: b7a25e4759c81f134ef20444ff98a9cc3f9305fd [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 = []
109
110 def append(self, line):
111 self.content.append(line)
112
113 def __repr__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100114 return '\n'.join(self.content).strip()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100115
116 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100117 def __init__(self, name):
118 QAPIDoc.Section.__init__(self, name)
119 self.member = None
120
121 def connect(self, member):
122 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100123
124 def __init__(self, parser, info):
125 # self.parser is used to report errors with QAPIParseError. The
126 # resulting error position depends on the state of the parser.
127 # It happens to be the beginning of the comment. More or less
128 # servicable, but action at a distance.
129 self.parser = parser
130 self.info = info
131 self.symbol = None
132 self.body = QAPIDoc.Section()
133 # dict mapping parameter name to ArgSection
134 self.args = OrderedDict()
135 # a list of Section
136 self.sections = []
137 # the current section
138 self.section = self.body
Marc-André Lureau3313b612017-01-13 15:41:29 +0100139
140 def has_section(self, name):
141 """Return True if we have a section with this name."""
142 for i in self.sections:
143 if i.name == name:
144 return True
145 return False
146
147 def append(self, line):
148 """Parse a comment line and add it to the documentation."""
149 line = line[1:]
150 if not line:
151 self._append_freeform(line)
152 return
153
154 if line[0] != ' ':
155 raise QAPIParseError(self.parser, "Missing space after #")
156 line = line[1:]
157
158 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
159 # recognized, and get silently treated as ordinary text
160 if self.symbol:
161 self._append_symbol_line(line)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100162 elif not self.body.content and line.startswith('@'):
163 if not line.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100164 raise QAPIParseError(self.parser, "Line should end with :")
165 self.symbol = line[1:-1]
166 # FIXME invalid names other than the empty string aren't flagged
167 if not self.symbol:
168 raise QAPIParseError(self.parser, "Invalid name")
169 else:
170 self._append_freeform(line)
171
Markus Armbruster4ea71482017-03-15 13:57:23 +0100172 def end_comment(self):
173 self._end_section()
174
Marc-André Lureau3313b612017-01-13 15:41:29 +0100175 def _append_symbol_line(self, line):
176 name = line.split(' ', 1)[0]
177
Markus Armbrusteref801a92017-03-15 13:57:08 +0100178 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100179 line = line[len(name)+1:]
180 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100181 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100182 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100183 'Note:', 'Notes:',
184 'Example:', 'Examples:',
185 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100186 line = line[len(name)+1:]
187 self._start_section(name[:-1])
188
189 self._append_freeform(line)
190
191 def _start_args_section(self, name):
192 # FIXME invalid names other than the empty string aren't flagged
193 if not name:
194 raise QAPIParseError(self.parser, "Invalid parameter name")
195 if name in self.args:
196 raise QAPIParseError(self.parser,
197 "'%s' parameter name duplicated" % name)
198 if self.sections:
199 raise QAPIParseError(self.parser,
200 "'@%s:' can't follow '%s' section"
201 % (name, self.sections[0].name))
Markus Armbruster4ea71482017-03-15 13:57:23 +0100202 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100203 self.section = QAPIDoc.ArgSection(name)
204 self.args[name] = self.section
205
Markus Armbrusteref801a92017-03-15 13:57:08 +0100206 def _start_section(self, name=''):
207 if name in ('Returns', 'Since') and self.has_section(name):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100208 raise QAPIParseError(self.parser,
209 "Duplicated '%s' section" % name)
Markus Armbruster4ea71482017-03-15 13:57:23 +0100210 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100211 self.section = QAPIDoc.Section(name)
212 self.sections.append(self.section)
213
Markus Armbruster4ea71482017-03-15 13:57:23 +0100214 def _end_section(self):
215 if self.section:
216 contents = str(self.section)
217 if self.section.name and (not contents or contents.isspace()):
218 raise QAPIParseError(self.parser, "Empty doc section '%s'"
219 % self.section.name)
220 self.section = None
221
Marc-André Lureau3313b612017-01-13 15:41:29 +0100222 def _append_freeform(self, line):
223 in_arg = isinstance(self.section, QAPIDoc.ArgSection)
224 if (in_arg and self.section.content
225 and not self.section.content[-1]
226 and line and not line[0].isspace()):
227 self._start_section()
228 if (in_arg or not self.section.name
Markus Armbrusteref801a92017-03-15 13:57:08 +0100229 or not self.section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100230 line = line.strip()
Markus Armbruster2d433232017-03-15 13:57:22 +0100231 match = re.match(r'(@\S+:)', line)
232 if match:
233 raise QAPIParseError(self.parser,
234 "'%s' not allowed in free-form documentation"
235 % match.group(1))
Markus Armbruster1d8bda12017-03-15 13:57:06 +0100236 # TODO Drop this once the dust has settled
237 if (isinstance(self.section, QAPIDoc.ArgSection)
238 and '#optional' in line):
239 raise QAPISemError(self.info, "Please drop the #optional tag")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100240 self.section.append(line)
241
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100242 def connect_member(self, member):
243 if member.name not in self.args:
244 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100245 self.args[member.name] = QAPIDoc.ArgSection(member.name)
246 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100247
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100248 def check_expr(self, expr):
249 if self.has_section('Returns') and 'command' not in expr:
250 raise QAPISemError(self.info,
251 "'Returns:' is only valid for commands")
252
Markus Armbruster816a57c2017-03-15 13:57:26 +0100253 def check(self):
254 bogus = [name for name, section in self.args.iteritems()
255 if not section.member]
256 if bogus:
257 raise QAPISemError(
258 self.info,
259 "The following documented members are not in "
260 "the declaration: %s" % ", ".join(bogus))
261
Marc-André Lureau3313b612017-01-13 15:41:29 +0100262
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200263class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500264
Eric Blake437db252015-09-29 16:21:02 -0600265 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200266 abs_fname = os.path.abspath(fp.name)
Markus Armbruster8608d252015-06-09 18:32:29 +0200267 fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200268 self.fname = fname
Markus Armbruster54414042015-06-09 16:22:45 +0200269 previously_included.append(abs_fname)
270 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200271 self.src = fp.read()
272 if self.src == '' or self.src[-1] != '\n':
273 self.src += '\n'
274 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800275 self.line = 1
276 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200277 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100278 self.docs = []
Markus Armbrustere7823a22017-03-15 13:57:20 +0100279 self.cur_doc = None
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200280 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500281
Eric Blake437db252015-09-29 16:21:02 -0600282 while self.tok is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100283 info = {'file': fname, 'line': self.line,
284 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100285 if self.tok == '#':
Markus Armbrustere7823a22017-03-15 13:57:20 +0100286 self.reject_expr_doc()
287 self.cur_doc = self.get_doc(info)
288 self.docs.append(self.cur_doc)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100289 continue
290
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200291 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100292 if 'include' in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100293 self.reject_expr_doc()
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200294 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100295 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100296 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200297 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100298 raise QAPISemError(info,
299 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100300 self._include(include, info, os.path.dirname(abs_fname),
301 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100302 elif "pragma" in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100303 self.reject_expr_doc()
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100304 if len(expr) != 1:
305 raise QAPISemError(info, "Invalid 'pragma' directive")
306 pragma = expr['pragma']
307 if not isinstance(pragma, dict):
308 raise QAPISemError(
309 info, "Value of 'pragma' must be a dictionary")
310 for name, value in pragma.iteritems():
311 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200312 else:
313 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100314 'info': info}
Markus Armbrustere7823a22017-03-15 13:57:20 +0100315 if self.cur_doc:
316 if not self.cur_doc.symbol:
317 raise QAPISemError(
318 self.cur_doc.info,
319 "Expression documentation required")
Markus Armbrustere7823a22017-03-15 13:57:20 +0100320 expr_elem['doc'] = self.cur_doc
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200321 self.exprs.append(expr_elem)
Markus Armbrustere7823a22017-03-15 13:57:20 +0100322 self.cur_doc = None
323 self.reject_expr_doc()
324
325 def reject_expr_doc(self):
326 if self.cur_doc and self.cur_doc.symbol:
327 raise QAPISemError(
328 self.cur_doc.info,
329 "Documentation for '%s' is not followed by the definition"
330 % self.cur_doc.symbol)
Michael Roth0f923be2011-07-19 14:50:39 -0500331
Markus Armbrustere04dea82017-03-15 13:56:50 +0100332 def _include(self, include, info, base_dir, previously_included):
333 incl_abs_fname = os.path.join(base_dir, include)
334 # catch inclusion cycle
335 inf = info
336 while inf:
337 if incl_abs_fname == os.path.abspath(inf['file']):
338 raise QAPISemError(info, "Inclusion loop for %s" % include)
339 inf = inf['parent']
340
341 # skip multiple include of the same file
342 if incl_abs_fname in previously_included:
343 return
344 try:
345 fobj = open(incl_abs_fname, 'r')
346 except IOError as e:
347 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
348 exprs_include = QAPISchemaParser(fobj, previously_included, info)
349 self.exprs.extend(exprs_include.exprs)
350 self.docs.extend(exprs_include.docs)
351
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100352 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100353 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100354 if name == 'doc-required':
355 if not isinstance(value, bool):
356 raise QAPISemError(info,
357 "Pragma 'doc-required' must be boolean")
358 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100359 elif name == 'returns-whitelist':
360 if (not isinstance(value, list)
361 or any([not isinstance(elt, str) for elt in value])):
362 raise QAPISemError(info,
363 "Pragma returns-whitelist must be"
364 " a list of strings")
365 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100366 elif name == 'name-case-whitelist':
367 if (not isinstance(value, list)
368 or any([not isinstance(elt, str) for elt in value])):
369 raise QAPISemError(info,
370 "Pragma name-case-whitelist must be"
371 " a list of strings")
372 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100373 else:
374 raise QAPISemError(info, "Unknown pragma '%s'" % name)
375
Marc-André Lureau3313b612017-01-13 15:41:29 +0100376 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200377 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200378 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200379 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200380 self.cursor += 1
381 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500382
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200383 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100384 if self.src[self.cursor] == '#':
385 # Start of doc comment
386 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200387 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100388 if not skip_comment:
389 self.val = self.src[self.pos:self.cursor]
390 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100391 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200392 return
393 elif self.tok == "'":
394 string = ''
395 esc = False
396 while True:
397 ch = self.src[self.cursor]
398 self.cursor += 1
399 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100400 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200401 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600402 if ch == 'b':
403 string += '\b'
404 elif ch == 'f':
405 string += '\f'
406 elif ch == 'n':
407 string += '\n'
408 elif ch == 'r':
409 string += '\r'
410 elif ch == 't':
411 string += '\t'
412 elif ch == 'u':
413 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600414 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600415 ch = self.src[self.cursor]
416 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100417 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100418 raise QAPIParseError(self,
419 '\\u escape needs 4 '
420 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600421 value = (value << 4) + int(ch, 16)
422 # If Python 2 and 3 didn't disagree so much on
423 # how to handle Unicode, then we could allow
424 # Unicode string defaults. But most of QAPI is
425 # ASCII-only, so we aren't losing much for now.
426 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100427 raise QAPIParseError(self,
428 'For now, \\u escape '
429 'only supports non-zero '
430 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600431 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100432 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600433 string += ch
434 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100435 raise QAPIParseError(self,
436 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200437 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100438 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200439 esc = True
440 elif ch == "'":
441 self.val = string
442 return
443 else:
444 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100445 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200446 self.val = True
447 self.cursor += 3
448 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100449 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200450 self.val = False
451 self.cursor += 4
452 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100453 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200454 self.val = None
455 self.cursor += 3
456 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200457 elif self.tok == '\n':
458 if self.cursor == len(self.src):
459 self.tok = None
460 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800461 self.line += 1
462 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200463 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100464 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500465
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200466 def get_members(self):
467 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200468 if self.tok == '}':
469 self.accept()
470 return expr
471 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100472 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200473 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200474 key = self.val
475 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200476 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100477 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200478 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800479 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100480 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200481 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200482 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200483 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200484 return expr
485 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100486 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200487 self.accept()
488 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100489 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500490
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200491 def get_values(self):
492 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200493 if self.tok == ']':
494 self.accept()
495 return expr
Eric Blake437db252015-09-29 16:21:02 -0600496 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100497 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
498 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200499 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200500 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200501 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200502 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200503 return expr
504 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100505 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200506 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500507
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200508 def get_expr(self, nested):
509 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100510 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200511 if self.tok == '{':
512 self.accept()
513 expr = self.get_members()
514 elif self.tok == '[':
515 self.accept()
516 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600517 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200518 expr = self.val
519 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200520 else:
Markus Armbruster012b1262017-03-15 13:57:36 +0100521 raise QAPIParseError(self, 'Expected "{", "[", string, '
522 'boolean or "null"')
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))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200815 conflicting = set([qtype])
816 if qtype == 'QTYPE_QSTRING':
817 enum_expr = enum_types.get(value)
818 if enum_expr:
819 for v in enum_expr['data']:
820 if v in ['on', 'off']:
821 conflicting.add('QTYPE_QBOOL')
822 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
823 conflicting.add('QTYPE_QINT')
824 conflicting.add('QTYPE_QFLOAT')
825 else:
826 conflicting.add('QTYPE_QINT')
827 conflicting.add('QTYPE_QFLOAT')
828 conflicting.add('QTYPE_QBOOL')
829 if conflicting & set(types_seen):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100830 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
831 "be distinguished from member '%s'"
832 % (name, key, types_seen[qtype]))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200833 for qt in conflicting:
834 types_seen[qt] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800835
Eric Blake437db252015-09-29 16:21:02 -0600836
Marc-André Lureau4148c292017-01-13 15:41:25 +0100837def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600838 name = expr['enum']
839 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100840 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600841
842 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100843 raise QAPISemError(info,
844 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100845 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100846 raise QAPISemError(info,
847 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600848 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100849 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600850 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600851
Eric Blake437db252015-09-29 16:21:02 -0600852
Marc-André Lureau4148c292017-01-13 15:41:25 +0100853def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600854 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600855 members = expr['data']
856
Marc-André Lureau4148c292017-01-13 15:41:25 +0100857 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600858 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100859 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600860 allow_metas=['struct'])
861
Eric Blake437db252015-09-29 16:21:02 -0600862
Eric Blake0545f6b2015-05-04 09:05:15 -0600863def check_keys(expr_elem, meta, required, optional=[]):
864 expr = expr_elem['expr']
865 info = expr_elem['info']
866 name = expr[meta]
867 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100868 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600869 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600870 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600871 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100872 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
873 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600874 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100875 raise QAPISemError(info,
876 "'%s' of %s '%s' should only use false value"
877 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600878 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100879 raise QAPISemError(info,
880 "'%s' of %s '%s' should only use true value"
881 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600882 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600883 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100884 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
885 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600886
Eric Blake437db252015-09-29 16:21:02 -0600887
Markus Armbruster4d076d62015-06-10 08:55:21 +0200888def check_exprs(exprs):
889 global all_names
890
Markus Armbruster79470162017-03-15 13:57:21 +0100891 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200892 for builtin in builtin_types.keys():
893 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100894
895 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200896 for expr_elem in exprs:
897 expr = expr_elem['expr']
898 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100899 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100900
Markus Armbruster79470162017-03-15 13:57:21 +0100901 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100902 raise QAPISemError(info,
903 "Expression missing documentation comment")
904
Eric Blake437db252015-09-29 16:21:02 -0600905 if 'enum' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100906 meta = 'enum'
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100907 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster5f018442017-03-15 13:57:31 +0100908 enum_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600909 elif 'union' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100910 meta = 'union'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200911 check_keys(expr_elem, 'union', ['data'],
912 ['base', 'discriminator'])
Markus Armbruster768562d2017-03-15 13:57:33 +0100913 union_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600914 elif 'alternate' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100915 meta = 'alternate'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200916 check_keys(expr_elem, 'alternate', ['data'])
Eric Blake437db252015-09-29 16:21:02 -0600917 elif 'struct' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100918 meta = 'struct'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200919 check_keys(expr_elem, 'struct', ['data'], ['base'])
Markus Armbrustered285bf2017-03-15 13:57:32 +0100920 struct_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600921 elif 'command' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100922 meta = 'command'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200923 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600924 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Eric Blake437db252015-09-29 16:21:02 -0600925 elif 'event' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100926 meta = 'event'
Eric Blakec8184082016-07-13 21:50:20 -0600927 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200928 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100929 raise QAPISemError(expr_elem['info'],
930 "Expression is missing metatype")
Markus Armbruster6f053452017-03-15 13:57:30 +0100931 name = expr[meta]
932 add_name(name, info, meta)
Markus Armbruster79470162017-03-15 13:57:21 +0100933 if doc and doc.symbol != name:
934 raise QAPISemError(info, "Definition of '%s' follows documentation"
935 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200936
937 # Try again for hidden UnionKind enum
938 for expr_elem in exprs:
939 expr = expr_elem['expr']
Markus Armbrustereda43c62017-03-15 13:57:29 +0100940 if 'union' in expr and not discriminator_find_enum_define(expr):
941 name = '%sKind' % expr['union']
Eric Blake437db252015-09-29 16:21:02 -0600942 elif 'alternate' in expr:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100943 name = '%sKind' % expr['alternate']
944 else:
945 continue
Markus Armbruster5f018442017-03-15 13:57:31 +0100946 enum_types[name] = {'enum': name}
Markus Armbruster6f053452017-03-15 13:57:30 +0100947 add_name(name, info, 'enum', implicit=True)
Markus Armbruster4d076d62015-06-10 08:55:21 +0200948
949 # Validate that exprs make sense
950 for expr_elem in exprs:
951 expr = expr_elem['expr']
952 info = expr_elem['info']
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100953 doc = expr_elem.get('doc')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200954
Eric Blake437db252015-09-29 16:21:02 -0600955 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200956 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600957 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200958 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600959 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200960 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600961 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200962 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600963 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200964 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600965 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200966 check_event(expr, info)
967 else:
968 assert False, 'unexpected meta type'
969
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100970 if doc:
971 doc.check_expr(expr)
972
Markus Armbrusterac882192015-09-16 13:06:05 +0200973 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600974
Markus Armbrusterac882192015-09-16 13:06:05 +0200975
976#
977# Schema compiler frontend
978#
979
980class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100981 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +0200982 assert isinstance(name, str)
983 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -0600984 # For explicitly defined entities, info points to the (explicit)
985 # definition. For builtins (and their arrays), info is None.
986 # For implicitly defined entities, info points to a place that
987 # triggered the implicit definition (there may be more than one
988 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +0200989 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100990 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +0200991
Markus Armbrusterf51d8c32015-09-16 13:06:06 +0200992 def c_name(self):
993 return c_name(self.name)
994
Markus Armbrusterac882192015-09-16 13:06:05 +0200995 def check(self, schema):
996 pass
997
Eric Blake49823c42015-10-12 22:22:27 -0600998 def is_implicit(self):
999 return not self.info
1000
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001001 def visit(self, visitor):
1002 pass
1003
1004
1005class QAPISchemaVisitor(object):
1006 def visit_begin(self, schema):
1007 pass
1008
1009 def visit_end(self):
1010 pass
1011
Eric Blake25a0d9c2015-10-12 22:22:21 -06001012 def visit_needed(self, entity):
1013 # Default to visiting everything
1014 return True
1015
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001016 def visit_builtin_type(self, name, info, json_type):
1017 pass
1018
1019 def visit_enum_type(self, name, info, values, prefix):
1020 pass
1021
1022 def visit_array_type(self, name, info, element_type):
1023 pass
1024
1025 def visit_object_type(self, name, info, base, members, variants):
1026 pass
1027
Markus Armbruster39a18152015-09-16 13:06:28 +02001028 def visit_object_type_flat(self, name, info, members, variants):
1029 pass
1030
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001031 def visit_alternate_type(self, name, info, variants):
1032 pass
1033
1034 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001035 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001036 pass
1037
Eric Blake48825ca2016-07-13 21:50:19 -06001038 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001039 pass
1040
Markus Armbrusterac882192015-09-16 13:06:05 +02001041
1042class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001043 # Return the C type for common use.
1044 # For the types we commonly box, this is a pointer type.
1045 def c_type(self):
1046 pass
1047
1048 # Return the C type to be used in a parameter list.
1049 def c_param_type(self):
1050 return self.c_type()
1051
1052 # Return the C type to be used where we suppress boxing.
1053 def c_unboxed_type(self):
1054 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001055
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001056 def json_type(self):
1057 pass
1058
1059 def alternate_qtype(self):
1060 json2qtype = {
1061 'string': 'QTYPE_QSTRING',
1062 'number': 'QTYPE_QFLOAT',
1063 'int': 'QTYPE_QINT',
1064 'boolean': 'QTYPE_QBOOL',
1065 'object': 'QTYPE_QDICT'
1066 }
1067 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001068
Markus Armbruster691e0312017-03-15 13:57:14 +01001069 def doc_type(self):
1070 if self.is_implicit():
1071 return None
1072 return self.name
1073
Markus Armbrusterac882192015-09-16 13:06:05 +02001074
1075class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001076 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001077 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001078 assert not c_type or isinstance(c_type, str)
1079 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1080 'value')
1081 self._json_type_name = json_type
1082 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001083
1084 def c_name(self):
1085 return self.name
1086
Eric Blake4040d992016-03-17 16:48:28 -06001087 def c_type(self):
1088 return self._c_type_name
1089
1090 def c_param_type(self):
1091 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001092 return 'const ' + self._c_type_name
1093 return self._c_type_name
1094
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001095 def json_type(self):
1096 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001097
Markus Armbruster691e0312017-03-15 13:57:14 +01001098 def doc_type(self):
1099 return self.json_type()
1100
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001101 def visit(self, visitor):
1102 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1103
Markus Armbrusterac882192015-09-16 13:06:05 +02001104
1105class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001106 def __init__(self, name, info, doc, values, prefix):
1107 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001108 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001109 assert isinstance(v, QAPISchemaMember)
1110 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001111 assert prefix is None or isinstance(prefix, str)
1112 self.values = values
1113 self.prefix = prefix
1114
1115 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001116 seen = {}
1117 for v in self.values:
1118 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001119 if self.doc:
1120 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001121
Eric Blake99df5282015-10-12 22:22:32 -06001122 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001123 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1124 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001125
Eric Blake4040d992016-03-17 16:48:28 -06001126 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001127 return c_name(self.name)
1128
Eric Blake93bda4d2015-12-01 22:20:55 -07001129 def member_names(self):
1130 return [v.name for v in self.values]
1131
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001132 def json_type(self):
1133 return 'string'
1134
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001135 def visit(self, visitor):
1136 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001137 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001138
Markus Armbrusterac882192015-09-16 13:06:05 +02001139
1140class QAPISchemaArrayType(QAPISchemaType):
1141 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001142 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001143 assert isinstance(element_type, str)
1144 self._element_type_name = element_type
1145 self.element_type = None
1146
1147 def check(self, schema):
1148 self.element_type = schema.lookup_type(self._element_type_name)
1149 assert self.element_type
1150
Eric Blake99df5282015-10-12 22:22:32 -06001151 def is_implicit(self):
1152 return True
1153
Eric Blake4040d992016-03-17 16:48:28 -06001154 def c_type(self):
1155 return c_name(self.name) + pointer_suffix
1156
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001157 def json_type(self):
1158 return 'array'
1159
Markus Armbruster691e0312017-03-15 13:57:14 +01001160 def doc_type(self):
1161 elt_doc_type = self.element_type.doc_type()
1162 if not elt_doc_type:
1163 return None
1164 return 'array of ' + elt_doc_type
1165
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001166 def visit(self, visitor):
1167 visitor.visit_array_type(self.name, self.info, self.element_type)
1168
Markus Armbrusterac882192015-09-16 13:06:05 +02001169
1170class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001171 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001172 # struct has local_members, optional base, and no variants
1173 # flat union has base, variants, and no local_members
1174 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001175 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001176 assert base is None or isinstance(base, str)
1177 for m in local_members:
1178 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001179 m.set_owner(name)
1180 if variants is not None:
1181 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1182 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001183 self._base_name = base
1184 self.base = None
1185 self.local_members = local_members
1186 self.variants = variants
1187 self.members = None
1188
1189 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001190 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001191 raise QAPISemError(self.info,
1192 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001193 if self.members:
1194 return
1195 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001196 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001197 if self._base_name:
1198 self.base = schema.lookup_type(self._base_name)
1199 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001200 self.base.check(schema)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001201 self.base.check_clash(self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001202 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001203 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001204 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001205 if self.doc:
1206 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001207 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001208 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001209 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001210 assert self.variants.tag_member in self.members
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001211 self.variants.check_clash(self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001212 if self.doc:
1213 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001214
Eric Blake14f00c62016-03-03 09:16:43 -07001215 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001216 # and update seen to track the members seen so far. Report any errors
1217 # on behalf of info, which is not necessarily self.info
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001218 def check_clash(self, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001219 assert not self.variants # not implemented
1220 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001221 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001222
Eric Blake99df5282015-10-12 22:22:32 -06001223 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001224 # See QAPISchema._make_implicit_object_type(), as well as
1225 # _def_predefineds()
1226 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001227
Eric Blakeb6167702016-07-13 21:50:16 -06001228 def is_empty(self):
1229 assert self.members is not None
1230 return not self.members and not self.variants
1231
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001232 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001233 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001234 return QAPISchemaType.c_name(self)
1235
Eric Blake4040d992016-03-17 16:48:28 -06001236 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001237 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001238 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001239
Eric Blake4040d992016-03-17 16:48:28 -06001240 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001241 return c_name(self.name)
1242
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001243 def json_type(self):
1244 return 'object'
1245
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001246 def visit(self, visitor):
1247 visitor.visit_object_type(self.name, self.info,
1248 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001249 visitor.visit_object_type_flat(self.name, self.info,
1250 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001251
Markus Armbrusterac882192015-09-16 13:06:05 +02001252
Eric Blaked44f9ac2015-12-01 22:20:54 -07001253class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001254 role = 'member'
1255
Eric Blaked44f9ac2015-12-01 22:20:54 -07001256 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001257 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001258 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001259 self.owner = None
1260
1261 def set_owner(self, name):
1262 assert not self.owner
1263 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001264
Eric Blake27b60ab2015-11-18 01:52:51 -07001265 def check_clash(self, info, seen):
1266 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001267 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001268 raise QAPISemError(info,
1269 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001270 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001271 raise QAPISemError(info, "%s collides with %s" %
1272 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001273 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001274
Eric Blake88d4ef82015-11-18 01:52:50 -07001275 def _pretty_owner(self):
1276 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001277 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001278 # See QAPISchema._make_implicit_object_type() - reverse the
1279 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001280 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001281 if owner.endswith('-arg'):
1282 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001283 elif owner.endswith('-base'):
1284 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001285 else:
1286 assert owner.endswith('-wrapper')
1287 # Unreachable and not implemented
1288 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001289 if owner.endswith('Kind'):
1290 # See QAPISchema._make_implicit_enum_type()
1291 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001292 return '(%s of %s)' % (self.role, owner)
1293
1294 def describe(self):
1295 return "'%s' %s" % (self.name, self._pretty_owner())
1296
Markus Armbrusterac882192015-09-16 13:06:05 +02001297
Eric Blaked44f9ac2015-12-01 22:20:54 -07001298class QAPISchemaObjectTypeMember(QAPISchemaMember):
1299 def __init__(self, name, typ, optional):
1300 QAPISchemaMember.__init__(self, name)
1301 assert isinstance(typ, str)
1302 assert isinstance(optional, bool)
1303 self._type_name = typ
1304 self.type = None
1305 self.optional = optional
1306
1307 def check(self, schema):
1308 assert self.owner
1309 self.type = schema.lookup_type(self._type_name)
1310 assert self.type
1311
1312
Markus Armbrusterac882192015-09-16 13:06:05 +02001313class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001314 def __init__(self, tag_name, tag_member, variants):
1315 # Flat unions pass tag_name but not tag_member.
1316 # Simple unions and alternates pass tag_member but not tag_name.
1317 # After check(), tag_member is always set, and tag_name remains
1318 # a reliable witness of being used by a flat union.
1319 assert bool(tag_member) != bool(tag_name)
1320 assert (isinstance(tag_name, str) or
1321 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001322 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001323 for v in variants:
1324 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001325 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001326 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001327 self.variants = variants
1328
Eric Blake88d4ef82015-11-18 01:52:50 -07001329 def set_owner(self, name):
1330 for v in self.variants:
1331 v.set_owner(name)
1332
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001333 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001334 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001335 self.tag_member = seen[c_name(self._tag_name)]
1336 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001337 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1338 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001339 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001340 # Union names must match enum values; alternate names are
1341 # checked separately. Use 'seen' to tell the two apart.
1342 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001343 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001344 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001345 v.type.check(schema)
1346
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001347 def check_clash(self, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001348 for v in self.variants:
1349 # Reset seen map for each variant, since qapi names from one
1350 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001351 assert isinstance(v.type, QAPISchemaObjectType)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001352 v.type.check_clash(info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001353
Eric Blake437db252015-09-29 16:21:02 -06001354
Markus Armbrusterac882192015-09-16 13:06:05 +02001355class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001356 role = 'branch'
1357
Markus Armbrusterac882192015-09-16 13:06:05 +02001358 def __init__(self, name, typ):
1359 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1360
Markus Armbrusterac882192015-09-16 13:06:05 +02001361
1362class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001363 def __init__(self, name, info, doc, variants):
1364 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001365 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001366 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001367 variants.set_owner(name)
1368 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001369 self.variants = variants
1370
1371 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001372 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001373 # Not calling self.variants.check_clash(), because there's nothing
1374 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001375 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001376 # Alternate branch names have no relation to the tag enum values;
1377 # so we have to check for potential name collisions ourselves.
1378 seen = {}
1379 for v in self.variants.variants:
1380 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001381 if self.doc:
1382 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001383 if self.doc:
1384 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001385
Eric Blake4040d992016-03-17 16:48:28 -06001386 def c_type(self):
1387 return c_name(self.name) + pointer_suffix
1388
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001389 def json_type(self):
1390 return 'value'
1391
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001392 def visit(self, visitor):
1393 visitor.visit_alternate_type(self.name, self.info, self.variants)
1394
Eric Blakec8184082016-07-13 21:50:20 -06001395 def is_empty(self):
1396 return False
1397
Markus Armbrusterac882192015-09-16 13:06:05 +02001398
1399class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001400 def __init__(self, name, info, doc, arg_type, ret_type,
1401 gen, success_response, boxed):
1402 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001403 assert not arg_type or isinstance(arg_type, str)
1404 assert not ret_type or isinstance(ret_type, str)
1405 self._arg_type_name = arg_type
1406 self.arg_type = None
1407 self._ret_type_name = ret_type
1408 self.ret_type = None
1409 self.gen = gen
1410 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001411 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001412
1413 def check(self, schema):
1414 if self._arg_type_name:
1415 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001416 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1417 isinstance(self.arg_type, QAPISchemaAlternateType))
1418 self.arg_type.check(schema)
1419 if self.boxed:
1420 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001421 raise QAPISemError(self.info,
1422 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001423 else:
1424 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1425 assert not self.arg_type.variants
1426 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001427 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001428 if self._ret_type_name:
1429 self.ret_type = schema.lookup_type(self._ret_type_name)
1430 assert isinstance(self.ret_type, QAPISchemaType)
1431
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001432 def visit(self, visitor):
1433 visitor.visit_command(self.name, self.info,
1434 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001435 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001436
Markus Armbrusterac882192015-09-16 13:06:05 +02001437
1438class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001439 def __init__(self, name, info, doc, arg_type, boxed):
1440 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001441 assert not arg_type or isinstance(arg_type, str)
1442 self._arg_type_name = arg_type
1443 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001444 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001445
1446 def check(self, schema):
1447 if self._arg_type_name:
1448 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001449 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1450 isinstance(self.arg_type, QAPISchemaAlternateType))
1451 self.arg_type.check(schema)
1452 if self.boxed:
1453 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001454 raise QAPISemError(self.info,
1455 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001456 else:
1457 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1458 assert not self.arg_type.variants
1459 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001460 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001461
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001462 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001463 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001464
Markus Armbrusterac882192015-09-16 13:06:05 +02001465
1466class QAPISchema(object):
1467 def __init__(self, fname):
1468 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001469 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001470 self.exprs = check_exprs(parser.exprs)
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001471 self.docs = parser.docs
Eric Blake7618b912015-10-12 22:22:22 -06001472 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001473 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001474 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001475 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001476 self._def_exprs()
1477 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001478 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001479 print >>sys.stderr, err
1480 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001481
Markus Armbrusterac882192015-09-16 13:06:05 +02001482 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001483 # Only the predefined types are allowed to not have info
1484 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001485 assert ent.name not in self._entity_dict
1486 self._entity_dict[ent.name] = ent
1487
1488 def lookup_entity(self, name, typ=None):
1489 ent = self._entity_dict.get(name)
1490 if typ and not isinstance(ent, typ):
1491 return None
1492 return ent
1493
1494 def lookup_type(self, name):
1495 return self.lookup_entity(name, QAPISchemaType)
1496
Eric Blake861877a2016-03-17 16:48:36 -06001497 def _def_builtin_type(self, name, json_type, c_type):
1498 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001499 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1500 # qapi-types.h from a single .c, all arrays of builtins must be
1501 # declared in the first file whether or not they are used. Nicer
1502 # would be to use lazy instantiation, while figuring out how to
1503 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001504 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001505
1506 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001507 for t in [('str', 'string', 'char' + pointer_suffix),
1508 ('number', 'number', 'double'),
1509 ('int', 'int', 'int64_t'),
1510 ('int8', 'int', 'int8_t'),
1511 ('int16', 'int', 'int16_t'),
1512 ('int32', 'int', 'int32_t'),
1513 ('int64', 'int', 'int64_t'),
1514 ('uint8', 'int', 'uint8_t'),
1515 ('uint16', 'int', 'uint16_t'),
1516 ('uint32', 'int', 'uint32_t'),
1517 ('uint64', 'int', 'uint64_t'),
1518 ('size', 'int', 'uint64_t'),
1519 ('bool', 'boolean', 'bool'),
1520 ('any', 'value', 'QObject' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001521 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001522 self.the_empty_object_type = QAPISchemaObjectType(
1523 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001524 self._def_entity(self.the_empty_object_type)
Eric Blake93bda4d2015-12-01 22:20:55 -07001525 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1526 'qstring', 'qdict', 'qlist',
1527 'qfloat', 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001528 self._def_entity(QAPISchemaEnumType('QType', None, None,
1529 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001530
Eric Blake93bda4d2015-12-01 22:20:55 -07001531 def _make_enum_members(self, values):
1532 return [QAPISchemaMember(v) for v in values]
1533
Eric Blake99df5282015-10-12 22:22:32 -06001534 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001535 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001536 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001537 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001538 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001539 return name
1540
Eric Blake99df5282015-10-12 22:22:32 -06001541 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001542 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001543 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001544 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001545 return name
1546
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001547 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001548 if not members:
1549 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001550 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001551 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001552 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001553 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001554 members, None))
1555 return name
1556
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001557 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001558 name = expr['enum']
1559 data = expr['data']
1560 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001561 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001562 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001563
Eric Blake99df5282015-10-12 22:22:32 -06001564 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001565 optional = False
1566 if name.startswith('*'):
1567 name = name[1:]
1568 optional = True
1569 if isinstance(typ, list):
1570 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001571 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001572 return QAPISchemaObjectTypeMember(name, typ, optional)
1573
Eric Blake99df5282015-10-12 22:22:32 -06001574 def _make_members(self, data, info):
1575 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001576 for (key, value) in data.iteritems()]
1577
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001578 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001579 name = expr['struct']
1580 base = expr.get('base')
1581 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001582 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001583 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001584 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001585
1586 def _make_variant(self, case, typ):
1587 return QAPISchemaObjectTypeVariant(case, typ)
1588
Eric Blake99df5282015-10-12 22:22:32 -06001589 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001590 if isinstance(typ, list):
1591 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001592 typ = self._make_array_type(typ[0], info)
1593 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001594 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001595 return QAPISchemaObjectTypeVariant(case, typ)
1596
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001597 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001598 name = expr['union']
1599 data = expr['data']
1600 base = expr.get('base')
1601 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001602 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001603 if isinstance(base, dict):
1604 base = (self._make_implicit_object_type(
Markus Armbrusterc2613942017-03-15 13:57:35 +01001605 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001606 if tag_name:
1607 variants = [self._make_variant(key, value)
1608 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001609 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001610 else:
Eric Blake99df5282015-10-12 22:22:32 -06001611 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001612 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001613 typ = self._make_implicit_enum_type(name, info,
1614 [v.name for v in variants])
1615 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001616 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001617 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001618 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001619 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001620 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001621 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001622
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001623 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001624 name = expr['alternate']
1625 data = expr['data']
1626 variants = [self._make_variant(key, value)
1627 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001628 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001629 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001630 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001631 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001632 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001633 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001634
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001635 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001636 name = expr['command']
1637 data = expr.get('data')
1638 rets = expr.get('returns')
1639 gen = expr.get('gen', True)
1640 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001641 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001642 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001643 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001644 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001645 if isinstance(rets, list):
1646 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001647 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001648 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1649 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001650
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001651 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001652 name = expr['event']
1653 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001654 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001655 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001656 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001657 name, info, doc, 'arg', self._make_members(data, info))
1658 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001659
1660 def _def_exprs(self):
1661 for expr_elem in self.exprs:
1662 expr = expr_elem['expr']
1663 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001664 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001665 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001666 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001667 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001668 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001669 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001670 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001671 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001672 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001673 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001674 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001675 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001676 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001677 else:
1678 assert False
1679
1680 def check(self):
1681 for ent in self._entity_dict.values():
1682 ent.check(self)
1683
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001684 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001685 visitor.visit_begin(self)
1686 for (name, entity) in sorted(self._entity_dict.items()):
1687 if visitor.visit_needed(entity):
1688 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001689 visitor.visit_end()
1690
Markus Armbruster2caba362013-07-27 17:41:56 +02001691
Markus Armbruster00e4b282015-06-10 10:04:36 +02001692#
1693# Code generation helpers
1694#
1695
Michael Roth0f923be2011-07-19 14:50:39 -05001696def camel_case(name):
1697 new_name = ''
1698 first = True
1699 for ch in name:
1700 if ch in ['_', '-']:
1701 first = True
1702 elif first:
1703 new_name += ch.upper()
1704 first = False
1705 else:
1706 new_name += ch.lower()
1707 return new_name
1708
Eric Blake437db252015-09-29 16:21:02 -06001709
Markus Armbruster849bc532015-05-14 06:50:53 -06001710# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1711# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1712# ENUM24_Name -> ENUM24_NAME
1713def camel_to_upper(value):
1714 c_fun_str = c_name(value, False)
1715 if value.isupper():
1716 return c_fun_str
1717
1718 new_name = ''
1719 l = len(c_fun_str)
1720 for i in range(l):
1721 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001722 # When c is upper and no '_' appears before, do more checks
1723 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001724 if i < l - 1 and c_fun_str[i + 1].islower():
1725 new_name += '_'
1726 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001727 new_name += '_'
1728 new_name += c
1729 return new_name.lstrip('_').upper()
1730
Eric Blake437db252015-09-29 16:21:02 -06001731
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001732def c_enum_const(type_name, const_name, prefix=None):
1733 if prefix is not None:
1734 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001735 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001736
Eric Blake18df5152015-05-14 06:50:48 -06001737c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001738
Eric Blake437db252015-09-29 16:21:02 -06001739
Eric Blakec6405b52015-05-14 06:50:55 -06001740# Map @name to a valid C identifier.
1741# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001742# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001743#
1744# Used for converting 'name' from a 'name':'type' qapi definition
1745# into a generated struct member, as well as converting type names
1746# into substrings of a generated C function name.
1747# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1748# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001749def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001750 # ANSI X3J11/88-090, 3.1.1
1751 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001752 'default', 'do', 'double', 'else', 'enum', 'extern',
1753 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1754 'return', 'short', 'signed', 'sizeof', 'static',
1755 'struct', 'switch', 'typedef', 'union', 'unsigned',
1756 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001757 # ISO/IEC 9899:1999, 6.4.1
1758 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1759 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001760 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1761 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001762 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1763 # excluding _.*
1764 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001765 # C++ ISO/IEC 14882:2003 2.11
1766 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1767 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1768 'namespace', 'new', 'operator', 'private', 'protected',
1769 'public', 'reinterpret_cast', 'static_cast', 'template',
1770 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1771 'using', 'virtual', 'wchar_t',
1772 # alternative representations
1773 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1774 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001775 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001776 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001777 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001778 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1779 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001780 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001781 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001782
Amos Kong05dfb262014-06-10 19:25:53 +08001783eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001784pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001785
Eric Blake437db252015-09-29 16:21:02 -06001786
Michael Roth0f923be2011-07-19 14:50:39 -05001787def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001788 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001789 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001790 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001791 return ret
1792
1793indent_level = 0
1794
Eric Blake437db252015-09-29 16:21:02 -06001795
Michael Roth0f923be2011-07-19 14:50:39 -05001796def push_indent(indent_amount=4):
1797 global indent_level
1798 indent_level += indent_amount
1799
Eric Blake437db252015-09-29 16:21:02 -06001800
Michael Roth0f923be2011-07-19 14:50:39 -05001801def pop_indent(indent_amount=4):
1802 global indent_level
1803 indent_level -= indent_amount
1804
Eric Blake437db252015-09-29 16:21:02 -06001805
Markus Armbruster77e703b2015-06-24 19:27:32 +02001806# Generate @code with @kwds interpolated.
1807# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001808def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001809 raw = code % kwds
1810 if indent_level:
1811 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001812 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001813 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001814 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001815 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001816 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001817
Eric Blake437db252015-09-29 16:21:02 -06001818
Michael Roth0f923be2011-07-19 14:50:39 -05001819def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001820 if code[0] == '\n':
1821 code = code[1:]
1822 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001823
Michael Roth0f923be2011-07-19 14:50:39 -05001824
1825def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001826 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001827
Eric Blake437db252015-09-29 16:21:02 -06001828
Michael Rothc0afa9c2013-05-10 17:46:00 -05001829def guardstart(name):
1830 return mcgen('''
1831
1832#ifndef %(name)s
1833#define %(name)s
1834
1835''',
1836 name=guardname(name))
1837
Eric Blake437db252015-09-29 16:21:02 -06001838
Michael Rothc0afa9c2013-05-10 17:46:00 -05001839def guardend(name):
1840 return mcgen('''
1841
1842#endif /* %(name)s */
1843
1844''',
1845 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001846
Eric Blake437db252015-09-29 16:21:02 -06001847
Markus Armbrustere98859a2015-09-16 13:06:16 +02001848def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001849 ret = mcgen('''
1850
Markus Armbrustere98859a2015-09-16 13:06:16 +02001851const char *const %(c_name)s_lookup[] = {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001852''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001853 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001854 for value in values:
1855 index = c_enum_const(name, value, prefix)
1856 ret += mcgen('''
1857 [%(index)s] = "%(value)s",
1858''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001859 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001860
Eric Blake7fb1cf12015-11-18 01:52:57 -07001861 max_index = c_enum_const(name, '_MAX', prefix)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001862 ret += mcgen('''
1863 [%(max_index)s] = NULL,
1864};
1865''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001866 max_index=max_index)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001867 return ret
1868
Eric Blake437db252015-09-29 16:21:02 -06001869
Markus Armbrustere98859a2015-09-16 13:06:16 +02001870def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001871 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001872 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001873
1874 ret = mcgen('''
1875
1876typedef enum %(c_name)s {
1877''',
1878 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001879
1880 i = 0
1881 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001882 ret += mcgen('''
1883 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001884''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001885 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001886 i=i)
1887 i += 1
1888
Markus Armbrustere98859a2015-09-16 13:06:16 +02001889 ret += mcgen('''
1890} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001891''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001892 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001893
Markus Armbrustere98859a2015-09-16 13:06:16 +02001894 ret += mcgen('''
1895
1896extern const char *const %(c_name)s_lookup[];
1897''',
1898 c_name=c_name(name))
1899 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001900
Eric Blake437db252015-09-29 16:21:02 -06001901
Eric Blake48825ca2016-07-13 21:50:19 -06001902def gen_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001903 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001904 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001905 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001906 ret = ''
1907 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001908 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001909 ret += '%s arg' % arg_type.c_param_type()
1910 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001911 else:
1912 assert not arg_type.variants
1913 for memb in arg_type.members:
1914 ret += sep
1915 sep = ', '
1916 if memb.optional:
1917 ret += 'bool has_%s, ' % c_name(memb.name)
1918 ret += '%s %s' % (memb.type.c_param_type(),
1919 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001920 if extra:
1921 ret += sep + extra
1922 return ret
1923
Eric Blake1f353342015-09-29 16:21:13 -06001924
Markus Armbruster00e4b282015-06-10 10:04:36 +02001925#
1926# Common command line parsing
1927#
1928
Eric Blake437db252015-09-29 16:21:02 -06001929
Markus Armbrusteref801a92017-03-15 13:57:08 +01001930def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001931
1932 try:
1933 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001934 'chp:o:' + extra_options,
1935 ['source', 'header', 'prefix=',
1936 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001937 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001938 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001939 sys.exit(1)
1940
Markus Armbrusteref801a92017-03-15 13:57:08 +01001941 output_dir = ''
1942 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001943 do_c = False
1944 do_h = False
1945 extra_opts = []
1946
1947 for oa in opts:
1948 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001949 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001950 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001951 if match.end() != len(a):
1952 print >>sys.stderr, \
1953 "%s: 'funny character '%s' in argument of --prefix" \
1954 % (sys.argv[0], a[match.end()])
1955 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001956 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001957 elif o in ('-o', '--output-dir'):
1958 output_dir = a + '/'
1959 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001960 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001961 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001962 do_h = True
1963 else:
1964 extra_opts.append(oa)
1965
1966 if not do_c and not do_h:
1967 do_c = True
1968 do_h = True
1969
Markus Armbruster16d80f62015-04-02 13:32:16 +02001970 if len(args) != 1:
1971 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001972 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02001973 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001974
Markus Armbruster54414042015-06-09 16:22:45 +02001975 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001976
Markus Armbruster00e4b282015-06-10 10:04:36 +02001977#
1978# Generate output files with boilerplate
1979#
1980
Eric Blake437db252015-09-29 16:21:02 -06001981
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001982def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1983 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001984 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001985 c_file = output_dir + prefix + c_file
1986 h_file = output_dir + prefix + h_file
1987
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001988 if output_dir:
1989 try:
1990 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01001991 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001992 if e.errno != errno.EEXIST:
1993 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001994
1995 def maybe_open(really, name, opt):
1996 if really:
1997 return open(name, opt)
1998 else:
1999 import StringIO
2000 return StringIO.StringIO()
2001
2002 fdef = maybe_open(do_c, c_file, 'w')
2003 fdecl = maybe_open(do_h, h_file, 'w')
2004
2005 fdef.write(mcgen('''
2006/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2007%(comment)s
2008''',
Eric Blake437db252015-09-29 16:21:02 -06002009 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002010
2011 fdecl.write(mcgen('''
2012/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2013%(comment)s
2014#ifndef %(guard)s
2015#define %(guard)s
2016
2017''',
Eric Blake437db252015-09-29 16:21:02 -06002018 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002019
2020 return (fdef, fdecl)
2021
Eric Blake437db252015-09-29 16:21:02 -06002022
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002023def close_output(fdef, fdecl):
2024 fdecl.write('''
2025#endif
2026''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002027 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002028 fdef.close()