blob: 62dc52ed6e019b6ba62b0464a37ca6fdbb70f67b [file] [log] [blame]
Michael Roth0f923be2011-07-19 14:50:39 -05001#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
Eric Blakee4083112016-01-29 06:48:41 -07005# Copyright (c) 2013-2016 Red Hat Inc.
Michael Roth0f923be2011-07-19 14:50:39 -05006#
7# Authors:
8# Anthony Liguori <aliguori@us.ibm.com>
Markus Armbrusterc7a3f252013-07-27 17:41:55 +02009# Markus Armbruster <armbru@redhat.com>
Michael Roth0f923be2011-07-19 14:50:39 -050010#
Markus Armbruster678e48a2014-03-01 08:40:34 +010011# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
Michael Roth0f923be2011-07-19 14:50:39 -050013
Markus Armbruster12f8e1b2015-04-02 14:46:39 +020014import errno
Markus Armbruster2114f5a2015-04-02 13:12:21 +020015import getopt
Lluís Vilanova33aaad52014-05-02 15:52:35 +020016import os
Markus Armbrusterc2613942017-03-15 13:57:35 +010017import re
Markus Armbruster47299262015-05-14 06:50:47 -060018import string
Markus Armbrusterc2613942017-03-15 13:57:35 +010019import sys
20from ordereddict import OrderedDict
Michael Roth0f923be2011-07-19 14:50:39 -050021
Eric Blakeb52c4b92015-05-04 09:05:00 -060022builtin_types = {
Markus Armbruster4d2d5c42017-06-26 19:25:14 +020023 'null': 'QTYPE_QNULL',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020024 'str': 'QTYPE_QSTRING',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +040025 'int': 'QTYPE_QNUM',
26 'number': 'QTYPE_QNUM',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020027 'bool': 'QTYPE_QBOOL',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +040028 'int8': 'QTYPE_QNUM',
29 'int16': 'QTYPE_QNUM',
30 'int32': 'QTYPE_QNUM',
31 'int64': 'QTYPE_QNUM',
32 'uint8': 'QTYPE_QNUM',
33 'uint16': 'QTYPE_QNUM',
34 'uint32': 'QTYPE_QNUM',
35 'uint64': 'QTYPE_QNUM',
36 'size': 'QTYPE_QNUM',
Eric Blake1310a3d2015-12-01 22:20:46 -070037 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070038 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020039}
40
Markus Armbrusterbc52d032017-03-15 13:56:51 +010041# Are documentation comments required?
42doc_required = False
43
Eric Blake10d4d992015-05-04 09:05:23 -060044# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010045returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060046
Eric Blake893e1f22015-12-01 22:20:57 -070047# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010048name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070049
Markus Armbruster5f018442017-03-15 13:57:31 +010050enum_types = {}
Markus Armbrustered285bf2017-03-15 13:57:32 +010051struct_types = {}
Markus Armbruster768562d2017-03-15 13:57:33 +010052union_types = {}
Eric Blake4dc2e692015-05-04 09:05:17 -060053all_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 = []
110
111 def append(self, line):
112 self.content.append(line)
113
114 def __repr__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100115 return '\n'.join(self.content).strip()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100116
117 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100118 def __init__(self, name):
119 QAPIDoc.Section.__init__(self, name)
120 self.member = None
121
122 def connect(self, member):
123 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100124
125 def __init__(self, parser, info):
126 # self.parser is used to report errors with QAPIParseError. The
127 # resulting error position depends on the state of the parser.
128 # It happens to be the beginning of the comment. More or less
129 # servicable, but action at a distance.
130 self.parser = parser
131 self.info = info
132 self.symbol = None
133 self.body = QAPIDoc.Section()
134 # dict mapping parameter name to ArgSection
135 self.args = OrderedDict()
136 # a list of Section
137 self.sections = []
138 # the current section
139 self.section = self.body
Marc-André Lureau3313b612017-01-13 15:41:29 +0100140
141 def has_section(self, name):
142 """Return True if we have a section with this name."""
143 for i in self.sections:
144 if i.name == name:
145 return True
146 return False
147
148 def append(self, line):
149 """Parse a comment line and add it to the documentation."""
150 line = line[1:]
151 if not line:
152 self._append_freeform(line)
153 return
154
155 if line[0] != ' ':
156 raise QAPIParseError(self.parser, "Missing space after #")
157 line = line[1:]
158
159 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
160 # recognized, and get silently treated as ordinary text
161 if self.symbol:
162 self._append_symbol_line(line)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100163 elif not self.body.content and line.startswith('@'):
164 if not line.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100165 raise QAPIParseError(self.parser, "Line should end with :")
166 self.symbol = line[1:-1]
167 # FIXME invalid names other than the empty string aren't flagged
168 if not self.symbol:
169 raise QAPIParseError(self.parser, "Invalid name")
170 else:
171 self._append_freeform(line)
172
Markus Armbruster4ea71482017-03-15 13:57:23 +0100173 def end_comment(self):
174 self._end_section()
175
Marc-André Lureau3313b612017-01-13 15:41:29 +0100176 def _append_symbol_line(self, line):
177 name = line.split(' ', 1)[0]
178
Markus Armbrusteref801a92017-03-15 13:57:08 +0100179 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100180 line = line[len(name)+1:]
181 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100182 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100183 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100184 'Note:', 'Notes:',
185 'Example:', 'Examples:',
186 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100187 line = line[len(name)+1:]
188 self._start_section(name[:-1])
189
190 self._append_freeform(line)
191
192 def _start_args_section(self, name):
193 # FIXME invalid names other than the empty string aren't flagged
194 if not name:
195 raise QAPIParseError(self.parser, "Invalid parameter name")
196 if name in self.args:
197 raise QAPIParseError(self.parser,
198 "'%s' parameter name duplicated" % name)
199 if self.sections:
200 raise QAPIParseError(self.parser,
201 "'@%s:' can't follow '%s' section"
202 % (name, self.sections[0].name))
Markus Armbruster4ea71482017-03-15 13:57:23 +0100203 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100204 self.section = QAPIDoc.ArgSection(name)
205 self.args[name] = self.section
206
Markus Armbrusteref801a92017-03-15 13:57:08 +0100207 def _start_section(self, name=''):
208 if name in ('Returns', 'Since') and self.has_section(name):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100209 raise QAPIParseError(self.parser,
210 "Duplicated '%s' section" % name)
Markus Armbruster4ea71482017-03-15 13:57:23 +0100211 self._end_section()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100212 self.section = QAPIDoc.Section(name)
213 self.sections.append(self.section)
214
Markus Armbruster4ea71482017-03-15 13:57:23 +0100215 def _end_section(self):
216 if self.section:
217 contents = str(self.section)
218 if self.section.name and (not contents or contents.isspace()):
219 raise QAPIParseError(self.parser, "Empty doc section '%s'"
220 % self.section.name)
221 self.section = None
222
Marc-André Lureau3313b612017-01-13 15:41:29 +0100223 def _append_freeform(self, line):
224 in_arg = isinstance(self.section, QAPIDoc.ArgSection)
225 if (in_arg and self.section.content
226 and not self.section.content[-1]
227 and line and not line[0].isspace()):
228 self._start_section()
229 if (in_arg or not self.section.name
Markus Armbrusteref801a92017-03-15 13:57:08 +0100230 or not self.section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100231 line = line.strip()
Markus Armbruster2d433232017-03-15 13:57:22 +0100232 match = re.match(r'(@\S+:)', line)
233 if match:
234 raise QAPIParseError(self.parser,
235 "'%s' not allowed in free-form documentation"
236 % match.group(1))
Markus Armbruster1d8bda12017-03-15 13:57:06 +0100237 # TODO Drop this once the dust has settled
238 if (isinstance(self.section, QAPIDoc.ArgSection)
239 and '#optional' in line):
240 raise QAPISemError(self.info, "Please drop the #optional tag")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100241 self.section.append(line)
242
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100243 def connect_member(self, member):
244 if member.name not in self.args:
245 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100246 self.args[member.name] = QAPIDoc.ArgSection(member.name)
247 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100248
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100249 def check_expr(self, expr):
250 if self.has_section('Returns') and 'command' not in expr:
251 raise QAPISemError(self.info,
252 "'Returns:' is only valid for commands")
253
Markus Armbruster816a57c2017-03-15 13:57:26 +0100254 def check(self):
255 bogus = [name for name, section in self.args.iteritems()
256 if not section.member]
257 if bogus:
258 raise QAPISemError(
259 self.info,
260 "The following documented members are not in "
261 "the declaration: %s" % ", ".join(bogus))
262
Marc-André Lureau3313b612017-01-13 15:41:29 +0100263
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200264class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500265
Eric Blake437db252015-09-29 16:21:02 -0600266 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200267 abs_fname = os.path.abspath(fp.name)
Markus Armbruster8608d252015-06-09 18:32:29 +0200268 fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200269 self.fname = fname
Markus Armbruster54414042015-06-09 16:22:45 +0200270 previously_included.append(abs_fname)
271 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200272 self.src = fp.read()
273 if self.src == '' or self.src[-1] != '\n':
274 self.src += '\n'
275 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800276 self.line = 1
277 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200278 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100279 self.docs = []
Markus Armbrustere7823a22017-03-15 13:57:20 +0100280 self.cur_doc = None
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200281 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500282
Eric Blake437db252015-09-29 16:21:02 -0600283 while self.tok is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100284 info = {'file': fname, 'line': self.line,
285 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100286 if self.tok == '#':
Markus Armbrustere7823a22017-03-15 13:57:20 +0100287 self.reject_expr_doc()
288 self.cur_doc = self.get_doc(info)
289 self.docs.append(self.cur_doc)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100290 continue
291
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200292 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100293 if 'include' in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100294 self.reject_expr_doc()
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200295 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100296 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100297 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200298 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100299 raise QAPISemError(info,
300 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100301 self._include(include, info, os.path.dirname(abs_fname),
302 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100303 elif "pragma" in expr:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100304 self.reject_expr_doc()
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100305 if len(expr) != 1:
306 raise QAPISemError(info, "Invalid 'pragma' directive")
307 pragma = expr['pragma']
308 if not isinstance(pragma, dict):
309 raise QAPISemError(
310 info, "Value of 'pragma' must be a dictionary")
311 for name, value in pragma.iteritems():
312 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200313 else:
314 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100315 'info': info}
Markus Armbrustere7823a22017-03-15 13:57:20 +0100316 if self.cur_doc:
317 if not self.cur_doc.symbol:
318 raise QAPISemError(
319 self.cur_doc.info,
320 "Expression documentation required")
Markus Armbrustere7823a22017-03-15 13:57:20 +0100321 expr_elem['doc'] = self.cur_doc
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200322 self.exprs.append(expr_elem)
Markus Armbrustere7823a22017-03-15 13:57:20 +0100323 self.cur_doc = None
324 self.reject_expr_doc()
325
326 def reject_expr_doc(self):
327 if self.cur_doc and self.cur_doc.symbol:
328 raise QAPISemError(
329 self.cur_doc.info,
330 "Documentation for '%s' is not followed by the definition"
331 % self.cur_doc.symbol)
Michael Roth0f923be2011-07-19 14:50:39 -0500332
Markus Armbrustere04dea82017-03-15 13:56:50 +0100333 def _include(self, include, info, base_dir, previously_included):
334 incl_abs_fname = os.path.join(base_dir, include)
335 # catch inclusion cycle
336 inf = info
337 while inf:
338 if incl_abs_fname == os.path.abspath(inf['file']):
339 raise QAPISemError(info, "Inclusion loop for %s" % include)
340 inf = inf['parent']
341
342 # skip multiple include of the same file
343 if incl_abs_fname in previously_included:
344 return
345 try:
346 fobj = open(incl_abs_fname, 'r')
347 except IOError as e:
348 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
349 exprs_include = QAPISchemaParser(fobj, previously_included, info)
350 self.exprs.extend(exprs_include.exprs)
351 self.docs.extend(exprs_include.docs)
352
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100353 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100354 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100355 if name == 'doc-required':
356 if not isinstance(value, bool):
357 raise QAPISemError(info,
358 "Pragma 'doc-required' must be boolean")
359 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100360 elif name == 'returns-whitelist':
361 if (not isinstance(value, list)
362 or any([not isinstance(elt, str) for elt in value])):
363 raise QAPISemError(info,
364 "Pragma returns-whitelist must be"
365 " a list of strings")
366 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100367 elif name == 'name-case-whitelist':
368 if (not isinstance(value, list)
369 or any([not isinstance(elt, str) for elt in value])):
370 raise QAPISemError(info,
371 "Pragma name-case-whitelist must be"
372 " a list of strings")
373 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100374 else:
375 raise QAPISemError(info, "Unknown pragma '%s'" % name)
376
Marc-André Lureau3313b612017-01-13 15:41:29 +0100377 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200378 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200379 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200380 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200381 self.cursor += 1
382 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500383
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200384 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100385 if self.src[self.cursor] == '#':
386 # Start of doc comment
387 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200388 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100389 if not skip_comment:
390 self.val = self.src[self.pos:self.cursor]
391 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100392 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200393 return
394 elif self.tok == "'":
395 string = ''
396 esc = False
397 while True:
398 ch = self.src[self.cursor]
399 self.cursor += 1
400 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100401 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200402 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600403 if ch == 'b':
404 string += '\b'
405 elif ch == 'f':
406 string += '\f'
407 elif ch == 'n':
408 string += '\n'
409 elif ch == 'r':
410 string += '\r'
411 elif ch == 't':
412 string += '\t'
413 elif ch == 'u':
414 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600415 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600416 ch = self.src[self.cursor]
417 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100418 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100419 raise QAPIParseError(self,
420 '\\u escape needs 4 '
421 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600422 value = (value << 4) + int(ch, 16)
423 # If Python 2 and 3 didn't disagree so much on
424 # how to handle Unicode, then we could allow
425 # Unicode string defaults. But most of QAPI is
426 # ASCII-only, so we aren't losing much for now.
427 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100428 raise QAPIParseError(self,
429 'For now, \\u escape '
430 'only supports non-zero '
431 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600432 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100433 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600434 string += ch
435 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100436 raise QAPIParseError(self,
437 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200438 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100439 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200440 esc = True
441 elif ch == "'":
442 self.val = string
443 return
444 else:
445 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100446 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200447 self.val = True
448 self.cursor += 3
449 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100450 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200451 self.val = False
452 self.cursor += 4
453 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100454 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200455 self.val = None
456 self.cursor += 3
457 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200458 elif self.tok == '\n':
459 if self.cursor == len(self.src):
460 self.tok = None
461 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800462 self.line += 1
463 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200464 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100465 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500466
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200467 def get_members(self):
468 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200469 if self.tok == '}':
470 self.accept()
471 return expr
472 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100473 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200474 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200475 key = self.val
476 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200477 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100478 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200479 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800480 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100481 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200482 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200483 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200484 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200485 return expr
486 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100487 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200488 self.accept()
489 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100490 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500491
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200492 def get_values(self):
493 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200494 if self.tok == ']':
495 self.accept()
496 return expr
Eric Blake437db252015-09-29 16:21:02 -0600497 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100498 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
499 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200500 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200501 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200502 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200503 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200504 return expr
505 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100506 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200507 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500508
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200509 def get_expr(self, nested):
510 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100511 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200512 if self.tok == '{':
513 self.accept()
514 expr = self.get_members()
515 elif self.tok == '[':
516 self.accept()
517 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600518 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200519 expr = self.val
520 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200521 else:
Markus Armbruster012b1262017-03-15 13:57:36 +0100522 raise QAPIParseError(self, 'Expected "{", "[", string, '
523 'boolean or "null"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200524 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200525
Marc-André Lureau3313b612017-01-13 15:41:29 +0100526 def get_doc(self, info):
527 if self.val != '##':
528 raise QAPIParseError(self, "Junk after '##' at start of "
529 "documentation comment")
530
531 doc = QAPIDoc(self, info)
532 self.accept(False)
533 while self.tok == '#':
534 if self.val.startswith('##'):
535 # End of doc comment
536 if self.val != '##':
537 raise QAPIParseError(self, "Junk after '##' at end of "
538 "documentation comment")
Markus Armbruster4ea71482017-03-15 13:57:23 +0100539 doc.end_comment()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100540 self.accept()
541 return doc
542 else:
543 doc.append(self.val)
544 self.accept(False)
545
546 raise QAPIParseError(self, "Documentation comment must end with '##'")
547
548
Markus Armbruster00e4b282015-06-10 10:04:36 +0200549#
550# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200551# TODO fold into QAPISchema
552# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200553#
554
Eric Blake437db252015-09-29 16:21:02 -0600555
Eric Blake14f00c62016-03-03 09:16:43 -0700556def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600557 if isinstance(base, dict):
558 return base
Markus Armbrustered285bf2017-03-15 13:57:32 +0100559 base_struct_define = struct_types.get(base)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800560 if not base_struct_define:
561 return None
562 return base_struct_define['data']
563
Eric Blake437db252015-09-29 16:21:02 -0600564
Eric Blake811d04f2015-05-04 09:05:10 -0600565# Return the qtype of an alternate branch, or None on error.
566def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600567 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600568 return builtin_types[qapi_type]
Markus Armbrustered285bf2017-03-15 13:57:32 +0100569 elif qapi_type in struct_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100570 return 'QTYPE_QDICT'
Markus Armbruster5f018442017-03-15 13:57:31 +0100571 elif qapi_type in enum_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100572 return 'QTYPE_QSTRING'
Markus Armbruster768562d2017-03-15 13:57:33 +0100573 elif qapi_type in union_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100574 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600575 return None
576
Eric Blake437db252015-09-29 16:21:02 -0600577
Wenchao Xiabceae762014-03-06 17:08:56 -0800578# Return the discriminator enum define if discriminator is specified as an
579# enum type, otherwise return None.
580def discriminator_find_enum_define(expr):
581 base = expr.get('base')
582 discriminator = expr.get('discriminator')
583
584 if not (discriminator and base):
585 return None
586
Eric Blake14f00c62016-03-03 09:16:43 -0700587 base_members = find_base_members(base)
588 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800589 return None
590
Eric Blake14f00c62016-03-03 09:16:43 -0700591 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800592 if not discriminator_type:
593 return None
594
Markus Armbruster5f018442017-03-15 13:57:31 +0100595 return enum_types.get(discriminator_type)
Wenchao Xiabceae762014-03-06 17:08:56 -0800596
Eric Blake437db252015-09-29 16:21:02 -0600597
Eric Blake59a92fe2015-11-18 01:52:56 -0700598# Names must be letters, numbers, -, and _. They must start with letter,
599# except for downstream extensions which must start with __RFQDN_.
600# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100601valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700602 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600603
604
Marc-André Lureau4148c292017-01-13 15:41:25 +0100605def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600606 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600607 global valid_name
608 membername = name
609
610 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100611 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600612 if name.startswith('*'):
613 membername = name[1:]
614 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100615 raise QAPISemError(info, "%s does not allow optional name '%s'"
616 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600617 # Enum members can start with a digit, because the generated C
618 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700619 if enum_member and membername[0].isdigit():
620 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600621 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
622 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600623 if not valid_name.match(membername) or \
624 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100625 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600626
Eric Blake437db252015-09-29 16:21:02 -0600627
628def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200629 global all_names
630 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200631 # FIXME should reject names that differ only in '_' vs. '.'
632 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200633 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100634 raise QAPISemError(info, "%s '%s' is already defined"
635 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600636 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100637 raise QAPISemError(info, "%s '%s' should not end in '%s'"
638 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200639 all_names[name] = meta
640
Eric Blake437db252015-09-29 16:21:02 -0600641
Marc-André Lureau4148c292017-01-13 15:41:25 +0100642def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600643 allow_dict=False, allow_optional=False,
644 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600645 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600646
647 if value is None:
648 return
649
Eric Blakedd883c62015-05-04 09:05:21 -0600650 # Check if array type for value is okay
651 if isinstance(value, list):
652 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100653 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600654 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100655 raise QAPISemError(info,
656 "%s: array type must contain single type name" %
657 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600658 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600659
660 # Check if type name for value is okay
661 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600662 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100663 raise QAPISemError(info, "%s uses unknown type '%s'"
664 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600665 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100666 raise QAPISemError(info, "%s cannot use %s type '%s'" %
667 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600668 return
669
Eric Blakedd883c62015-05-04 09:05:21 -0600670 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100671 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200672
673 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100674 raise QAPISemError(info,
675 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200676
677 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600678 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100679 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600680 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600681 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100682 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
683 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600684 # Todo: allow dictionaries to represent default values of
685 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100686 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200687 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600688 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600689 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600690
Eric Blake437db252015-09-29 16:21:02 -0600691
Marc-André Lureau4148c292017-01-13 15:41:25 +0100692def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600693 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600694 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600695
Eric Blakec8184082016-07-13 21:50:20 -0600696 args_meta = ['struct']
697 if boxed:
698 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100699 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600700 expr.get('data'), allow_dict=not boxed, allow_optional=True,
701 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600702 returns_meta = ['union', 'struct']
703 if name in returns_whitelist:
704 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100705 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200706 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200707 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600708
Eric Blake437db252015-09-29 16:21:02 -0600709
Marc-André Lureau4148c292017-01-13 15:41:25 +0100710def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600711 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600712 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600713
Eric Blakec8184082016-07-13 21:50:20 -0600714 meta = ['struct']
715 if boxed:
716 meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100717 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600718 expr.get('data'), allow_dict=not boxed, allow_optional=True,
719 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200720
Eric Blake437db252015-09-29 16:21:02 -0600721
Marc-André Lureau4148c292017-01-13 15:41:25 +0100722def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800723 name = expr['union']
724 base = expr.get('base')
725 discriminator = expr.get('discriminator')
726 members = expr['data']
727
Eric Blake811d04f2015-05-04 09:05:10 -0600728 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600729
730 # With no discriminator it is a simple union.
731 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600732 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600733 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600734 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100735 raise QAPISemError(info, "Simple union '%s' must not have a base" %
736 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600737
738 # Else, it's a flat union.
739 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600740 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100741 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600742 base, allow_dict=True, allow_optional=True,
743 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600744 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100745 raise QAPISemError(info, "Flat union '%s' must have a base"
746 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700747 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100748 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800749
Eric Blakec9e0a792015-05-04 09:05:22 -0600750 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600751 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100752 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600753 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700754 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800755 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100756 raise QAPISemError(info,
757 "Discriminator '%s' is not a member of base "
758 "struct '%s'"
759 % (discriminator, base))
Markus Armbruster5f018442017-03-15 13:57:31 +0100760 enum_define = enum_types.get(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600761 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800762 # Do not allow string discriminator
763 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100764 raise QAPISemError(info,
765 "Discriminator '%s' must be of enumeration "
766 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800767
Eric Blake02a57ae2016-02-17 23:48:16 -0700768 # Check every branch; don't allow an empty union
769 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100770 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800771 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100772 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600773
Eric Blake01cfbaa2015-12-01 22:20:58 -0700774 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100775 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200776 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600777
Eric Blake44bd1272015-05-04 09:05:08 -0600778 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700779 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600780 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100781 if key not in enum_define['data']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100782 raise QAPISemError(info,
783 "Discriminator value '%s' is not found in "
784 "enum '%s'"
Markus Armbrustereda43c62017-03-15 13:57:29 +0100785 % (key, enum_define['enum']))
Eric Blake44bd1272015-05-04 09:05:08 -0600786
Eric Blaked0b18232016-07-13 21:50:13 -0600787 # If discriminator is user-defined, ensure all values are covered
788 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100789 for value in enum_define['data']:
Eric Blaked0b18232016-07-13 21:50:13 -0600790 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100791 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
792 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600793
Eric Blake437db252015-09-29 16:21:02 -0600794
Marc-André Lureau4148c292017-01-13 15:41:25 +0100795def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600796 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600797 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600798 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600799
Eric Blake02a57ae2016-02-17 23:48:16 -0700800 # Check every branch; require at least two branches
801 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100802 raise QAPISemError(info,
803 "Alternate '%s' should have at least two branches "
804 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600805 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100806 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600807
Eric Blake811d04f2015-05-04 09:05:10 -0600808 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100809 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600810 value,
811 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600812 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700813 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100814 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
815 "type '%s'" % (name, key, value))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200816 conflicting = set([qtype])
817 if qtype == 'QTYPE_QSTRING':
818 enum_expr = enum_types.get(value)
819 if enum_expr:
820 for v in enum_expr['data']:
821 if v in ['on', 'off']:
822 conflicting.add('QTYPE_QBOOL')
823 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +0400824 conflicting.add('QTYPE_QNUM')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200825 else:
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +0400826 conflicting.add('QTYPE_QNUM')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200827 conflicting.add('QTYPE_QBOOL')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200828 for qt in conflicting:
Eduardo Habkostfda72ab2017-07-17 15:09:26 -0300829 if qt in types_seen:
830 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
831 "be distinguished from member '%s'"
832 % (name, key, types_seen[qt]))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200833 types_seen[qt] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800834
Eric Blake437db252015-09-29 16:21:02 -0600835
Marc-André Lureau4148c292017-01-13 15:41:25 +0100836def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600837 name = expr['enum']
838 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100839 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600840
841 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100842 raise QAPISemError(info,
843 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100844 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100845 raise QAPISemError(info,
846 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600847 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100848 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600849 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600850
Eric Blake437db252015-09-29 16:21:02 -0600851
Marc-André Lureau4148c292017-01-13 15:41:25 +0100852def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600853 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600854 members = expr['data']
855
Marc-André Lureau4148c292017-01-13 15:41:25 +0100856 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600857 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100858 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600859 allow_metas=['struct'])
860
Eric Blake437db252015-09-29 16:21:02 -0600861
Eric Blake0545f6b2015-05-04 09:05:15 -0600862def check_keys(expr_elem, meta, required, optional=[]):
863 expr = expr_elem['expr']
864 info = expr_elem['info']
865 name = expr[meta]
866 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100867 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600868 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600869 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600870 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100871 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
872 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600873 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100874 raise QAPISemError(info,
875 "'%s' of %s '%s' should only use false value"
876 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600877 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100878 raise QAPISemError(info,
879 "'%s' of %s '%s' should only use true value"
880 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600881 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600882 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100883 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
884 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600885
Eric Blake437db252015-09-29 16:21:02 -0600886
Markus Armbruster4d076d62015-06-10 08:55:21 +0200887def check_exprs(exprs):
888 global all_names
889
Markus Armbruster79470162017-03-15 13:57:21 +0100890 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200891 for builtin in builtin_types.keys():
892 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100893
894 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200895 for expr_elem in exprs:
896 expr = expr_elem['expr']
897 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100898 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100899
Markus Armbruster79470162017-03-15 13:57:21 +0100900 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100901 raise QAPISemError(info,
902 "Expression missing documentation comment")
903
Eric Blake437db252015-09-29 16:21:02 -0600904 if 'enum' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100905 meta = 'enum'
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100906 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster5f018442017-03-15 13:57:31 +0100907 enum_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600908 elif 'union' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100909 meta = 'union'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200910 check_keys(expr_elem, 'union', ['data'],
911 ['base', 'discriminator'])
Markus Armbruster768562d2017-03-15 13:57:33 +0100912 union_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600913 elif 'alternate' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100914 meta = 'alternate'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200915 check_keys(expr_elem, 'alternate', ['data'])
Eric Blake437db252015-09-29 16:21:02 -0600916 elif 'struct' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100917 meta = 'struct'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200918 check_keys(expr_elem, 'struct', ['data'], ['base'])
Markus Armbrustered285bf2017-03-15 13:57:32 +0100919 struct_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600920 elif 'command' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100921 meta = 'command'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200922 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600923 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Eric Blake437db252015-09-29 16:21:02 -0600924 elif 'event' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100925 meta = 'event'
Eric Blakec8184082016-07-13 21:50:20 -0600926 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200927 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100928 raise QAPISemError(expr_elem['info'],
929 "Expression is missing metatype")
Markus Armbruster6f053452017-03-15 13:57:30 +0100930 name = expr[meta]
931 add_name(name, info, meta)
Markus Armbruster79470162017-03-15 13:57:21 +0100932 if doc and doc.symbol != name:
933 raise QAPISemError(info, "Definition of '%s' follows documentation"
934 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200935
936 # Try again for hidden UnionKind enum
937 for expr_elem in exprs:
938 expr = expr_elem['expr']
Markus Armbrustereda43c62017-03-15 13:57:29 +0100939 if 'union' in expr and not discriminator_find_enum_define(expr):
940 name = '%sKind' % expr['union']
Eric Blake437db252015-09-29 16:21:02 -0600941 elif 'alternate' in expr:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100942 name = '%sKind' % expr['alternate']
943 else:
944 continue
Markus Armbruster5f018442017-03-15 13:57:31 +0100945 enum_types[name] = {'enum': name}
Markus Armbruster6f053452017-03-15 13:57:30 +0100946 add_name(name, info, 'enum', implicit=True)
Markus Armbruster4d076d62015-06-10 08:55:21 +0200947
948 # Validate that exprs make sense
949 for expr_elem in exprs:
950 expr = expr_elem['expr']
951 info = expr_elem['info']
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100952 doc = expr_elem.get('doc')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200953
Eric Blake437db252015-09-29 16:21:02 -0600954 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200955 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600956 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200957 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600958 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200959 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600960 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200961 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600962 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200963 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600964 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200965 check_event(expr, info)
966 else:
967 assert False, 'unexpected meta type'
968
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100969 if doc:
970 doc.check_expr(expr)
971
Markus Armbrusterac882192015-09-16 13:06:05 +0200972 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600973
Markus Armbrusterac882192015-09-16 13:06:05 +0200974
975#
976# Schema compiler frontend
977#
978
979class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100980 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +0200981 assert isinstance(name, str)
982 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -0600983 # For explicitly defined entities, info points to the (explicit)
984 # definition. For builtins (and their arrays), info is None.
985 # For implicitly defined entities, info points to a place that
986 # triggered the implicit definition (there may be more than one
987 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +0200988 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100989 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +0200990
Markus Armbrusterf51d8c32015-09-16 13:06:06 +0200991 def c_name(self):
992 return c_name(self.name)
993
Markus Armbrusterac882192015-09-16 13:06:05 +0200994 def check(self, schema):
995 pass
996
Eric Blake49823c42015-10-12 22:22:27 -0600997 def is_implicit(self):
998 return not self.info
999
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001000 def visit(self, visitor):
1001 pass
1002
1003
1004class QAPISchemaVisitor(object):
1005 def visit_begin(self, schema):
1006 pass
1007
1008 def visit_end(self):
1009 pass
1010
Eric Blake25a0d9c2015-10-12 22:22:21 -06001011 def visit_needed(self, entity):
1012 # Default to visiting everything
1013 return True
1014
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001015 def visit_builtin_type(self, name, info, json_type):
1016 pass
1017
1018 def visit_enum_type(self, name, info, values, prefix):
1019 pass
1020
1021 def visit_array_type(self, name, info, element_type):
1022 pass
1023
1024 def visit_object_type(self, name, info, base, members, variants):
1025 pass
1026
Markus Armbruster39a18152015-09-16 13:06:28 +02001027 def visit_object_type_flat(self, name, info, members, variants):
1028 pass
1029
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001030 def visit_alternate_type(self, name, info, variants):
1031 pass
1032
1033 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001034 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001035 pass
1036
Eric Blake48825ca2016-07-13 21:50:19 -06001037 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001038 pass
1039
Markus Armbrusterac882192015-09-16 13:06:05 +02001040
1041class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001042 # Return the C type for common use.
1043 # For the types we commonly box, this is a pointer type.
1044 def c_type(self):
1045 pass
1046
1047 # Return the C type to be used in a parameter list.
1048 def c_param_type(self):
1049 return self.c_type()
1050
1051 # Return the C type to be used where we suppress boxing.
1052 def c_unboxed_type(self):
1053 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001054
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001055 def json_type(self):
1056 pass
1057
1058 def alternate_qtype(self):
1059 json2qtype = {
Markus Armbruster4d2d5c42017-06-26 19:25:14 +02001060 'null': 'QTYPE_QNULL',
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001061 'string': 'QTYPE_QSTRING',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001062 'number': 'QTYPE_QNUM',
1063 'int': 'QTYPE_QNUM',
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001064 'boolean': 'QTYPE_QBOOL',
1065 'object': 'QTYPE_QDICT'
1066 }
1067 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001068
Markus Armbruster691e0312017-03-15 13:57:14 +01001069 def doc_type(self):
1070 if self.is_implicit():
1071 return None
1072 return self.name
1073
Markus Armbrusterac882192015-09-16 13:06:05 +02001074
1075class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001076 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001077 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001078 assert not c_type or isinstance(c_type, str)
1079 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1080 'value')
1081 self._json_type_name = json_type
1082 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001083
1084 def c_name(self):
1085 return self.name
1086
Eric Blake4040d992016-03-17 16:48:28 -06001087 def c_type(self):
1088 return self._c_type_name
1089
1090 def c_param_type(self):
1091 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001092 return 'const ' + self._c_type_name
1093 return self._c_type_name
1094
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001095 def json_type(self):
1096 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001097
Markus Armbruster691e0312017-03-15 13:57:14 +01001098 def doc_type(self):
1099 return self.json_type()
1100
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001101 def visit(self, visitor):
1102 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1103
Markus Armbrusterac882192015-09-16 13:06:05 +02001104
1105class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001106 def __init__(self, name, info, doc, values, prefix):
1107 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001108 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001109 assert isinstance(v, QAPISchemaMember)
1110 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001111 assert prefix is None or isinstance(prefix, str)
1112 self.values = values
1113 self.prefix = prefix
1114
1115 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001116 seen = {}
1117 for v in self.values:
1118 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001119 if self.doc:
1120 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001121
Eric Blake99df5282015-10-12 22:22:32 -06001122 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001123 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1124 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001125
Eric Blake4040d992016-03-17 16:48:28 -06001126 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001127 return c_name(self.name)
1128
Eric Blake93bda4d2015-12-01 22:20:55 -07001129 def member_names(self):
1130 return [v.name for v in self.values]
1131
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001132 def json_type(self):
1133 return 'string'
1134
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001135 def visit(self, visitor):
1136 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001137 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001138
Markus Armbrusterac882192015-09-16 13:06:05 +02001139
1140class QAPISchemaArrayType(QAPISchemaType):
1141 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001142 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001143 assert isinstance(element_type, str)
1144 self._element_type_name = element_type
1145 self.element_type = None
1146
1147 def check(self, schema):
1148 self.element_type = schema.lookup_type(self._element_type_name)
1149 assert self.element_type
1150
Eric Blake99df5282015-10-12 22:22:32 -06001151 def is_implicit(self):
1152 return True
1153
Eric Blake4040d992016-03-17 16:48:28 -06001154 def c_type(self):
1155 return c_name(self.name) + pointer_suffix
1156
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001157 def json_type(self):
1158 return 'array'
1159
Markus Armbruster691e0312017-03-15 13:57:14 +01001160 def doc_type(self):
1161 elt_doc_type = self.element_type.doc_type()
1162 if not elt_doc_type:
1163 return None
1164 return 'array of ' + elt_doc_type
1165
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001166 def visit(self, visitor):
1167 visitor.visit_array_type(self.name, self.info, self.element_type)
1168
Markus Armbrusterac882192015-09-16 13:06:05 +02001169
1170class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001171 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001172 # struct has local_members, optional base, and no variants
1173 # flat union has base, variants, and no local_members
1174 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001175 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001176 assert base is None or isinstance(base, str)
1177 for m in local_members:
1178 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001179 m.set_owner(name)
1180 if variants is not None:
1181 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1182 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001183 self._base_name = base
1184 self.base = None
1185 self.local_members = local_members
1186 self.variants = variants
1187 self.members = None
1188
1189 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001190 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001191 raise QAPISemError(self.info,
1192 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001193 if self.members:
1194 return
1195 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001196 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001197 if self._base_name:
1198 self.base = schema.lookup_type(self._base_name)
1199 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001200 self.base.check(schema)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001201 self.base.check_clash(self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001202 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001203 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001204 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001205 if self.doc:
1206 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001207 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001208 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001209 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001210 assert self.variants.tag_member in self.members
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001211 self.variants.check_clash(self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001212 if self.doc:
1213 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001214
Eric Blake14f00c62016-03-03 09:16:43 -07001215 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001216 # and update seen to track the members seen so far. Report any errors
1217 # on behalf of info, which is not necessarily self.info
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001218 def check_clash(self, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001219 assert not self.variants # not implemented
1220 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001221 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001222
Eric Blake99df5282015-10-12 22:22:32 -06001223 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001224 # See QAPISchema._make_implicit_object_type(), as well as
1225 # _def_predefineds()
1226 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001227
Eric Blakeb6167702016-07-13 21:50:16 -06001228 def is_empty(self):
1229 assert self.members is not None
1230 return not self.members and not self.variants
1231
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001232 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001233 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001234 return QAPISchemaType.c_name(self)
1235
Eric Blake4040d992016-03-17 16:48:28 -06001236 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001237 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001238 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001239
Eric Blake4040d992016-03-17 16:48:28 -06001240 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001241 return c_name(self.name)
1242
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001243 def json_type(self):
1244 return 'object'
1245
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001246 def visit(self, visitor):
1247 visitor.visit_object_type(self.name, self.info,
1248 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001249 visitor.visit_object_type_flat(self.name, self.info,
1250 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001251
Markus Armbrusterac882192015-09-16 13:06:05 +02001252
Eric Blaked44f9ac2015-12-01 22:20:54 -07001253class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001254 role = 'member'
1255
Eric Blaked44f9ac2015-12-01 22:20:54 -07001256 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001257 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001258 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001259 self.owner = None
1260
1261 def set_owner(self, name):
1262 assert not self.owner
1263 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001264
Eric Blake27b60ab2015-11-18 01:52:51 -07001265 def check_clash(self, info, seen):
1266 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001267 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001268 raise QAPISemError(info,
1269 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001270 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001271 raise QAPISemError(info, "%s collides with %s" %
1272 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001273 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001274
Eric Blake88d4ef82015-11-18 01:52:50 -07001275 def _pretty_owner(self):
1276 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001277 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001278 # See QAPISchema._make_implicit_object_type() - reverse the
1279 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001280 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001281 if owner.endswith('-arg'):
1282 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001283 elif owner.endswith('-base'):
1284 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001285 else:
1286 assert owner.endswith('-wrapper')
1287 # Unreachable and not implemented
1288 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001289 if owner.endswith('Kind'):
1290 # See QAPISchema._make_implicit_enum_type()
1291 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001292 return '(%s of %s)' % (self.role, owner)
1293
1294 def describe(self):
1295 return "'%s' %s" % (self.name, self._pretty_owner())
1296
Markus Armbrusterac882192015-09-16 13:06:05 +02001297
Eric Blaked44f9ac2015-12-01 22:20:54 -07001298class QAPISchemaObjectTypeMember(QAPISchemaMember):
1299 def __init__(self, name, typ, optional):
1300 QAPISchemaMember.__init__(self, name)
1301 assert isinstance(typ, str)
1302 assert isinstance(optional, bool)
1303 self._type_name = typ
1304 self.type = None
1305 self.optional = optional
1306
1307 def check(self, schema):
1308 assert self.owner
1309 self.type = schema.lookup_type(self._type_name)
1310 assert self.type
1311
1312
Markus Armbrusterac882192015-09-16 13:06:05 +02001313class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001314 def __init__(self, tag_name, tag_member, variants):
1315 # Flat unions pass tag_name but not tag_member.
1316 # Simple unions and alternates pass tag_member but not tag_name.
1317 # After check(), tag_member is always set, and tag_name remains
1318 # a reliable witness of being used by a flat union.
1319 assert bool(tag_member) != bool(tag_name)
1320 assert (isinstance(tag_name, str) or
1321 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001322 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001323 for v in variants:
1324 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001325 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001326 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001327 self.variants = variants
1328
Eric Blake88d4ef82015-11-18 01:52:50 -07001329 def set_owner(self, name):
1330 for v in self.variants:
1331 v.set_owner(name)
1332
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001333 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001334 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001335 self.tag_member = seen[c_name(self._tag_name)]
1336 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001337 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1338 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001339 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001340 # Union names must match enum values; alternate names are
1341 # checked separately. Use 'seen' to tell the two apart.
1342 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001343 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001344 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001345 v.type.check(schema)
1346
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001347 def check_clash(self, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001348 for v in self.variants:
1349 # Reset seen map for each variant, since qapi names from one
1350 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001351 assert isinstance(v.type, QAPISchemaObjectType)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001352 v.type.check_clash(info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001353
Eric Blake437db252015-09-29 16:21:02 -06001354
Markus Armbrusterac882192015-09-16 13:06:05 +02001355class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001356 role = 'branch'
1357
Markus Armbrusterac882192015-09-16 13:06:05 +02001358 def __init__(self, name, typ):
1359 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1360
Markus Armbrusterac882192015-09-16 13:06:05 +02001361
1362class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001363 def __init__(self, name, info, doc, variants):
1364 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001365 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001366 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001367 variants.set_owner(name)
1368 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001369 self.variants = variants
1370
1371 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001372 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001373 # Not calling self.variants.check_clash(), because there's nothing
1374 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001375 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001376 # Alternate branch names have no relation to the tag enum values;
1377 # so we have to check for potential name collisions ourselves.
1378 seen = {}
1379 for v in self.variants.variants:
1380 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001381 if self.doc:
1382 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001383 if self.doc:
1384 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001385
Eric Blake4040d992016-03-17 16:48:28 -06001386 def c_type(self):
1387 return c_name(self.name) + pointer_suffix
1388
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001389 def json_type(self):
1390 return 'value'
1391
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001392 def visit(self, visitor):
1393 visitor.visit_alternate_type(self.name, self.info, self.variants)
1394
Eric Blakec8184082016-07-13 21:50:20 -06001395 def is_empty(self):
1396 return False
1397
Markus Armbrusterac882192015-09-16 13:06:05 +02001398
1399class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001400 def __init__(self, name, info, doc, arg_type, ret_type,
1401 gen, success_response, boxed):
1402 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001403 assert not arg_type or isinstance(arg_type, str)
1404 assert not ret_type or isinstance(ret_type, str)
1405 self._arg_type_name = arg_type
1406 self.arg_type = None
1407 self._ret_type_name = ret_type
1408 self.ret_type = None
1409 self.gen = gen
1410 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001411 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001412
1413 def check(self, schema):
1414 if self._arg_type_name:
1415 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001416 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1417 isinstance(self.arg_type, QAPISchemaAlternateType))
1418 self.arg_type.check(schema)
1419 if self.boxed:
1420 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001421 raise QAPISemError(self.info,
1422 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001423 else:
1424 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1425 assert not self.arg_type.variants
1426 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001427 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001428 if self._ret_type_name:
1429 self.ret_type = schema.lookup_type(self._ret_type_name)
1430 assert isinstance(self.ret_type, QAPISchemaType)
1431
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001432 def visit(self, visitor):
1433 visitor.visit_command(self.name, self.info,
1434 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001435 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001436
Markus Armbrusterac882192015-09-16 13:06:05 +02001437
1438class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001439 def __init__(self, name, info, doc, arg_type, boxed):
1440 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001441 assert not arg_type or isinstance(arg_type, str)
1442 self._arg_type_name = arg_type
1443 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001444 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001445
1446 def check(self, schema):
1447 if self._arg_type_name:
1448 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001449 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1450 isinstance(self.arg_type, QAPISchemaAlternateType))
1451 self.arg_type.check(schema)
1452 if self.boxed:
1453 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001454 raise QAPISemError(self.info,
1455 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001456 else:
1457 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1458 assert not self.arg_type.variants
1459 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001460 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001461
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001462 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001463 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001464
Markus Armbrusterac882192015-09-16 13:06:05 +02001465
1466class QAPISchema(object):
1467 def __init__(self, fname):
1468 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001469 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001470 self.exprs = check_exprs(parser.exprs)
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001471 self.docs = parser.docs
Eric Blake7618b912015-10-12 22:22:22 -06001472 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001473 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001474 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001475 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001476 self._def_exprs()
1477 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001478 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001479 print >>sys.stderr, err
1480 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001481
Markus Armbrusterac882192015-09-16 13:06:05 +02001482 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001483 # Only the predefined types are allowed to not have info
1484 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001485 assert ent.name not in self._entity_dict
1486 self._entity_dict[ent.name] = ent
1487
1488 def lookup_entity(self, name, typ=None):
1489 ent = self._entity_dict.get(name)
1490 if typ and not isinstance(ent, typ):
1491 return None
1492 return ent
1493
1494 def lookup_type(self, name):
1495 return self.lookup_entity(name, QAPISchemaType)
1496
Eric Blake861877a2016-03-17 16:48:36 -06001497 def _def_builtin_type(self, name, json_type, c_type):
1498 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001499 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1500 # qapi-types.h from a single .c, all arrays of builtins must be
1501 # declared in the first file whether or not they are used. Nicer
1502 # would be to use lazy instantiation, while figuring out how to
1503 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001504 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001505
1506 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001507 for t in [('str', 'string', 'char' + pointer_suffix),
1508 ('number', 'number', 'double'),
1509 ('int', 'int', 'int64_t'),
1510 ('int8', 'int', 'int8_t'),
1511 ('int16', 'int', 'int16_t'),
1512 ('int32', 'int', 'int32_t'),
1513 ('int64', 'int', 'int64_t'),
1514 ('uint8', 'int', 'uint8_t'),
1515 ('uint16', 'int', 'uint16_t'),
1516 ('uint32', 'int', 'uint32_t'),
1517 ('uint64', 'int', 'uint64_t'),
1518 ('size', 'int', 'uint64_t'),
1519 ('bool', 'boolean', 'bool'),
Markus Armbruster4d2d5c42017-06-26 19:25:14 +02001520 ('any', 'value', 'QObject' + pointer_suffix),
1521 ('null', 'null', 'QNull' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001522 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001523 self.the_empty_object_type = QAPISchemaObjectType(
1524 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001525 self._def_entity(self.the_empty_object_type)
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001526 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
Eric Blake93bda4d2015-12-01 22:20:55 -07001527 'qstring', 'qdict', 'qlist',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001528 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001529 self._def_entity(QAPISchemaEnumType('QType', None, None,
1530 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001531
Eric Blake93bda4d2015-12-01 22:20:55 -07001532 def _make_enum_members(self, values):
1533 return [QAPISchemaMember(v) for v in values]
1534
Eric Blake99df5282015-10-12 22:22:32 -06001535 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001536 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001537 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001538 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001539 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001540 return name
1541
Eric Blake99df5282015-10-12 22:22:32 -06001542 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001543 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001544 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001545 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001546 return name
1547
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001548 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001549 if not members:
1550 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001551 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001552 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001553 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001554 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001555 members, None))
1556 return name
1557
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001558 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001559 name = expr['enum']
1560 data = expr['data']
1561 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001562 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001563 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001564
Eric Blake99df5282015-10-12 22:22:32 -06001565 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001566 optional = False
1567 if name.startswith('*'):
1568 name = name[1:]
1569 optional = True
1570 if isinstance(typ, list):
1571 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001572 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001573 return QAPISchemaObjectTypeMember(name, typ, optional)
1574
Eric Blake99df5282015-10-12 22:22:32 -06001575 def _make_members(self, data, info):
1576 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001577 for (key, value) in data.iteritems()]
1578
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001579 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001580 name = expr['struct']
1581 base = expr.get('base')
1582 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001583 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001584 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001585 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001586
1587 def _make_variant(self, case, typ):
1588 return QAPISchemaObjectTypeVariant(case, typ)
1589
Eric Blake99df5282015-10-12 22:22:32 -06001590 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001591 if isinstance(typ, list):
1592 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001593 typ = self._make_array_type(typ[0], info)
1594 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001595 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001596 return QAPISchemaObjectTypeVariant(case, typ)
1597
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001598 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001599 name = expr['union']
1600 data = expr['data']
1601 base = expr.get('base')
1602 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001603 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001604 if isinstance(base, dict):
1605 base = (self._make_implicit_object_type(
Markus Armbrusterc2613942017-03-15 13:57:35 +01001606 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001607 if tag_name:
1608 variants = [self._make_variant(key, value)
1609 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001610 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001611 else:
Eric Blake99df5282015-10-12 22:22:32 -06001612 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001613 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001614 typ = self._make_implicit_enum_type(name, info,
1615 [v.name for v in variants])
1616 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001617 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001618 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001619 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001620 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001621 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001622 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001623
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001624 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001625 name = expr['alternate']
1626 data = expr['data']
1627 variants = [self._make_variant(key, value)
1628 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001629 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001630 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001631 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001632 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001633 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001634 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001635
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001636 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001637 name = expr['command']
1638 data = expr.get('data')
1639 rets = expr.get('returns')
1640 gen = expr.get('gen', True)
1641 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001642 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001643 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001644 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001645 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001646 if isinstance(rets, list):
1647 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001648 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001649 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1650 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001651
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001652 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001653 name = expr['event']
1654 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001655 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001656 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001657 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001658 name, info, doc, 'arg', self._make_members(data, info))
1659 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001660
1661 def _def_exprs(self):
1662 for expr_elem in self.exprs:
1663 expr = expr_elem['expr']
1664 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001665 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001666 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001667 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001668 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001669 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001670 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001671 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001672 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001673 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001674 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001675 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001676 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001677 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001678 else:
1679 assert False
1680
1681 def check(self):
1682 for ent in self._entity_dict.values():
1683 ent.check(self)
1684
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001685 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001686 visitor.visit_begin(self)
1687 for (name, entity) in sorted(self._entity_dict.items()):
1688 if visitor.visit_needed(entity):
1689 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001690 visitor.visit_end()
1691
Markus Armbruster2caba362013-07-27 17:41:56 +02001692
Markus Armbruster00e4b282015-06-10 10:04:36 +02001693#
1694# Code generation helpers
1695#
1696
Michael Roth0f923be2011-07-19 14:50:39 -05001697def camel_case(name):
1698 new_name = ''
1699 first = True
1700 for ch in name:
1701 if ch in ['_', '-']:
1702 first = True
1703 elif first:
1704 new_name += ch.upper()
1705 first = False
1706 else:
1707 new_name += ch.lower()
1708 return new_name
1709
Eric Blake437db252015-09-29 16:21:02 -06001710
Markus Armbruster849bc532015-05-14 06:50:53 -06001711# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1712# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1713# ENUM24_Name -> ENUM24_NAME
1714def camel_to_upper(value):
1715 c_fun_str = c_name(value, False)
1716 if value.isupper():
1717 return c_fun_str
1718
1719 new_name = ''
1720 l = len(c_fun_str)
1721 for i in range(l):
1722 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001723 # When c is upper and no '_' appears before, do more checks
1724 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001725 if i < l - 1 and c_fun_str[i + 1].islower():
1726 new_name += '_'
1727 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001728 new_name += '_'
1729 new_name += c
1730 return new_name.lstrip('_').upper()
1731
Eric Blake437db252015-09-29 16:21:02 -06001732
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001733def c_enum_const(type_name, const_name, prefix=None):
1734 if prefix is not None:
1735 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001736 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001737
Eric Blake18df5152015-05-14 06:50:48 -06001738c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001739
Eric Blake437db252015-09-29 16:21:02 -06001740
Eric Blakec6405b52015-05-14 06:50:55 -06001741# Map @name to a valid C identifier.
1742# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001743# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001744#
1745# Used for converting 'name' from a 'name':'type' qapi definition
1746# into a generated struct member, as well as converting type names
1747# into substrings of a generated C function name.
1748# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1749# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001750def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001751 # ANSI X3J11/88-090, 3.1.1
1752 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001753 'default', 'do', 'double', 'else', 'enum', 'extern',
1754 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1755 'return', 'short', 'signed', 'sizeof', 'static',
1756 'struct', 'switch', 'typedef', 'union', 'unsigned',
1757 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001758 # ISO/IEC 9899:1999, 6.4.1
1759 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1760 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001761 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1762 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001763 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1764 # excluding _.*
1765 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001766 # C++ ISO/IEC 14882:2003 2.11
1767 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1768 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1769 'namespace', 'new', 'operator', 'private', 'protected',
1770 'public', 'reinterpret_cast', 'static_cast', 'template',
1771 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1772 'using', 'virtual', 'wchar_t',
1773 # alternative representations
1774 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1775 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001776 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001777 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001778 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001779 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1780 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001781 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001782 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001783
Amos Kong05dfb262014-06-10 19:25:53 +08001784eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001785pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001786
Eric Blake437db252015-09-29 16:21:02 -06001787
Michael Roth0f923be2011-07-19 14:50:39 -05001788def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001789 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001790 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001791 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001792 return ret
1793
1794indent_level = 0
1795
Eric Blake437db252015-09-29 16:21:02 -06001796
Michael Roth0f923be2011-07-19 14:50:39 -05001797def push_indent(indent_amount=4):
1798 global indent_level
1799 indent_level += indent_amount
1800
Eric Blake437db252015-09-29 16:21:02 -06001801
Michael Roth0f923be2011-07-19 14:50:39 -05001802def pop_indent(indent_amount=4):
1803 global indent_level
1804 indent_level -= indent_amount
1805
Eric Blake437db252015-09-29 16:21:02 -06001806
Markus Armbruster77e703b2015-06-24 19:27:32 +02001807# Generate @code with @kwds interpolated.
1808# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001809def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001810 raw = code % kwds
1811 if indent_level:
1812 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001813 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001814 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001815 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001816 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001817 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001818
Eric Blake437db252015-09-29 16:21:02 -06001819
Michael Roth0f923be2011-07-19 14:50:39 -05001820def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001821 if code[0] == '\n':
1822 code = code[1:]
1823 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001824
Michael Roth0f923be2011-07-19 14:50:39 -05001825
1826def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001827 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001828
Eric Blake437db252015-09-29 16:21:02 -06001829
Michael Rothc0afa9c2013-05-10 17:46:00 -05001830def guardstart(name):
1831 return mcgen('''
1832
1833#ifndef %(name)s
1834#define %(name)s
1835
1836''',
1837 name=guardname(name))
1838
Eric Blake437db252015-09-29 16:21:02 -06001839
Michael Rothc0afa9c2013-05-10 17:46:00 -05001840def guardend(name):
1841 return mcgen('''
1842
1843#endif /* %(name)s */
1844
1845''',
1846 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001847
Eric Blake437db252015-09-29 16:21:02 -06001848
Markus Armbrustere98859a2015-09-16 13:06:16 +02001849def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001850 ret = mcgen('''
1851
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001852const QEnumLookup %(c_name)s_lookup = {
1853 .array = (const char *const[]) {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001854''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001855 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001856 for value in values:
1857 index = c_enum_const(name, value, prefix)
1858 ret += mcgen('''
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001859 [%(index)s] = "%(value)s",
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001860''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001861 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001862
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001863 ret += mcgen('''
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001864 },
1865 .size = %(max_index)s
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001866};
1867''',
Marc-André Lureauebf677c2017-08-24 10:46:11 +02001868 max_index=c_enum_const(name, '_MAX', prefix))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001869 return ret
1870
Eric Blake437db252015-09-29 16:21:02 -06001871
Markus Armbrustere98859a2015-09-16 13:06:16 +02001872def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001873 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001874 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001875
1876 ret = mcgen('''
1877
1878typedef enum %(c_name)s {
1879''',
1880 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001881
1882 i = 0
1883 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001884 ret += mcgen('''
1885 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001886''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001887 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001888 i=i)
1889 i += 1
1890
Markus Armbrustere98859a2015-09-16 13:06:16 +02001891 ret += mcgen('''
1892} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001893''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001894 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001895
Markus Armbrustere98859a2015-09-16 13:06:16 +02001896 ret += mcgen('''
1897
Markus Armbruster5b5f8252017-08-24 10:46:07 +02001898#define %(c_name)s_str(val) \\
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001899 qapi_enum_lookup(&%(c_name)s_lookup, (val))
Markus Armbruster5b5f8252017-08-24 10:46:07 +02001900
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001901extern const QEnumLookup %(c_name)s_lookup;
Markus Armbrustere98859a2015-09-16 13:06:16 +02001902''',
1903 c_name=c_name(name))
1904 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001905
Eric Blake437db252015-09-29 16:21:02 -06001906
Marc-André Lureau086ee7a2017-06-01 16:41:41 +04001907def build_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001908 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001909 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001910 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001911 ret = ''
1912 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001913 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001914 ret += '%s arg' % arg_type.c_param_type()
1915 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001916 else:
1917 assert not arg_type.variants
1918 for memb in arg_type.members:
1919 ret += sep
1920 sep = ', '
1921 if memb.optional:
1922 ret += 'bool has_%s, ' % c_name(memb.name)
1923 ret += '%s %s' % (memb.type.c_param_type(),
1924 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001925 if extra:
1926 ret += sep + extra
1927 return ret
1928
Eric Blake1f353342015-09-29 16:21:13 -06001929
Markus Armbruster00e4b282015-06-10 10:04:36 +02001930#
1931# Common command line parsing
1932#
1933
Eric Blake437db252015-09-29 16:21:02 -06001934
Markus Armbrusteref801a92017-03-15 13:57:08 +01001935def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001936
1937 try:
1938 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001939 'chp:o:' + extra_options,
1940 ['source', 'header', 'prefix=',
1941 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001942 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001943 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001944 sys.exit(1)
1945
Markus Armbrusteref801a92017-03-15 13:57:08 +01001946 output_dir = ''
1947 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001948 do_c = False
1949 do_h = False
1950 extra_opts = []
1951
1952 for oa in opts:
1953 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001954 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001955 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001956 if match.end() != len(a):
1957 print >>sys.stderr, \
1958 "%s: 'funny character '%s' in argument of --prefix" \
1959 % (sys.argv[0], a[match.end()])
1960 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001961 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001962 elif o in ('-o', '--output-dir'):
1963 output_dir = a + '/'
1964 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001965 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001966 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001967 do_h = True
1968 else:
1969 extra_opts.append(oa)
1970
1971 if not do_c and not do_h:
1972 do_c = True
1973 do_h = True
1974
Markus Armbruster16d80f62015-04-02 13:32:16 +02001975 if len(args) != 1:
1976 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001977 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02001978 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001979
Markus Armbruster54414042015-06-09 16:22:45 +02001980 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001981
Markus Armbruster00e4b282015-06-10 10:04:36 +02001982#
1983# Generate output files with boilerplate
1984#
1985
Eric Blake437db252015-09-29 16:21:02 -06001986
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001987def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1988 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001989 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001990 c_file = output_dir + prefix + c_file
1991 h_file = output_dir + prefix + h_file
1992
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001993 if output_dir:
1994 try:
1995 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01001996 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001997 if e.errno != errno.EEXIST:
1998 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001999
2000 def maybe_open(really, name, opt):
2001 if really:
2002 return open(name, opt)
2003 else:
2004 import StringIO
2005 return StringIO.StringIO()
2006
2007 fdef = maybe_open(do_c, c_file, 'w')
2008 fdecl = maybe_open(do_h, h_file, 'w')
2009
2010 fdef.write(mcgen('''
2011/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2012%(comment)s
2013''',
Eric Blake437db252015-09-29 16:21:02 -06002014 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002015
2016 fdecl.write(mcgen('''
2017/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2018%(comment)s
2019#ifndef %(guard)s
2020#define %(guard)s
2021
2022''',
Eric Blake437db252015-09-29 16:21:02 -06002023 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002024
2025 return (fdef, fdecl)
2026
Eric Blake437db252015-09-29 16:21:02 -06002027
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002028def close_output(fdef, fdecl):
2029 fdecl.write('''
2030#endif
2031''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002032 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002033 fdef.close()