blob: 514b7bb5a48d44d664a95e8fbe74d038ae62a24e [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
Daniel P. Berrange38710a82018-01-16 13:42:06 +000021try:
22 from collections import OrderedDict
23except:
24 from ordereddict import OrderedDict
Michael Roth0f923be2011-07-19 14:50:39 -050025
Eric Blakeb52c4b92015-05-04 09:05:00 -060026builtin_types = {
Markus Armbruster4d2d5c42017-06-26 19:25:14 +020027 'null': 'QTYPE_QNULL',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020028 'str': 'QTYPE_QSTRING',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +040029 'int': 'QTYPE_QNUM',
30 'number': 'QTYPE_QNUM',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020031 'bool': 'QTYPE_QBOOL',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +040032 'int8': 'QTYPE_QNUM',
33 'int16': 'QTYPE_QNUM',
34 'int32': 'QTYPE_QNUM',
35 'int64': 'QTYPE_QNUM',
36 'uint8': 'QTYPE_QNUM',
37 'uint16': 'QTYPE_QNUM',
38 'uint32': 'QTYPE_QNUM',
39 'uint64': 'QTYPE_QNUM',
40 'size': 'QTYPE_QNUM',
Eric Blake1310a3d2015-12-01 22:20:46 -070041 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070042 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020043}
44
Markus Armbrusterbc52d032017-03-15 13:56:51 +010045# Are documentation comments required?
46doc_required = False
47
Eric Blake10d4d992015-05-04 09:05:23 -060048# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010049returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060050
Eric Blake893e1f22015-12-01 22:20:57 -070051# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010052name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070053
Markus Armbruster5f018442017-03-15 13:57:31 +010054enum_types = {}
Markus Armbrustered285bf2017-03-15 13:57:32 +010055struct_types = {}
Markus Armbruster768562d2017-03-15 13:57:33 +010056union_types = {}
Eric Blake4dc2e692015-05-04 09:05:17 -060057all_names = {}
58
Markus Armbruster00e4b282015-06-10 10:04:36 +020059#
60# Parsing the schema into expressions
61#
62
Eric Blake437db252015-09-29 16:21:02 -060063
Lluís Vilanovaa719a272014-05-07 20:46:15 +020064def error_path(parent):
Markus Armbrusteref801a92017-03-15 13:57:08 +010065 res = ''
Lluís Vilanovaa719a272014-05-07 20:46:15 +020066 while parent:
Markus Armbrusteref801a92017-03-15 13:57:08 +010067 res = ('In file included from %s:%d:\n' % (parent['file'],
Lluís Vilanovaa719a272014-05-07 20:46:15 +020068 parent['line'])) + res
69 parent = parent['parent']
70 return res
71
Eric Blake437db252015-09-29 16:21:02 -060072
Marc-André Lureau4148c292017-01-13 15:41:25 +010073class QAPIError(Exception):
74 def __init__(self, fname, line, col, incl_info, msg):
Eric Blake59b00542015-09-29 16:21:01 -060075 Exception.__init__(self)
Marc-André Lureau4148c292017-01-13 15:41:25 +010076 self.fname = fname
77 self.line = line
78 self.col = col
79 self.info = incl_info
Markus Armbruster2caba362013-07-27 17:41:56 +020080 self.msg = msg
Marc-André Lureau4148c292017-01-13 15:41:25 +010081
82 def __str__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +010083 loc = '%s:%d' % (self.fname, self.line)
Marc-André Lureau4148c292017-01-13 15:41:25 +010084 if self.col is not None:
Markus Armbrusteref801a92017-03-15 13:57:08 +010085 loc += ':%s' % self.col
86 return error_path(self.info) + '%s: %s' % (loc, self.msg)
Marc-André Lureau4148c292017-01-13 15:41:25 +010087
88
89class QAPIParseError(QAPIError):
90 def __init__(self, parser, msg):
91 col = 1
92 for ch in parser.src[parser.line_pos:parser.pos]:
Wenchao Xia515b9432014-03-04 18:44:33 -080093 if ch == '\t':
Marc-André Lureau4148c292017-01-13 15:41:25 +010094 col = (col + 7) % 8 + 1
Markus Armbruster2caba362013-07-27 17:41:56 +020095 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +010096 col += 1
97 QAPIError.__init__(self, parser.fname, parser.line, col,
98 parser.incl_info, msg)
Markus Armbruster2caba362013-07-27 17:41:56 +020099
Eric Blake437db252015-09-29 16:21:02 -0600100
Marc-André Lureau4148c292017-01-13 15:41:25 +0100101class QAPISemError(QAPIError):
102 def __init__(self, info, msg):
103 QAPIError.__init__(self, info['file'], info['line'], None,
104 info['parent'], msg)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800105
Eric Blake437db252015-09-29 16:21:02 -0600106
Marc-André Lureau3313b612017-01-13 15:41:29 +0100107class QAPIDoc(object):
108 class Section(object):
109 def __init__(self, name=None):
110 # optional section name (argument/member or section name)
111 self.name = name
112 # the list of lines for this section
Markus Armbruster09331fc2017-10-02 16:13:38 +0200113 self.text = ''
Marc-André Lureau3313b612017-01-13 15:41:29 +0100114
115 def append(self, line):
Markus Armbruster09331fc2017-10-02 16:13:38 +0200116 self.text += line.rstrip() + '\n'
Marc-André Lureau3313b612017-01-13 15:41:29 +0100117
118 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100119 def __init__(self, name):
120 QAPIDoc.Section.__init__(self, name)
121 self.member = None
122
123 def connect(self, member):
124 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100125
126 def __init__(self, parser, info):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200127 # self._parser is used to report errors with QAPIParseError. The
Marc-André Lureau3313b612017-01-13 15:41:29 +0100128 # resulting error position depends on the state of the parser.
129 # It happens to be the beginning of the comment. More or less
130 # servicable, but action at a distance.
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200131 self._parser = parser
Marc-André Lureau3313b612017-01-13 15:41:29 +0100132 self.info = info
133 self.symbol = None
134 self.body = QAPIDoc.Section()
135 # dict mapping parameter name to ArgSection
136 self.args = OrderedDict()
137 # a list of Section
138 self.sections = []
139 # the current section
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200140 self._section = self.body
Marc-André Lureau3313b612017-01-13 15:41:29 +0100141
142 def has_section(self, name):
143 """Return True if we have a section with this name."""
144 for i in self.sections:
145 if i.name == name:
146 return True
147 return False
148
149 def append(self, line):
150 """Parse a comment line and add it to the documentation."""
151 line = line[1:]
152 if not line:
153 self._append_freeform(line)
154 return
155
156 if line[0] != ' ':
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200157 raise QAPIParseError(self._parser, "Missing space after #")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100158 line = line[1:]
159
160 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
161 # recognized, and get silently treated as ordinary text
162 if self.symbol:
163 self._append_symbol_line(line)
Markus Armbruster09331fc2017-10-02 16:13:38 +0200164 elif not self.body.text and line.startswith('@'):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100165 if not line.endswith(':'):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200166 raise QAPIParseError(self._parser, "Line should end with :")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100167 self.symbol = line[1:-1]
168 # FIXME invalid names other than the empty string aren't flagged
169 if not self.symbol:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200170 raise QAPIParseError(self._parser, "Invalid name")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100171 else:
172 self._append_freeform(line)
173
Markus Armbruster4ea71482017-03-15 13:57:23 +0100174 def end_comment(self):
175 self._end_section()
176
Marc-André Lureau3313b612017-01-13 15:41:29 +0100177 def _append_symbol_line(self, line):
178 name = line.split(' ', 1)[0]
179
Markus Armbrusteref801a92017-03-15 13:57:08 +0100180 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100181 line = line[len(name)+1:]
182 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100183 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100184 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100185 'Note:', 'Notes:',
186 'Example:', 'Examples:',
187 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100188 line = line[len(name)+1:]
189 self._start_section(name[:-1])
190
191 self._append_freeform(line)
192
193 def _start_args_section(self, name):
194 # FIXME invalid names other than the empty string aren't flagged
195 if not name:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200196 raise QAPIParseError(self._parser, "Invalid parameter name")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100197 if name in self.args:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200198 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100199 "'%s' parameter name duplicated" % name)
200 if self.sections:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200201 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100202 "'@%s:' can't follow '%s' section"
203 % (name, self.sections[0].name))
Markus Armbruster4ea71482017-03-15 13:57:23 +0100204 self._end_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200205 self._section = QAPIDoc.ArgSection(name)
206 self.args[name] = self._section
Marc-André Lureau3313b612017-01-13 15:41:29 +0100207
Markus Armbrusterfc3f0df2017-10-02 16:13:37 +0200208 def _start_section(self, name=None):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100209 if name in ('Returns', 'Since') and self.has_section(name):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200210 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100211 "Duplicated '%s' section" % name)
Markus Armbruster4ea71482017-03-15 13:57:23 +0100212 self._end_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200213 self._section = QAPIDoc.Section(name)
214 self.sections.append(self._section)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100215
Markus Armbruster4ea71482017-03-15 13:57:23 +0100216 def _end_section(self):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200217 if self._section:
218 text = self._section.text = self._section.text.strip()
219 if self._section.name and (not text or text.isspace()):
220 raise QAPIParseError(self._parser, "Empty doc section '%s'"
221 % self._section.name)
222 self._section = None
Markus Armbruster4ea71482017-03-15 13:57:23 +0100223
Marc-André Lureau3313b612017-01-13 15:41:29 +0100224 def _append_freeform(self, line):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200225 in_arg = isinstance(self._section, QAPIDoc.ArgSection)
226 if (in_arg and self._section.text.endswith('\n\n')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100227 and line and not line[0].isspace()):
228 self._start_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200229 if (in_arg or not self._section.name
230 or not self._section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100231 line = line.strip()
Markus Armbruster2d433232017-03-15 13:57:22 +0100232 match = re.match(r'(@\S+:)', line)
233 if match:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200234 raise QAPIParseError(self._parser,
Markus Armbruster2d433232017-03-15 13:57:22 +0100235 "'%s' not allowed in free-form documentation"
236 % match.group(1))
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200237 self._section.append(line)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100238
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100239 def connect_member(self, member):
240 if member.name not in self.args:
241 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100242 self.args[member.name] = QAPIDoc.ArgSection(member.name)
243 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100244
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100245 def check_expr(self, expr):
246 if self.has_section('Returns') and 'command' not in expr:
247 raise QAPISemError(self.info,
248 "'Returns:' is only valid for commands")
249
Markus Armbruster816a57c2017-03-15 13:57:26 +0100250 def check(self):
Daniel P. Berrange2f848042018-01-16 13:42:05 +0000251 bogus = [name for name, section in self.args.items()
Markus Armbruster816a57c2017-03-15 13:57:26 +0100252 if not section.member]
253 if bogus:
254 raise QAPISemError(
255 self.info,
256 "The following documented members are not in "
257 "the declaration: %s" % ", ".join(bogus))
258
Marc-André Lureau3313b612017-01-13 15:41:29 +0100259
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200260class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500261
Eric Blake437db252015-09-29 16:21:02 -0600262 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200263 abs_fname = os.path.abspath(fp.name)
Markus Armbruster2281d002017-10-02 16:13:33 +0200264 self.fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200265 previously_included.append(abs_fname)
266 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200267 self.src = fp.read()
268 if self.src == '' or self.src[-1] != '\n':
269 self.src += '\n'
270 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800271 self.line = 1
272 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200273 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100274 self.docs = []
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200275 self.accept()
Markus Armbruster64d60332017-10-02 16:13:34 +0200276 cur_doc = None
Michael Roth0f923be2011-07-19 14:50:39 -0500277
Eric Blake437db252015-09-29 16:21:02 -0600278 while self.tok is not None:
Markus Armbruster2281d002017-10-02 16:13:33 +0200279 info = {'file': self.fname, 'line': self.line,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100280 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100281 if self.tok == '#':
Markus Armbruster64d60332017-10-02 16:13:34 +0200282 self.reject_expr_doc(cur_doc)
283 cur_doc = self.get_doc(info)
284 self.docs.append(cur_doc)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100285 continue
286
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200287 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100288 if 'include' in expr:
Markus Armbruster64d60332017-10-02 16:13:34 +0200289 self.reject_expr_doc(cur_doc)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200290 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100291 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100292 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200293 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100294 raise QAPISemError(info,
295 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100296 self._include(include, info, os.path.dirname(abs_fname),
297 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100298 elif "pragma" in expr:
Markus Armbruster64d60332017-10-02 16:13:34 +0200299 self.reject_expr_doc(cur_doc)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100300 if len(expr) != 1:
301 raise QAPISemError(info, "Invalid 'pragma' directive")
302 pragma = expr['pragma']
303 if not isinstance(pragma, dict):
304 raise QAPISemError(
305 info, "Value of 'pragma' must be a dictionary")
Daniel P. Berrange2f848042018-01-16 13:42:05 +0000306 for name, value in pragma.items():
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100307 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200308 else:
309 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100310 'info': info}
Markus Armbruster64d60332017-10-02 16:13:34 +0200311 if cur_doc:
312 if not cur_doc.symbol:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100313 raise QAPISemError(
Markus Armbruster64d60332017-10-02 16:13:34 +0200314 cur_doc.info, "Expression documentation required")
315 expr_elem['doc'] = cur_doc
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200316 self.exprs.append(expr_elem)
Markus Armbruster64d60332017-10-02 16:13:34 +0200317 cur_doc = None
318 self.reject_expr_doc(cur_doc)
Markus Armbrustere7823a22017-03-15 13:57:20 +0100319
Markus Armbruster64d60332017-10-02 16:13:34 +0200320 @staticmethod
321 def reject_expr_doc(doc):
322 if doc and doc.symbol:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100323 raise QAPISemError(
Markus Armbruster64d60332017-10-02 16:13:34 +0200324 doc.info,
Markus Armbrustere7823a22017-03-15 13:57:20 +0100325 "Documentation for '%s' is not followed by the definition"
Markus Armbruster64d60332017-10-02 16:13:34 +0200326 % doc.symbol)
Michael Roth0f923be2011-07-19 14:50:39 -0500327
Markus Armbrustere04dea82017-03-15 13:56:50 +0100328 def _include(self, include, info, base_dir, previously_included):
329 incl_abs_fname = os.path.join(base_dir, include)
330 # catch inclusion cycle
331 inf = info
332 while inf:
333 if incl_abs_fname == os.path.abspath(inf['file']):
334 raise QAPISemError(info, "Inclusion loop for %s" % include)
335 inf = inf['parent']
336
337 # skip multiple include of the same file
338 if incl_abs_fname in previously_included:
339 return
340 try:
341 fobj = open(incl_abs_fname, 'r')
342 except IOError as e:
343 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
344 exprs_include = QAPISchemaParser(fobj, previously_included, info)
345 self.exprs.extend(exprs_include.exprs)
346 self.docs.extend(exprs_include.docs)
347
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100348 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100349 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100350 if name == 'doc-required':
351 if not isinstance(value, bool):
352 raise QAPISemError(info,
353 "Pragma 'doc-required' must be boolean")
354 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100355 elif name == 'returns-whitelist':
356 if (not isinstance(value, list)
357 or any([not isinstance(elt, str) for elt in value])):
358 raise QAPISemError(info,
359 "Pragma returns-whitelist must be"
360 " a list of strings")
361 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100362 elif name == 'name-case-whitelist':
363 if (not isinstance(value, list)
364 or any([not isinstance(elt, str) for elt in value])):
365 raise QAPISemError(info,
366 "Pragma name-case-whitelist must be"
367 " a list of strings")
368 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100369 else:
370 raise QAPISemError(info, "Unknown pragma '%s'" % name)
371
Marc-André Lureau3313b612017-01-13 15:41:29 +0100372 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200373 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200374 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200375 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200376 self.cursor += 1
377 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500378
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200379 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100380 if self.src[self.cursor] == '#':
381 # Start of doc comment
382 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200383 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100384 if not skip_comment:
385 self.val = self.src[self.pos:self.cursor]
386 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100387 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200388 return
389 elif self.tok == "'":
390 string = ''
391 esc = False
392 while True:
393 ch = self.src[self.cursor]
394 self.cursor += 1
395 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100396 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200397 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600398 if ch == 'b':
399 string += '\b'
400 elif ch == 'f':
401 string += '\f'
402 elif ch == 'n':
403 string += '\n'
404 elif ch == 'r':
405 string += '\r'
406 elif ch == 't':
407 string += '\t'
408 elif ch == 'u':
409 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600410 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600411 ch = self.src[self.cursor]
412 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100413 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100414 raise QAPIParseError(self,
415 '\\u escape needs 4 '
416 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600417 value = (value << 4) + int(ch, 16)
418 # If Python 2 and 3 didn't disagree so much on
419 # how to handle Unicode, then we could allow
420 # Unicode string defaults. But most of QAPI is
421 # ASCII-only, so we aren't losing much for now.
422 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100423 raise QAPIParseError(self,
424 'For now, \\u escape '
425 'only supports non-zero '
426 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600427 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100428 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600429 string += ch
430 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100431 raise QAPIParseError(self,
432 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200433 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100434 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200435 esc = True
436 elif ch == "'":
437 self.val = string
438 return
439 else:
440 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100441 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200442 self.val = True
443 self.cursor += 3
444 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100445 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200446 self.val = False
447 self.cursor += 4
448 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100449 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200450 self.val = None
451 self.cursor += 3
452 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200453 elif self.tok == '\n':
454 if self.cursor == len(self.src):
455 self.tok = None
456 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800457 self.line += 1
458 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200459 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100460 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500461
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200462 def get_members(self):
463 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200464 if self.tok == '}':
465 self.accept()
466 return expr
467 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100468 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200469 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200470 key = self.val
471 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200472 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100473 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200474 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800475 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100476 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200477 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200478 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200479 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200480 return expr
481 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100482 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200483 self.accept()
484 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100485 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500486
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200487 def get_values(self):
488 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200489 if self.tok == ']':
490 self.accept()
491 return expr
Eric Blake437db252015-09-29 16:21:02 -0600492 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100493 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
494 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200495 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200496 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200497 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200498 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200499 return expr
500 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100501 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200502 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500503
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200504 def get_expr(self, nested):
505 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100506 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200507 if self.tok == '{':
508 self.accept()
509 expr = self.get_members()
510 elif self.tok == '[':
511 self.accept()
512 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600513 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200514 expr = self.val
515 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200516 else:
Markus Armbruster012b1262017-03-15 13:57:36 +0100517 raise QAPIParseError(self, 'Expected "{", "[", string, '
518 'boolean or "null"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200519 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200520
Marc-André Lureau3313b612017-01-13 15:41:29 +0100521 def get_doc(self, info):
522 if self.val != '##':
523 raise QAPIParseError(self, "Junk after '##' at start of "
524 "documentation comment")
525
526 doc = QAPIDoc(self, info)
527 self.accept(False)
528 while self.tok == '#':
529 if self.val.startswith('##'):
530 # End of doc comment
531 if self.val != '##':
532 raise QAPIParseError(self, "Junk after '##' at end of "
533 "documentation comment")
Markus Armbruster4ea71482017-03-15 13:57:23 +0100534 doc.end_comment()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100535 self.accept()
536 return doc
537 else:
538 doc.append(self.val)
539 self.accept(False)
540
541 raise QAPIParseError(self, "Documentation comment must end with '##'")
542
543
Markus Armbruster00e4b282015-06-10 10:04:36 +0200544#
545# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200546# TODO fold into QAPISchema
547# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200548#
549
Eric Blake437db252015-09-29 16:21:02 -0600550
Eric Blake14f00c62016-03-03 09:16:43 -0700551def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600552 if isinstance(base, dict):
553 return base
Markus Armbrustered285bf2017-03-15 13:57:32 +0100554 base_struct_define = struct_types.get(base)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800555 if not base_struct_define:
556 return None
557 return base_struct_define['data']
558
Eric Blake437db252015-09-29 16:21:02 -0600559
Eric Blake811d04f2015-05-04 09:05:10 -0600560# Return the qtype of an alternate branch, or None on error.
561def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600562 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600563 return builtin_types[qapi_type]
Markus Armbrustered285bf2017-03-15 13:57:32 +0100564 elif qapi_type in struct_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100565 return 'QTYPE_QDICT'
Markus Armbruster5f018442017-03-15 13:57:31 +0100566 elif qapi_type in enum_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100567 return 'QTYPE_QSTRING'
Markus Armbruster768562d2017-03-15 13:57:33 +0100568 elif qapi_type in union_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100569 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600570 return None
571
Eric Blake437db252015-09-29 16:21:02 -0600572
Wenchao Xiabceae762014-03-06 17:08:56 -0800573# Return the discriminator enum define if discriminator is specified as an
574# enum type, otherwise return None.
575def discriminator_find_enum_define(expr):
576 base = expr.get('base')
577 discriminator = expr.get('discriminator')
578
579 if not (discriminator and base):
580 return None
581
Eric Blake14f00c62016-03-03 09:16:43 -0700582 base_members = find_base_members(base)
583 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800584 return None
585
Eric Blake14f00c62016-03-03 09:16:43 -0700586 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800587 if not discriminator_type:
588 return None
589
Markus Armbruster5f018442017-03-15 13:57:31 +0100590 return enum_types.get(discriminator_type)
Wenchao Xiabceae762014-03-06 17:08:56 -0800591
Eric Blake437db252015-09-29 16:21:02 -0600592
Eric Blake59a92fe2015-11-18 01:52:56 -0700593# Names must be letters, numbers, -, and _. They must start with letter,
594# except for downstream extensions which must start with __RFQDN_.
595# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100596valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700597 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600598
599
Marc-André Lureau4148c292017-01-13 15:41:25 +0100600def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600601 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600602 global valid_name
603 membername = name
604
605 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100606 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600607 if name.startswith('*'):
608 membername = name[1:]
609 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100610 raise QAPISemError(info, "%s does not allow optional name '%s'"
611 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600612 # Enum members can start with a digit, because the generated C
613 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700614 if enum_member and membername[0].isdigit():
615 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600616 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
617 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600618 if not valid_name.match(membername) or \
619 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100620 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600621
Eric Blake437db252015-09-29 16:21:02 -0600622
623def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200624 global all_names
625 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200626 # FIXME should reject names that differ only in '_' vs. '.'
627 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200628 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100629 raise QAPISemError(info, "%s '%s' is already defined"
630 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600631 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100632 raise QAPISemError(info, "%s '%s' should not end in '%s'"
633 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200634 all_names[name] = meta
635
Eric Blake437db252015-09-29 16:21:02 -0600636
Marc-André Lureau4148c292017-01-13 15:41:25 +0100637def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600638 allow_dict=False, allow_optional=False,
639 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600640 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600641
642 if value is None:
643 return
644
Eric Blakedd883c62015-05-04 09:05:21 -0600645 # Check if array type for value is okay
646 if isinstance(value, list):
647 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100648 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600649 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100650 raise QAPISemError(info,
651 "%s: array type must contain single type name" %
652 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600653 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600654
655 # Check if type name for value is okay
656 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600657 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100658 raise QAPISemError(info, "%s uses unknown type '%s'"
659 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600660 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100661 raise QAPISemError(info, "%s cannot use %s type '%s'" %
662 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600663 return
664
Eric Blakedd883c62015-05-04 09:05:21 -0600665 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100666 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200667
668 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100669 raise QAPISemError(info,
670 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200671
672 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600673 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100674 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600675 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600676 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100677 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
678 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600679 # Todo: allow dictionaries to represent default values of
680 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100681 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200682 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600683 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600684 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600685
Eric Blake437db252015-09-29 16:21:02 -0600686
Marc-André Lureau4148c292017-01-13 15:41:25 +0100687def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600688 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600689 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600690
Eric Blakec8184082016-07-13 21:50:20 -0600691 args_meta = ['struct']
692 if boxed:
693 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100694 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600695 expr.get('data'), allow_dict=not boxed, allow_optional=True,
696 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600697 returns_meta = ['union', 'struct']
698 if name in returns_whitelist:
699 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100700 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200701 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200702 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600703
Eric Blake437db252015-09-29 16:21:02 -0600704
Marc-André Lureau4148c292017-01-13 15:41:25 +0100705def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600706 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600707 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600708
Eric Blakec8184082016-07-13 21:50:20 -0600709 meta = ['struct']
710 if boxed:
711 meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100712 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600713 expr.get('data'), allow_dict=not boxed, allow_optional=True,
714 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200715
Eric Blake437db252015-09-29 16:21:02 -0600716
Marc-André Lureau4148c292017-01-13 15:41:25 +0100717def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800718 name = expr['union']
719 base = expr.get('base')
720 discriminator = expr.get('discriminator')
721 members = expr['data']
722
Eric Blake811d04f2015-05-04 09:05:10 -0600723 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600724
725 # With no discriminator it is a simple union.
726 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600727 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600728 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600729 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100730 raise QAPISemError(info, "Simple union '%s' must not have a base" %
731 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600732
733 # Else, it's a flat union.
734 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600735 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100736 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600737 base, allow_dict=True, allow_optional=True,
738 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600739 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100740 raise QAPISemError(info, "Flat union '%s' must have a base"
741 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700742 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100743 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800744
Eric Blakec9e0a792015-05-04 09:05:22 -0600745 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600746 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100747 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600748 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700749 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800750 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100751 raise QAPISemError(info,
752 "Discriminator '%s' is not a member of base "
753 "struct '%s'"
754 % (discriminator, base))
Markus Armbruster5f018442017-03-15 13:57:31 +0100755 enum_define = enum_types.get(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600756 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800757 # Do not allow string discriminator
758 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100759 raise QAPISemError(info,
760 "Discriminator '%s' must be of enumeration "
761 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800762
Eric Blake02a57ae2016-02-17 23:48:16 -0700763 # Check every branch; don't allow an empty union
764 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100765 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800766 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100767 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600768
Eric Blake01cfbaa2015-12-01 22:20:58 -0700769 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100770 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200771 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600772
Eric Blake44bd1272015-05-04 09:05:08 -0600773 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700774 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600775 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100776 if key not in enum_define['data']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100777 raise QAPISemError(info,
778 "Discriminator value '%s' is not found in "
779 "enum '%s'"
Markus Armbrustereda43c62017-03-15 13:57:29 +0100780 % (key, enum_define['enum']))
Eric Blake44bd1272015-05-04 09:05:08 -0600781
Eric Blaked0b18232016-07-13 21:50:13 -0600782 # If discriminator is user-defined, ensure all values are covered
783 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100784 for value in enum_define['data']:
Eric Blaked0b18232016-07-13 21:50:13 -0600785 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100786 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
787 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600788
Eric Blake437db252015-09-29 16:21:02 -0600789
Marc-André Lureau4148c292017-01-13 15:41:25 +0100790def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600791 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600792 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600793 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600794
Eric Blake02a57ae2016-02-17 23:48:16 -0700795 # Check every branch; require at least two branches
796 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100797 raise QAPISemError(info,
798 "Alternate '%s' should have at least two branches "
799 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600800 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100801 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600802
Eric Blake811d04f2015-05-04 09:05:10 -0600803 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100804 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600805 value,
806 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600807 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700808 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100809 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
810 "type '%s'" % (name, key, value))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200811 conflicting = set([qtype])
812 if qtype == 'QTYPE_QSTRING':
813 enum_expr = enum_types.get(value)
814 if enum_expr:
815 for v in enum_expr['data']:
816 if v in ['on', 'off']:
817 conflicting.add('QTYPE_QBOOL')
818 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +0400819 conflicting.add('QTYPE_QNUM')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200820 else:
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +0400821 conflicting.add('QTYPE_QNUM')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200822 conflicting.add('QTYPE_QBOOL')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200823 for qt in conflicting:
Eduardo Habkostfda72ab2017-07-17 15:09:26 -0300824 if qt in types_seen:
825 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
826 "be distinguished from member '%s'"
827 % (name, key, types_seen[qt]))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200828 types_seen[qt] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800829
Eric Blake437db252015-09-29 16:21:02 -0600830
Marc-André Lureau4148c292017-01-13 15:41:25 +0100831def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600832 name = expr['enum']
833 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100834 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600835
836 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100837 raise QAPISemError(info,
838 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100839 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100840 raise QAPISemError(info,
841 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600842 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100843 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600844 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600845
Eric Blake437db252015-09-29 16:21:02 -0600846
Marc-André Lureau4148c292017-01-13 15:41:25 +0100847def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600848 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600849 members = expr['data']
850
Marc-André Lureau4148c292017-01-13 15:41:25 +0100851 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600852 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100853 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600854 allow_metas=['struct'])
855
Eric Blake437db252015-09-29 16:21:02 -0600856
Eric Blake0545f6b2015-05-04 09:05:15 -0600857def check_keys(expr_elem, meta, required, optional=[]):
858 expr = expr_elem['expr']
859 info = expr_elem['info']
860 name = expr[meta]
861 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100862 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600863 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600864 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600865 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100866 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
867 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600868 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100869 raise QAPISemError(info,
870 "'%s' of %s '%s' should only use false value"
871 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600872 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100873 raise QAPISemError(info,
874 "'%s' of %s '%s' should only use true value"
875 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600876 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600877 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100878 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
879 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600880
Eric Blake437db252015-09-29 16:21:02 -0600881
Markus Armbruster4d076d62015-06-10 08:55:21 +0200882def check_exprs(exprs):
883 global all_names
884
Markus Armbruster79470162017-03-15 13:57:21 +0100885 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200886 for builtin in builtin_types.keys():
887 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100888
889 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200890 for expr_elem in exprs:
891 expr = expr_elem['expr']
892 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100893 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100894
Markus Armbruster79470162017-03-15 13:57:21 +0100895 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100896 raise QAPISemError(info,
897 "Expression missing documentation comment")
898
Eric Blake437db252015-09-29 16:21:02 -0600899 if 'enum' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100900 meta = 'enum'
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100901 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster5f018442017-03-15 13:57:31 +0100902 enum_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600903 elif 'union' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100904 meta = 'union'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200905 check_keys(expr_elem, 'union', ['data'],
906 ['base', 'discriminator'])
Markus Armbruster768562d2017-03-15 13:57:33 +0100907 union_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600908 elif 'alternate' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100909 meta = 'alternate'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200910 check_keys(expr_elem, 'alternate', ['data'])
Eric Blake437db252015-09-29 16:21:02 -0600911 elif 'struct' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100912 meta = 'struct'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200913 check_keys(expr_elem, 'struct', ['data'], ['base'])
Markus Armbrustered285bf2017-03-15 13:57:32 +0100914 struct_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600915 elif 'command' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100916 meta = 'command'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200917 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600918 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Eric Blake437db252015-09-29 16:21:02 -0600919 elif 'event' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100920 meta = 'event'
Eric Blakec8184082016-07-13 21:50:20 -0600921 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200922 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100923 raise QAPISemError(expr_elem['info'],
924 "Expression is missing metatype")
Markus Armbruster6f053452017-03-15 13:57:30 +0100925 name = expr[meta]
926 add_name(name, info, meta)
Markus Armbruster79470162017-03-15 13:57:21 +0100927 if doc and doc.symbol != name:
928 raise QAPISemError(info, "Definition of '%s' follows documentation"
929 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200930
931 # Try again for hidden UnionKind enum
932 for expr_elem in exprs:
933 expr = expr_elem['expr']
Markus Armbrustereda43c62017-03-15 13:57:29 +0100934 if 'union' in expr and not discriminator_find_enum_define(expr):
935 name = '%sKind' % expr['union']
Eric Blake437db252015-09-29 16:21:02 -0600936 elif 'alternate' in expr:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100937 name = '%sKind' % expr['alternate']
938 else:
939 continue
Markus Armbruster5f018442017-03-15 13:57:31 +0100940 enum_types[name] = {'enum': name}
Markus Armbruster6f053452017-03-15 13:57:30 +0100941 add_name(name, info, 'enum', implicit=True)
Markus Armbruster4d076d62015-06-10 08:55:21 +0200942
943 # Validate that exprs make sense
944 for expr_elem in exprs:
945 expr = expr_elem['expr']
946 info = expr_elem['info']
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100947 doc = expr_elem.get('doc')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200948
Eric Blake437db252015-09-29 16:21:02 -0600949 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200950 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600951 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200952 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600953 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200954 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600955 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200956 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600957 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200958 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600959 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200960 check_event(expr, info)
961 else:
962 assert False, 'unexpected meta type'
963
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100964 if doc:
965 doc.check_expr(expr)
966
Markus Armbrusterac882192015-09-16 13:06:05 +0200967 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600968
Markus Armbrusterac882192015-09-16 13:06:05 +0200969
970#
971# Schema compiler frontend
972#
973
974class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100975 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +0200976 assert isinstance(name, str)
977 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -0600978 # For explicitly defined entities, info points to the (explicit)
979 # definition. For builtins (and their arrays), info is None.
980 # For implicitly defined entities, info points to a place that
981 # triggered the implicit definition (there may be more than one
982 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +0200983 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100984 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +0200985
Markus Armbrusterf51d8c32015-09-16 13:06:06 +0200986 def c_name(self):
987 return c_name(self.name)
988
Markus Armbrusterac882192015-09-16 13:06:05 +0200989 def check(self, schema):
990 pass
991
Eric Blake49823c42015-10-12 22:22:27 -0600992 def is_implicit(self):
993 return not self.info
994
Markus Armbruster3f7dc212015-09-16 13:06:07 +0200995 def visit(self, visitor):
996 pass
997
998
999class QAPISchemaVisitor(object):
1000 def visit_begin(self, schema):
1001 pass
1002
1003 def visit_end(self):
1004 pass
1005
Eric Blake25a0d9c2015-10-12 22:22:21 -06001006 def visit_needed(self, entity):
1007 # Default to visiting everything
1008 return True
1009
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001010 def visit_builtin_type(self, name, info, json_type):
1011 pass
1012
1013 def visit_enum_type(self, name, info, values, prefix):
1014 pass
1015
1016 def visit_array_type(self, name, info, element_type):
1017 pass
1018
1019 def visit_object_type(self, name, info, base, members, variants):
1020 pass
1021
Markus Armbruster39a18152015-09-16 13:06:28 +02001022 def visit_object_type_flat(self, name, info, members, variants):
1023 pass
1024
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001025 def visit_alternate_type(self, name, info, variants):
1026 pass
1027
1028 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001029 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001030 pass
1031
Eric Blake48825ca2016-07-13 21:50:19 -06001032 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001033 pass
1034
Markus Armbrusterac882192015-09-16 13:06:05 +02001035
1036class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001037 # Return the C type for common use.
1038 # For the types we commonly box, this is a pointer type.
1039 def c_type(self):
1040 pass
1041
1042 # Return the C type to be used in a parameter list.
1043 def c_param_type(self):
1044 return self.c_type()
1045
1046 # Return the C type to be used where we suppress boxing.
1047 def c_unboxed_type(self):
1048 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001049
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001050 def json_type(self):
1051 pass
1052
1053 def alternate_qtype(self):
1054 json2qtype = {
Markus Armbruster4d2d5c42017-06-26 19:25:14 +02001055 'null': 'QTYPE_QNULL',
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001056 'string': 'QTYPE_QSTRING',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001057 'number': 'QTYPE_QNUM',
1058 'int': 'QTYPE_QNUM',
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001059 'boolean': 'QTYPE_QBOOL',
1060 'object': 'QTYPE_QDICT'
1061 }
1062 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001063
Markus Armbruster691e0312017-03-15 13:57:14 +01001064 def doc_type(self):
1065 if self.is_implicit():
1066 return None
1067 return self.name
1068
Markus Armbrusterac882192015-09-16 13:06:05 +02001069
1070class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001071 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001072 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001073 assert not c_type or isinstance(c_type, str)
1074 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1075 'value')
1076 self._json_type_name = json_type
1077 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001078
1079 def c_name(self):
1080 return self.name
1081
Eric Blake4040d992016-03-17 16:48:28 -06001082 def c_type(self):
1083 return self._c_type_name
1084
1085 def c_param_type(self):
1086 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001087 return 'const ' + self._c_type_name
1088 return self._c_type_name
1089
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001090 def json_type(self):
1091 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001092
Markus Armbruster691e0312017-03-15 13:57:14 +01001093 def doc_type(self):
1094 return self.json_type()
1095
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001096 def visit(self, visitor):
1097 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1098
Markus Armbrusterac882192015-09-16 13:06:05 +02001099
1100class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001101 def __init__(self, name, info, doc, values, prefix):
1102 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001103 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001104 assert isinstance(v, QAPISchemaMember)
1105 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001106 assert prefix is None or isinstance(prefix, str)
1107 self.values = values
1108 self.prefix = prefix
1109
1110 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001111 seen = {}
1112 for v in self.values:
1113 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001114 if self.doc:
1115 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001116
Eric Blake99df5282015-10-12 22:22:32 -06001117 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001118 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1119 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001120
Eric Blake4040d992016-03-17 16:48:28 -06001121 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001122 return c_name(self.name)
1123
Eric Blake93bda4d2015-12-01 22:20:55 -07001124 def member_names(self):
1125 return [v.name for v in self.values]
1126
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001127 def json_type(self):
1128 return 'string'
1129
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001130 def visit(self, visitor):
1131 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001132 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001133
Markus Armbrusterac882192015-09-16 13:06:05 +02001134
1135class QAPISchemaArrayType(QAPISchemaType):
1136 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001137 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001138 assert isinstance(element_type, str)
1139 self._element_type_name = element_type
1140 self.element_type = None
1141
1142 def check(self, schema):
1143 self.element_type = schema.lookup_type(self._element_type_name)
1144 assert self.element_type
1145
Eric Blake99df5282015-10-12 22:22:32 -06001146 def is_implicit(self):
1147 return True
1148
Eric Blake4040d992016-03-17 16:48:28 -06001149 def c_type(self):
1150 return c_name(self.name) + pointer_suffix
1151
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001152 def json_type(self):
1153 return 'array'
1154
Markus Armbruster691e0312017-03-15 13:57:14 +01001155 def doc_type(self):
1156 elt_doc_type = self.element_type.doc_type()
1157 if not elt_doc_type:
1158 return None
1159 return 'array of ' + elt_doc_type
1160
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001161 def visit(self, visitor):
1162 visitor.visit_array_type(self.name, self.info, self.element_type)
1163
Markus Armbrusterac882192015-09-16 13:06:05 +02001164
1165class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001166 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001167 # struct has local_members, optional base, and no variants
1168 # flat union has base, variants, and no local_members
1169 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001170 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001171 assert base is None or isinstance(base, str)
1172 for m in local_members:
1173 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001174 m.set_owner(name)
1175 if variants is not None:
1176 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1177 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001178 self._base_name = base
1179 self.base = None
1180 self.local_members = local_members
1181 self.variants = variants
1182 self.members = None
1183
1184 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001185 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001186 raise QAPISemError(self.info,
1187 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001188 if self.members:
1189 return
1190 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001191 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001192 if self._base_name:
1193 self.base = schema.lookup_type(self._base_name)
1194 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001195 self.base.check(schema)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001196 self.base.check_clash(self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001197 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001198 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001199 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001200 if self.doc:
1201 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001202 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001203 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001204 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001205 assert self.variants.tag_member in self.members
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001206 self.variants.check_clash(self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001207 if self.doc:
1208 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001209
Eric Blake14f00c62016-03-03 09:16:43 -07001210 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001211 # and update seen to track the members seen so far. Report any errors
1212 # on behalf of info, which is not necessarily self.info
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001213 def check_clash(self, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001214 assert not self.variants # not implemented
1215 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001216 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001217
Eric Blake99df5282015-10-12 22:22:32 -06001218 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001219 # See QAPISchema._make_implicit_object_type(), as well as
1220 # _def_predefineds()
1221 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001222
Eric Blakeb6167702016-07-13 21:50:16 -06001223 def is_empty(self):
1224 assert self.members is not None
1225 return not self.members and not self.variants
1226
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001227 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001228 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001229 return QAPISchemaType.c_name(self)
1230
Eric Blake4040d992016-03-17 16:48:28 -06001231 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001232 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001233 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001234
Eric Blake4040d992016-03-17 16:48:28 -06001235 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001236 return c_name(self.name)
1237
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001238 def json_type(self):
1239 return 'object'
1240
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001241 def visit(self, visitor):
1242 visitor.visit_object_type(self.name, self.info,
1243 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001244 visitor.visit_object_type_flat(self.name, self.info,
1245 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001246
Markus Armbrusterac882192015-09-16 13:06:05 +02001247
Eric Blaked44f9ac2015-12-01 22:20:54 -07001248class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001249 role = 'member'
1250
Eric Blaked44f9ac2015-12-01 22:20:54 -07001251 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001252 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001253 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001254 self.owner = None
1255
1256 def set_owner(self, name):
1257 assert not self.owner
1258 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001259
Eric Blake27b60ab2015-11-18 01:52:51 -07001260 def check_clash(self, info, seen):
1261 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001262 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001263 raise QAPISemError(info,
1264 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001265 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001266 raise QAPISemError(info, "%s collides with %s" %
1267 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001268 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001269
Eric Blake88d4ef82015-11-18 01:52:50 -07001270 def _pretty_owner(self):
1271 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001272 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001273 # See QAPISchema._make_implicit_object_type() - reverse the
1274 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001275 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001276 if owner.endswith('-arg'):
1277 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001278 elif owner.endswith('-base'):
1279 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001280 else:
1281 assert owner.endswith('-wrapper')
1282 # Unreachable and not implemented
1283 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001284 if owner.endswith('Kind'):
1285 # See QAPISchema._make_implicit_enum_type()
1286 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001287 return '(%s of %s)' % (self.role, owner)
1288
1289 def describe(self):
1290 return "'%s' %s" % (self.name, self._pretty_owner())
1291
Markus Armbrusterac882192015-09-16 13:06:05 +02001292
Eric Blaked44f9ac2015-12-01 22:20:54 -07001293class QAPISchemaObjectTypeMember(QAPISchemaMember):
1294 def __init__(self, name, typ, optional):
1295 QAPISchemaMember.__init__(self, name)
1296 assert isinstance(typ, str)
1297 assert isinstance(optional, bool)
1298 self._type_name = typ
1299 self.type = None
1300 self.optional = optional
1301
1302 def check(self, schema):
1303 assert self.owner
1304 self.type = schema.lookup_type(self._type_name)
1305 assert self.type
1306
1307
Markus Armbrusterac882192015-09-16 13:06:05 +02001308class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001309 def __init__(self, tag_name, tag_member, variants):
1310 # Flat unions pass tag_name but not tag_member.
1311 # Simple unions and alternates pass tag_member but not tag_name.
1312 # After check(), tag_member is always set, and tag_name remains
1313 # a reliable witness of being used by a flat union.
1314 assert bool(tag_member) != bool(tag_name)
1315 assert (isinstance(tag_name, str) or
1316 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001317 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001318 for v in variants:
1319 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001320 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001321 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001322 self.variants = variants
1323
Eric Blake88d4ef82015-11-18 01:52:50 -07001324 def set_owner(self, name):
1325 for v in self.variants:
1326 v.set_owner(name)
1327
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001328 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001329 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001330 self.tag_member = seen[c_name(self._tag_name)]
1331 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001332 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1333 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001334 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001335 # Union names must match enum values; alternate names are
1336 # checked separately. Use 'seen' to tell the two apart.
1337 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001338 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001339 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001340 v.type.check(schema)
1341
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001342 def check_clash(self, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001343 for v in self.variants:
1344 # Reset seen map for each variant, since qapi names from one
1345 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001346 assert isinstance(v.type, QAPISchemaObjectType)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001347 v.type.check_clash(info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001348
Eric Blake437db252015-09-29 16:21:02 -06001349
Markus Armbrusterac882192015-09-16 13:06:05 +02001350class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001351 role = 'branch'
1352
Markus Armbrusterac882192015-09-16 13:06:05 +02001353 def __init__(self, name, typ):
1354 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1355
Markus Armbrusterac882192015-09-16 13:06:05 +02001356
1357class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001358 def __init__(self, name, info, doc, variants):
1359 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001360 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001361 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001362 variants.set_owner(name)
1363 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001364 self.variants = variants
1365
1366 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001367 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001368 # Not calling self.variants.check_clash(), because there's nothing
1369 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001370 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001371 # Alternate branch names have no relation to the tag enum values;
1372 # so we have to check for potential name collisions ourselves.
1373 seen = {}
1374 for v in self.variants.variants:
1375 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001376 if self.doc:
1377 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001378 if self.doc:
1379 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001380
Eric Blake4040d992016-03-17 16:48:28 -06001381 def c_type(self):
1382 return c_name(self.name) + pointer_suffix
1383
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001384 def json_type(self):
1385 return 'value'
1386
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001387 def visit(self, visitor):
1388 visitor.visit_alternate_type(self.name, self.info, self.variants)
1389
Eric Blakec8184082016-07-13 21:50:20 -06001390 def is_empty(self):
1391 return False
1392
Markus Armbrusterac882192015-09-16 13:06:05 +02001393
1394class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001395 def __init__(self, name, info, doc, arg_type, ret_type,
1396 gen, success_response, boxed):
1397 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001398 assert not arg_type or isinstance(arg_type, str)
1399 assert not ret_type or isinstance(ret_type, str)
1400 self._arg_type_name = arg_type
1401 self.arg_type = None
1402 self._ret_type_name = ret_type
1403 self.ret_type = None
1404 self.gen = gen
1405 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001406 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001407
1408 def check(self, schema):
1409 if self._arg_type_name:
1410 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001411 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1412 isinstance(self.arg_type, QAPISchemaAlternateType))
1413 self.arg_type.check(schema)
1414 if self.boxed:
1415 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001416 raise QAPISemError(self.info,
1417 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001418 else:
1419 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1420 assert not self.arg_type.variants
1421 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001422 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001423 if self._ret_type_name:
1424 self.ret_type = schema.lookup_type(self._ret_type_name)
1425 assert isinstance(self.ret_type, QAPISchemaType)
1426
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001427 def visit(self, visitor):
1428 visitor.visit_command(self.name, self.info,
1429 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001430 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001431
Markus Armbrusterac882192015-09-16 13:06:05 +02001432
1433class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001434 def __init__(self, name, info, doc, arg_type, boxed):
1435 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001436 assert not arg_type or isinstance(arg_type, str)
1437 self._arg_type_name = arg_type
1438 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001439 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001440
1441 def check(self, schema):
1442 if self._arg_type_name:
1443 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001444 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1445 isinstance(self.arg_type, QAPISchemaAlternateType))
1446 self.arg_type.check(schema)
1447 if self.boxed:
1448 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001449 raise QAPISemError(self.info,
1450 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001451 else:
1452 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1453 assert not self.arg_type.variants
1454 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001455 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001456
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001457 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001458 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001459
Markus Armbrusterac882192015-09-16 13:06:05 +02001460
1461class QAPISchema(object):
1462 def __init__(self, fname):
1463 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001464 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001465 self.exprs = check_exprs(parser.exprs)
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001466 self.docs = parser.docs
Eric Blake7618b912015-10-12 22:22:22 -06001467 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001468 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001469 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001470 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001471 self._def_exprs()
1472 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001473 except QAPIError as err:
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001474 print(err, file=sys.stderr)
Markus Armbrusterac882192015-09-16 13:06:05 +02001475 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001476
Markus Armbrusterac882192015-09-16 13:06:05 +02001477 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001478 # Only the predefined types are allowed to not have info
1479 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001480 assert ent.name not in self._entity_dict
1481 self._entity_dict[ent.name] = ent
1482
1483 def lookup_entity(self, name, typ=None):
1484 ent = self._entity_dict.get(name)
1485 if typ and not isinstance(ent, typ):
1486 return None
1487 return ent
1488
1489 def lookup_type(self, name):
1490 return self.lookup_entity(name, QAPISchemaType)
1491
Eric Blake861877a2016-03-17 16:48:36 -06001492 def _def_builtin_type(self, name, json_type, c_type):
1493 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001494 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1495 # qapi-types.h from a single .c, all arrays of builtins must be
1496 # declared in the first file whether or not they are used. Nicer
1497 # would be to use lazy instantiation, while figuring out how to
1498 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001499 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001500
1501 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001502 for t in [('str', 'string', 'char' + pointer_suffix),
1503 ('number', 'number', 'double'),
1504 ('int', 'int', 'int64_t'),
1505 ('int8', 'int', 'int8_t'),
1506 ('int16', 'int', 'int16_t'),
1507 ('int32', 'int', 'int32_t'),
1508 ('int64', 'int', 'int64_t'),
1509 ('uint8', 'int', 'uint8_t'),
1510 ('uint16', 'int', 'uint16_t'),
1511 ('uint32', 'int', 'uint32_t'),
1512 ('uint64', 'int', 'uint64_t'),
1513 ('size', 'int', 'uint64_t'),
1514 ('bool', 'boolean', 'bool'),
Markus Armbruster4d2d5c42017-06-26 19:25:14 +02001515 ('any', 'value', 'QObject' + pointer_suffix),
1516 ('null', 'null', 'QNull' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001517 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001518 self.the_empty_object_type = QAPISchemaObjectType(
1519 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001520 self._def_entity(self.the_empty_object_type)
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001521 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
Eric Blake93bda4d2015-12-01 22:20:55 -07001522 'qstring', 'qdict', 'qlist',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001523 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001524 self._def_entity(QAPISchemaEnumType('QType', None, None,
1525 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001526
Eric Blake93bda4d2015-12-01 22:20:55 -07001527 def _make_enum_members(self, values):
1528 return [QAPISchemaMember(v) for v in values]
1529
Eric Blake99df5282015-10-12 22:22:32 -06001530 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001531 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001532 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001533 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001534 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001535 return name
1536
Eric Blake99df5282015-10-12 22:22:32 -06001537 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001538 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001539 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001540 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001541 return name
1542
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001543 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001544 if not members:
1545 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001546 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001547 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001548 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001549 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001550 members, None))
1551 return name
1552
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001553 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001554 name = expr['enum']
1555 data = expr['data']
1556 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001557 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001558 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001559
Eric Blake99df5282015-10-12 22:22:32 -06001560 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001561 optional = False
1562 if name.startswith('*'):
1563 name = name[1:]
1564 optional = True
1565 if isinstance(typ, list):
1566 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001567 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001568 return QAPISchemaObjectTypeMember(name, typ, optional)
1569
Eric Blake99df5282015-10-12 22:22:32 -06001570 def _make_members(self, data, info):
1571 return [self._make_member(key, value, info)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001572 for (key, value) in data.items()]
Markus Armbrusterac882192015-09-16 13:06:05 +02001573
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001574 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001575 name = expr['struct']
1576 base = expr.get('base')
1577 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001578 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001579 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001580 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001581
1582 def _make_variant(self, case, typ):
1583 return QAPISchemaObjectTypeVariant(case, typ)
1584
Eric Blake99df5282015-10-12 22:22:32 -06001585 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001586 if isinstance(typ, list):
1587 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001588 typ = self._make_array_type(typ[0], info)
1589 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001590 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001591 return QAPISchemaObjectTypeVariant(case, typ)
1592
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001593 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001594 name = expr['union']
1595 data = expr['data']
1596 base = expr.get('base')
1597 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001598 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001599 if isinstance(base, dict):
1600 base = (self._make_implicit_object_type(
Markus Armbrusterc2613942017-03-15 13:57:35 +01001601 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001602 if tag_name:
1603 variants = [self._make_variant(key, value)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001604 for (key, value) in data.items()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001605 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001606 else:
Eric Blake99df5282015-10-12 22:22:32 -06001607 variants = [self._make_simple_variant(key, value, info)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001608 for (key, value) in data.items()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001609 typ = self._make_implicit_enum_type(name, info,
1610 [v.name for v in variants])
1611 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001612 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001613 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001614 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001615 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001616 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001617 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001618
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001619 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001620 name = expr['alternate']
1621 data = expr['data']
1622 variants = [self._make_variant(key, value)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001623 for (key, value) in data.items()]
Eric Blake0426d532015-12-01 22:20:48 -07001624 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001625 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001626 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001627 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001628 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001629 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001630
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001631 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001632 name = expr['command']
1633 data = expr.get('data')
1634 rets = expr.get('returns')
1635 gen = expr.get('gen', True)
1636 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001637 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001638 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001639 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001640 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001641 if isinstance(rets, list):
1642 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001643 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001644 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1645 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001646
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001647 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001648 name = expr['event']
1649 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001650 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001651 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001652 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001653 name, info, doc, 'arg', self._make_members(data, info))
1654 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001655
1656 def _def_exprs(self):
1657 for expr_elem in self.exprs:
1658 expr = expr_elem['expr']
1659 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001660 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001661 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001662 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001663 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001664 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001665 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001666 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001667 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001668 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001669 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001670 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001671 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001672 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001673 else:
1674 assert False
1675
1676 def check(self):
1677 for ent in self._entity_dict.values():
1678 ent.check(self)
1679
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001680 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001681 visitor.visit_begin(self)
1682 for (name, entity) in sorted(self._entity_dict.items()):
1683 if visitor.visit_needed(entity):
1684 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001685 visitor.visit_end()
1686
Markus Armbruster2caba362013-07-27 17:41:56 +02001687
Markus Armbruster00e4b282015-06-10 10:04:36 +02001688#
1689# Code generation helpers
1690#
1691
Michael Roth0f923be2011-07-19 14:50:39 -05001692def camel_case(name):
1693 new_name = ''
1694 first = True
1695 for ch in name:
1696 if ch in ['_', '-']:
1697 first = True
1698 elif first:
1699 new_name += ch.upper()
1700 first = False
1701 else:
1702 new_name += ch.lower()
1703 return new_name
1704
Eric Blake437db252015-09-29 16:21:02 -06001705
Markus Armbruster849bc532015-05-14 06:50:53 -06001706# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1707# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1708# ENUM24_Name -> ENUM24_NAME
1709def camel_to_upper(value):
1710 c_fun_str = c_name(value, False)
1711 if value.isupper():
1712 return c_fun_str
1713
1714 new_name = ''
1715 l = len(c_fun_str)
1716 for i in range(l):
1717 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001718 # When c is upper and no '_' appears before, do more checks
1719 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001720 if i < l - 1 and c_fun_str[i + 1].islower():
1721 new_name += '_'
1722 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001723 new_name += '_'
1724 new_name += c
1725 return new_name.lstrip('_').upper()
1726
Eric Blake437db252015-09-29 16:21:02 -06001727
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001728def c_enum_const(type_name, const_name, prefix=None):
1729 if prefix is not None:
1730 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001731 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001732
Eric Blake18df5152015-05-14 06:50:48 -06001733c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001734
Eric Blake437db252015-09-29 16:21:02 -06001735
Eric Blakec6405b52015-05-14 06:50:55 -06001736# Map @name to a valid C identifier.
1737# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001738# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001739#
1740# Used for converting 'name' from a 'name':'type' qapi definition
1741# into a generated struct member, as well as converting type names
1742# into substrings of a generated C function name.
1743# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1744# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001745def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001746 # ANSI X3J11/88-090, 3.1.1
1747 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001748 'default', 'do', 'double', 'else', 'enum', 'extern',
1749 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1750 'return', 'short', 'signed', 'sizeof', 'static',
1751 'struct', 'switch', 'typedef', 'union', 'unsigned',
1752 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001753 # ISO/IEC 9899:1999, 6.4.1
1754 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1755 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001756 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1757 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001758 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1759 # excluding _.*
1760 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001761 # C++ ISO/IEC 14882:2003 2.11
1762 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1763 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1764 'namespace', 'new', 'operator', 'private', 'protected',
1765 'public', 'reinterpret_cast', 'static_cast', 'template',
1766 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1767 'using', 'virtual', 'wchar_t',
1768 # alternative representations
1769 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1770 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001771 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001772 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001773 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001774 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1775 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001776 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001777 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001778
Amos Kong05dfb262014-06-10 19:25:53 +08001779eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001780pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001781
Eric Blake437db252015-09-29 16:21:02 -06001782
Michael Roth0f923be2011-07-19 14:50:39 -05001783def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001784 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001785 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001786 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001787 return ret
1788
1789indent_level = 0
1790
Eric Blake437db252015-09-29 16:21:02 -06001791
Michael Roth0f923be2011-07-19 14:50:39 -05001792def push_indent(indent_amount=4):
1793 global indent_level
1794 indent_level += indent_amount
1795
Eric Blake437db252015-09-29 16:21:02 -06001796
Michael Roth0f923be2011-07-19 14:50:39 -05001797def pop_indent(indent_amount=4):
1798 global indent_level
1799 indent_level -= indent_amount
1800
Eric Blake437db252015-09-29 16:21:02 -06001801
Markus Armbruster77e703b2015-06-24 19:27:32 +02001802# Generate @code with @kwds interpolated.
1803# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001804def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001805 raw = code % kwds
1806 if indent_level:
1807 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001808 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001809 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001810 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001811 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001812 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001813
Eric Blake437db252015-09-29 16:21:02 -06001814
Michael Roth0f923be2011-07-19 14:50:39 -05001815def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001816 if code[0] == '\n':
1817 code = code[1:]
1818 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001819
Michael Roth0f923be2011-07-19 14:50:39 -05001820
1821def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001822 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001823
Eric Blake437db252015-09-29 16:21:02 -06001824
Michael Rothc0afa9c2013-05-10 17:46:00 -05001825def guardstart(name):
1826 return mcgen('''
1827
1828#ifndef %(name)s
1829#define %(name)s
1830
1831''',
1832 name=guardname(name))
1833
Eric Blake437db252015-09-29 16:21:02 -06001834
Michael Rothc0afa9c2013-05-10 17:46:00 -05001835def guardend(name):
1836 return mcgen('''
1837
1838#endif /* %(name)s */
1839
1840''',
1841 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001842
Eric Blake437db252015-09-29 16:21:02 -06001843
Markus Armbrustere98859a2015-09-16 13:06:16 +02001844def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001845 ret = mcgen('''
1846
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001847const QEnumLookup %(c_name)s_lookup = {
1848 .array = (const char *const[]) {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001849''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001850 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001851 for value in values:
1852 index = c_enum_const(name, value, prefix)
1853 ret += mcgen('''
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001854 [%(index)s] = "%(value)s",
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001855''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001856 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001857
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001858 ret += mcgen('''
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001859 },
1860 .size = %(max_index)s
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001861};
1862''',
Marc-André Lureauebf677c2017-08-24 10:46:11 +02001863 max_index=c_enum_const(name, '_MAX', prefix))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001864 return ret
1865
Eric Blake437db252015-09-29 16:21:02 -06001866
Markus Armbrustere98859a2015-09-16 13:06:16 +02001867def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001868 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001869 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001870
1871 ret = mcgen('''
1872
1873typedef enum %(c_name)s {
1874''',
1875 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001876
1877 i = 0
1878 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001879 ret += mcgen('''
1880 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001881''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001882 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001883 i=i)
1884 i += 1
1885
Markus Armbrustere98859a2015-09-16 13:06:16 +02001886 ret += mcgen('''
1887} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001888''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001889 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001890
Markus Armbrustere98859a2015-09-16 13:06:16 +02001891 ret += mcgen('''
1892
Markus Armbruster5b5f8252017-08-24 10:46:07 +02001893#define %(c_name)s_str(val) \\
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001894 qapi_enum_lookup(&%(c_name)s_lookup, (val))
Markus Armbruster5b5f8252017-08-24 10:46:07 +02001895
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001896extern const QEnumLookup %(c_name)s_lookup;
Markus Armbrustere98859a2015-09-16 13:06:16 +02001897''',
1898 c_name=c_name(name))
1899 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001900
Eric Blake437db252015-09-29 16:21:02 -06001901
Marc-André Lureau086ee7a2017-06-01 16:41:41 +04001902def build_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001903 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001904 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001905 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001906 ret = ''
1907 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001908 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001909 ret += '%s arg' % arg_type.c_param_type()
1910 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001911 else:
1912 assert not arg_type.variants
1913 for memb in arg_type.members:
1914 ret += sep
1915 sep = ', '
1916 if memb.optional:
1917 ret += 'bool has_%s, ' % c_name(memb.name)
1918 ret += '%s %s' % (memb.type.c_param_type(),
1919 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001920 if extra:
1921 ret += sep + extra
1922 return ret
1923
Eric Blake1f353342015-09-29 16:21:13 -06001924
Markus Armbruster00e4b282015-06-10 10:04:36 +02001925#
1926# Common command line parsing
1927#
1928
Eric Blake437db252015-09-29 16:21:02 -06001929
Markus Armbrusteref801a92017-03-15 13:57:08 +01001930def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001931
1932 try:
1933 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001934 'chp:o:' + extra_options,
1935 ['source', 'header', 'prefix=',
1936 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001937 except getopt.GetoptError as err:
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001938 print("%s: %s" % (sys.argv[0], str(err)), file=sys.stderr)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001939 sys.exit(1)
1940
Markus Armbrusteref801a92017-03-15 13:57:08 +01001941 output_dir = ''
1942 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001943 do_c = False
1944 do_h = False
1945 extra_opts = []
1946
1947 for oa in opts:
1948 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001949 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001950 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001951 if match.end() != len(a):
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001952 print("%s: 'funny character '%s' in argument of --prefix" \
1953 % (sys.argv[0], a[match.end()]), file=sys.stderr)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001954 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001955 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001956 elif o in ('-o', '--output-dir'):
1957 output_dir = a + '/'
1958 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001959 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001960 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001961 do_h = True
1962 else:
1963 extra_opts.append(oa)
1964
1965 if not do_c and not do_h:
1966 do_c = True
1967 do_h = True
1968
Markus Armbruster16d80f62015-04-02 13:32:16 +02001969 if len(args) != 1:
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001970 print("%s: need exactly one argument" % sys.argv[0], file=sys.stderr)
Markus Armbrusterb4540962015-04-02 13:17:34 +02001971 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02001972 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001973
Markus Armbruster54414042015-06-09 16:22:45 +02001974 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001975
Markus Armbruster00e4b282015-06-10 10:04:36 +02001976#
1977# Generate output files with boilerplate
1978#
1979
Eric Blake437db252015-09-29 16:21:02 -06001980
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001981def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1982 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001983 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001984 c_file = output_dir + prefix + c_file
1985 h_file = output_dir + prefix + h_file
1986
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001987 if output_dir:
1988 try:
1989 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01001990 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001991 if e.errno != errno.EEXIST:
1992 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001993
1994 def maybe_open(really, name, opt):
1995 if really:
1996 return open(name, opt)
1997 else:
1998 import StringIO
1999 return StringIO.StringIO()
2000
2001 fdef = maybe_open(do_c, c_file, 'w')
2002 fdecl = maybe_open(do_h, h_file, 'w')
2003
2004 fdef.write(mcgen('''
2005/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2006%(comment)s
2007''',
Eric Blake437db252015-09-29 16:21:02 -06002008 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002009
2010 fdecl.write(mcgen('''
2011/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2012%(comment)s
2013#ifndef %(guard)s
2014#define %(guard)s
2015
2016''',
Eric Blake437db252015-09-29 16:21:02 -06002017 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002018
2019 return (fdef, fdecl)
2020
Eric Blake437db252015-09-29 16:21:02 -06002021
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002022def close_output(fdef, fdecl):
2023 fdecl.write('''
2024#endif
2025''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002026 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002027 fdef.close()