blob: 21a15918dcc8535865bab498b872641ee1453757 [file] [log] [blame]
Michael Roth0f923be2011-07-19 14:50:39 -05001#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
Eric Blakee4083112016-01-29 06:48:41 -07005# Copyright (c) 2013-2016 Red Hat Inc.
Michael Roth0f923be2011-07-19 14:50:39 -05006#
7# Authors:
8# Anthony Liguori <aliguori@us.ibm.com>
Markus Armbrusterc7a3f252013-07-27 17:41:55 +02009# Markus Armbruster <armbru@redhat.com>
Michael Roth0f923be2011-07-19 14:50:39 -050010#
Markus Armbruster678e48a2014-03-01 08:40:34 +010011# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
Michael Roth0f923be2011-07-19 14:50:39 -050013
Lluís Vilanovaa719a272014-05-07 20:46:15 +020014import re
Michael Roth0f923be2011-07-19 14:50:39 -050015from ordereddict import OrderedDict
Markus Armbruster12f8e1b2015-04-02 14:46:39 +020016import errno
Markus Armbruster2114f5a2015-04-02 13:12:21 +020017import getopt
Lluís Vilanova33aaad52014-05-02 15:52:35 +020018import os
Markus Armbruster2caba362013-07-27 17:41:56 +020019import sys
Markus Armbruster47299262015-05-14 06:50:47 -060020import string
Michael Roth0f923be2011-07-19 14:50:39 -050021
Eric Blakeb52c4b92015-05-04 09:05:00 -060022builtin_types = {
Kevin Wolf69dd62d2013-07-08 16:14:21 +020023 'str': 'QTYPE_QSTRING',
24 'int': 'QTYPE_QINT',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
27 'int8': 'QTYPE_QINT',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
Eric Blakecb17f792015-05-04 09:05:01 -060035 'size': 'QTYPE_QINT',
Eric Blake1310a3d2015-12-01 22:20:46 -070036 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070037 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020038}
39
Markus Armbrusterbc52d032017-03-15 13:56:51 +010040# Are documentation comments required?
41doc_required = False
42
Eric Blake10d4d992015-05-04 09:05:23 -060043# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010044returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060045
Eric Blake893e1f22015-12-01 22:20:57 -070046# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010047name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070048
Eric Blake4dc2e692015-05-04 09:05:17 -060049enum_types = []
50struct_types = []
51union_types = []
52events = []
53all_names = {}
54
Markus Armbruster00e4b282015-06-10 10:04:36 +020055#
56# Parsing the schema into expressions
57#
58
Eric Blake437db252015-09-29 16:21:02 -060059
Lluís Vilanovaa719a272014-05-07 20:46:15 +020060def error_path(parent):
Markus Armbrusteref801a92017-03-15 13:57:08 +010061 res = ''
Lluís Vilanovaa719a272014-05-07 20:46:15 +020062 while parent:
Markus Armbrusteref801a92017-03-15 13:57:08 +010063 res = ('In file included from %s:%d:\n' % (parent['file'],
Lluís Vilanovaa719a272014-05-07 20:46:15 +020064 parent['line'])) + res
65 parent = parent['parent']
66 return res
67
Eric Blake437db252015-09-29 16:21:02 -060068
Marc-André Lureau4148c292017-01-13 15:41:25 +010069class QAPIError(Exception):
70 def __init__(self, fname, line, col, incl_info, msg):
Eric Blake59b00542015-09-29 16:21:01 -060071 Exception.__init__(self)
Marc-André Lureau4148c292017-01-13 15:41:25 +010072 self.fname = fname
73 self.line = line
74 self.col = col
75 self.info = incl_info
Markus Armbruster2caba362013-07-27 17:41:56 +020076 self.msg = msg
Marc-André Lureau4148c292017-01-13 15:41:25 +010077
78 def __str__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +010079 loc = '%s:%d' % (self.fname, self.line)
Marc-André Lureau4148c292017-01-13 15:41:25 +010080 if self.col is not None:
Markus Armbrusteref801a92017-03-15 13:57:08 +010081 loc += ':%s' % self.col
82 return error_path(self.info) + '%s: %s' % (loc, self.msg)
Marc-André Lureau4148c292017-01-13 15:41:25 +010083
84
85class QAPIParseError(QAPIError):
86 def __init__(self, parser, msg):
87 col = 1
88 for ch in parser.src[parser.line_pos:parser.pos]:
Wenchao Xia515b9432014-03-04 18:44:33 -080089 if ch == '\t':
Marc-André Lureau4148c292017-01-13 15:41:25 +010090 col = (col + 7) % 8 + 1
Markus Armbruster2caba362013-07-27 17:41:56 +020091 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +010092 col += 1
93 QAPIError.__init__(self, parser.fname, parser.line, col,
94 parser.incl_info, msg)
Markus Armbruster2caba362013-07-27 17:41:56 +020095
Eric Blake437db252015-09-29 16:21:02 -060096
Marc-André Lureau4148c292017-01-13 15:41:25 +010097class QAPISemError(QAPIError):
98 def __init__(self, info, msg):
99 QAPIError.__init__(self, info['file'], info['line'], None,
100 info['parent'], msg)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800101
Eric Blake437db252015-09-29 16:21:02 -0600102
Marc-André Lureau3313b612017-01-13 15:41:29 +0100103class QAPIDoc(object):
104 class Section(object):
105 def __init__(self, name=None):
106 # optional section name (argument/member or section name)
107 self.name = name
108 # the list of lines for this section
109 self.content = []
Markus Armbrusterb116fd82017-03-15 13:57:00 +0100110 self.optional = False
Marc-André Lureau3313b612017-01-13 15:41:29 +0100111
112 def append(self, line):
113 self.content.append(line)
114
115 def __repr__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100116 return '\n'.join(self.content).strip()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100117
118 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100119 def __init__(self, name):
120 QAPIDoc.Section.__init__(self, name)
121 self.member = None
122
123 def connect(self, member):
124 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100125
126 def __init__(self, parser, info):
127 # self.parser is used to report errors with QAPIParseError. The
128 # resulting error position depends on the state of the parser.
129 # It happens to be the beginning of the comment. More or less
130 # servicable, but action at a distance.
131 self.parser = parser
132 self.info = info
133 self.symbol = None
134 self.body = QAPIDoc.Section()
135 # dict mapping parameter name to ArgSection
136 self.args = OrderedDict()
137 # a list of Section
138 self.sections = []
139 # the current section
140 self.section = self.body
141 # associated expression (to be set by expression parser)
142 self.expr = None
143
144 def has_section(self, name):
145 """Return True if we have a section with this name."""
146 for i in self.sections:
147 if i.name == name:
148 return True
149 return False
150
151 def append(self, line):
152 """Parse a comment line and add it to the documentation."""
153 line = line[1:]
154 if not line:
155 self._append_freeform(line)
156 return
157
158 if line[0] != ' ':
159 raise QAPIParseError(self.parser, "Missing space after #")
160 line = line[1:]
161
162 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
163 # recognized, and get silently treated as ordinary text
164 if self.symbol:
165 self._append_symbol_line(line)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100166 elif not self.body.content and line.startswith('@'):
167 if not line.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100168 raise QAPIParseError(self.parser, "Line should end with :")
169 self.symbol = line[1:-1]
170 # FIXME invalid names other than the empty string aren't flagged
171 if not self.symbol:
172 raise QAPIParseError(self.parser, "Invalid name")
173 else:
174 self._append_freeform(line)
175
176 def _append_symbol_line(self, line):
177 name = line.split(' ', 1)[0]
178
Markus Armbrusteref801a92017-03-15 13:57:08 +0100179 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100180 line = line[len(name)+1:]
181 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100182 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100183 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100184 'Note:', 'Notes:',
185 'Example:', 'Examples:',
186 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100187 line = line[len(name)+1:]
188 self._start_section(name[:-1])
189
190 self._append_freeform(line)
191
192 def _start_args_section(self, name):
193 # FIXME invalid names other than the empty string aren't flagged
194 if not name:
195 raise QAPIParseError(self.parser, "Invalid parameter name")
196 if name in self.args:
197 raise QAPIParseError(self.parser,
198 "'%s' parameter name duplicated" % name)
199 if self.sections:
200 raise QAPIParseError(self.parser,
201 "'@%s:' can't follow '%s' section"
202 % (name, self.sections[0].name))
203 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)
210 self.section = QAPIDoc.Section(name)
211 self.sections.append(self.section)
212
213 def _append_freeform(self, line):
214 in_arg = isinstance(self.section, QAPIDoc.ArgSection)
215 if (in_arg and self.section.content
216 and not self.section.content[-1]
217 and line and not line[0].isspace()):
218 self._start_section()
219 if (in_arg or not self.section.name
Markus Armbrusteref801a92017-03-15 13:57:08 +0100220 or not self.section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100221 line = line.strip()
Markus Armbruster1d8bda12017-03-15 13:57:06 +0100222 # TODO Drop this once the dust has settled
223 if (isinstance(self.section, QAPIDoc.ArgSection)
224 and '#optional' in line):
225 raise QAPISemError(self.info, "Please drop the #optional tag")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100226 self.section.append(line)
227
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100228 def connect_member(self, member):
229 if member.name not in self.args:
230 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100231 self.args[member.name] = QAPIDoc.ArgSection(member.name)
232 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100233
Marc-André Lureau3313b612017-01-13 15:41:29 +0100234
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200235class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500236
Eric Blake437db252015-09-29 16:21:02 -0600237 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200238 abs_fname = os.path.abspath(fp.name)
Markus Armbruster8608d252015-06-09 18:32:29 +0200239 fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200240 self.fname = fname
Markus Armbruster54414042015-06-09 16:22:45 +0200241 previously_included.append(abs_fname)
242 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200243 self.src = fp.read()
244 if self.src == '' or self.src[-1] != '\n':
245 self.src += '\n'
246 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800247 self.line = 1
248 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200249 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100250 self.docs = []
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200251 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500252
Eric Blake437db252015-09-29 16:21:02 -0600253 while self.tok is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100254 info = {'file': fname, 'line': self.line,
255 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100256 if self.tok == '#':
257 doc = self.get_doc(info)
258 self.docs.append(doc)
259 continue
260
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200261 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100262 if 'include' in expr:
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200263 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100264 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100265 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200266 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100267 raise QAPISemError(info,
268 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100269 self._include(include, info, os.path.dirname(abs_fname),
270 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100271 elif "pragma" in expr:
272 if len(expr) != 1:
273 raise QAPISemError(info, "Invalid 'pragma' directive")
274 pragma = expr['pragma']
275 if not isinstance(pragma, dict):
276 raise QAPISemError(
277 info, "Value of 'pragma' must be a dictionary")
278 for name, value in pragma.iteritems():
279 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200280 else:
281 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100282 'info': info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100283 if (self.docs
284 and self.docs[-1].info['file'] == fname
285 and not self.docs[-1].expr):
286 self.docs[-1].expr = expr
287 expr_elem['doc'] = self.docs[-1]
288
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200289 self.exprs.append(expr_elem)
Michael Roth0f923be2011-07-19 14:50:39 -0500290
Markus Armbrustere04dea82017-03-15 13:56:50 +0100291 def _include(self, include, info, base_dir, previously_included):
292 incl_abs_fname = os.path.join(base_dir, include)
293 # catch inclusion cycle
294 inf = info
295 while inf:
296 if incl_abs_fname == os.path.abspath(inf['file']):
297 raise QAPISemError(info, "Inclusion loop for %s" % include)
298 inf = inf['parent']
299
300 # skip multiple include of the same file
301 if incl_abs_fname in previously_included:
302 return
303 try:
304 fobj = open(incl_abs_fname, 'r')
305 except IOError as e:
306 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
307 exprs_include = QAPISchemaParser(fobj, previously_included, info)
308 self.exprs.extend(exprs_include.exprs)
309 self.docs.extend(exprs_include.docs)
310
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100311 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100312 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100313 if name == 'doc-required':
314 if not isinstance(value, bool):
315 raise QAPISemError(info,
316 "Pragma 'doc-required' must be boolean")
317 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100318 elif name == 'returns-whitelist':
319 if (not isinstance(value, list)
320 or any([not isinstance(elt, str) for elt in value])):
321 raise QAPISemError(info,
322 "Pragma returns-whitelist must be"
323 " a list of strings")
324 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100325 elif name == 'name-case-whitelist':
326 if (not isinstance(value, list)
327 or any([not isinstance(elt, str) for elt in value])):
328 raise QAPISemError(info,
329 "Pragma name-case-whitelist must be"
330 " a list of strings")
331 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100332 else:
333 raise QAPISemError(info, "Unknown pragma '%s'" % name)
334
Marc-André Lureau3313b612017-01-13 15:41:29 +0100335 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200336 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200337 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200338 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200339 self.cursor += 1
340 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500341
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200342 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100343 if self.src[self.cursor] == '#':
344 # Start of doc comment
345 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200346 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100347 if not skip_comment:
348 self.val = self.src[self.pos:self.cursor]
349 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100350 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200351 return
352 elif self.tok == "'":
353 string = ''
354 esc = False
355 while True:
356 ch = self.src[self.cursor]
357 self.cursor += 1
358 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100359 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200360 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600361 if ch == 'b':
362 string += '\b'
363 elif ch == 'f':
364 string += '\f'
365 elif ch == 'n':
366 string += '\n'
367 elif ch == 'r':
368 string += '\r'
369 elif ch == 't':
370 string += '\t'
371 elif ch == 'u':
372 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600373 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600374 ch = self.src[self.cursor]
375 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100376 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100377 raise QAPIParseError(self,
378 '\\u escape needs 4 '
379 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600380 value = (value << 4) + int(ch, 16)
381 # If Python 2 and 3 didn't disagree so much on
382 # how to handle Unicode, then we could allow
383 # Unicode string defaults. But most of QAPI is
384 # ASCII-only, so we aren't losing much for now.
385 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100386 raise QAPIParseError(self,
387 'For now, \\u escape '
388 'only supports non-zero '
389 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600390 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100391 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600392 string += ch
393 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100394 raise QAPIParseError(self,
395 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200396 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100397 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200398 esc = True
399 elif ch == "'":
400 self.val = string
401 return
402 else:
403 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100404 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200405 self.val = True
406 self.cursor += 3
407 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100408 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200409 self.val = False
410 self.cursor += 4
411 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100412 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200413 self.val = None
414 self.cursor += 3
415 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200416 elif self.tok == '\n':
417 if self.cursor == len(self.src):
418 self.tok = None
419 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800420 self.line += 1
421 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200422 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100423 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500424
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200425 def get_members(self):
426 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200427 if self.tok == '}':
428 self.accept()
429 return expr
430 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100431 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200432 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200433 key = self.val
434 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200435 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100436 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200437 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800438 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100439 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200440 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200441 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200442 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200443 return expr
444 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100445 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200446 self.accept()
447 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100448 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500449
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200450 def get_values(self):
451 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200452 if self.tok == ']':
453 self.accept()
454 return expr
Eric Blake437db252015-09-29 16:21:02 -0600455 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100456 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
457 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200458 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200459 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200460 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200461 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200462 return expr
463 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100464 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200465 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500466
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200467 def get_expr(self, nested):
468 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100469 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200470 if self.tok == '{':
471 self.accept()
472 expr = self.get_members()
473 elif self.tok == '[':
474 self.accept()
475 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600476 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200477 expr = self.val
478 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200479 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100480 raise QAPIParseError(self, 'Expected "{", "[" or string')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200481 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200482
Marc-André Lureau3313b612017-01-13 15:41:29 +0100483 def get_doc(self, info):
484 if self.val != '##':
485 raise QAPIParseError(self, "Junk after '##' at start of "
486 "documentation comment")
487
488 doc = QAPIDoc(self, info)
489 self.accept(False)
490 while self.tok == '#':
491 if self.val.startswith('##'):
492 # End of doc comment
493 if self.val != '##':
494 raise QAPIParseError(self, "Junk after '##' at end of "
495 "documentation comment")
496 self.accept()
497 return doc
498 else:
499 doc.append(self.val)
500 self.accept(False)
501
502 raise QAPIParseError(self, "Documentation comment must end with '##'")
503
504
Markus Armbruster00e4b282015-06-10 10:04:36 +0200505#
506# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200507# TODO fold into QAPISchema
508# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200509#
510
Eric Blake437db252015-09-29 16:21:02 -0600511
Eric Blake14f00c62016-03-03 09:16:43 -0700512def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600513 if isinstance(base, dict):
514 return base
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800515 base_struct_define = find_struct(base)
516 if not base_struct_define:
517 return None
518 return base_struct_define['data']
519
Eric Blake437db252015-09-29 16:21:02 -0600520
Eric Blake811d04f2015-05-04 09:05:10 -0600521# Return the qtype of an alternate branch, or None on error.
522def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600523 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600524 return builtin_types[qapi_type]
525 elif find_struct(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100526 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600527 elif find_enum(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100528 return 'QTYPE_QSTRING'
Eric Blake811d04f2015-05-04 09:05:10 -0600529 elif find_union(qapi_type):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100530 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600531 return None
532
Eric Blake437db252015-09-29 16:21:02 -0600533
Wenchao Xiabceae762014-03-06 17:08:56 -0800534# Return the discriminator enum define if discriminator is specified as an
535# enum type, otherwise return None.
536def discriminator_find_enum_define(expr):
537 base = expr.get('base')
538 discriminator = expr.get('discriminator')
539
540 if not (discriminator and base):
541 return None
542
Eric Blake14f00c62016-03-03 09:16:43 -0700543 base_members = find_base_members(base)
544 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800545 return None
546
Eric Blake14f00c62016-03-03 09:16:43 -0700547 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800548 if not discriminator_type:
549 return None
550
551 return find_enum(discriminator_type)
552
Eric Blake437db252015-09-29 16:21:02 -0600553
Eric Blake59a92fe2015-11-18 01:52:56 -0700554# Names must be letters, numbers, -, and _. They must start with letter,
555# except for downstream extensions which must start with __RFQDN_.
556# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100557valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700558 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600559
560
Marc-André Lureau4148c292017-01-13 15:41:25 +0100561def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600562 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600563 global valid_name
564 membername = name
565
566 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100567 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600568 if name.startswith('*'):
569 membername = name[1:]
570 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100571 raise QAPISemError(info, "%s does not allow optional name '%s'"
572 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600573 # Enum members can start with a digit, because the generated C
574 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700575 if enum_member and membername[0].isdigit():
576 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600577 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
578 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600579 if not valid_name.match(membername) or \
580 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100581 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600582
Eric Blake437db252015-09-29 16:21:02 -0600583
584def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200585 global all_names
586 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200587 # FIXME should reject names that differ only in '_' vs. '.'
588 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200589 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100590 raise QAPISemError(info, "%s '%s' is already defined"
591 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600592 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100593 raise QAPISemError(info, "%s '%s' should not end in '%s'"
594 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200595 all_names[name] = meta
596
Eric Blake437db252015-09-29 16:21:02 -0600597
Markus Armbruster00e4b282015-06-10 10:04:36 +0200598def add_struct(definition, info):
599 global struct_types
600 name = definition['struct']
601 add_name(name, info, 'struct')
602 struct_types.append(definition)
603
Eric Blake437db252015-09-29 16:21:02 -0600604
Markus Armbruster00e4b282015-06-10 10:04:36 +0200605def find_struct(name):
606 global struct_types
607 for struct in struct_types:
608 if struct['struct'] == name:
609 return struct
610 return None
611
Eric Blake437db252015-09-29 16:21:02 -0600612
Markus Armbruster00e4b282015-06-10 10:04:36 +0200613def add_union(definition, info):
614 global union_types
615 name = definition['union']
616 add_name(name, info, 'union')
617 union_types.append(definition)
618
Eric Blake437db252015-09-29 16:21:02 -0600619
Markus Armbruster00e4b282015-06-10 10:04:36 +0200620def find_union(name):
621 global union_types
622 for union in union_types:
623 if union['union'] == name:
624 return union
625 return None
626
Eric Blake437db252015-09-29 16:21:02 -0600627
628def add_enum(name, info, enum_values=None, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200629 global enum_types
630 add_name(name, info, 'enum', implicit)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100631 enum_types.append({'enum_name': name, 'enum_values': enum_values})
Markus Armbruster00e4b282015-06-10 10:04:36 +0200632
Eric Blake437db252015-09-29 16:21:02 -0600633
Markus Armbruster00e4b282015-06-10 10:04:36 +0200634def find_enum(name):
635 global enum_types
636 for enum in enum_types:
637 if enum['enum_name'] == name:
638 return enum
639 return None
640
Markus Armbruster00e4b282015-06-10 10:04:36 +0200641
Eric Blake437db252015-09-29 16:21:02 -0600642def is_enum(name):
643 return find_enum(name) is not None
644
645
Marc-André Lureau4148c292017-01-13 15:41:25 +0100646def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600647 allow_dict=False, allow_optional=False,
648 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600649 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600650
651 if value is None:
652 return
653
Eric Blakedd883c62015-05-04 09:05:21 -0600654 # Check if array type for value is okay
655 if isinstance(value, list):
656 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100657 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600658 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100659 raise QAPISemError(info,
660 "%s: array type must contain single type name" %
661 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600662 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600663
664 # Check if type name for value is okay
665 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600666 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100667 raise QAPISemError(info, "%s uses unknown type '%s'"
668 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600669 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100670 raise QAPISemError(info, "%s cannot use %s type '%s'" %
671 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600672 return
673
Eric Blakedd883c62015-05-04 09:05:21 -0600674 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100675 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200676
677 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100678 raise QAPISemError(info,
679 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200680
681 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600682 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100683 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600684 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600685 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100686 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
687 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600688 # Todo: allow dictionaries to represent default values of
689 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100690 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200691 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600692 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600693 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600694
Eric Blake437db252015-09-29 16:21:02 -0600695
Marc-André Lureau4148c292017-01-13 15:41:25 +0100696def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600697 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600698 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600699
Eric Blakec8184082016-07-13 21:50:20 -0600700 args_meta = ['struct']
701 if boxed:
702 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100703 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600704 expr.get('data'), allow_dict=not boxed, allow_optional=True,
705 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600706 returns_meta = ['union', 'struct']
707 if name in returns_whitelist:
708 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100709 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200710 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200711 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600712
Eric Blake437db252015-09-29 16:21:02 -0600713
Marc-André Lureau4148c292017-01-13 15:41:25 +0100714def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600715 global events
716 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600717 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600718
Eric Blakec8184082016-07-13 21:50:20 -0600719 meta = ['struct']
720 if boxed:
721 meta += ['union', 'alternate']
Eric Blake4dc2e692015-05-04 09:05:17 -0600722 events.append(name)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100723 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600724 expr.get('data'), allow_dict=not boxed, allow_optional=True,
725 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200726
Eric Blake437db252015-09-29 16:21:02 -0600727
Marc-André Lureau4148c292017-01-13 15:41:25 +0100728def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800729 name = expr['union']
730 base = expr.get('base')
731 discriminator = expr.get('discriminator')
732 members = expr['data']
733
Eric Blake811d04f2015-05-04 09:05:10 -0600734 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600735
736 # With no discriminator it is a simple union.
737 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600738 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600739 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600740 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100741 raise QAPISemError(info, "Simple union '%s' must not have a base" %
742 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600743
744 # Else, it's a flat union.
745 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600746 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100747 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600748 base, allow_dict=True, allow_optional=True,
749 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600750 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100751 raise QAPISemError(info, "Flat union '%s' must have a base"
752 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700753 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100754 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800755
Eric Blakec9e0a792015-05-04 09:05:22 -0600756 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600757 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100758 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600759 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700760 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800761 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100762 raise QAPISemError(info,
763 "Discriminator '%s' is not a member of base "
764 "struct '%s'"
765 % (discriminator, base))
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800766 enum_define = find_enum(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600767 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800768 # Do not allow string discriminator
769 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100770 raise QAPISemError(info,
771 "Discriminator '%s' must be of enumeration "
772 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800773
Eric Blake02a57ae2016-02-17 23:48:16 -0700774 # Check every branch; don't allow an empty union
775 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100776 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800777 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100778 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600779
Eric Blake01cfbaa2015-12-01 22:20:58 -0700780 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100781 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200782 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600783
Eric Blake44bd1272015-05-04 09:05:08 -0600784 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700785 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600786 if enum_define:
Eric Blake437db252015-09-29 16:21:02 -0600787 if key not in enum_define['enum_values']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100788 raise QAPISemError(info,
789 "Discriminator value '%s' is not found in "
790 "enum '%s'"
Markus Armbrusteref801a92017-03-15 13:57:08 +0100791 % (key, enum_define['enum_name']))
Eric Blake44bd1272015-05-04 09:05:08 -0600792
Eric Blaked0b18232016-07-13 21:50:13 -0600793 # If discriminator is user-defined, ensure all values are covered
794 if enum_define:
795 for value in enum_define['enum_values']:
796 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100797 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
798 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600799
Eric Blake437db252015-09-29 16:21:02 -0600800
Marc-André Lureau4148c292017-01-13 15:41:25 +0100801def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600802 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600803 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600804 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600805
Eric Blake02a57ae2016-02-17 23:48:16 -0700806 # Check every branch; require at least two branches
807 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100808 raise QAPISemError(info,
809 "Alternate '%s' should have at least two branches "
810 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600811 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100812 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600813
Eric Blake811d04f2015-05-04 09:05:10 -0600814 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100815 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600816 value,
817 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600818 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700819 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100820 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
821 "type '%s'" % (name, key, value))
Eric Blake811d04f2015-05-04 09:05:10 -0600822 if qtype in types_seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100823 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
824 "be distinguished from member '%s'"
825 % (name, key, types_seen[qtype]))
Eric Blake811d04f2015-05-04 09:05:10 -0600826 types_seen[qtype] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800827
Eric Blake437db252015-09-29 16:21:02 -0600828
Marc-André Lureau4148c292017-01-13 15:41:25 +0100829def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600830 name = expr['enum']
831 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100832 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600833
834 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100835 raise QAPISemError(info,
836 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100837 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100838 raise QAPISemError(info,
839 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600840 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100841 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600842 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600843
Eric Blake437db252015-09-29 16:21:02 -0600844
Marc-André Lureau4148c292017-01-13 15:41:25 +0100845def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600846 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600847 members = expr['data']
848
Marc-André Lureau4148c292017-01-13 15:41:25 +0100849 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600850 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100851 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600852 allow_metas=['struct'])
853
Eric Blake437db252015-09-29 16:21:02 -0600854
Eric Blake0545f6b2015-05-04 09:05:15 -0600855def check_keys(expr_elem, meta, required, optional=[]):
856 expr = expr_elem['expr']
857 info = expr_elem['info']
858 name = expr[meta]
859 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100860 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600861 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600862 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600863 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100864 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
865 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600866 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100867 raise QAPISemError(info,
868 "'%s' of %s '%s' should only use false value"
869 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600870 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100871 raise QAPISemError(info,
872 "'%s' of %s '%s' should only use true value"
873 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600874 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600875 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100876 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
877 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600878
Eric Blake437db252015-09-29 16:21:02 -0600879
Markus Armbruster4d076d62015-06-10 08:55:21 +0200880def check_exprs(exprs):
881 global all_names
882
883 # Learn the types and check for valid expression keys
884 for builtin in builtin_types.keys():
885 all_names[builtin] = 'built-in'
886 for expr_elem in exprs:
887 expr = expr_elem['expr']
888 info = expr_elem['info']
Marc-André Lureau3313b612017-01-13 15:41:29 +0100889
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100890 if 'doc' not in expr_elem and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100891 raise QAPISemError(info,
892 "Expression missing documentation comment")
893
Eric Blake437db252015-09-29 16:21:02 -0600894 if 'enum' in expr:
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100895 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200896 add_enum(expr['enum'], info, expr['data'])
Eric Blake437db252015-09-29 16:21:02 -0600897 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200898 check_keys(expr_elem, 'union', ['data'],
899 ['base', 'discriminator'])
900 add_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600901 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200902 check_keys(expr_elem, 'alternate', ['data'])
903 add_name(expr['alternate'], info, 'alternate')
Eric Blake437db252015-09-29 16:21:02 -0600904 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200905 check_keys(expr_elem, 'struct', ['data'], ['base'])
906 add_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600907 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200908 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600909 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200910 add_name(expr['command'], info, 'command')
Eric Blake437db252015-09-29 16:21:02 -0600911 elif 'event' in expr:
Eric Blakec8184082016-07-13 21:50:20 -0600912 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200913 add_name(expr['event'], info, 'event')
914 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100915 raise QAPISemError(expr_elem['info'],
916 "Expression is missing metatype")
Markus Armbruster4d076d62015-06-10 08:55:21 +0200917
918 # Try again for hidden UnionKind enum
919 for expr_elem in exprs:
920 expr = expr_elem['expr']
Eric Blake437db252015-09-29 16:21:02 -0600921 if 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200922 if not discriminator_find_enum_define(expr):
923 add_enum('%sKind' % expr['union'], expr_elem['info'],
924 implicit=True)
Eric Blake437db252015-09-29 16:21:02 -0600925 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200926 add_enum('%sKind' % expr['alternate'], expr_elem['info'],
927 implicit=True)
928
929 # Validate that exprs make sense
930 for expr_elem in exprs:
931 expr = expr_elem['expr']
932 info = expr_elem['info']
933
Eric Blake437db252015-09-29 16:21:02 -0600934 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200935 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600936 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200937 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600938 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200939 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600940 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200941 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600942 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200943 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600944 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200945 check_event(expr, info)
946 else:
947 assert False, 'unexpected meta type'
948
Markus Armbrusterac882192015-09-16 13:06:05 +0200949 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600950
Markus Armbrusterac882192015-09-16 13:06:05 +0200951
Marc-André Lureau3313b612017-01-13 15:41:29 +0100952def check_freeform_doc(doc):
953 if doc.symbol:
954 raise QAPISemError(doc.info,
955 "Documention for '%s' is not followed"
956 " by the definition" % doc.symbol)
957
958 body = str(doc.body)
959 if re.search(r'@\S+:', body, re.MULTILINE):
960 raise QAPISemError(doc.info,
961 "Free-form documentation block must not contain"
962 " @NAME: sections")
963
964
965def check_definition_doc(doc, expr, info):
966 for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
967 if i in expr:
968 meta = i
969 break
970
971 name = expr[meta]
972 if doc.symbol != name:
973 raise QAPISemError(info, "Definition of '%s' follows documentation"
974 " for '%s'" % (name, doc.symbol))
975 if doc.has_section('Returns') and 'command' not in expr:
976 raise QAPISemError(info, "'Returns:' is only valid for commands")
977
978 if meta == 'union':
979 args = expr.get('base', [])
980 else:
981 args = expr.get('data', [])
982 if isinstance(args, str):
983 return
984 if isinstance(args, dict):
985 args = args.keys()
986 assert isinstance(args, list)
987
988 if (meta == 'alternate'
989 or (meta == 'union' and not expr.get('discriminator'))):
990 args.append('type')
991
Marc-André Lureau3313b612017-01-13 15:41:29 +0100992 doc_args = set(doc.args.keys())
993 args = set([name.strip('*') for name in args])
994 if not doc_args.issubset(args):
995 raise QAPISemError(info, "The following documented members are not in "
Markus Armbrusteref801a92017-03-15 13:57:08 +0100996 "the declaration: %s" % ', '.join(doc_args - args))
Marc-André Lureau3313b612017-01-13 15:41:29 +0100997
998
999def check_docs(docs):
1000 for doc in docs:
1001 for section in doc.args.values() + doc.sections:
1002 content = str(section)
1003 if not content or content.isspace():
1004 raise QAPISemError(doc.info,
1005 "Empty doc section '%s'" % section.name)
1006
1007 if not doc.expr:
1008 check_freeform_doc(doc)
1009 else:
1010 check_definition_doc(doc, doc.expr, doc.info)
1011
1012 return docs
1013
1014
Markus Armbrusterac882192015-09-16 13:06:05 +02001015#
1016# Schema compiler frontend
1017#
1018
1019class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001020 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001021 assert isinstance(name, str)
1022 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -06001023 # For explicitly defined entities, info points to the (explicit)
1024 # definition. For builtins (and their arrays), info is None.
1025 # For implicitly defined entities, info points to a place that
1026 # triggered the implicit definition (there may be more than one
1027 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +02001028 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001029 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +02001030
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001031 def c_name(self):
1032 return c_name(self.name)
1033
Markus Armbrusterac882192015-09-16 13:06:05 +02001034 def check(self, schema):
1035 pass
1036
Eric Blake49823c42015-10-12 22:22:27 -06001037 def is_implicit(self):
1038 return not self.info
1039
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001040 def visit(self, visitor):
1041 pass
1042
1043
1044class QAPISchemaVisitor(object):
1045 def visit_begin(self, schema):
1046 pass
1047
1048 def visit_end(self):
1049 pass
1050
Eric Blake25a0d9c2015-10-12 22:22:21 -06001051 def visit_needed(self, entity):
1052 # Default to visiting everything
1053 return True
1054
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001055 def visit_builtin_type(self, name, info, json_type):
1056 pass
1057
1058 def visit_enum_type(self, name, info, values, prefix):
1059 pass
1060
1061 def visit_array_type(self, name, info, element_type):
1062 pass
1063
1064 def visit_object_type(self, name, info, base, members, variants):
1065 pass
1066
Markus Armbruster39a18152015-09-16 13:06:28 +02001067 def visit_object_type_flat(self, name, info, members, variants):
1068 pass
1069
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001070 def visit_alternate_type(self, name, info, variants):
1071 pass
1072
1073 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001074 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001075 pass
1076
Eric Blake48825ca2016-07-13 21:50:19 -06001077 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001078 pass
1079
Markus Armbrusterac882192015-09-16 13:06:05 +02001080
1081class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001082 # Return the C type for common use.
1083 # For the types we commonly box, this is a pointer type.
1084 def c_type(self):
1085 pass
1086
1087 # Return the C type to be used in a parameter list.
1088 def c_param_type(self):
1089 return self.c_type()
1090
1091 # Return the C type to be used where we suppress boxing.
1092 def c_unboxed_type(self):
1093 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001094
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001095 def json_type(self):
1096 pass
1097
1098 def alternate_qtype(self):
1099 json2qtype = {
1100 'string': 'QTYPE_QSTRING',
1101 'number': 'QTYPE_QFLOAT',
1102 'int': 'QTYPE_QINT',
1103 'boolean': 'QTYPE_QBOOL',
1104 'object': 'QTYPE_QDICT'
1105 }
1106 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001107
Markus Armbruster691e0312017-03-15 13:57:14 +01001108 def doc_type(self):
1109 if self.is_implicit():
1110 return None
1111 return self.name
1112
Markus Armbrusterac882192015-09-16 13:06:05 +02001113
1114class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001115 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001116 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001117 assert not c_type or isinstance(c_type, str)
1118 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1119 'value')
1120 self._json_type_name = json_type
1121 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001122
1123 def c_name(self):
1124 return self.name
1125
Eric Blake4040d992016-03-17 16:48:28 -06001126 def c_type(self):
1127 return self._c_type_name
1128
1129 def c_param_type(self):
1130 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001131 return 'const ' + self._c_type_name
1132 return self._c_type_name
1133
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001134 def json_type(self):
1135 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001136
Markus Armbruster691e0312017-03-15 13:57:14 +01001137 def doc_type(self):
1138 return self.json_type()
1139
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001140 def visit(self, visitor):
1141 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1142
Markus Armbrusterac882192015-09-16 13:06:05 +02001143
1144class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001145 def __init__(self, name, info, doc, values, prefix):
1146 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001147 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001148 assert isinstance(v, QAPISchemaMember)
1149 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001150 assert prefix is None or isinstance(prefix, str)
1151 self.values = values
1152 self.prefix = prefix
1153
1154 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001155 seen = {}
1156 for v in self.values:
1157 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001158 if self.doc:
1159 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001160
Eric Blake99df5282015-10-12 22:22:32 -06001161 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001162 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1163 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001164
Eric Blake4040d992016-03-17 16:48:28 -06001165 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001166 return c_name(self.name)
1167
Eric Blake93bda4d2015-12-01 22:20:55 -07001168 def member_names(self):
1169 return [v.name for v in self.values]
1170
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001171 def json_type(self):
1172 return 'string'
1173
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001174 def visit(self, visitor):
1175 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001176 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001177
Markus Armbrusterac882192015-09-16 13:06:05 +02001178
1179class QAPISchemaArrayType(QAPISchemaType):
1180 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001181 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001182 assert isinstance(element_type, str)
1183 self._element_type_name = element_type
1184 self.element_type = None
1185
1186 def check(self, schema):
1187 self.element_type = schema.lookup_type(self._element_type_name)
1188 assert self.element_type
1189
Eric Blake99df5282015-10-12 22:22:32 -06001190 def is_implicit(self):
1191 return True
1192
Eric Blake4040d992016-03-17 16:48:28 -06001193 def c_type(self):
1194 return c_name(self.name) + pointer_suffix
1195
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001196 def json_type(self):
1197 return 'array'
1198
Markus Armbruster691e0312017-03-15 13:57:14 +01001199 def doc_type(self):
1200 elt_doc_type = self.element_type.doc_type()
1201 if not elt_doc_type:
1202 return None
1203 return 'array of ' + elt_doc_type
1204
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001205 def visit(self, visitor):
1206 visitor.visit_array_type(self.name, self.info, self.element_type)
1207
Markus Armbrusterac882192015-09-16 13:06:05 +02001208
1209class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001210 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001211 # struct has local_members, optional base, and no variants
1212 # flat union has base, variants, and no local_members
1213 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001214 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001215 assert base is None or isinstance(base, str)
1216 for m in local_members:
1217 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001218 m.set_owner(name)
1219 if variants is not None:
1220 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1221 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001222 self._base_name = base
1223 self.base = None
1224 self.local_members = local_members
1225 self.variants = variants
1226 self.members = None
1227
1228 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001229 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001230 raise QAPISemError(self.info,
1231 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001232 if self.members:
1233 return
1234 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001235 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001236 if self._base_name:
1237 self.base = schema.lookup_type(self._base_name)
1238 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001239 self.base.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001240 self.base.check_clash(schema, self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001241 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001242 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001243 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001244 if self.doc:
1245 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001246 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001247 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001248 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001249 assert self.variants.tag_member in self.members
Eric Blake27b60ab2015-11-18 01:52:51 -07001250 self.variants.check_clash(schema, self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001251
Eric Blake14f00c62016-03-03 09:16:43 -07001252 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001253 # and update seen to track the members seen so far. Report any errors
1254 # on behalf of info, which is not necessarily self.info
1255 def check_clash(self, schema, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001256 assert not self.variants # not implemented
1257 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001258 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001259
Eric Blake99df5282015-10-12 22:22:32 -06001260 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001261 # See QAPISchema._make_implicit_object_type(), as well as
1262 # _def_predefineds()
1263 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001264
Eric Blakeb6167702016-07-13 21:50:16 -06001265 def is_empty(self):
1266 assert self.members is not None
1267 return not self.members and not self.variants
1268
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001269 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001270 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001271 return QAPISchemaType.c_name(self)
1272
Eric Blake4040d992016-03-17 16:48:28 -06001273 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001274 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001275 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001276
Eric Blake4040d992016-03-17 16:48:28 -06001277 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001278 return c_name(self.name)
1279
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001280 def json_type(self):
1281 return 'object'
1282
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001283 def visit(self, visitor):
1284 visitor.visit_object_type(self.name, self.info,
1285 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001286 visitor.visit_object_type_flat(self.name, self.info,
1287 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001288
Markus Armbrusterac882192015-09-16 13:06:05 +02001289
Eric Blaked44f9ac2015-12-01 22:20:54 -07001290class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001291 role = 'member'
1292
Eric Blaked44f9ac2015-12-01 22:20:54 -07001293 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001294 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001295 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001296 self.owner = None
1297
1298 def set_owner(self, name):
1299 assert not self.owner
1300 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001301
Eric Blake27b60ab2015-11-18 01:52:51 -07001302 def check_clash(self, info, seen):
1303 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001304 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001305 raise QAPISemError(info,
1306 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001307 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001308 raise QAPISemError(info, "%s collides with %s" %
1309 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001310 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001311
Eric Blake88d4ef82015-11-18 01:52:50 -07001312 def _pretty_owner(self):
1313 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001314 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001315 # See QAPISchema._make_implicit_object_type() - reverse the
1316 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001317 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001318 if owner.endswith('-arg'):
1319 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001320 elif owner.endswith('-base'):
1321 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001322 else:
1323 assert owner.endswith('-wrapper')
1324 # Unreachable and not implemented
1325 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001326 if owner.endswith('Kind'):
1327 # See QAPISchema._make_implicit_enum_type()
1328 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001329 return '(%s of %s)' % (self.role, owner)
1330
1331 def describe(self):
1332 return "'%s' %s" % (self.name, self._pretty_owner())
1333
Markus Armbrusterac882192015-09-16 13:06:05 +02001334
Eric Blaked44f9ac2015-12-01 22:20:54 -07001335class QAPISchemaObjectTypeMember(QAPISchemaMember):
1336 def __init__(self, name, typ, optional):
1337 QAPISchemaMember.__init__(self, name)
1338 assert isinstance(typ, str)
1339 assert isinstance(optional, bool)
1340 self._type_name = typ
1341 self.type = None
1342 self.optional = optional
1343
1344 def check(self, schema):
1345 assert self.owner
1346 self.type = schema.lookup_type(self._type_name)
1347 assert self.type
1348
1349
Markus Armbrusterac882192015-09-16 13:06:05 +02001350class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001351 def __init__(self, tag_name, tag_member, variants):
1352 # Flat unions pass tag_name but not tag_member.
1353 # Simple unions and alternates pass tag_member but not tag_name.
1354 # After check(), tag_member is always set, and tag_name remains
1355 # a reliable witness of being used by a flat union.
1356 assert bool(tag_member) != bool(tag_name)
1357 assert (isinstance(tag_name, str) or
1358 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001359 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001360 for v in variants:
1361 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001362 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001363 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001364 self.variants = variants
1365
Eric Blake88d4ef82015-11-18 01:52:50 -07001366 def set_owner(self, name):
1367 for v in self.variants:
1368 v.set_owner(name)
1369
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001370 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001371 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001372 self.tag_member = seen[c_name(self._tag_name)]
1373 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001374 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1375 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001376 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001377 # Union names must match enum values; alternate names are
1378 # checked separately. Use 'seen' to tell the two apart.
1379 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001380 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001381 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001382 v.type.check(schema)
1383
Eric Blake27b60ab2015-11-18 01:52:51 -07001384 def check_clash(self, schema, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001385 for v in self.variants:
1386 # Reset seen map for each variant, since qapi names from one
1387 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001388 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blake27b60ab2015-11-18 01:52:51 -07001389 v.type.check_clash(schema, info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001390
Eric Blake437db252015-09-29 16:21:02 -06001391
Markus Armbrusterac882192015-09-16 13:06:05 +02001392class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001393 role = 'branch'
1394
Markus Armbrusterac882192015-09-16 13:06:05 +02001395 def __init__(self, name, typ):
1396 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1397
Markus Armbrusterac882192015-09-16 13:06:05 +02001398
1399class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001400 def __init__(self, name, info, doc, variants):
1401 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001402 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001403 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001404 variants.set_owner(name)
1405 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001406 self.variants = variants
1407
1408 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001409 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001410 # Not calling self.variants.check_clash(), because there's nothing
1411 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001412 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001413 # Alternate branch names have no relation to the tag enum values;
1414 # so we have to check for potential name collisions ourselves.
1415 seen = {}
1416 for v in self.variants.variants:
1417 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001418 if self.doc:
1419 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001420
Eric Blake4040d992016-03-17 16:48:28 -06001421 def c_type(self):
1422 return c_name(self.name) + pointer_suffix
1423
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001424 def json_type(self):
1425 return 'value'
1426
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001427 def visit(self, visitor):
1428 visitor.visit_alternate_type(self.name, self.info, self.variants)
1429
Eric Blakec8184082016-07-13 21:50:20 -06001430 def is_empty(self):
1431 return False
1432
Markus Armbrusterac882192015-09-16 13:06:05 +02001433
1434class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001435 def __init__(self, name, info, doc, arg_type, ret_type,
1436 gen, success_response, boxed):
1437 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001438 assert not arg_type or isinstance(arg_type, str)
1439 assert not ret_type or isinstance(ret_type, str)
1440 self._arg_type_name = arg_type
1441 self.arg_type = None
1442 self._ret_type_name = ret_type
1443 self.ret_type = None
1444 self.gen = gen
1445 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001446 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001447
1448 def check(self, schema):
1449 if self._arg_type_name:
1450 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001451 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1452 isinstance(self.arg_type, QAPISchemaAlternateType))
1453 self.arg_type.check(schema)
1454 if self.boxed:
1455 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001456 raise QAPISemError(self.info,
1457 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001458 else:
1459 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1460 assert not self.arg_type.variants
1461 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001462 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001463 if self._ret_type_name:
1464 self.ret_type = schema.lookup_type(self._ret_type_name)
1465 assert isinstance(self.ret_type, QAPISchemaType)
1466
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001467 def visit(self, visitor):
1468 visitor.visit_command(self.name, self.info,
1469 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001470 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001471
Markus Armbrusterac882192015-09-16 13:06:05 +02001472
1473class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001474 def __init__(self, name, info, doc, arg_type, boxed):
1475 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001476 assert not arg_type or isinstance(arg_type, str)
1477 self._arg_type_name = arg_type
1478 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001479 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001480
1481 def check(self, schema):
1482 if self._arg_type_name:
1483 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001484 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1485 isinstance(self.arg_type, QAPISchemaAlternateType))
1486 self.arg_type.check(schema)
1487 if self.boxed:
1488 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001489 raise QAPISemError(self.info,
1490 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001491 else:
1492 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1493 assert not self.arg_type.variants
1494 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001495 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001496
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001497 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001498 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001499
Markus Armbrusterac882192015-09-16 13:06:05 +02001500
1501class QAPISchema(object):
1502 def __init__(self, fname):
1503 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001504 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001505 self.exprs = check_exprs(parser.exprs)
1506 self.docs = check_docs(parser.docs)
Eric Blake7618b912015-10-12 22:22:22 -06001507 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001508 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001509 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001510 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001511 self._def_exprs()
1512 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001513 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001514 print >>sys.stderr, err
1515 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001516
Markus Armbrusterac882192015-09-16 13:06:05 +02001517 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001518 # Only the predefined types are allowed to not have info
1519 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001520 assert ent.name not in self._entity_dict
1521 self._entity_dict[ent.name] = ent
1522
1523 def lookup_entity(self, name, typ=None):
1524 ent = self._entity_dict.get(name)
1525 if typ and not isinstance(ent, typ):
1526 return None
1527 return ent
1528
1529 def lookup_type(self, name):
1530 return self.lookup_entity(name, QAPISchemaType)
1531
Eric Blake861877a2016-03-17 16:48:36 -06001532 def _def_builtin_type(self, name, json_type, c_type):
1533 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001534 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1535 # qapi-types.h from a single .c, all arrays of builtins must be
1536 # declared in the first file whether or not they are used. Nicer
1537 # would be to use lazy instantiation, while figuring out how to
1538 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001539 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001540
1541 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001542 for t in [('str', 'string', 'char' + pointer_suffix),
1543 ('number', 'number', 'double'),
1544 ('int', 'int', 'int64_t'),
1545 ('int8', 'int', 'int8_t'),
1546 ('int16', 'int', 'int16_t'),
1547 ('int32', 'int', 'int32_t'),
1548 ('int64', 'int', 'int64_t'),
1549 ('uint8', 'int', 'uint8_t'),
1550 ('uint16', 'int', 'uint16_t'),
1551 ('uint32', 'int', 'uint32_t'),
1552 ('uint64', 'int', 'uint64_t'),
1553 ('size', 'int', 'uint64_t'),
1554 ('bool', 'boolean', 'bool'),
1555 ('any', 'value', 'QObject' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001556 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001557 self.the_empty_object_type = QAPISchemaObjectType(
1558 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001559 self._def_entity(self.the_empty_object_type)
Eric Blake93bda4d2015-12-01 22:20:55 -07001560 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1561 'qstring', 'qdict', 'qlist',
1562 'qfloat', 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001563 self._def_entity(QAPISchemaEnumType('QType', None, None,
1564 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001565
Eric Blake93bda4d2015-12-01 22:20:55 -07001566 def _make_enum_members(self, values):
1567 return [QAPISchemaMember(v) for v in values]
1568
Eric Blake99df5282015-10-12 22:22:32 -06001569 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001570 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001571 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001572 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001573 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001574 return name
1575
Eric Blake99df5282015-10-12 22:22:32 -06001576 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001577 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001578 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001579 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001580 return name
1581
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001582 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001583 if not members:
1584 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001585 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001586 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001587 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001588 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001589 members, None))
1590 return name
1591
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001592 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001593 name = expr['enum']
1594 data = expr['data']
1595 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001596 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001597 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001598
Eric Blake99df5282015-10-12 22:22:32 -06001599 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001600 optional = False
1601 if name.startswith('*'):
1602 name = name[1:]
1603 optional = True
1604 if isinstance(typ, list):
1605 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001606 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001607 return QAPISchemaObjectTypeMember(name, typ, optional)
1608
Eric Blake99df5282015-10-12 22:22:32 -06001609 def _make_members(self, data, info):
1610 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001611 for (key, value) in data.iteritems()]
1612
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001613 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001614 name = expr['struct']
1615 base = expr.get('base')
1616 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001617 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001618 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001619 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001620
1621 def _make_variant(self, case, typ):
1622 return QAPISchemaObjectTypeVariant(case, typ)
1623
Eric Blake99df5282015-10-12 22:22:32 -06001624 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001625 if isinstance(typ, list):
1626 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001627 typ = self._make_array_type(typ[0], info)
1628 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001629 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001630 return QAPISchemaObjectTypeVariant(case, typ)
1631
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001632 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001633 name = expr['union']
1634 data = expr['data']
1635 base = expr.get('base')
1636 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001637 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001638 if isinstance(base, dict):
1639 base = (self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001640 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001641 if tag_name:
1642 variants = [self._make_variant(key, value)
1643 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001644 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001645 else:
Eric Blake99df5282015-10-12 22:22:32 -06001646 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001647 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001648 typ = self._make_implicit_enum_type(name, info,
1649 [v.name for v in variants])
1650 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001651 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001652 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001653 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001654 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001655 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001656 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001657
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001658 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001659 name = expr['alternate']
1660 data = expr['data']
1661 variants = [self._make_variant(key, value)
1662 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001663 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001664 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001665 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001666 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001667 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001668 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001669
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001670 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001671 name = expr['command']
1672 data = expr.get('data')
1673 rets = expr.get('returns')
1674 gen = expr.get('gen', True)
1675 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001676 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001677 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001678 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001679 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001680 if isinstance(rets, list):
1681 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001682 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001683 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1684 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001685
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001686 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001687 name = expr['event']
1688 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001689 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001690 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001691 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001692 name, info, doc, 'arg', self._make_members(data, info))
1693 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001694
1695 def _def_exprs(self):
1696 for expr_elem in self.exprs:
1697 expr = expr_elem['expr']
1698 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001699 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001700 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001701 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001702 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001703 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001704 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001705 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001706 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001707 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001708 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001709 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001710 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001711 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001712 else:
1713 assert False
1714
1715 def check(self):
1716 for ent in self._entity_dict.values():
1717 ent.check(self)
1718
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001719 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001720 visitor.visit_begin(self)
1721 for (name, entity) in sorted(self._entity_dict.items()):
1722 if visitor.visit_needed(entity):
1723 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001724 visitor.visit_end()
1725
Markus Armbruster2caba362013-07-27 17:41:56 +02001726
Markus Armbruster00e4b282015-06-10 10:04:36 +02001727#
1728# Code generation helpers
1729#
1730
Michael Roth0f923be2011-07-19 14:50:39 -05001731def camel_case(name):
1732 new_name = ''
1733 first = True
1734 for ch in name:
1735 if ch in ['_', '-']:
1736 first = True
1737 elif first:
1738 new_name += ch.upper()
1739 first = False
1740 else:
1741 new_name += ch.lower()
1742 return new_name
1743
Eric Blake437db252015-09-29 16:21:02 -06001744
Markus Armbruster849bc532015-05-14 06:50:53 -06001745# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1746# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1747# ENUM24_Name -> ENUM24_NAME
1748def camel_to_upper(value):
1749 c_fun_str = c_name(value, False)
1750 if value.isupper():
1751 return c_fun_str
1752
1753 new_name = ''
1754 l = len(c_fun_str)
1755 for i in range(l):
1756 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001757 # When c is upper and no '_' appears before, do more checks
1758 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001759 if i < l - 1 and c_fun_str[i + 1].islower():
1760 new_name += '_'
1761 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001762 new_name += '_'
1763 new_name += c
1764 return new_name.lstrip('_').upper()
1765
Eric Blake437db252015-09-29 16:21:02 -06001766
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001767def c_enum_const(type_name, const_name, prefix=None):
1768 if prefix is not None:
1769 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001770 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001771
Eric Blake18df5152015-05-14 06:50:48 -06001772c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001773
Eric Blake437db252015-09-29 16:21:02 -06001774
Eric Blakec6405b52015-05-14 06:50:55 -06001775# Map @name to a valid C identifier.
1776# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001777# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001778#
1779# Used for converting 'name' from a 'name':'type' qapi definition
1780# into a generated struct member, as well as converting type names
1781# into substrings of a generated C function name.
1782# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1783# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001784def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001785 # ANSI X3J11/88-090, 3.1.1
1786 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001787 'default', 'do', 'double', 'else', 'enum', 'extern',
1788 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1789 'return', 'short', 'signed', 'sizeof', 'static',
1790 'struct', 'switch', 'typedef', 'union', 'unsigned',
1791 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001792 # ISO/IEC 9899:1999, 6.4.1
1793 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1794 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001795 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1796 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001797 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1798 # excluding _.*
1799 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001800 # C++ ISO/IEC 14882:2003 2.11
1801 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1802 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1803 'namespace', 'new', 'operator', 'private', 'protected',
1804 'public', 'reinterpret_cast', 'static_cast', 'template',
1805 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1806 'using', 'virtual', 'wchar_t',
1807 # alternative representations
1808 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1809 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001810 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001811 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001812 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001813 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1814 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001815 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001816 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001817
Amos Kong05dfb262014-06-10 19:25:53 +08001818eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001819pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001820
Eric Blake437db252015-09-29 16:21:02 -06001821
Michael Roth0f923be2011-07-19 14:50:39 -05001822def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001823 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001824 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001825 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001826 return ret
1827
1828indent_level = 0
1829
Eric Blake437db252015-09-29 16:21:02 -06001830
Michael Roth0f923be2011-07-19 14:50:39 -05001831def push_indent(indent_amount=4):
1832 global indent_level
1833 indent_level += indent_amount
1834
Eric Blake437db252015-09-29 16:21:02 -06001835
Michael Roth0f923be2011-07-19 14:50:39 -05001836def pop_indent(indent_amount=4):
1837 global indent_level
1838 indent_level -= indent_amount
1839
Eric Blake437db252015-09-29 16:21:02 -06001840
Markus Armbruster77e703b2015-06-24 19:27:32 +02001841# Generate @code with @kwds interpolated.
1842# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001843def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001844 raw = code % kwds
1845 if indent_level:
1846 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001847 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001848 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001849 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001850 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001851 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001852
Eric Blake437db252015-09-29 16:21:02 -06001853
Michael Roth0f923be2011-07-19 14:50:39 -05001854def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001855 if code[0] == '\n':
1856 code = code[1:]
1857 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001858
Michael Roth0f923be2011-07-19 14:50:39 -05001859
1860def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001861 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001862
Eric Blake437db252015-09-29 16:21:02 -06001863
Michael Rothc0afa9c2013-05-10 17:46:00 -05001864def guardstart(name):
1865 return mcgen('''
1866
1867#ifndef %(name)s
1868#define %(name)s
1869
1870''',
1871 name=guardname(name))
1872
Eric Blake437db252015-09-29 16:21:02 -06001873
Michael Rothc0afa9c2013-05-10 17:46:00 -05001874def guardend(name):
1875 return mcgen('''
1876
1877#endif /* %(name)s */
1878
1879''',
1880 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001881
Eric Blake437db252015-09-29 16:21:02 -06001882
Markus Armbrustere98859a2015-09-16 13:06:16 +02001883def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001884 ret = mcgen('''
1885
Markus Armbrustere98859a2015-09-16 13:06:16 +02001886const char *const %(c_name)s_lookup[] = {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001887''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001888 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001889 for value in values:
1890 index = c_enum_const(name, value, prefix)
1891 ret += mcgen('''
1892 [%(index)s] = "%(value)s",
1893''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001894 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001895
Eric Blake7fb1cf12015-11-18 01:52:57 -07001896 max_index = c_enum_const(name, '_MAX', prefix)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001897 ret += mcgen('''
1898 [%(max_index)s] = NULL,
1899};
1900''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001901 max_index=max_index)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001902 return ret
1903
Eric Blake437db252015-09-29 16:21:02 -06001904
Markus Armbrustere98859a2015-09-16 13:06:16 +02001905def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001906 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001907 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001908
1909 ret = mcgen('''
1910
1911typedef enum %(c_name)s {
1912''',
1913 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001914
1915 i = 0
1916 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001917 ret += mcgen('''
1918 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001919''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001920 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001921 i=i)
1922 i += 1
1923
Markus Armbrustere98859a2015-09-16 13:06:16 +02001924 ret += mcgen('''
1925} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001926''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001927 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001928
Markus Armbrustere98859a2015-09-16 13:06:16 +02001929 ret += mcgen('''
1930
1931extern const char *const %(c_name)s_lookup[];
1932''',
1933 c_name=c_name(name))
1934 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001935
Eric Blake437db252015-09-29 16:21:02 -06001936
Eric Blake48825ca2016-07-13 21:50:19 -06001937def gen_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001938 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001939 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001940 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001941 ret = ''
1942 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001943 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001944 ret += '%s arg' % arg_type.c_param_type()
1945 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001946 else:
1947 assert not arg_type.variants
1948 for memb in arg_type.members:
1949 ret += sep
1950 sep = ', '
1951 if memb.optional:
1952 ret += 'bool has_%s, ' % c_name(memb.name)
1953 ret += '%s %s' % (memb.type.c_param_type(),
1954 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001955 if extra:
1956 ret += sep + extra
1957 return ret
1958
Eric Blake1f353342015-09-29 16:21:13 -06001959
Markus Armbruster00e4b282015-06-10 10:04:36 +02001960#
1961# Common command line parsing
1962#
1963
Eric Blake437db252015-09-29 16:21:02 -06001964
Markus Armbrusteref801a92017-03-15 13:57:08 +01001965def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001966
1967 try:
1968 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001969 'chp:o:' + extra_options,
1970 ['source', 'header', 'prefix=',
1971 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001972 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001973 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001974 sys.exit(1)
1975
Markus Armbrusteref801a92017-03-15 13:57:08 +01001976 output_dir = ''
1977 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001978 do_c = False
1979 do_h = False
1980 extra_opts = []
1981
1982 for oa in opts:
1983 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001984 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001985 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001986 if match.end() != len(a):
1987 print >>sys.stderr, \
1988 "%s: 'funny character '%s' in argument of --prefix" \
1989 % (sys.argv[0], a[match.end()])
1990 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001991 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001992 elif o in ('-o', '--output-dir'):
1993 output_dir = a + '/'
1994 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001995 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001996 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001997 do_h = True
1998 else:
1999 extra_opts.append(oa)
2000
2001 if not do_c and not do_h:
2002 do_c = True
2003 do_h = True
2004
Markus Armbruster16d80f62015-04-02 13:32:16 +02002005 if len(args) != 1:
2006 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002007 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02002008 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002009
Markus Armbruster54414042015-06-09 16:22:45 +02002010 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002011
Markus Armbruster00e4b282015-06-10 10:04:36 +02002012#
2013# Generate output files with boilerplate
2014#
2015
Eric Blake437db252015-09-29 16:21:02 -06002016
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002017def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
2018 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02002019 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002020 c_file = output_dir + prefix + c_file
2021 h_file = output_dir + prefix + h_file
2022
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002023 if output_dir:
2024 try:
2025 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01002026 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002027 if e.errno != errno.EEXIST:
2028 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002029
2030 def maybe_open(really, name, opt):
2031 if really:
2032 return open(name, opt)
2033 else:
2034 import StringIO
2035 return StringIO.StringIO()
2036
2037 fdef = maybe_open(do_c, c_file, 'w')
2038 fdecl = maybe_open(do_h, h_file, 'w')
2039
2040 fdef.write(mcgen('''
2041/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2042%(comment)s
2043''',
Eric Blake437db252015-09-29 16:21:02 -06002044 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002045
2046 fdecl.write(mcgen('''
2047/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2048%(comment)s
2049#ifndef %(guard)s
2050#define %(guard)s
2051
2052''',
Eric Blake437db252015-09-29 16:21:02 -06002053 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002054
2055 return (fdef, fdecl)
2056
Eric Blake437db252015-09-29 16:21:02 -06002057
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002058def close_output(fdef, fdecl):
2059 fdecl.write('''
2060#endif
2061''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002062 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002063 fdef.close()