blob: 98d7123d27260a53b02ec5b28697632d8fb8afa5 [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
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +000014from __future__ import print_function
Markus Armbruster12f8e1b2015-04-02 14:46:39 +020015import errno
Markus Armbruster2114f5a2015-04-02 13:12:21 +020016import getopt
Lluís Vilanova33aaad52014-05-02 15:52:35 +020017import os
Markus Armbrusterc2613942017-03-15 13:57:35 +010018import re
Markus Armbruster47299262015-05-14 06:50:47 -060019import string
Markus Armbrusterc2613942017-03-15 13:57:35 +010020import sys
21from ordereddict import OrderedDict
Michael Roth0f923be2011-07-19 14:50:39 -050022
Eric Blakeb52c4b92015-05-04 09:05:00 -060023builtin_types = {
Markus Armbruster4d2d5c42017-06-26 19:25:14 +020024 'null': 'QTYPE_QNULL',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020025 'str': 'QTYPE_QSTRING',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +040026 'int': 'QTYPE_QNUM',
27 'number': 'QTYPE_QNUM',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020028 'bool': 'QTYPE_QBOOL',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +040029 'int8': 'QTYPE_QNUM',
30 'int16': 'QTYPE_QNUM',
31 'int32': 'QTYPE_QNUM',
32 'int64': 'QTYPE_QNUM',
33 'uint8': 'QTYPE_QNUM',
34 'uint16': 'QTYPE_QNUM',
35 'uint32': 'QTYPE_QNUM',
36 'uint64': 'QTYPE_QNUM',
37 'size': 'QTYPE_QNUM',
Eric Blake1310a3d2015-12-01 22:20:46 -070038 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070039 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020040}
41
Markus Armbrusterbc52d032017-03-15 13:56:51 +010042# Are documentation comments required?
43doc_required = False
44
Eric Blake10d4d992015-05-04 09:05:23 -060045# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010046returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060047
Eric Blake893e1f22015-12-01 22:20:57 -070048# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010049name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070050
Markus Armbruster5f018442017-03-15 13:57:31 +010051enum_types = {}
Markus Armbrustered285bf2017-03-15 13:57:32 +010052struct_types = {}
Markus Armbruster768562d2017-03-15 13:57:33 +010053union_types = {}
Eric Blake4dc2e692015-05-04 09:05:17 -060054all_names = {}
55
Markus Armbruster00e4b282015-06-10 10:04:36 +020056#
57# Parsing the schema into expressions
58#
59
Eric Blake437db252015-09-29 16:21:02 -060060
Lluís Vilanovaa719a272014-05-07 20:46:15 +020061def error_path(parent):
Markus Armbrusteref801a92017-03-15 13:57:08 +010062 res = ''
Lluís Vilanovaa719a272014-05-07 20:46:15 +020063 while parent:
Markus Armbrusteref801a92017-03-15 13:57:08 +010064 res = ('In file included from %s:%d:\n' % (parent['file'],
Lluís Vilanovaa719a272014-05-07 20:46:15 +020065 parent['line'])) + res
66 parent = parent['parent']
67 return res
68
Eric Blake437db252015-09-29 16:21:02 -060069
Marc-André Lureau4148c292017-01-13 15:41:25 +010070class QAPIError(Exception):
71 def __init__(self, fname, line, col, incl_info, msg):
Eric Blake59b00542015-09-29 16:21:01 -060072 Exception.__init__(self)
Marc-André Lureau4148c292017-01-13 15:41:25 +010073 self.fname = fname
74 self.line = line
75 self.col = col
76 self.info = incl_info
Markus Armbruster2caba362013-07-27 17:41:56 +020077 self.msg = msg
Marc-André Lureau4148c292017-01-13 15:41:25 +010078
79 def __str__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +010080 loc = '%s:%d' % (self.fname, self.line)
Marc-André Lureau4148c292017-01-13 15:41:25 +010081 if self.col is not None:
Markus Armbrusteref801a92017-03-15 13:57:08 +010082 loc += ':%s' % self.col
83 return error_path(self.info) + '%s: %s' % (loc, self.msg)
Marc-André Lureau4148c292017-01-13 15:41:25 +010084
85
86class QAPIParseError(QAPIError):
87 def __init__(self, parser, msg):
88 col = 1
89 for ch in parser.src[parser.line_pos:parser.pos]:
Wenchao Xia515b9432014-03-04 18:44:33 -080090 if ch == '\t':
Marc-André Lureau4148c292017-01-13 15:41:25 +010091 col = (col + 7) % 8 + 1
Markus Armbruster2caba362013-07-27 17:41:56 +020092 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +010093 col += 1
94 QAPIError.__init__(self, parser.fname, parser.line, col,
95 parser.incl_info, msg)
Markus Armbruster2caba362013-07-27 17:41:56 +020096
Eric Blake437db252015-09-29 16:21:02 -060097
Marc-André Lureau4148c292017-01-13 15:41:25 +010098class QAPISemError(QAPIError):
99 def __init__(self, info, msg):
100 QAPIError.__init__(self, info['file'], info['line'], None,
101 info['parent'], msg)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800102
Eric Blake437db252015-09-29 16:21:02 -0600103
Marc-André Lureau3313b612017-01-13 15:41:29 +0100104class QAPIDoc(object):
105 class Section(object):
106 def __init__(self, name=None):
107 # optional section name (argument/member or section name)
108 self.name = name
109 # the list of lines for this section
Markus Armbruster09331fc2017-10-02 16:13:38 +0200110 self.text = ''
Marc-André Lureau3313b612017-01-13 15:41:29 +0100111
112 def append(self, line):
Markus Armbruster09331fc2017-10-02 16:13:38 +0200113 self.text += line.rstrip() + '\n'
Marc-André Lureau3313b612017-01-13 15:41:29 +0100114
115 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100116 def __init__(self, name):
117 QAPIDoc.Section.__init__(self, name)
118 self.member = None
119
120 def connect(self, member):
121 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100122
123 def __init__(self, parser, info):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200124 # self._parser is used to report errors with QAPIParseError. The
Marc-André Lureau3313b612017-01-13 15:41:29 +0100125 # resulting error position depends on the state of the parser.
126 # It happens to be the beginning of the comment. More or less
127 # servicable, but action at a distance.
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200128 self._parser = parser
Marc-André Lureau3313b612017-01-13 15:41:29 +0100129 self.info = info
130 self.symbol = None
131 self.body = QAPIDoc.Section()
132 # dict mapping parameter name to ArgSection
133 self.args = OrderedDict()
134 # a list of Section
135 self.sections = []
136 # the current section
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200137 self._section = self.body
Marc-André Lureau3313b612017-01-13 15:41:29 +0100138
139 def has_section(self, name):
140 """Return True if we have a section with this name."""
141 for i in self.sections:
142 if i.name == name:
143 return True
144 return False
145
146 def append(self, line):
147 """Parse a comment line and add it to the documentation."""
148 line = line[1:]
149 if not line:
150 self._append_freeform(line)
151 return
152
153 if line[0] != ' ':
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200154 raise QAPIParseError(self._parser, "Missing space after #")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100155 line = line[1:]
156
157 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
158 # recognized, and get silently treated as ordinary text
159 if self.symbol:
160 self._append_symbol_line(line)
Markus Armbruster09331fc2017-10-02 16:13:38 +0200161 elif not self.body.text and line.startswith('@'):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100162 if not line.endswith(':'):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200163 raise QAPIParseError(self._parser, "Line should end with :")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100164 self.symbol = line[1:-1]
165 # FIXME invalid names other than the empty string aren't flagged
166 if not self.symbol:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200167 raise QAPIParseError(self._parser, "Invalid name")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100168 else:
169 self._append_freeform(line)
170
Markus Armbruster4ea71482017-03-15 13:57:23 +0100171 def end_comment(self):
172 self._end_section()
173
Marc-André Lureau3313b612017-01-13 15:41:29 +0100174 def _append_symbol_line(self, line):
175 name = line.split(' ', 1)[0]
176
Markus Armbrusteref801a92017-03-15 13:57:08 +0100177 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100178 line = line[len(name)+1:]
179 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100180 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100181 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100182 'Note:', 'Notes:',
183 'Example:', 'Examples:',
184 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100185 line = line[len(name)+1:]
186 self._start_section(name[:-1])
187
188 self._append_freeform(line)
189
190 def _start_args_section(self, name):
191 # FIXME invalid names other than the empty string aren't flagged
192 if not name:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200193 raise QAPIParseError(self._parser, "Invalid parameter name")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100194 if name in self.args:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200195 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100196 "'%s' parameter name duplicated" % name)
197 if self.sections:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200198 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100199 "'@%s:' can't follow '%s' section"
200 % (name, self.sections[0].name))
Markus Armbruster4ea71482017-03-15 13:57:23 +0100201 self._end_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200202 self._section = QAPIDoc.ArgSection(name)
203 self.args[name] = self._section
Marc-André Lureau3313b612017-01-13 15:41:29 +0100204
Markus Armbrusterfc3f0df2017-10-02 16:13:37 +0200205 def _start_section(self, name=None):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100206 if name in ('Returns', 'Since') and self.has_section(name):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200207 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100208 "Duplicated '%s' section" % name)
Markus Armbruster4ea71482017-03-15 13:57:23 +0100209 self._end_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200210 self._section = QAPIDoc.Section(name)
211 self.sections.append(self._section)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100212
Markus Armbruster4ea71482017-03-15 13:57:23 +0100213 def _end_section(self):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200214 if self._section:
215 text = self._section.text = self._section.text.strip()
216 if self._section.name and (not text or text.isspace()):
217 raise QAPIParseError(self._parser, "Empty doc section '%s'"
218 % self._section.name)
219 self._section = None
Markus Armbruster4ea71482017-03-15 13:57:23 +0100220
Marc-André Lureau3313b612017-01-13 15:41:29 +0100221 def _append_freeform(self, line):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200222 in_arg = isinstance(self._section, QAPIDoc.ArgSection)
223 if (in_arg and self._section.text.endswith('\n\n')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100224 and line and not line[0].isspace()):
225 self._start_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200226 if (in_arg or not self._section.name
227 or not self._section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100228 line = line.strip()
Markus Armbruster2d433232017-03-15 13:57:22 +0100229 match = re.match(r'(@\S+:)', line)
230 if match:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200231 raise QAPIParseError(self._parser,
Markus Armbruster2d433232017-03-15 13:57:22 +0100232 "'%s' not allowed in free-form documentation"
233 % match.group(1))
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200234 self._section.append(line)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100235
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100236 def connect_member(self, member):
237 if member.name not in self.args:
238 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100239 self.args[member.name] = QAPIDoc.ArgSection(member.name)
240 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100241
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100242 def check_expr(self, expr):
243 if self.has_section('Returns') and 'command' not in expr:
244 raise QAPISemError(self.info,
245 "'Returns:' is only valid for commands")
246
Markus Armbruster816a57c2017-03-15 13:57:26 +0100247 def check(self):
Daniel P. Berrange2f848042018-01-16 13:42:05 +0000248 bogus = [name for name, section in self.args.items()
Markus Armbruster816a57c2017-03-15 13:57:26 +0100249 if not section.member]
250 if bogus:
251 raise QAPISemError(
252 self.info,
253 "The following documented members are not in "
254 "the declaration: %s" % ", ".join(bogus))
255
Marc-André Lureau3313b612017-01-13 15:41:29 +0100256
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200257class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500258
Eric Blake437db252015-09-29 16:21:02 -0600259 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200260 abs_fname = os.path.abspath(fp.name)
Markus Armbruster2281d002017-10-02 16:13:33 +0200261 self.fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200262 previously_included.append(abs_fname)
263 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200264 self.src = fp.read()
265 if self.src == '' or self.src[-1] != '\n':
266 self.src += '\n'
267 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800268 self.line = 1
269 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200270 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100271 self.docs = []
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200272 self.accept()
Markus Armbruster64d60332017-10-02 16:13:34 +0200273 cur_doc = None
Michael Roth0f923be2011-07-19 14:50:39 -0500274
Eric Blake437db252015-09-29 16:21:02 -0600275 while self.tok is not None:
Markus Armbruster2281d002017-10-02 16:13:33 +0200276 info = {'file': self.fname, 'line': self.line,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100277 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100278 if self.tok == '#':
Markus Armbruster64d60332017-10-02 16:13:34 +0200279 self.reject_expr_doc(cur_doc)
280 cur_doc = self.get_doc(info)
281 self.docs.append(cur_doc)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100282 continue
283
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200284 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100285 if 'include' in expr:
Markus Armbruster64d60332017-10-02 16:13:34 +0200286 self.reject_expr_doc(cur_doc)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200287 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100288 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100289 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200290 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100291 raise QAPISemError(info,
292 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100293 self._include(include, info, os.path.dirname(abs_fname),
294 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100295 elif "pragma" in expr:
Markus Armbruster64d60332017-10-02 16:13:34 +0200296 self.reject_expr_doc(cur_doc)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100297 if len(expr) != 1:
298 raise QAPISemError(info, "Invalid 'pragma' directive")
299 pragma = expr['pragma']
300 if not isinstance(pragma, dict):
301 raise QAPISemError(
302 info, "Value of 'pragma' must be a dictionary")
Daniel P. Berrange2f848042018-01-16 13:42:05 +0000303 for name, value in pragma.items():
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100304 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200305 else:
306 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100307 'info': info}
Markus Armbruster64d60332017-10-02 16:13:34 +0200308 if cur_doc:
309 if not cur_doc.symbol:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100310 raise QAPISemError(
Markus Armbruster64d60332017-10-02 16:13:34 +0200311 cur_doc.info, "Expression documentation required")
312 expr_elem['doc'] = cur_doc
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200313 self.exprs.append(expr_elem)
Markus Armbruster64d60332017-10-02 16:13:34 +0200314 cur_doc = None
315 self.reject_expr_doc(cur_doc)
Markus Armbrustere7823a22017-03-15 13:57:20 +0100316
Markus Armbruster64d60332017-10-02 16:13:34 +0200317 @staticmethod
318 def reject_expr_doc(doc):
319 if doc and doc.symbol:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100320 raise QAPISemError(
Markus Armbruster64d60332017-10-02 16:13:34 +0200321 doc.info,
Markus Armbrustere7823a22017-03-15 13:57:20 +0100322 "Documentation for '%s' is not followed by the definition"
Markus Armbruster64d60332017-10-02 16:13:34 +0200323 % doc.symbol)
Michael Roth0f923be2011-07-19 14:50:39 -0500324
Markus Armbrustere04dea82017-03-15 13:56:50 +0100325 def _include(self, include, info, base_dir, previously_included):
326 incl_abs_fname = os.path.join(base_dir, include)
327 # catch inclusion cycle
328 inf = info
329 while inf:
330 if incl_abs_fname == os.path.abspath(inf['file']):
331 raise QAPISemError(info, "Inclusion loop for %s" % include)
332 inf = inf['parent']
333
334 # skip multiple include of the same file
335 if incl_abs_fname in previously_included:
336 return
337 try:
338 fobj = open(incl_abs_fname, 'r')
339 except IOError as e:
340 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
341 exprs_include = QAPISchemaParser(fobj, previously_included, info)
342 self.exprs.extend(exprs_include.exprs)
343 self.docs.extend(exprs_include.docs)
344
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100345 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100346 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100347 if name == 'doc-required':
348 if not isinstance(value, bool):
349 raise QAPISemError(info,
350 "Pragma 'doc-required' must be boolean")
351 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100352 elif name == 'returns-whitelist':
353 if (not isinstance(value, list)
354 or any([not isinstance(elt, str) for elt in value])):
355 raise QAPISemError(info,
356 "Pragma returns-whitelist must be"
357 " a list of strings")
358 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100359 elif name == 'name-case-whitelist':
360 if (not isinstance(value, list)
361 or any([not isinstance(elt, str) for elt in value])):
362 raise QAPISemError(info,
363 "Pragma name-case-whitelist must be"
364 " a list of strings")
365 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100366 else:
367 raise QAPISemError(info, "Unknown pragma '%s'" % name)
368
Marc-André Lureau3313b612017-01-13 15:41:29 +0100369 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200370 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200371 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200372 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200373 self.cursor += 1
374 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500375
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200376 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100377 if self.src[self.cursor] == '#':
378 # Start of doc comment
379 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200380 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100381 if not skip_comment:
382 self.val = self.src[self.pos:self.cursor]
383 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100384 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200385 return
386 elif self.tok == "'":
387 string = ''
388 esc = False
389 while True:
390 ch = self.src[self.cursor]
391 self.cursor += 1
392 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100393 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200394 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600395 if ch == 'b':
396 string += '\b'
397 elif ch == 'f':
398 string += '\f'
399 elif ch == 'n':
400 string += '\n'
401 elif ch == 'r':
402 string += '\r'
403 elif ch == 't':
404 string += '\t'
405 elif ch == 'u':
406 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600407 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600408 ch = self.src[self.cursor]
409 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100410 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100411 raise QAPIParseError(self,
412 '\\u escape needs 4 '
413 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600414 value = (value << 4) + int(ch, 16)
415 # If Python 2 and 3 didn't disagree so much on
416 # how to handle Unicode, then we could allow
417 # Unicode string defaults. But most of QAPI is
418 # ASCII-only, so we aren't losing much for now.
419 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100420 raise QAPIParseError(self,
421 'For now, \\u escape '
422 'only supports non-zero '
423 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600424 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100425 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600426 string += ch
427 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100428 raise QAPIParseError(self,
429 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200430 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100431 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200432 esc = True
433 elif ch == "'":
434 self.val = string
435 return
436 else:
437 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100438 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200439 self.val = True
440 self.cursor += 3
441 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100442 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200443 self.val = False
444 self.cursor += 4
445 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100446 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200447 self.val = None
448 self.cursor += 3
449 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200450 elif self.tok == '\n':
451 if self.cursor == len(self.src):
452 self.tok = None
453 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800454 self.line += 1
455 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200456 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100457 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500458
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200459 def get_members(self):
460 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200461 if self.tok == '}':
462 self.accept()
463 return expr
464 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100465 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200466 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200467 key = self.val
468 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200469 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100470 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200471 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800472 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100473 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200474 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200475 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200476 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200477 return expr
478 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100479 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200480 self.accept()
481 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100482 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500483
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200484 def get_values(self):
485 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200486 if self.tok == ']':
487 self.accept()
488 return expr
Eric Blake437db252015-09-29 16:21:02 -0600489 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100490 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
491 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200492 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200493 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200494 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200495 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200496 return expr
497 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100498 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200499 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500500
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200501 def get_expr(self, nested):
502 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100503 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200504 if self.tok == '{':
505 self.accept()
506 expr = self.get_members()
507 elif self.tok == '[':
508 self.accept()
509 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600510 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200511 expr = self.val
512 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200513 else:
Markus Armbruster012b1262017-03-15 13:57:36 +0100514 raise QAPIParseError(self, 'Expected "{", "[", string, '
515 'boolean or "null"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200516 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200517
Marc-André Lureau3313b612017-01-13 15:41:29 +0100518 def get_doc(self, info):
519 if self.val != '##':
520 raise QAPIParseError(self, "Junk after '##' at start of "
521 "documentation comment")
522
523 doc = QAPIDoc(self, info)
524 self.accept(False)
525 while self.tok == '#':
526 if self.val.startswith('##'):
527 # End of doc comment
528 if self.val != '##':
529 raise QAPIParseError(self, "Junk after '##' at end of "
530 "documentation comment")
Markus Armbruster4ea71482017-03-15 13:57:23 +0100531 doc.end_comment()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100532 self.accept()
533 return doc
534 else:
535 doc.append(self.val)
536 self.accept(False)
537
538 raise QAPIParseError(self, "Documentation comment must end with '##'")
539
540
Markus Armbruster00e4b282015-06-10 10:04:36 +0200541#
542# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200543# TODO fold into QAPISchema
544# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200545#
546
Eric Blake437db252015-09-29 16:21:02 -0600547
Eric Blake14f00c62016-03-03 09:16:43 -0700548def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600549 if isinstance(base, dict):
550 return base
Markus Armbrustered285bf2017-03-15 13:57:32 +0100551 base_struct_define = struct_types.get(base)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800552 if not base_struct_define:
553 return None
554 return base_struct_define['data']
555
Eric Blake437db252015-09-29 16:21:02 -0600556
Eric Blake811d04f2015-05-04 09:05:10 -0600557# Return the qtype of an alternate branch, or None on error.
558def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600559 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600560 return builtin_types[qapi_type]
Markus Armbrustered285bf2017-03-15 13:57:32 +0100561 elif qapi_type in struct_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100562 return 'QTYPE_QDICT'
Markus Armbruster5f018442017-03-15 13:57:31 +0100563 elif qapi_type in enum_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100564 return 'QTYPE_QSTRING'
Markus Armbruster768562d2017-03-15 13:57:33 +0100565 elif qapi_type in union_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100566 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600567 return None
568
Eric Blake437db252015-09-29 16:21:02 -0600569
Wenchao Xiabceae762014-03-06 17:08:56 -0800570# Return the discriminator enum define if discriminator is specified as an
571# enum type, otherwise return None.
572def discriminator_find_enum_define(expr):
573 base = expr.get('base')
574 discriminator = expr.get('discriminator')
575
576 if not (discriminator and base):
577 return None
578
Eric Blake14f00c62016-03-03 09:16:43 -0700579 base_members = find_base_members(base)
580 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800581 return None
582
Eric Blake14f00c62016-03-03 09:16:43 -0700583 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800584 if not discriminator_type:
585 return None
586
Markus Armbruster5f018442017-03-15 13:57:31 +0100587 return enum_types.get(discriminator_type)
Wenchao Xiabceae762014-03-06 17:08:56 -0800588
Eric Blake437db252015-09-29 16:21:02 -0600589
Eric Blake59a92fe2015-11-18 01:52:56 -0700590# Names must be letters, numbers, -, and _. They must start with letter,
591# except for downstream extensions which must start with __RFQDN_.
592# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100593valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700594 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600595
596
Marc-André Lureau4148c292017-01-13 15:41:25 +0100597def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600598 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600599 global valid_name
600 membername = name
601
602 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100603 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600604 if name.startswith('*'):
605 membername = name[1:]
606 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100607 raise QAPISemError(info, "%s does not allow optional name '%s'"
608 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600609 # Enum members can start with a digit, because the generated C
610 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700611 if enum_member and membername[0].isdigit():
612 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600613 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
614 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600615 if not valid_name.match(membername) or \
616 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100617 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600618
Eric Blake437db252015-09-29 16:21:02 -0600619
620def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200621 global all_names
622 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200623 # FIXME should reject names that differ only in '_' vs. '.'
624 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200625 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100626 raise QAPISemError(info, "%s '%s' is already defined"
627 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600628 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100629 raise QAPISemError(info, "%s '%s' should not end in '%s'"
630 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200631 all_names[name] = meta
632
Eric Blake437db252015-09-29 16:21:02 -0600633
Marc-André Lureau4148c292017-01-13 15:41:25 +0100634def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600635 allow_dict=False, allow_optional=False,
636 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600637 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600638
639 if value is None:
640 return
641
Eric Blakedd883c62015-05-04 09:05:21 -0600642 # Check if array type for value is okay
643 if isinstance(value, list):
644 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100645 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600646 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100647 raise QAPISemError(info,
648 "%s: array type must contain single type name" %
649 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600650 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600651
652 # Check if type name for value is okay
653 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600654 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100655 raise QAPISemError(info, "%s uses unknown type '%s'"
656 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600657 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100658 raise QAPISemError(info, "%s cannot use %s type '%s'" %
659 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600660 return
661
Eric Blakedd883c62015-05-04 09:05:21 -0600662 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100663 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200664
665 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100666 raise QAPISemError(info,
667 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200668
669 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600670 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100671 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600672 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600673 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100674 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
675 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600676 # Todo: allow dictionaries to represent default values of
677 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100678 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200679 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600680 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600681 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600682
Eric Blake437db252015-09-29 16:21:02 -0600683
Marc-André Lureau4148c292017-01-13 15:41:25 +0100684def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600685 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600686 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600687
Eric Blakec8184082016-07-13 21:50:20 -0600688 args_meta = ['struct']
689 if boxed:
690 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100691 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600692 expr.get('data'), allow_dict=not boxed, allow_optional=True,
693 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600694 returns_meta = ['union', 'struct']
695 if name in returns_whitelist:
696 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100697 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200698 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200699 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600700
Eric Blake437db252015-09-29 16:21:02 -0600701
Marc-André Lureau4148c292017-01-13 15:41:25 +0100702def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600703 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600704 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600705
Eric Blakec8184082016-07-13 21:50:20 -0600706 meta = ['struct']
707 if boxed:
708 meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100709 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600710 expr.get('data'), allow_dict=not boxed, allow_optional=True,
711 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200712
Eric Blake437db252015-09-29 16:21:02 -0600713
Marc-André Lureau4148c292017-01-13 15:41:25 +0100714def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800715 name = expr['union']
716 base = expr.get('base')
717 discriminator = expr.get('discriminator')
718 members = expr['data']
719
Eric Blake811d04f2015-05-04 09:05:10 -0600720 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600721
722 # With no discriminator it is a simple union.
723 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600724 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600725 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600726 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100727 raise QAPISemError(info, "Simple union '%s' must not have a base" %
728 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600729
730 # Else, it's a flat union.
731 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600732 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100733 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600734 base, allow_dict=True, allow_optional=True,
735 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600736 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100737 raise QAPISemError(info, "Flat union '%s' must have a base"
738 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700739 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100740 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800741
Eric Blakec9e0a792015-05-04 09:05:22 -0600742 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600743 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100744 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600745 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700746 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800747 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100748 raise QAPISemError(info,
749 "Discriminator '%s' is not a member of base "
750 "struct '%s'"
751 % (discriminator, base))
Markus Armbruster5f018442017-03-15 13:57:31 +0100752 enum_define = enum_types.get(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600753 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800754 # Do not allow string discriminator
755 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100756 raise QAPISemError(info,
757 "Discriminator '%s' must be of enumeration "
758 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800759
Eric Blake02a57ae2016-02-17 23:48:16 -0700760 # Check every branch; don't allow an empty union
761 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100762 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800763 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100764 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600765
Eric Blake01cfbaa2015-12-01 22:20:58 -0700766 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100767 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200768 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600769
Eric Blake44bd1272015-05-04 09:05:08 -0600770 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700771 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600772 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100773 if key not in enum_define['data']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100774 raise QAPISemError(info,
775 "Discriminator value '%s' is not found in "
776 "enum '%s'"
Markus Armbrustereda43c62017-03-15 13:57:29 +0100777 % (key, enum_define['enum']))
Eric Blake44bd1272015-05-04 09:05:08 -0600778
Eric Blaked0b18232016-07-13 21:50:13 -0600779 # If discriminator is user-defined, ensure all values are covered
780 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100781 for value in enum_define['data']:
Eric Blaked0b18232016-07-13 21:50:13 -0600782 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100783 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
784 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600785
Eric Blake437db252015-09-29 16:21:02 -0600786
Marc-André Lureau4148c292017-01-13 15:41:25 +0100787def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600788 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600789 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600790 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600791
Eric Blake02a57ae2016-02-17 23:48:16 -0700792 # Check every branch; require at least two branches
793 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100794 raise QAPISemError(info,
795 "Alternate '%s' should have at least two branches "
796 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600797 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100798 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600799
Eric Blake811d04f2015-05-04 09:05:10 -0600800 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100801 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600802 value,
803 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600804 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700805 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100806 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
807 "type '%s'" % (name, key, value))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200808 conflicting = set([qtype])
809 if qtype == 'QTYPE_QSTRING':
810 enum_expr = enum_types.get(value)
811 if enum_expr:
812 for v in enum_expr['data']:
813 if v in ['on', 'off']:
814 conflicting.add('QTYPE_QBOOL')
815 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +0400816 conflicting.add('QTYPE_QNUM')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200817 else:
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +0400818 conflicting.add('QTYPE_QNUM')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200819 conflicting.add('QTYPE_QBOOL')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200820 for qt in conflicting:
Eduardo Habkostfda72ab2017-07-17 15:09:26 -0300821 if qt in types_seen:
822 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
823 "be distinguished from member '%s'"
824 % (name, key, types_seen[qt]))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200825 types_seen[qt] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800826
Eric Blake437db252015-09-29 16:21:02 -0600827
Marc-André Lureau4148c292017-01-13 15:41:25 +0100828def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600829 name = expr['enum']
830 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100831 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600832
833 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100834 raise QAPISemError(info,
835 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100836 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100837 raise QAPISemError(info,
838 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600839 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100840 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600841 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600842
Eric Blake437db252015-09-29 16:21:02 -0600843
Marc-André Lureau4148c292017-01-13 15:41:25 +0100844def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600845 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600846 members = expr['data']
847
Marc-André Lureau4148c292017-01-13 15:41:25 +0100848 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600849 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100850 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600851 allow_metas=['struct'])
852
Eric Blake437db252015-09-29 16:21:02 -0600853
Eric Blake0545f6b2015-05-04 09:05:15 -0600854def check_keys(expr_elem, meta, required, optional=[]):
855 expr = expr_elem['expr']
856 info = expr_elem['info']
857 name = expr[meta]
858 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100859 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600860 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600861 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600862 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100863 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
864 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600865 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100866 raise QAPISemError(info,
867 "'%s' of %s '%s' should only use false value"
868 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600869 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100870 raise QAPISemError(info,
871 "'%s' of %s '%s' should only use true value"
872 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600873 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600874 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100875 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
876 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600877
Eric Blake437db252015-09-29 16:21:02 -0600878
Markus Armbruster4d076d62015-06-10 08:55:21 +0200879def check_exprs(exprs):
880 global all_names
881
Markus Armbruster79470162017-03-15 13:57:21 +0100882 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200883 for builtin in builtin_types.keys():
884 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100885
886 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200887 for expr_elem in exprs:
888 expr = expr_elem['expr']
889 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100890 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100891
Markus Armbruster79470162017-03-15 13:57:21 +0100892 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100893 raise QAPISemError(info,
894 "Expression missing documentation comment")
895
Eric Blake437db252015-09-29 16:21:02 -0600896 if 'enum' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100897 meta = 'enum'
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100898 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster5f018442017-03-15 13:57:31 +0100899 enum_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600900 elif 'union' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100901 meta = 'union'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200902 check_keys(expr_elem, 'union', ['data'],
903 ['base', 'discriminator'])
Markus Armbruster768562d2017-03-15 13:57:33 +0100904 union_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600905 elif 'alternate' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100906 meta = 'alternate'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200907 check_keys(expr_elem, 'alternate', ['data'])
Eric Blake437db252015-09-29 16:21:02 -0600908 elif 'struct' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100909 meta = 'struct'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200910 check_keys(expr_elem, 'struct', ['data'], ['base'])
Markus Armbrustered285bf2017-03-15 13:57:32 +0100911 struct_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600912 elif 'command' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100913 meta = 'command'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200914 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600915 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Eric Blake437db252015-09-29 16:21:02 -0600916 elif 'event' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100917 meta = 'event'
Eric Blakec8184082016-07-13 21:50:20 -0600918 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200919 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100920 raise QAPISemError(expr_elem['info'],
921 "Expression is missing metatype")
Markus Armbruster6f053452017-03-15 13:57:30 +0100922 name = expr[meta]
923 add_name(name, info, meta)
Markus Armbruster79470162017-03-15 13:57:21 +0100924 if doc and doc.symbol != name:
925 raise QAPISemError(info, "Definition of '%s' follows documentation"
926 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200927
928 # Try again for hidden UnionKind enum
929 for expr_elem in exprs:
930 expr = expr_elem['expr']
Markus Armbrustereda43c62017-03-15 13:57:29 +0100931 if 'union' in expr and not discriminator_find_enum_define(expr):
932 name = '%sKind' % expr['union']
Eric Blake437db252015-09-29 16:21:02 -0600933 elif 'alternate' in expr:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100934 name = '%sKind' % expr['alternate']
935 else:
936 continue
Markus Armbruster5f018442017-03-15 13:57:31 +0100937 enum_types[name] = {'enum': name}
Markus Armbruster6f053452017-03-15 13:57:30 +0100938 add_name(name, info, 'enum', implicit=True)
Markus Armbruster4d076d62015-06-10 08:55:21 +0200939
940 # Validate that exprs make sense
941 for expr_elem in exprs:
942 expr = expr_elem['expr']
943 info = expr_elem['info']
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100944 doc = expr_elem.get('doc')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200945
Eric Blake437db252015-09-29 16:21:02 -0600946 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200947 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600948 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200949 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600950 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200951 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600952 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200953 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600954 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200955 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600956 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200957 check_event(expr, info)
958 else:
959 assert False, 'unexpected meta type'
960
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100961 if doc:
962 doc.check_expr(expr)
963
Markus Armbrusterac882192015-09-16 13:06:05 +0200964 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600965
Markus Armbrusterac882192015-09-16 13:06:05 +0200966
967#
968# Schema compiler frontend
969#
970
971class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100972 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +0200973 assert isinstance(name, str)
974 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -0600975 # For explicitly defined entities, info points to the (explicit)
976 # definition. For builtins (and their arrays), info is None.
977 # For implicitly defined entities, info points to a place that
978 # triggered the implicit definition (there may be more than one
979 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +0200980 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100981 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +0200982
Markus Armbrusterf51d8c32015-09-16 13:06:06 +0200983 def c_name(self):
984 return c_name(self.name)
985
Markus Armbrusterac882192015-09-16 13:06:05 +0200986 def check(self, schema):
987 pass
988
Eric Blake49823c42015-10-12 22:22:27 -0600989 def is_implicit(self):
990 return not self.info
991
Markus Armbruster3f7dc212015-09-16 13:06:07 +0200992 def visit(self, visitor):
993 pass
994
995
996class QAPISchemaVisitor(object):
997 def visit_begin(self, schema):
998 pass
999
1000 def visit_end(self):
1001 pass
1002
Eric Blake25a0d9c2015-10-12 22:22:21 -06001003 def visit_needed(self, entity):
1004 # Default to visiting everything
1005 return True
1006
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001007 def visit_builtin_type(self, name, info, json_type):
1008 pass
1009
1010 def visit_enum_type(self, name, info, values, prefix):
1011 pass
1012
1013 def visit_array_type(self, name, info, element_type):
1014 pass
1015
1016 def visit_object_type(self, name, info, base, members, variants):
1017 pass
1018
Markus Armbruster39a18152015-09-16 13:06:28 +02001019 def visit_object_type_flat(self, name, info, members, variants):
1020 pass
1021
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001022 def visit_alternate_type(self, name, info, variants):
1023 pass
1024
1025 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001026 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001027 pass
1028
Eric Blake48825ca2016-07-13 21:50:19 -06001029 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001030 pass
1031
Markus Armbrusterac882192015-09-16 13:06:05 +02001032
1033class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001034 # Return the C type for common use.
1035 # For the types we commonly box, this is a pointer type.
1036 def c_type(self):
1037 pass
1038
1039 # Return the C type to be used in a parameter list.
1040 def c_param_type(self):
1041 return self.c_type()
1042
1043 # Return the C type to be used where we suppress boxing.
1044 def c_unboxed_type(self):
1045 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001046
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001047 def json_type(self):
1048 pass
1049
1050 def alternate_qtype(self):
1051 json2qtype = {
Markus Armbruster4d2d5c42017-06-26 19:25:14 +02001052 'null': 'QTYPE_QNULL',
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001053 'string': 'QTYPE_QSTRING',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001054 'number': 'QTYPE_QNUM',
1055 'int': 'QTYPE_QNUM',
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001056 'boolean': 'QTYPE_QBOOL',
1057 'object': 'QTYPE_QDICT'
1058 }
1059 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001060
Markus Armbruster691e0312017-03-15 13:57:14 +01001061 def doc_type(self):
1062 if self.is_implicit():
1063 return None
1064 return self.name
1065
Markus Armbrusterac882192015-09-16 13:06:05 +02001066
1067class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001068 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001069 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001070 assert not c_type or isinstance(c_type, str)
1071 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1072 'value')
1073 self._json_type_name = json_type
1074 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001075
1076 def c_name(self):
1077 return self.name
1078
Eric Blake4040d992016-03-17 16:48:28 -06001079 def c_type(self):
1080 return self._c_type_name
1081
1082 def c_param_type(self):
1083 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001084 return 'const ' + self._c_type_name
1085 return self._c_type_name
1086
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001087 def json_type(self):
1088 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001089
Markus Armbruster691e0312017-03-15 13:57:14 +01001090 def doc_type(self):
1091 return self.json_type()
1092
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001093 def visit(self, visitor):
1094 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1095
Markus Armbrusterac882192015-09-16 13:06:05 +02001096
1097class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001098 def __init__(self, name, info, doc, values, prefix):
1099 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001100 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001101 assert isinstance(v, QAPISchemaMember)
1102 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001103 assert prefix is None or isinstance(prefix, str)
1104 self.values = values
1105 self.prefix = prefix
1106
1107 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001108 seen = {}
1109 for v in self.values:
1110 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001111 if self.doc:
1112 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001113
Eric Blake99df5282015-10-12 22:22:32 -06001114 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001115 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1116 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001117
Eric Blake4040d992016-03-17 16:48:28 -06001118 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001119 return c_name(self.name)
1120
Eric Blake93bda4d2015-12-01 22:20:55 -07001121 def member_names(self):
1122 return [v.name for v in self.values]
1123
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001124 def json_type(self):
1125 return 'string'
1126
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001127 def visit(self, visitor):
1128 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001129 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001130
Markus Armbrusterac882192015-09-16 13:06:05 +02001131
1132class QAPISchemaArrayType(QAPISchemaType):
1133 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001134 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001135 assert isinstance(element_type, str)
1136 self._element_type_name = element_type
1137 self.element_type = None
1138
1139 def check(self, schema):
1140 self.element_type = schema.lookup_type(self._element_type_name)
1141 assert self.element_type
1142
Eric Blake99df5282015-10-12 22:22:32 -06001143 def is_implicit(self):
1144 return True
1145
Eric Blake4040d992016-03-17 16:48:28 -06001146 def c_type(self):
1147 return c_name(self.name) + pointer_suffix
1148
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001149 def json_type(self):
1150 return 'array'
1151
Markus Armbruster691e0312017-03-15 13:57:14 +01001152 def doc_type(self):
1153 elt_doc_type = self.element_type.doc_type()
1154 if not elt_doc_type:
1155 return None
1156 return 'array of ' + elt_doc_type
1157
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001158 def visit(self, visitor):
1159 visitor.visit_array_type(self.name, self.info, self.element_type)
1160
Markus Armbrusterac882192015-09-16 13:06:05 +02001161
1162class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001163 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001164 # struct has local_members, optional base, and no variants
1165 # flat union has base, variants, and no local_members
1166 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001167 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001168 assert base is None or isinstance(base, str)
1169 for m in local_members:
1170 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001171 m.set_owner(name)
1172 if variants is not None:
1173 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1174 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001175 self._base_name = base
1176 self.base = None
1177 self.local_members = local_members
1178 self.variants = variants
1179 self.members = None
1180
1181 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001182 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001183 raise QAPISemError(self.info,
1184 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001185 if self.members:
1186 return
1187 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001188 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001189 if self._base_name:
1190 self.base = schema.lookup_type(self._base_name)
1191 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001192 self.base.check(schema)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001193 self.base.check_clash(self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001194 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001195 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001196 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001197 if self.doc:
1198 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001199 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001200 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001201 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001202 assert self.variants.tag_member in self.members
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001203 self.variants.check_clash(self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001204 if self.doc:
1205 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001206
Eric Blake14f00c62016-03-03 09:16:43 -07001207 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001208 # and update seen to track the members seen so far. Report any errors
1209 # on behalf of info, which is not necessarily self.info
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001210 def check_clash(self, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001211 assert not self.variants # not implemented
1212 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001213 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001214
Eric Blake99df5282015-10-12 22:22:32 -06001215 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001216 # See QAPISchema._make_implicit_object_type(), as well as
1217 # _def_predefineds()
1218 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001219
Eric Blakeb6167702016-07-13 21:50:16 -06001220 def is_empty(self):
1221 assert self.members is not None
1222 return not self.members and not self.variants
1223
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001224 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001225 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001226 return QAPISchemaType.c_name(self)
1227
Eric Blake4040d992016-03-17 16:48:28 -06001228 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001229 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001230 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001231
Eric Blake4040d992016-03-17 16:48:28 -06001232 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001233 return c_name(self.name)
1234
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001235 def json_type(self):
1236 return 'object'
1237
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001238 def visit(self, visitor):
1239 visitor.visit_object_type(self.name, self.info,
1240 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001241 visitor.visit_object_type_flat(self.name, self.info,
1242 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001243
Markus Armbrusterac882192015-09-16 13:06:05 +02001244
Eric Blaked44f9ac2015-12-01 22:20:54 -07001245class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001246 role = 'member'
1247
Eric Blaked44f9ac2015-12-01 22:20:54 -07001248 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001249 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001250 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001251 self.owner = None
1252
1253 def set_owner(self, name):
1254 assert not self.owner
1255 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001256
Eric Blake27b60ab2015-11-18 01:52:51 -07001257 def check_clash(self, info, seen):
1258 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001259 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001260 raise QAPISemError(info,
1261 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001262 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001263 raise QAPISemError(info, "%s collides with %s" %
1264 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001265 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001266
Eric Blake88d4ef82015-11-18 01:52:50 -07001267 def _pretty_owner(self):
1268 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001269 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001270 # See QAPISchema._make_implicit_object_type() - reverse the
1271 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001272 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001273 if owner.endswith('-arg'):
1274 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001275 elif owner.endswith('-base'):
1276 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001277 else:
1278 assert owner.endswith('-wrapper')
1279 # Unreachable and not implemented
1280 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001281 if owner.endswith('Kind'):
1282 # See QAPISchema._make_implicit_enum_type()
1283 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001284 return '(%s of %s)' % (self.role, owner)
1285
1286 def describe(self):
1287 return "'%s' %s" % (self.name, self._pretty_owner())
1288
Markus Armbrusterac882192015-09-16 13:06:05 +02001289
Eric Blaked44f9ac2015-12-01 22:20:54 -07001290class QAPISchemaObjectTypeMember(QAPISchemaMember):
1291 def __init__(self, name, typ, optional):
1292 QAPISchemaMember.__init__(self, name)
1293 assert isinstance(typ, str)
1294 assert isinstance(optional, bool)
1295 self._type_name = typ
1296 self.type = None
1297 self.optional = optional
1298
1299 def check(self, schema):
1300 assert self.owner
1301 self.type = schema.lookup_type(self._type_name)
1302 assert self.type
1303
1304
Markus Armbrusterac882192015-09-16 13:06:05 +02001305class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001306 def __init__(self, tag_name, tag_member, variants):
1307 # Flat unions pass tag_name but not tag_member.
1308 # Simple unions and alternates pass tag_member but not tag_name.
1309 # After check(), tag_member is always set, and tag_name remains
1310 # a reliable witness of being used by a flat union.
1311 assert bool(tag_member) != bool(tag_name)
1312 assert (isinstance(tag_name, str) or
1313 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001314 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001315 for v in variants:
1316 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001317 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001318 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001319 self.variants = variants
1320
Eric Blake88d4ef82015-11-18 01:52:50 -07001321 def set_owner(self, name):
1322 for v in self.variants:
1323 v.set_owner(name)
1324
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001325 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001326 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001327 self.tag_member = seen[c_name(self._tag_name)]
1328 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001329 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1330 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001331 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001332 # Union names must match enum values; alternate names are
1333 # checked separately. Use 'seen' to tell the two apart.
1334 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001335 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001336 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001337 v.type.check(schema)
1338
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001339 def check_clash(self, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001340 for v in self.variants:
1341 # Reset seen map for each variant, since qapi names from one
1342 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001343 assert isinstance(v.type, QAPISchemaObjectType)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001344 v.type.check_clash(info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001345
Eric Blake437db252015-09-29 16:21:02 -06001346
Markus Armbrusterac882192015-09-16 13:06:05 +02001347class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001348 role = 'branch'
1349
Markus Armbrusterac882192015-09-16 13:06:05 +02001350 def __init__(self, name, typ):
1351 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1352
Markus Armbrusterac882192015-09-16 13:06:05 +02001353
1354class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001355 def __init__(self, name, info, doc, variants):
1356 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001357 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001358 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001359 variants.set_owner(name)
1360 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001361 self.variants = variants
1362
1363 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001364 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001365 # Not calling self.variants.check_clash(), because there's nothing
1366 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001367 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001368 # Alternate branch names have no relation to the tag enum values;
1369 # so we have to check for potential name collisions ourselves.
1370 seen = {}
1371 for v in self.variants.variants:
1372 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001373 if self.doc:
1374 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001375 if self.doc:
1376 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001377
Eric Blake4040d992016-03-17 16:48:28 -06001378 def c_type(self):
1379 return c_name(self.name) + pointer_suffix
1380
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001381 def json_type(self):
1382 return 'value'
1383
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001384 def visit(self, visitor):
1385 visitor.visit_alternate_type(self.name, self.info, self.variants)
1386
Eric Blakec8184082016-07-13 21:50:20 -06001387 def is_empty(self):
1388 return False
1389
Markus Armbrusterac882192015-09-16 13:06:05 +02001390
1391class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001392 def __init__(self, name, info, doc, arg_type, ret_type,
1393 gen, success_response, boxed):
1394 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001395 assert not arg_type or isinstance(arg_type, str)
1396 assert not ret_type or isinstance(ret_type, str)
1397 self._arg_type_name = arg_type
1398 self.arg_type = None
1399 self._ret_type_name = ret_type
1400 self.ret_type = None
1401 self.gen = gen
1402 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001403 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001404
1405 def check(self, schema):
1406 if self._arg_type_name:
1407 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001408 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1409 isinstance(self.arg_type, QAPISchemaAlternateType))
1410 self.arg_type.check(schema)
1411 if self.boxed:
1412 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001413 raise QAPISemError(self.info,
1414 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001415 else:
1416 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1417 assert not self.arg_type.variants
1418 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001419 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001420 if self._ret_type_name:
1421 self.ret_type = schema.lookup_type(self._ret_type_name)
1422 assert isinstance(self.ret_type, QAPISchemaType)
1423
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001424 def visit(self, visitor):
1425 visitor.visit_command(self.name, self.info,
1426 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001427 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001428
Markus Armbrusterac882192015-09-16 13:06:05 +02001429
1430class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001431 def __init__(self, name, info, doc, arg_type, boxed):
1432 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001433 assert not arg_type or isinstance(arg_type, str)
1434 self._arg_type_name = arg_type
1435 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001436 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001437
1438 def check(self, schema):
1439 if self._arg_type_name:
1440 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001441 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1442 isinstance(self.arg_type, QAPISchemaAlternateType))
1443 self.arg_type.check(schema)
1444 if self.boxed:
1445 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001446 raise QAPISemError(self.info,
1447 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001448 else:
1449 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1450 assert not self.arg_type.variants
1451 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001452 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001453
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001454 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001455 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001456
Markus Armbrusterac882192015-09-16 13:06:05 +02001457
1458class QAPISchema(object):
1459 def __init__(self, fname):
1460 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001461 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001462 self.exprs = check_exprs(parser.exprs)
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001463 self.docs = parser.docs
Eric Blake7618b912015-10-12 22:22:22 -06001464 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001465 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001466 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001467 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001468 self._def_exprs()
1469 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001470 except QAPIError as err:
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001471 print(err, file=sys.stderr)
Markus Armbrusterac882192015-09-16 13:06:05 +02001472 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001473
Markus Armbrusterac882192015-09-16 13:06:05 +02001474 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001475 # Only the predefined types are allowed to not have info
1476 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001477 assert ent.name not in self._entity_dict
1478 self._entity_dict[ent.name] = ent
1479
1480 def lookup_entity(self, name, typ=None):
1481 ent = self._entity_dict.get(name)
1482 if typ and not isinstance(ent, typ):
1483 return None
1484 return ent
1485
1486 def lookup_type(self, name):
1487 return self.lookup_entity(name, QAPISchemaType)
1488
Eric Blake861877a2016-03-17 16:48:36 -06001489 def _def_builtin_type(self, name, json_type, c_type):
1490 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001491 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1492 # qapi-types.h from a single .c, all arrays of builtins must be
1493 # declared in the first file whether or not they are used. Nicer
1494 # would be to use lazy instantiation, while figuring out how to
1495 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001496 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001497
1498 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001499 for t in [('str', 'string', 'char' + pointer_suffix),
1500 ('number', 'number', 'double'),
1501 ('int', 'int', 'int64_t'),
1502 ('int8', 'int', 'int8_t'),
1503 ('int16', 'int', 'int16_t'),
1504 ('int32', 'int', 'int32_t'),
1505 ('int64', 'int', 'int64_t'),
1506 ('uint8', 'int', 'uint8_t'),
1507 ('uint16', 'int', 'uint16_t'),
1508 ('uint32', 'int', 'uint32_t'),
1509 ('uint64', 'int', 'uint64_t'),
1510 ('size', 'int', 'uint64_t'),
1511 ('bool', 'boolean', 'bool'),
Markus Armbruster4d2d5c42017-06-26 19:25:14 +02001512 ('any', 'value', 'QObject' + pointer_suffix),
1513 ('null', 'null', 'QNull' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001514 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001515 self.the_empty_object_type = QAPISchemaObjectType(
1516 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001517 self._def_entity(self.the_empty_object_type)
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001518 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
Eric Blake93bda4d2015-12-01 22:20:55 -07001519 'qstring', 'qdict', 'qlist',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001520 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001521 self._def_entity(QAPISchemaEnumType('QType', None, None,
1522 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001523
Eric Blake93bda4d2015-12-01 22:20:55 -07001524 def _make_enum_members(self, values):
1525 return [QAPISchemaMember(v) for v in values]
1526
Eric Blake99df5282015-10-12 22:22:32 -06001527 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001528 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001529 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001530 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001531 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001532 return name
1533
Eric Blake99df5282015-10-12 22:22:32 -06001534 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001535 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001536 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001537 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001538 return name
1539
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001540 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001541 if not members:
1542 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001543 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001544 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001545 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001546 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001547 members, None))
1548 return name
1549
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001550 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001551 name = expr['enum']
1552 data = expr['data']
1553 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001554 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001555 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001556
Eric Blake99df5282015-10-12 22:22:32 -06001557 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001558 optional = False
1559 if name.startswith('*'):
1560 name = name[1:]
1561 optional = True
1562 if isinstance(typ, list):
1563 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001564 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001565 return QAPISchemaObjectTypeMember(name, typ, optional)
1566
Eric Blake99df5282015-10-12 22:22:32 -06001567 def _make_members(self, data, info):
1568 return [self._make_member(key, value, info)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001569 for (key, value) in data.items()]
Markus Armbrusterac882192015-09-16 13:06:05 +02001570
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001571 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001572 name = expr['struct']
1573 base = expr.get('base')
1574 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001575 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001576 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001577 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001578
1579 def _make_variant(self, case, typ):
1580 return QAPISchemaObjectTypeVariant(case, typ)
1581
Eric Blake99df5282015-10-12 22:22:32 -06001582 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001583 if isinstance(typ, list):
1584 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001585 typ = self._make_array_type(typ[0], info)
1586 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001587 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001588 return QAPISchemaObjectTypeVariant(case, typ)
1589
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001590 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001591 name = expr['union']
1592 data = expr['data']
1593 base = expr.get('base')
1594 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001595 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001596 if isinstance(base, dict):
1597 base = (self._make_implicit_object_type(
Markus Armbrusterc2613942017-03-15 13:57:35 +01001598 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001599 if tag_name:
1600 variants = [self._make_variant(key, value)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001601 for (key, value) in data.items()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001602 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001603 else:
Eric Blake99df5282015-10-12 22:22:32 -06001604 variants = [self._make_simple_variant(key, value, info)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001605 for (key, value) in data.items()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001606 typ = self._make_implicit_enum_type(name, info,
1607 [v.name for v in variants])
1608 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001609 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001610 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001611 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001612 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001613 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001614 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001615
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001616 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001617 name = expr['alternate']
1618 data = expr['data']
1619 variants = [self._make_variant(key, value)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001620 for (key, value) in data.items()]
Eric Blake0426d532015-12-01 22:20:48 -07001621 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001622 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001623 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001624 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001625 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001626 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001627
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001628 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001629 name = expr['command']
1630 data = expr.get('data')
1631 rets = expr.get('returns')
1632 gen = expr.get('gen', True)
1633 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001634 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001635 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001636 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001637 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001638 if isinstance(rets, list):
1639 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001640 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001641 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1642 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001643
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001644 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001645 name = expr['event']
1646 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001647 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001648 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001649 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001650 name, info, doc, 'arg', self._make_members(data, info))
1651 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001652
1653 def _def_exprs(self):
1654 for expr_elem in self.exprs:
1655 expr = expr_elem['expr']
1656 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001657 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001658 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001659 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001660 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001661 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001662 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001663 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001664 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001665 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001666 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001667 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001668 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001669 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001670 else:
1671 assert False
1672
1673 def check(self):
1674 for ent in self._entity_dict.values():
1675 ent.check(self)
1676
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001677 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001678 visitor.visit_begin(self)
1679 for (name, entity) in sorted(self._entity_dict.items()):
1680 if visitor.visit_needed(entity):
1681 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001682 visitor.visit_end()
1683
Markus Armbruster2caba362013-07-27 17:41:56 +02001684
Markus Armbruster00e4b282015-06-10 10:04:36 +02001685#
1686# Code generation helpers
1687#
1688
Michael Roth0f923be2011-07-19 14:50:39 -05001689def camel_case(name):
1690 new_name = ''
1691 first = True
1692 for ch in name:
1693 if ch in ['_', '-']:
1694 first = True
1695 elif first:
1696 new_name += ch.upper()
1697 first = False
1698 else:
1699 new_name += ch.lower()
1700 return new_name
1701
Eric Blake437db252015-09-29 16:21:02 -06001702
Markus Armbruster849bc532015-05-14 06:50:53 -06001703# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1704# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1705# ENUM24_Name -> ENUM24_NAME
1706def camel_to_upper(value):
1707 c_fun_str = c_name(value, False)
1708 if value.isupper():
1709 return c_fun_str
1710
1711 new_name = ''
1712 l = len(c_fun_str)
1713 for i in range(l):
1714 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001715 # When c is upper and no '_' appears before, do more checks
1716 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001717 if i < l - 1 and c_fun_str[i + 1].islower():
1718 new_name += '_'
1719 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001720 new_name += '_'
1721 new_name += c
1722 return new_name.lstrip('_').upper()
1723
Eric Blake437db252015-09-29 16:21:02 -06001724
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001725def c_enum_const(type_name, const_name, prefix=None):
1726 if prefix is not None:
1727 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001728 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001729
Eric Blake18df5152015-05-14 06:50:48 -06001730c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001731
Eric Blake437db252015-09-29 16:21:02 -06001732
Eric Blakec6405b52015-05-14 06:50:55 -06001733# Map @name to a valid C identifier.
1734# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001735# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001736#
1737# Used for converting 'name' from a 'name':'type' qapi definition
1738# into a generated struct member, as well as converting type names
1739# into substrings of a generated C function name.
1740# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1741# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001742def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001743 # ANSI X3J11/88-090, 3.1.1
1744 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001745 'default', 'do', 'double', 'else', 'enum', 'extern',
1746 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1747 'return', 'short', 'signed', 'sizeof', 'static',
1748 'struct', 'switch', 'typedef', 'union', 'unsigned',
1749 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001750 # ISO/IEC 9899:1999, 6.4.1
1751 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1752 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001753 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1754 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001755 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1756 # excluding _.*
1757 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001758 # C++ ISO/IEC 14882:2003 2.11
1759 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1760 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1761 'namespace', 'new', 'operator', 'private', 'protected',
1762 'public', 'reinterpret_cast', 'static_cast', 'template',
1763 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1764 'using', 'virtual', 'wchar_t',
1765 # alternative representations
1766 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1767 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001768 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001769 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001770 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001771 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1772 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001773 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001774 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001775
Amos Kong05dfb262014-06-10 19:25:53 +08001776eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001777pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001778
Eric Blake437db252015-09-29 16:21:02 -06001779
Michael Roth0f923be2011-07-19 14:50:39 -05001780def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001781 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001782 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001783 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001784 return ret
1785
1786indent_level = 0
1787
Eric Blake437db252015-09-29 16:21:02 -06001788
Michael Roth0f923be2011-07-19 14:50:39 -05001789def push_indent(indent_amount=4):
1790 global indent_level
1791 indent_level += indent_amount
1792
Eric Blake437db252015-09-29 16:21:02 -06001793
Michael Roth0f923be2011-07-19 14:50:39 -05001794def pop_indent(indent_amount=4):
1795 global indent_level
1796 indent_level -= indent_amount
1797
Eric Blake437db252015-09-29 16:21:02 -06001798
Markus Armbruster77e703b2015-06-24 19:27:32 +02001799# Generate @code with @kwds interpolated.
1800# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001801def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001802 raw = code % kwds
1803 if indent_level:
1804 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001805 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001806 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001807 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001808 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001809 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001810
Eric Blake437db252015-09-29 16:21:02 -06001811
Michael Roth0f923be2011-07-19 14:50:39 -05001812def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001813 if code[0] == '\n':
1814 code = code[1:]
1815 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001816
Michael Roth0f923be2011-07-19 14:50:39 -05001817
1818def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001819 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001820
Eric Blake437db252015-09-29 16:21:02 -06001821
Michael Rothc0afa9c2013-05-10 17:46:00 -05001822def guardstart(name):
1823 return mcgen('''
1824
1825#ifndef %(name)s
1826#define %(name)s
1827
1828''',
1829 name=guardname(name))
1830
Eric Blake437db252015-09-29 16:21:02 -06001831
Michael Rothc0afa9c2013-05-10 17:46:00 -05001832def guardend(name):
1833 return mcgen('''
1834
1835#endif /* %(name)s */
1836
1837''',
1838 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001839
Eric Blake437db252015-09-29 16:21:02 -06001840
Markus Armbrustere98859a2015-09-16 13:06:16 +02001841def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001842 ret = mcgen('''
1843
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001844const QEnumLookup %(c_name)s_lookup = {
1845 .array = (const char *const[]) {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001846''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001847 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001848 for value in values:
1849 index = c_enum_const(name, value, prefix)
1850 ret += mcgen('''
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001851 [%(index)s] = "%(value)s",
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001852''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001853 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001854
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001855 ret += mcgen('''
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001856 },
1857 .size = %(max_index)s
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001858};
1859''',
Marc-André Lureauebf677c2017-08-24 10:46:11 +02001860 max_index=c_enum_const(name, '_MAX', prefix))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001861 return ret
1862
Eric Blake437db252015-09-29 16:21:02 -06001863
Markus Armbrustere98859a2015-09-16 13:06:16 +02001864def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001865 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001866 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001867
1868 ret = mcgen('''
1869
1870typedef enum %(c_name)s {
1871''',
1872 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001873
1874 i = 0
1875 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001876 ret += mcgen('''
1877 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001878''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001879 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001880 i=i)
1881 i += 1
1882
Markus Armbrustere98859a2015-09-16 13:06:16 +02001883 ret += mcgen('''
1884} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001885''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001886 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001887
Markus Armbrustere98859a2015-09-16 13:06:16 +02001888 ret += mcgen('''
1889
Markus Armbruster5b5f8252017-08-24 10:46:07 +02001890#define %(c_name)s_str(val) \\
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001891 qapi_enum_lookup(&%(c_name)s_lookup, (val))
Markus Armbruster5b5f8252017-08-24 10:46:07 +02001892
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001893extern const QEnumLookup %(c_name)s_lookup;
Markus Armbrustere98859a2015-09-16 13:06:16 +02001894''',
1895 c_name=c_name(name))
1896 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001897
Eric Blake437db252015-09-29 16:21:02 -06001898
Marc-André Lureau086ee7a2017-06-01 16:41:41 +04001899def build_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001900 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001901 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001902 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001903 ret = ''
1904 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001905 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001906 ret += '%s arg' % arg_type.c_param_type()
1907 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001908 else:
1909 assert not arg_type.variants
1910 for memb in arg_type.members:
1911 ret += sep
1912 sep = ', '
1913 if memb.optional:
1914 ret += 'bool has_%s, ' % c_name(memb.name)
1915 ret += '%s %s' % (memb.type.c_param_type(),
1916 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001917 if extra:
1918 ret += sep + extra
1919 return ret
1920
Eric Blake1f353342015-09-29 16:21:13 -06001921
Markus Armbruster00e4b282015-06-10 10:04:36 +02001922#
1923# Common command line parsing
1924#
1925
Eric Blake437db252015-09-29 16:21:02 -06001926
Markus Armbrusteref801a92017-03-15 13:57:08 +01001927def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001928
1929 try:
1930 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001931 'chp:o:' + extra_options,
1932 ['source', 'header', 'prefix=',
1933 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001934 except getopt.GetoptError as err:
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001935 print("%s: %s" % (sys.argv[0], str(err)), file=sys.stderr)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001936 sys.exit(1)
1937
Markus Armbrusteref801a92017-03-15 13:57:08 +01001938 output_dir = ''
1939 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001940 do_c = False
1941 do_h = False
1942 extra_opts = []
1943
1944 for oa in opts:
1945 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001946 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001947 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001948 if match.end() != len(a):
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001949 print("%s: 'funny character '%s' in argument of --prefix" \
1950 % (sys.argv[0], a[match.end()]), file=sys.stderr)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001951 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001952 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001953 elif o in ('-o', '--output-dir'):
1954 output_dir = a + '/'
1955 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001956 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001957 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001958 do_h = True
1959 else:
1960 extra_opts.append(oa)
1961
1962 if not do_c and not do_h:
1963 do_c = True
1964 do_h = True
1965
Markus Armbruster16d80f62015-04-02 13:32:16 +02001966 if len(args) != 1:
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001967 print("%s: need exactly one argument" % sys.argv[0], file=sys.stderr)
Markus Armbrusterb4540962015-04-02 13:17:34 +02001968 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02001969 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001970
Markus Armbruster54414042015-06-09 16:22:45 +02001971 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001972
Markus Armbruster00e4b282015-06-10 10:04:36 +02001973#
1974# Generate output files with boilerplate
1975#
1976
Eric Blake437db252015-09-29 16:21:02 -06001977
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001978def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1979 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001980 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001981 c_file = output_dir + prefix + c_file
1982 h_file = output_dir + prefix + h_file
1983
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001984 if output_dir:
1985 try:
1986 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01001987 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001988 if e.errno != errno.EEXIST:
1989 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001990
1991 def maybe_open(really, name, opt):
1992 if really:
1993 return open(name, opt)
1994 else:
1995 import StringIO
1996 return StringIO.StringIO()
1997
1998 fdef = maybe_open(do_c, c_file, 'w')
1999 fdecl = maybe_open(do_h, h_file, 'w')
2000
2001 fdef.write(mcgen('''
2002/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2003%(comment)s
2004''',
Eric Blake437db252015-09-29 16:21:02 -06002005 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002006
2007 fdecl.write(mcgen('''
2008/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2009%(comment)s
2010#ifndef %(guard)s
2011#define %(guard)s
2012
2013''',
Eric Blake437db252015-09-29 16:21:02 -06002014 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002015
2016 return (fdef, fdecl)
2017
Eric Blake437db252015-09-29 16:21:02 -06002018
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002019def close_output(fdef, fdecl):
2020 fdecl.write('''
2021#endif
2022''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002023 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002024 fdef.close()