blob: 58f995b07fd23299d1becf10b3165b0070c7fed7 [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
Daniel P. Berrange5f90af82018-01-16 13:42:07 +000025try:
26 from StringIO import StringIO
27except ImportError:
28 from io import StringIO
Michael Roth0f923be2011-07-19 14:50:39 -050029
Eric Blakeb52c4b92015-05-04 09:05:00 -060030builtin_types = {
Markus Armbruster4d2d5c42017-06-26 19:25:14 +020031 'null': 'QTYPE_QNULL',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020032 'str': 'QTYPE_QSTRING',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +040033 'int': 'QTYPE_QNUM',
34 'number': 'QTYPE_QNUM',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020035 'bool': 'QTYPE_QBOOL',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +040036 'int8': 'QTYPE_QNUM',
37 'int16': 'QTYPE_QNUM',
38 'int32': 'QTYPE_QNUM',
39 'int64': 'QTYPE_QNUM',
40 'uint8': 'QTYPE_QNUM',
41 'uint16': 'QTYPE_QNUM',
42 'uint32': 'QTYPE_QNUM',
43 'uint64': 'QTYPE_QNUM',
44 'size': 'QTYPE_QNUM',
Eric Blake1310a3d2015-12-01 22:20:46 -070045 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070046 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020047}
48
Markus Armbrusterbc52d032017-03-15 13:56:51 +010049# Are documentation comments required?
50doc_required = False
51
Eric Blake10d4d992015-05-04 09:05:23 -060052# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010053returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060054
Eric Blake893e1f22015-12-01 22:20:57 -070055# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010056name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070057
Markus Armbruster5f018442017-03-15 13:57:31 +010058enum_types = {}
Markus Armbrustered285bf2017-03-15 13:57:32 +010059struct_types = {}
Markus Armbruster768562d2017-03-15 13:57:33 +010060union_types = {}
Eric Blake4dc2e692015-05-04 09:05:17 -060061all_names = {}
62
Markus Armbruster00e4b282015-06-10 10:04:36 +020063#
64# Parsing the schema into expressions
65#
66
Eric Blake437db252015-09-29 16:21:02 -060067
Lluís Vilanovaa719a272014-05-07 20:46:15 +020068def error_path(parent):
Markus Armbrusteref801a92017-03-15 13:57:08 +010069 res = ''
Lluís Vilanovaa719a272014-05-07 20:46:15 +020070 while parent:
Markus Armbrusteref801a92017-03-15 13:57:08 +010071 res = ('In file included from %s:%d:\n' % (parent['file'],
Lluís Vilanovaa719a272014-05-07 20:46:15 +020072 parent['line'])) + res
73 parent = parent['parent']
74 return res
75
Eric Blake437db252015-09-29 16:21:02 -060076
Marc-André Lureau4148c292017-01-13 15:41:25 +010077class QAPIError(Exception):
78 def __init__(self, fname, line, col, incl_info, msg):
Eric Blake59b00542015-09-29 16:21:01 -060079 Exception.__init__(self)
Marc-André Lureau4148c292017-01-13 15:41:25 +010080 self.fname = fname
81 self.line = line
82 self.col = col
83 self.info = incl_info
Markus Armbruster2caba362013-07-27 17:41:56 +020084 self.msg = msg
Marc-André Lureau4148c292017-01-13 15:41:25 +010085
86 def __str__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +010087 loc = '%s:%d' % (self.fname, self.line)
Marc-André Lureau4148c292017-01-13 15:41:25 +010088 if self.col is not None:
Markus Armbrusteref801a92017-03-15 13:57:08 +010089 loc += ':%s' % self.col
90 return error_path(self.info) + '%s: %s' % (loc, self.msg)
Marc-André Lureau4148c292017-01-13 15:41:25 +010091
92
93class QAPIParseError(QAPIError):
94 def __init__(self, parser, msg):
95 col = 1
96 for ch in parser.src[parser.line_pos:parser.pos]:
Wenchao Xia515b9432014-03-04 18:44:33 -080097 if ch == '\t':
Marc-André Lureau4148c292017-01-13 15:41:25 +010098 col = (col + 7) % 8 + 1
Markus Armbruster2caba362013-07-27 17:41:56 +020099 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100100 col += 1
101 QAPIError.__init__(self, parser.fname, parser.line, col,
102 parser.incl_info, msg)
Markus Armbruster2caba362013-07-27 17:41:56 +0200103
Eric Blake437db252015-09-29 16:21:02 -0600104
Marc-André Lureau4148c292017-01-13 15:41:25 +0100105class QAPISemError(QAPIError):
106 def __init__(self, info, msg):
107 QAPIError.__init__(self, info['file'], info['line'], None,
108 info['parent'], msg)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800109
Eric Blake437db252015-09-29 16:21:02 -0600110
Marc-André Lureau3313b612017-01-13 15:41:29 +0100111class QAPIDoc(object):
112 class Section(object):
113 def __init__(self, name=None):
114 # optional section name (argument/member or section name)
115 self.name = name
116 # the list of lines for this section
Markus Armbruster09331fc2017-10-02 16:13:38 +0200117 self.text = ''
Marc-André Lureau3313b612017-01-13 15:41:29 +0100118
119 def append(self, line):
Markus Armbruster09331fc2017-10-02 16:13:38 +0200120 self.text += line.rstrip() + '\n'
Marc-André Lureau3313b612017-01-13 15:41:29 +0100121
122 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100123 def __init__(self, name):
124 QAPIDoc.Section.__init__(self, name)
125 self.member = None
126
127 def connect(self, member):
128 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100129
130 def __init__(self, parser, info):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200131 # self._parser is used to report errors with QAPIParseError. The
Marc-André Lureau3313b612017-01-13 15:41:29 +0100132 # resulting error position depends on the state of the parser.
133 # It happens to be the beginning of the comment. More or less
134 # servicable, but action at a distance.
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200135 self._parser = parser
Marc-André Lureau3313b612017-01-13 15:41:29 +0100136 self.info = info
137 self.symbol = None
138 self.body = QAPIDoc.Section()
139 # dict mapping parameter name to ArgSection
140 self.args = OrderedDict()
141 # a list of Section
142 self.sections = []
143 # the current section
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200144 self._section = self.body
Marc-André Lureau3313b612017-01-13 15:41:29 +0100145
146 def has_section(self, name):
147 """Return True if we have a section with this name."""
148 for i in self.sections:
149 if i.name == name:
150 return True
151 return False
152
153 def append(self, line):
154 """Parse a comment line and add it to the documentation."""
155 line = line[1:]
156 if not line:
157 self._append_freeform(line)
158 return
159
160 if line[0] != ' ':
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200161 raise QAPIParseError(self._parser, "Missing space after #")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100162 line = line[1:]
163
164 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
165 # recognized, and get silently treated as ordinary text
166 if self.symbol:
167 self._append_symbol_line(line)
Markus Armbruster09331fc2017-10-02 16:13:38 +0200168 elif not self.body.text and line.startswith('@'):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100169 if not line.endswith(':'):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200170 raise QAPIParseError(self._parser, "Line should end with :")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100171 self.symbol = line[1:-1]
172 # FIXME invalid names other than the empty string aren't flagged
173 if not self.symbol:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200174 raise QAPIParseError(self._parser, "Invalid name")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100175 else:
176 self._append_freeform(line)
177
Markus Armbruster4ea71482017-03-15 13:57:23 +0100178 def end_comment(self):
179 self._end_section()
180
Marc-André Lureau3313b612017-01-13 15:41:29 +0100181 def _append_symbol_line(self, line):
182 name = line.split(' ', 1)[0]
183
Markus Armbrusteref801a92017-03-15 13:57:08 +0100184 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100185 line = line[len(name)+1:]
186 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100187 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100188 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100189 'Note:', 'Notes:',
190 'Example:', 'Examples:',
191 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100192 line = line[len(name)+1:]
193 self._start_section(name[:-1])
194
195 self._append_freeform(line)
196
197 def _start_args_section(self, name):
198 # FIXME invalid names other than the empty string aren't flagged
199 if not name:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200200 raise QAPIParseError(self._parser, "Invalid parameter name")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100201 if name in self.args:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200202 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100203 "'%s' parameter name duplicated" % name)
204 if self.sections:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200205 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100206 "'@%s:' can't follow '%s' section"
207 % (name, self.sections[0].name))
Markus Armbruster4ea71482017-03-15 13:57:23 +0100208 self._end_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200209 self._section = QAPIDoc.ArgSection(name)
210 self.args[name] = self._section
Marc-André Lureau3313b612017-01-13 15:41:29 +0100211
Markus Armbrusterfc3f0df2017-10-02 16:13:37 +0200212 def _start_section(self, name=None):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100213 if name in ('Returns', 'Since') and self.has_section(name):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200214 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100215 "Duplicated '%s' section" % name)
Markus Armbruster4ea71482017-03-15 13:57:23 +0100216 self._end_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200217 self._section = QAPIDoc.Section(name)
218 self.sections.append(self._section)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100219
Markus Armbruster4ea71482017-03-15 13:57:23 +0100220 def _end_section(self):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200221 if self._section:
222 text = self._section.text = self._section.text.strip()
223 if self._section.name and (not text or text.isspace()):
224 raise QAPIParseError(self._parser, "Empty doc section '%s'"
225 % self._section.name)
226 self._section = None
Markus Armbruster4ea71482017-03-15 13:57:23 +0100227
Marc-André Lureau3313b612017-01-13 15:41:29 +0100228 def _append_freeform(self, line):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200229 in_arg = isinstance(self._section, QAPIDoc.ArgSection)
230 if (in_arg and self._section.text.endswith('\n\n')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100231 and line and not line[0].isspace()):
232 self._start_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200233 if (in_arg or not self._section.name
234 or not self._section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100235 line = line.strip()
Markus Armbruster2d433232017-03-15 13:57:22 +0100236 match = re.match(r'(@\S+:)', line)
237 if match:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200238 raise QAPIParseError(self._parser,
Markus Armbruster2d433232017-03-15 13:57:22 +0100239 "'%s' not allowed in free-form documentation"
240 % match.group(1))
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200241 self._section.append(line)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100242
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100243 def connect_member(self, member):
244 if member.name not in self.args:
245 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100246 self.args[member.name] = QAPIDoc.ArgSection(member.name)
247 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100248
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100249 def check_expr(self, expr):
250 if self.has_section('Returns') and 'command' not in expr:
251 raise QAPISemError(self.info,
252 "'Returns:' is only valid for commands")
253
Markus Armbruster816a57c2017-03-15 13:57:26 +0100254 def check(self):
Daniel P. Berrange2f848042018-01-16 13:42:05 +0000255 bogus = [name for name, section in self.args.items()
Markus Armbruster816a57c2017-03-15 13:57:26 +0100256 if not section.member]
257 if bogus:
258 raise QAPISemError(
259 self.info,
260 "The following documented members are not in "
261 "the declaration: %s" % ", ".join(bogus))
262
Marc-André Lureau3313b612017-01-13 15:41:29 +0100263
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200264class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500265
Eric Blake437db252015-09-29 16:21:02 -0600266 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200267 abs_fname = os.path.abspath(fp.name)
Markus Armbruster2281d002017-10-02 16:13:33 +0200268 self.fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200269 previously_included.append(abs_fname)
270 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200271 self.src = fp.read()
272 if self.src == '' or self.src[-1] != '\n':
273 self.src += '\n'
274 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800275 self.line = 1
276 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200277 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100278 self.docs = []
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200279 self.accept()
Markus Armbruster64d60332017-10-02 16:13:34 +0200280 cur_doc = None
Michael Roth0f923be2011-07-19 14:50:39 -0500281
Eric Blake437db252015-09-29 16:21:02 -0600282 while self.tok is not None:
Markus Armbruster2281d002017-10-02 16:13:33 +0200283 info = {'file': self.fname, 'line': self.line,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100284 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100285 if self.tok == '#':
Markus Armbruster64d60332017-10-02 16:13:34 +0200286 self.reject_expr_doc(cur_doc)
287 cur_doc = self.get_doc(info)
288 self.docs.append(cur_doc)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100289 continue
290
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200291 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100292 if 'include' in expr:
Markus Armbruster64d60332017-10-02 16:13:34 +0200293 self.reject_expr_doc(cur_doc)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200294 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100295 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100296 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200297 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100298 raise QAPISemError(info,
299 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100300 self._include(include, info, os.path.dirname(abs_fname),
301 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100302 elif "pragma" in expr:
Markus Armbruster64d60332017-10-02 16:13:34 +0200303 self.reject_expr_doc(cur_doc)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100304 if len(expr) != 1:
305 raise QAPISemError(info, "Invalid 'pragma' directive")
306 pragma = expr['pragma']
307 if not isinstance(pragma, dict):
308 raise QAPISemError(
309 info, "Value of 'pragma' must be a dictionary")
Daniel P. Berrange2f848042018-01-16 13:42:05 +0000310 for name, value in pragma.items():
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100311 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200312 else:
313 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100314 'info': info}
Markus Armbruster64d60332017-10-02 16:13:34 +0200315 if cur_doc:
316 if not cur_doc.symbol:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100317 raise QAPISemError(
Markus Armbruster64d60332017-10-02 16:13:34 +0200318 cur_doc.info, "Expression documentation required")
319 expr_elem['doc'] = cur_doc
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200320 self.exprs.append(expr_elem)
Markus Armbruster64d60332017-10-02 16:13:34 +0200321 cur_doc = None
322 self.reject_expr_doc(cur_doc)
Markus Armbrustere7823a22017-03-15 13:57:20 +0100323
Markus Armbruster64d60332017-10-02 16:13:34 +0200324 @staticmethod
325 def reject_expr_doc(doc):
326 if doc and doc.symbol:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100327 raise QAPISemError(
Markus Armbruster64d60332017-10-02 16:13:34 +0200328 doc.info,
Markus Armbrustere7823a22017-03-15 13:57:20 +0100329 "Documentation for '%s' is not followed by the definition"
Markus Armbruster64d60332017-10-02 16:13:34 +0200330 % doc.symbol)
Michael Roth0f923be2011-07-19 14:50:39 -0500331
Markus Armbrustere04dea82017-03-15 13:56:50 +0100332 def _include(self, include, info, base_dir, previously_included):
333 incl_abs_fname = os.path.join(base_dir, include)
334 # catch inclusion cycle
335 inf = info
336 while inf:
337 if incl_abs_fname == os.path.abspath(inf['file']):
338 raise QAPISemError(info, "Inclusion loop for %s" % include)
339 inf = inf['parent']
340
341 # skip multiple include of the same file
342 if incl_abs_fname in previously_included:
343 return
344 try:
345 fobj = open(incl_abs_fname, 'r')
346 except IOError as e:
347 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
348 exprs_include = QAPISchemaParser(fobj, previously_included, info)
349 self.exprs.extend(exprs_include.exprs)
350 self.docs.extend(exprs_include.docs)
351
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100352 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100353 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100354 if name == 'doc-required':
355 if not isinstance(value, bool):
356 raise QAPISemError(info,
357 "Pragma 'doc-required' must be boolean")
358 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100359 elif name == 'returns-whitelist':
360 if (not isinstance(value, list)
361 or any([not isinstance(elt, str) for elt in value])):
362 raise QAPISemError(info,
363 "Pragma returns-whitelist must be"
364 " a list of strings")
365 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100366 elif name == 'name-case-whitelist':
367 if (not isinstance(value, list)
368 or any([not isinstance(elt, str) for elt in value])):
369 raise QAPISemError(info,
370 "Pragma name-case-whitelist must be"
371 " a list of strings")
372 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100373 else:
374 raise QAPISemError(info, "Unknown pragma '%s'" % name)
375
Marc-André Lureau3313b612017-01-13 15:41:29 +0100376 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200377 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200378 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200379 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200380 self.cursor += 1
381 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500382
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200383 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100384 if self.src[self.cursor] == '#':
385 # Start of doc comment
386 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200387 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100388 if not skip_comment:
389 self.val = self.src[self.pos:self.cursor]
390 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100391 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200392 return
393 elif self.tok == "'":
394 string = ''
395 esc = False
396 while True:
397 ch = self.src[self.cursor]
398 self.cursor += 1
399 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100400 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200401 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600402 if ch == 'b':
403 string += '\b'
404 elif ch == 'f':
405 string += '\f'
406 elif ch == 'n':
407 string += '\n'
408 elif ch == 'r':
409 string += '\r'
410 elif ch == 't':
411 string += '\t'
412 elif ch == 'u':
413 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600414 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600415 ch = self.src[self.cursor]
416 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100417 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100418 raise QAPIParseError(self,
419 '\\u escape needs 4 '
420 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600421 value = (value << 4) + int(ch, 16)
422 # If Python 2 and 3 didn't disagree so much on
423 # how to handle Unicode, then we could allow
424 # Unicode string defaults. But most of QAPI is
425 # ASCII-only, so we aren't losing much for now.
426 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100427 raise QAPIParseError(self,
428 'For now, \\u escape '
429 'only supports non-zero '
430 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600431 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100432 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600433 string += ch
434 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100435 raise QAPIParseError(self,
436 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200437 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100438 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200439 esc = True
440 elif ch == "'":
441 self.val = string
442 return
443 else:
444 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100445 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200446 self.val = True
447 self.cursor += 3
448 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100449 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200450 self.val = False
451 self.cursor += 4
452 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100453 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200454 self.val = None
455 self.cursor += 3
456 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200457 elif self.tok == '\n':
458 if self.cursor == len(self.src):
459 self.tok = None
460 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800461 self.line += 1
462 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200463 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100464 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500465
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200466 def get_members(self):
467 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200468 if self.tok == '}':
469 self.accept()
470 return expr
471 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100472 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200473 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200474 key = self.val
475 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200476 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100477 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200478 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800479 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100480 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200481 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200482 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200483 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200484 return expr
485 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100486 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200487 self.accept()
488 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100489 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500490
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200491 def get_values(self):
492 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200493 if self.tok == ']':
494 self.accept()
495 return expr
Eric Blake437db252015-09-29 16:21:02 -0600496 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100497 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
498 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200499 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200500 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200501 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200502 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200503 return expr
504 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100505 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200506 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500507
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200508 def get_expr(self, nested):
509 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100510 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200511 if self.tok == '{':
512 self.accept()
513 expr = self.get_members()
514 elif self.tok == '[':
515 self.accept()
516 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600517 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200518 expr = self.val
519 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200520 else:
Markus Armbruster012b1262017-03-15 13:57:36 +0100521 raise QAPIParseError(self, 'Expected "{", "[", string, '
522 'boolean or "null"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200523 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200524
Marc-André Lureau3313b612017-01-13 15:41:29 +0100525 def get_doc(self, info):
526 if self.val != '##':
527 raise QAPIParseError(self, "Junk after '##' at start of "
528 "documentation comment")
529
530 doc = QAPIDoc(self, info)
531 self.accept(False)
532 while self.tok == '#':
533 if self.val.startswith('##'):
534 # End of doc comment
535 if self.val != '##':
536 raise QAPIParseError(self, "Junk after '##' at end of "
537 "documentation comment")
Markus Armbruster4ea71482017-03-15 13:57:23 +0100538 doc.end_comment()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100539 self.accept()
540 return doc
541 else:
542 doc.append(self.val)
543 self.accept(False)
544
545 raise QAPIParseError(self, "Documentation comment must end with '##'")
546
547
Markus Armbruster00e4b282015-06-10 10:04:36 +0200548#
549# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200550# TODO fold into QAPISchema
551# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200552#
553
Eric Blake437db252015-09-29 16:21:02 -0600554
Eric Blake14f00c62016-03-03 09:16:43 -0700555def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600556 if isinstance(base, dict):
557 return base
Markus Armbrustered285bf2017-03-15 13:57:32 +0100558 base_struct_define = struct_types.get(base)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800559 if not base_struct_define:
560 return None
561 return base_struct_define['data']
562
Eric Blake437db252015-09-29 16:21:02 -0600563
Eric Blake811d04f2015-05-04 09:05:10 -0600564# Return the qtype of an alternate branch, or None on error.
565def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600566 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600567 return builtin_types[qapi_type]
Markus Armbrustered285bf2017-03-15 13:57:32 +0100568 elif qapi_type in struct_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100569 return 'QTYPE_QDICT'
Markus Armbruster5f018442017-03-15 13:57:31 +0100570 elif qapi_type in enum_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100571 return 'QTYPE_QSTRING'
Markus Armbruster768562d2017-03-15 13:57:33 +0100572 elif qapi_type in union_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100573 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600574 return None
575
Eric Blake437db252015-09-29 16:21:02 -0600576
Wenchao Xiabceae762014-03-06 17:08:56 -0800577# Return the discriminator enum define if discriminator is specified as an
578# enum type, otherwise return None.
579def discriminator_find_enum_define(expr):
580 base = expr.get('base')
581 discriminator = expr.get('discriminator')
582
583 if not (discriminator and base):
584 return None
585
Eric Blake14f00c62016-03-03 09:16:43 -0700586 base_members = find_base_members(base)
587 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800588 return None
589
Eric Blake14f00c62016-03-03 09:16:43 -0700590 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800591 if not discriminator_type:
592 return None
593
Markus Armbruster5f018442017-03-15 13:57:31 +0100594 return enum_types.get(discriminator_type)
Wenchao Xiabceae762014-03-06 17:08:56 -0800595
Eric Blake437db252015-09-29 16:21:02 -0600596
Eric Blake59a92fe2015-11-18 01:52:56 -0700597# Names must be letters, numbers, -, and _. They must start with letter,
598# except for downstream extensions which must start with __RFQDN_.
599# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100600valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700601 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600602
603
Marc-André Lureau4148c292017-01-13 15:41:25 +0100604def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600605 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600606 global valid_name
607 membername = name
608
609 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100610 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600611 if name.startswith('*'):
612 membername = name[1:]
613 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100614 raise QAPISemError(info, "%s does not allow optional name '%s'"
615 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600616 # Enum members can start with a digit, because the generated C
617 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700618 if enum_member and membername[0].isdigit():
619 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600620 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
621 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600622 if not valid_name.match(membername) or \
623 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100624 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600625
Eric Blake437db252015-09-29 16:21:02 -0600626
627def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200628 global all_names
629 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200630 # FIXME should reject names that differ only in '_' vs. '.'
631 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200632 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100633 raise QAPISemError(info, "%s '%s' is already defined"
634 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600635 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100636 raise QAPISemError(info, "%s '%s' should not end in '%s'"
637 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200638 all_names[name] = meta
639
Eric Blake437db252015-09-29 16:21:02 -0600640
Marc-André Lureau4148c292017-01-13 15:41:25 +0100641def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600642 allow_dict=False, allow_optional=False,
643 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600644 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600645
646 if value is None:
647 return
648
Eric Blakedd883c62015-05-04 09:05:21 -0600649 # Check if array type for value is okay
650 if isinstance(value, list):
651 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100652 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600653 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100654 raise QAPISemError(info,
655 "%s: array type must contain single type name" %
656 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600657 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600658
659 # Check if type name for value is okay
660 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600661 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100662 raise QAPISemError(info, "%s uses unknown type '%s'"
663 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600664 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100665 raise QAPISemError(info, "%s cannot use %s type '%s'" %
666 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600667 return
668
Eric Blakedd883c62015-05-04 09:05:21 -0600669 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100670 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200671
672 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100673 raise QAPISemError(info,
674 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200675
676 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600677 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100678 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600679 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600680 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100681 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
682 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600683 # Todo: allow dictionaries to represent default values of
684 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100685 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200686 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600687 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600688 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600689
Eric Blake437db252015-09-29 16:21:02 -0600690
Marc-André Lureau4148c292017-01-13 15:41:25 +0100691def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600692 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600693 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600694
Eric Blakec8184082016-07-13 21:50:20 -0600695 args_meta = ['struct']
696 if boxed:
697 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100698 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600699 expr.get('data'), allow_dict=not boxed, allow_optional=True,
700 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600701 returns_meta = ['union', 'struct']
702 if name in returns_whitelist:
703 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100704 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200705 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200706 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600707
Eric Blake437db252015-09-29 16:21:02 -0600708
Marc-André Lureau4148c292017-01-13 15:41:25 +0100709def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600710 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600711 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600712
Eric Blakec8184082016-07-13 21:50:20 -0600713 meta = ['struct']
714 if boxed:
715 meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100716 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600717 expr.get('data'), allow_dict=not boxed, allow_optional=True,
718 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200719
Eric Blake437db252015-09-29 16:21:02 -0600720
Marc-André Lureau4148c292017-01-13 15:41:25 +0100721def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800722 name = expr['union']
723 base = expr.get('base')
724 discriminator = expr.get('discriminator')
725 members = expr['data']
726
Eric Blake811d04f2015-05-04 09:05:10 -0600727 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600728
729 # With no discriminator it is a simple union.
730 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600731 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600732 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600733 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100734 raise QAPISemError(info, "Simple union '%s' must not have a base" %
735 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600736
737 # Else, it's a flat union.
738 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600739 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100740 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600741 base, allow_dict=True, allow_optional=True,
742 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600743 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100744 raise QAPISemError(info, "Flat union '%s' must have a base"
745 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700746 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100747 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800748
Eric Blakec9e0a792015-05-04 09:05:22 -0600749 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600750 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100751 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600752 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700753 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800754 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100755 raise QAPISemError(info,
756 "Discriminator '%s' is not a member of base "
757 "struct '%s'"
758 % (discriminator, base))
Markus Armbruster5f018442017-03-15 13:57:31 +0100759 enum_define = enum_types.get(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600760 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800761 # Do not allow string discriminator
762 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100763 raise QAPISemError(info,
764 "Discriminator '%s' must be of enumeration "
765 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800766
Eric Blake02a57ae2016-02-17 23:48:16 -0700767 # Check every branch; don't allow an empty union
768 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100769 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800770 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100771 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600772
Eric Blake01cfbaa2015-12-01 22:20:58 -0700773 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100774 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200775 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600776
Eric Blake44bd1272015-05-04 09:05:08 -0600777 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700778 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600779 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100780 if key not in enum_define['data']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100781 raise QAPISemError(info,
782 "Discriminator value '%s' is not found in "
783 "enum '%s'"
Markus Armbrustereda43c62017-03-15 13:57:29 +0100784 % (key, enum_define['enum']))
Eric Blake44bd1272015-05-04 09:05:08 -0600785
Eric Blaked0b18232016-07-13 21:50:13 -0600786 # If discriminator is user-defined, ensure all values are covered
787 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100788 for value in enum_define['data']:
Eric Blaked0b18232016-07-13 21:50:13 -0600789 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100790 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
791 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600792
Eric Blake437db252015-09-29 16:21:02 -0600793
Marc-André Lureau4148c292017-01-13 15:41:25 +0100794def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600795 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600796 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600797 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600798
Eric Blake02a57ae2016-02-17 23:48:16 -0700799 # Check every branch; require at least two branches
800 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100801 raise QAPISemError(info,
802 "Alternate '%s' should have at least two branches "
803 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600804 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100805 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600806
Eric Blake811d04f2015-05-04 09:05:10 -0600807 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100808 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600809 value,
810 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600811 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700812 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100813 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
814 "type '%s'" % (name, key, value))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200815 conflicting = set([qtype])
816 if qtype == 'QTYPE_QSTRING':
817 enum_expr = enum_types.get(value)
818 if enum_expr:
819 for v in enum_expr['data']:
820 if v in ['on', 'off']:
821 conflicting.add('QTYPE_QBOOL')
822 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +0400823 conflicting.add('QTYPE_QNUM')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200824 else:
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +0400825 conflicting.add('QTYPE_QNUM')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200826 conflicting.add('QTYPE_QBOOL')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200827 for qt in conflicting:
Eduardo Habkostfda72ab2017-07-17 15:09:26 -0300828 if qt in types_seen:
829 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
830 "be distinguished from member '%s'"
831 % (name, key, types_seen[qt]))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200832 types_seen[qt] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800833
Eric Blake437db252015-09-29 16:21:02 -0600834
Marc-André Lureau4148c292017-01-13 15:41:25 +0100835def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600836 name = expr['enum']
837 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100838 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600839
840 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100841 raise QAPISemError(info,
842 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100843 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100844 raise QAPISemError(info,
845 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600846 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100847 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600848 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600849
Eric Blake437db252015-09-29 16:21:02 -0600850
Marc-André Lureau4148c292017-01-13 15:41:25 +0100851def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600852 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600853 members = expr['data']
854
Marc-André Lureau4148c292017-01-13 15:41:25 +0100855 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600856 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100857 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600858 allow_metas=['struct'])
859
Eric Blake437db252015-09-29 16:21:02 -0600860
Eric Blake0545f6b2015-05-04 09:05:15 -0600861def check_keys(expr_elem, meta, required, optional=[]):
862 expr = expr_elem['expr']
863 info = expr_elem['info']
864 name = expr[meta]
865 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100866 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600867 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600868 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600869 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100870 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
871 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600872 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100873 raise QAPISemError(info,
874 "'%s' of %s '%s' should only use false value"
875 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600876 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100877 raise QAPISemError(info,
878 "'%s' of %s '%s' should only use true value"
879 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600880 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600881 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100882 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
883 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600884
Eric Blake437db252015-09-29 16:21:02 -0600885
Markus Armbruster4d076d62015-06-10 08:55:21 +0200886def check_exprs(exprs):
887 global all_names
888
Markus Armbruster79470162017-03-15 13:57:21 +0100889 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200890 for builtin in builtin_types.keys():
891 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100892
893 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200894 for expr_elem in exprs:
895 expr = expr_elem['expr']
896 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100897 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100898
Markus Armbruster79470162017-03-15 13:57:21 +0100899 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100900 raise QAPISemError(info,
901 "Expression missing documentation comment")
902
Eric Blake437db252015-09-29 16:21:02 -0600903 if 'enum' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100904 meta = 'enum'
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100905 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster5f018442017-03-15 13:57:31 +0100906 enum_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600907 elif 'union' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100908 meta = 'union'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200909 check_keys(expr_elem, 'union', ['data'],
910 ['base', 'discriminator'])
Markus Armbruster768562d2017-03-15 13:57:33 +0100911 union_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600912 elif 'alternate' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100913 meta = 'alternate'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200914 check_keys(expr_elem, 'alternate', ['data'])
Eric Blake437db252015-09-29 16:21:02 -0600915 elif 'struct' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100916 meta = 'struct'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200917 check_keys(expr_elem, 'struct', ['data'], ['base'])
Markus Armbrustered285bf2017-03-15 13:57:32 +0100918 struct_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600919 elif 'command' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100920 meta = 'command'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200921 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600922 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Eric Blake437db252015-09-29 16:21:02 -0600923 elif 'event' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100924 meta = 'event'
Eric Blakec8184082016-07-13 21:50:20 -0600925 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200926 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100927 raise QAPISemError(expr_elem['info'],
928 "Expression is missing metatype")
Markus Armbruster6f053452017-03-15 13:57:30 +0100929 name = expr[meta]
930 add_name(name, info, meta)
Markus Armbruster79470162017-03-15 13:57:21 +0100931 if doc and doc.symbol != name:
932 raise QAPISemError(info, "Definition of '%s' follows documentation"
933 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200934
935 # Try again for hidden UnionKind enum
936 for expr_elem in exprs:
937 expr = expr_elem['expr']
Markus Armbrustereda43c62017-03-15 13:57:29 +0100938 if 'union' in expr and not discriminator_find_enum_define(expr):
939 name = '%sKind' % expr['union']
Eric Blake437db252015-09-29 16:21:02 -0600940 elif 'alternate' in expr:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100941 name = '%sKind' % expr['alternate']
942 else:
943 continue
Markus Armbruster5f018442017-03-15 13:57:31 +0100944 enum_types[name] = {'enum': name}
Markus Armbruster6f053452017-03-15 13:57:30 +0100945 add_name(name, info, 'enum', implicit=True)
Markus Armbruster4d076d62015-06-10 08:55:21 +0200946
947 # Validate that exprs make sense
948 for expr_elem in exprs:
949 expr = expr_elem['expr']
950 info = expr_elem['info']
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100951 doc = expr_elem.get('doc')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200952
Eric Blake437db252015-09-29 16:21:02 -0600953 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200954 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600955 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200956 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600957 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200958 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600959 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200960 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600961 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200962 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600963 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200964 check_event(expr, info)
965 else:
966 assert False, 'unexpected meta type'
967
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100968 if doc:
969 doc.check_expr(expr)
970
Markus Armbrusterac882192015-09-16 13:06:05 +0200971 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600972
Markus Armbrusterac882192015-09-16 13:06:05 +0200973
974#
975# Schema compiler frontend
976#
977
978class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100979 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +0200980 assert isinstance(name, str)
981 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -0600982 # For explicitly defined entities, info points to the (explicit)
983 # definition. For builtins (and their arrays), info is None.
984 # For implicitly defined entities, info points to a place that
985 # triggered the implicit definition (there may be more than one
986 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +0200987 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100988 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +0200989
Markus Armbrusterf51d8c32015-09-16 13:06:06 +0200990 def c_name(self):
991 return c_name(self.name)
992
Markus Armbrusterac882192015-09-16 13:06:05 +0200993 def check(self, schema):
994 pass
995
Eric Blake49823c42015-10-12 22:22:27 -0600996 def is_implicit(self):
997 return not self.info
998
Markus Armbruster3f7dc212015-09-16 13:06:07 +0200999 def visit(self, visitor):
1000 pass
1001
1002
1003class QAPISchemaVisitor(object):
1004 def visit_begin(self, schema):
1005 pass
1006
1007 def visit_end(self):
1008 pass
1009
Eric Blake25a0d9c2015-10-12 22:22:21 -06001010 def visit_needed(self, entity):
1011 # Default to visiting everything
1012 return True
1013
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001014 def visit_builtin_type(self, name, info, json_type):
1015 pass
1016
1017 def visit_enum_type(self, name, info, values, prefix):
1018 pass
1019
1020 def visit_array_type(self, name, info, element_type):
1021 pass
1022
1023 def visit_object_type(self, name, info, base, members, variants):
1024 pass
1025
Markus Armbruster39a18152015-09-16 13:06:28 +02001026 def visit_object_type_flat(self, name, info, members, variants):
1027 pass
1028
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001029 def visit_alternate_type(self, name, info, variants):
1030 pass
1031
1032 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001033 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001034 pass
1035
Eric Blake48825ca2016-07-13 21:50:19 -06001036 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001037 pass
1038
Markus Armbrusterac882192015-09-16 13:06:05 +02001039
1040class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001041 # Return the C type for common use.
1042 # For the types we commonly box, this is a pointer type.
1043 def c_type(self):
1044 pass
1045
1046 # Return the C type to be used in a parameter list.
1047 def c_param_type(self):
1048 return self.c_type()
1049
1050 # Return the C type to be used where we suppress boxing.
1051 def c_unboxed_type(self):
1052 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001053
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001054 def json_type(self):
1055 pass
1056
1057 def alternate_qtype(self):
1058 json2qtype = {
Markus Armbruster4d2d5c42017-06-26 19:25:14 +02001059 'null': 'QTYPE_QNULL',
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001060 'string': 'QTYPE_QSTRING',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001061 'number': 'QTYPE_QNUM',
1062 'int': 'QTYPE_QNUM',
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001063 'boolean': 'QTYPE_QBOOL',
1064 'object': 'QTYPE_QDICT'
1065 }
1066 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001067
Markus Armbruster691e0312017-03-15 13:57:14 +01001068 def doc_type(self):
1069 if self.is_implicit():
1070 return None
1071 return self.name
1072
Markus Armbrusterac882192015-09-16 13:06:05 +02001073
1074class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001075 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001076 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001077 assert not c_type or isinstance(c_type, str)
1078 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1079 'value')
1080 self._json_type_name = json_type
1081 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001082
1083 def c_name(self):
1084 return self.name
1085
Eric Blake4040d992016-03-17 16:48:28 -06001086 def c_type(self):
1087 return self._c_type_name
1088
1089 def c_param_type(self):
1090 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001091 return 'const ' + self._c_type_name
1092 return self._c_type_name
1093
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001094 def json_type(self):
1095 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001096
Markus Armbruster691e0312017-03-15 13:57:14 +01001097 def doc_type(self):
1098 return self.json_type()
1099
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001100 def visit(self, visitor):
1101 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1102
Markus Armbrusterac882192015-09-16 13:06:05 +02001103
1104class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001105 def __init__(self, name, info, doc, values, prefix):
1106 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001107 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001108 assert isinstance(v, QAPISchemaMember)
1109 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001110 assert prefix is None or isinstance(prefix, str)
1111 self.values = values
1112 self.prefix = prefix
1113
1114 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001115 seen = {}
1116 for v in self.values:
1117 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001118 if self.doc:
1119 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001120
Eric Blake99df5282015-10-12 22:22:32 -06001121 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001122 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1123 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001124
Eric Blake4040d992016-03-17 16:48:28 -06001125 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001126 return c_name(self.name)
1127
Eric Blake93bda4d2015-12-01 22:20:55 -07001128 def member_names(self):
1129 return [v.name for v in self.values]
1130
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001131 def json_type(self):
1132 return 'string'
1133
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001134 def visit(self, visitor):
1135 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001136 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001137
Markus Armbrusterac882192015-09-16 13:06:05 +02001138
1139class QAPISchemaArrayType(QAPISchemaType):
1140 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001141 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001142 assert isinstance(element_type, str)
1143 self._element_type_name = element_type
1144 self.element_type = None
1145
1146 def check(self, schema):
1147 self.element_type = schema.lookup_type(self._element_type_name)
1148 assert self.element_type
1149
Eric Blake99df5282015-10-12 22:22:32 -06001150 def is_implicit(self):
1151 return True
1152
Eric Blake4040d992016-03-17 16:48:28 -06001153 def c_type(self):
1154 return c_name(self.name) + pointer_suffix
1155
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001156 def json_type(self):
1157 return 'array'
1158
Markus Armbruster691e0312017-03-15 13:57:14 +01001159 def doc_type(self):
1160 elt_doc_type = self.element_type.doc_type()
1161 if not elt_doc_type:
1162 return None
1163 return 'array of ' + elt_doc_type
1164
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001165 def visit(self, visitor):
1166 visitor.visit_array_type(self.name, self.info, self.element_type)
1167
Markus Armbrusterac882192015-09-16 13:06:05 +02001168
1169class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001170 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001171 # struct has local_members, optional base, and no variants
1172 # flat union has base, variants, and no local_members
1173 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001174 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001175 assert base is None or isinstance(base, str)
1176 for m in local_members:
1177 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001178 m.set_owner(name)
1179 if variants is not None:
1180 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1181 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001182 self._base_name = base
1183 self.base = None
1184 self.local_members = local_members
1185 self.variants = variants
1186 self.members = None
1187
1188 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001189 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001190 raise QAPISemError(self.info,
1191 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001192 if self.members:
1193 return
1194 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001195 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001196 if self._base_name:
1197 self.base = schema.lookup_type(self._base_name)
1198 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001199 self.base.check(schema)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001200 self.base.check_clash(self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001201 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001202 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001203 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001204 if self.doc:
1205 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001206 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001207 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001208 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001209 assert self.variants.tag_member in self.members
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001210 self.variants.check_clash(self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001211 if self.doc:
1212 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001213
Eric Blake14f00c62016-03-03 09:16:43 -07001214 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001215 # and update seen to track the members seen so far. Report any errors
1216 # on behalf of info, which is not necessarily self.info
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001217 def check_clash(self, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001218 assert not self.variants # not implemented
1219 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001220 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001221
Eric Blake99df5282015-10-12 22:22:32 -06001222 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001223 # See QAPISchema._make_implicit_object_type(), as well as
1224 # _def_predefineds()
1225 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001226
Eric Blakeb6167702016-07-13 21:50:16 -06001227 def is_empty(self):
1228 assert self.members is not None
1229 return not self.members and not self.variants
1230
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001231 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001232 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001233 return QAPISchemaType.c_name(self)
1234
Eric Blake4040d992016-03-17 16:48:28 -06001235 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001236 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001237 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001238
Eric Blake4040d992016-03-17 16:48:28 -06001239 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001240 return c_name(self.name)
1241
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001242 def json_type(self):
1243 return 'object'
1244
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001245 def visit(self, visitor):
1246 visitor.visit_object_type(self.name, self.info,
1247 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001248 visitor.visit_object_type_flat(self.name, self.info,
1249 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001250
Markus Armbrusterac882192015-09-16 13:06:05 +02001251
Eric Blaked44f9ac2015-12-01 22:20:54 -07001252class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001253 role = 'member'
1254
Eric Blaked44f9ac2015-12-01 22:20:54 -07001255 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001256 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001257 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001258 self.owner = None
1259
1260 def set_owner(self, name):
1261 assert not self.owner
1262 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001263
Eric Blake27b60ab2015-11-18 01:52:51 -07001264 def check_clash(self, info, seen):
1265 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001266 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001267 raise QAPISemError(info,
1268 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001269 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001270 raise QAPISemError(info, "%s collides with %s" %
1271 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001272 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001273
Eric Blake88d4ef82015-11-18 01:52:50 -07001274 def _pretty_owner(self):
1275 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001276 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001277 # See QAPISchema._make_implicit_object_type() - reverse the
1278 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001279 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001280 if owner.endswith('-arg'):
1281 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001282 elif owner.endswith('-base'):
1283 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001284 else:
1285 assert owner.endswith('-wrapper')
1286 # Unreachable and not implemented
1287 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001288 if owner.endswith('Kind'):
1289 # See QAPISchema._make_implicit_enum_type()
1290 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001291 return '(%s of %s)' % (self.role, owner)
1292
1293 def describe(self):
1294 return "'%s' %s" % (self.name, self._pretty_owner())
1295
Markus Armbrusterac882192015-09-16 13:06:05 +02001296
Eric Blaked44f9ac2015-12-01 22:20:54 -07001297class QAPISchemaObjectTypeMember(QAPISchemaMember):
1298 def __init__(self, name, typ, optional):
1299 QAPISchemaMember.__init__(self, name)
1300 assert isinstance(typ, str)
1301 assert isinstance(optional, bool)
1302 self._type_name = typ
1303 self.type = None
1304 self.optional = optional
1305
1306 def check(self, schema):
1307 assert self.owner
1308 self.type = schema.lookup_type(self._type_name)
1309 assert self.type
1310
1311
Markus Armbrusterac882192015-09-16 13:06:05 +02001312class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001313 def __init__(self, tag_name, tag_member, variants):
1314 # Flat unions pass tag_name but not tag_member.
1315 # Simple unions and alternates pass tag_member but not tag_name.
1316 # After check(), tag_member is always set, and tag_name remains
1317 # a reliable witness of being used by a flat union.
1318 assert bool(tag_member) != bool(tag_name)
1319 assert (isinstance(tag_name, str) or
1320 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001321 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001322 for v in variants:
1323 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001324 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001325 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001326 self.variants = variants
1327
Eric Blake88d4ef82015-11-18 01:52:50 -07001328 def set_owner(self, name):
1329 for v in self.variants:
1330 v.set_owner(name)
1331
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001332 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001333 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001334 self.tag_member = seen[c_name(self._tag_name)]
1335 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001336 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1337 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001338 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001339 # Union names must match enum values; alternate names are
1340 # checked separately. Use 'seen' to tell the two apart.
1341 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001342 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001343 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001344 v.type.check(schema)
1345
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001346 def check_clash(self, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001347 for v in self.variants:
1348 # Reset seen map for each variant, since qapi names from one
1349 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001350 assert isinstance(v.type, QAPISchemaObjectType)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001351 v.type.check_clash(info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001352
Eric Blake437db252015-09-29 16:21:02 -06001353
Markus Armbrusterac882192015-09-16 13:06:05 +02001354class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001355 role = 'branch'
1356
Markus Armbrusterac882192015-09-16 13:06:05 +02001357 def __init__(self, name, typ):
1358 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1359
Markus Armbrusterac882192015-09-16 13:06:05 +02001360
1361class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001362 def __init__(self, name, info, doc, variants):
1363 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001364 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001365 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001366 variants.set_owner(name)
1367 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001368 self.variants = variants
1369
1370 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001371 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001372 # Not calling self.variants.check_clash(), because there's nothing
1373 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001374 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001375 # Alternate branch names have no relation to the tag enum values;
1376 # so we have to check for potential name collisions ourselves.
1377 seen = {}
1378 for v in self.variants.variants:
1379 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001380 if self.doc:
1381 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001382 if self.doc:
1383 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001384
Eric Blake4040d992016-03-17 16:48:28 -06001385 def c_type(self):
1386 return c_name(self.name) + pointer_suffix
1387
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001388 def json_type(self):
1389 return 'value'
1390
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001391 def visit(self, visitor):
1392 visitor.visit_alternate_type(self.name, self.info, self.variants)
1393
Eric Blakec8184082016-07-13 21:50:20 -06001394 def is_empty(self):
1395 return False
1396
Markus Armbrusterac882192015-09-16 13:06:05 +02001397
1398class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001399 def __init__(self, name, info, doc, arg_type, ret_type,
1400 gen, success_response, boxed):
1401 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001402 assert not arg_type or isinstance(arg_type, str)
1403 assert not ret_type or isinstance(ret_type, str)
1404 self._arg_type_name = arg_type
1405 self.arg_type = None
1406 self._ret_type_name = ret_type
1407 self.ret_type = None
1408 self.gen = gen
1409 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001410 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001411
1412 def check(self, schema):
1413 if self._arg_type_name:
1414 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001415 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1416 isinstance(self.arg_type, QAPISchemaAlternateType))
1417 self.arg_type.check(schema)
1418 if self.boxed:
1419 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001420 raise QAPISemError(self.info,
1421 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001422 else:
1423 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1424 assert not self.arg_type.variants
1425 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001426 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001427 if self._ret_type_name:
1428 self.ret_type = schema.lookup_type(self._ret_type_name)
1429 assert isinstance(self.ret_type, QAPISchemaType)
1430
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001431 def visit(self, visitor):
1432 visitor.visit_command(self.name, self.info,
1433 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001434 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001435
Markus Armbrusterac882192015-09-16 13:06:05 +02001436
1437class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001438 def __init__(self, name, info, doc, arg_type, boxed):
1439 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001440 assert not arg_type or isinstance(arg_type, str)
1441 self._arg_type_name = arg_type
1442 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001443 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001444
1445 def check(self, schema):
1446 if self._arg_type_name:
1447 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001448 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1449 isinstance(self.arg_type, QAPISchemaAlternateType))
1450 self.arg_type.check(schema)
1451 if self.boxed:
1452 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001453 raise QAPISemError(self.info,
1454 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001455 else:
1456 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1457 assert not self.arg_type.variants
1458 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001459 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001460
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001461 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001462 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001463
Markus Armbrusterac882192015-09-16 13:06:05 +02001464
1465class QAPISchema(object):
1466 def __init__(self, fname):
1467 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001468 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001469 self.exprs = check_exprs(parser.exprs)
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001470 self.docs = parser.docs
Eric Blake7618b912015-10-12 22:22:22 -06001471 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001472 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001473 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001474 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001475 self._def_exprs()
1476 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001477 except QAPIError as err:
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001478 print(err, file=sys.stderr)
Markus Armbrusterac882192015-09-16 13:06:05 +02001479 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001480
Markus Armbrusterac882192015-09-16 13:06:05 +02001481 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001482 # Only the predefined types are allowed to not have info
1483 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001484 assert ent.name not in self._entity_dict
1485 self._entity_dict[ent.name] = ent
1486
1487 def lookup_entity(self, name, typ=None):
1488 ent = self._entity_dict.get(name)
1489 if typ and not isinstance(ent, typ):
1490 return None
1491 return ent
1492
1493 def lookup_type(self, name):
1494 return self.lookup_entity(name, QAPISchemaType)
1495
Eric Blake861877a2016-03-17 16:48:36 -06001496 def _def_builtin_type(self, name, json_type, c_type):
1497 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001498 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1499 # qapi-types.h from a single .c, all arrays of builtins must be
1500 # declared in the first file whether or not they are used. Nicer
1501 # would be to use lazy instantiation, while figuring out how to
1502 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001503 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001504
1505 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001506 for t in [('str', 'string', 'char' + pointer_suffix),
1507 ('number', 'number', 'double'),
1508 ('int', 'int', 'int64_t'),
1509 ('int8', 'int', 'int8_t'),
1510 ('int16', 'int', 'int16_t'),
1511 ('int32', 'int', 'int32_t'),
1512 ('int64', 'int', 'int64_t'),
1513 ('uint8', 'int', 'uint8_t'),
1514 ('uint16', 'int', 'uint16_t'),
1515 ('uint32', 'int', 'uint32_t'),
1516 ('uint64', 'int', 'uint64_t'),
1517 ('size', 'int', 'uint64_t'),
1518 ('bool', 'boolean', 'bool'),
Markus Armbruster4d2d5c42017-06-26 19:25:14 +02001519 ('any', 'value', 'QObject' + pointer_suffix),
1520 ('null', 'null', 'QNull' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001521 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001522 self.the_empty_object_type = QAPISchemaObjectType(
1523 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001524 self._def_entity(self.the_empty_object_type)
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001525 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
Eric Blake93bda4d2015-12-01 22:20:55 -07001526 'qstring', 'qdict', 'qlist',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001527 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001528 self._def_entity(QAPISchemaEnumType('QType', None, None,
1529 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001530
Eric Blake93bda4d2015-12-01 22:20:55 -07001531 def _make_enum_members(self, values):
1532 return [QAPISchemaMember(v) for v in values]
1533
Eric Blake99df5282015-10-12 22:22:32 -06001534 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001535 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001536 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001537 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001538 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001539 return name
1540
Eric Blake99df5282015-10-12 22:22:32 -06001541 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001542 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001543 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001544 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001545 return name
1546
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001547 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001548 if not members:
1549 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001550 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001551 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001552 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001553 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001554 members, None))
1555 return name
1556
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001557 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001558 name = expr['enum']
1559 data = expr['data']
1560 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001561 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001562 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001563
Eric Blake99df5282015-10-12 22:22:32 -06001564 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001565 optional = False
1566 if name.startswith('*'):
1567 name = name[1:]
1568 optional = True
1569 if isinstance(typ, list):
1570 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001571 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001572 return QAPISchemaObjectTypeMember(name, typ, optional)
1573
Eric Blake99df5282015-10-12 22:22:32 -06001574 def _make_members(self, data, info):
1575 return [self._make_member(key, value, info)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001576 for (key, value) in data.items()]
Markus Armbrusterac882192015-09-16 13:06:05 +02001577
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001578 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001579 name = expr['struct']
1580 base = expr.get('base')
1581 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001582 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001583 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001584 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001585
1586 def _make_variant(self, case, typ):
1587 return QAPISchemaObjectTypeVariant(case, typ)
1588
Eric Blake99df5282015-10-12 22:22:32 -06001589 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001590 if isinstance(typ, list):
1591 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001592 typ = self._make_array_type(typ[0], info)
1593 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001594 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001595 return QAPISchemaObjectTypeVariant(case, typ)
1596
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001597 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001598 name = expr['union']
1599 data = expr['data']
1600 base = expr.get('base')
1601 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001602 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001603 if isinstance(base, dict):
1604 base = (self._make_implicit_object_type(
Markus Armbrusterc2613942017-03-15 13:57:35 +01001605 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001606 if tag_name:
1607 variants = [self._make_variant(key, value)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001608 for (key, value) in data.items()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001609 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001610 else:
Eric Blake99df5282015-10-12 22:22:32 -06001611 variants = [self._make_simple_variant(key, value, info)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001612 for (key, value) in data.items()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001613 typ = self._make_implicit_enum_type(name, info,
1614 [v.name for v in variants])
1615 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001616 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001617 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001618 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001619 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001620 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001621 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001622
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001623 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001624 name = expr['alternate']
1625 data = expr['data']
1626 variants = [self._make_variant(key, value)
Daniel P. Berrange2f848042018-01-16 13:42:05 +00001627 for (key, value) in data.items()]
Eric Blake0426d532015-12-01 22:20:48 -07001628 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001629 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001630 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001631 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001632 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001633 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001634
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001635 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001636 name = expr['command']
1637 data = expr.get('data')
1638 rets = expr.get('returns')
1639 gen = expr.get('gen', True)
1640 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001641 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001642 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001643 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001644 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001645 if isinstance(rets, list):
1646 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001647 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001648 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1649 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001650
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001651 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001652 name = expr['event']
1653 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001654 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001655 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001656 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001657 name, info, doc, 'arg', self._make_members(data, info))
1658 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001659
1660 def _def_exprs(self):
1661 for expr_elem in self.exprs:
1662 expr = expr_elem['expr']
1663 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001664 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001665 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001666 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001667 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001668 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001669 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001670 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001671 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001672 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001673 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001674 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001675 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001676 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001677 else:
1678 assert False
1679
1680 def check(self):
Daniel P. Berrangef7a53762018-01-16 13:42:10 +00001681 for (name, ent) in sorted(self._entity_dict.items()):
Markus Armbrusterac882192015-09-16 13:06:05 +02001682 ent.check(self)
1683
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001684 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001685 visitor.visit_begin(self)
1686 for (name, entity) in sorted(self._entity_dict.items()):
1687 if visitor.visit_needed(entity):
1688 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001689 visitor.visit_end()
1690
Markus Armbruster2caba362013-07-27 17:41:56 +02001691
Markus Armbruster00e4b282015-06-10 10:04:36 +02001692#
1693# Code generation helpers
1694#
1695
Michael Roth0f923be2011-07-19 14:50:39 -05001696def camel_case(name):
1697 new_name = ''
1698 first = True
1699 for ch in name:
1700 if ch in ['_', '-']:
1701 first = True
1702 elif first:
1703 new_name += ch.upper()
1704 first = False
1705 else:
1706 new_name += ch.lower()
1707 return new_name
1708
Eric Blake437db252015-09-29 16:21:02 -06001709
Markus Armbruster849bc532015-05-14 06:50:53 -06001710# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1711# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1712# ENUM24_Name -> ENUM24_NAME
1713def camel_to_upper(value):
1714 c_fun_str = c_name(value, False)
1715 if value.isupper():
1716 return c_fun_str
1717
1718 new_name = ''
1719 l = len(c_fun_str)
1720 for i in range(l):
1721 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001722 # When c is upper and no '_' appears before, do more checks
1723 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001724 if i < l - 1 and c_fun_str[i + 1].islower():
1725 new_name += '_'
1726 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001727 new_name += '_'
1728 new_name += c
1729 return new_name.lstrip('_').upper()
1730
Eric Blake437db252015-09-29 16:21:02 -06001731
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001732def c_enum_const(type_name, const_name, prefix=None):
1733 if prefix is not None:
1734 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001735 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001736
Daniel P. Berrange52c42722018-01-16 13:42:08 +00001737if hasattr(str, 'maketrans'):
1738 c_name_trans = str.maketrans('.-', '__')
1739else:
1740 c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001741
Eric Blake437db252015-09-29 16:21:02 -06001742
Eric Blakec6405b52015-05-14 06:50:55 -06001743# Map @name to a valid C identifier.
1744# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001745# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001746#
1747# Used for converting 'name' from a 'name':'type' qapi definition
1748# into a generated struct member, as well as converting type names
1749# into substrings of a generated C function name.
1750# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1751# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001752def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001753 # ANSI X3J11/88-090, 3.1.1
1754 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001755 'default', 'do', 'double', 'else', 'enum', 'extern',
1756 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1757 'return', 'short', 'signed', 'sizeof', 'static',
1758 'struct', 'switch', 'typedef', 'union', 'unsigned',
1759 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001760 # ISO/IEC 9899:1999, 6.4.1
1761 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1762 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001763 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1764 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001765 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1766 # excluding _.*
1767 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001768 # C++ ISO/IEC 14882:2003 2.11
1769 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1770 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1771 'namespace', 'new', 'operator', 'private', 'protected',
1772 'public', 'reinterpret_cast', 'static_cast', 'template',
1773 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1774 'using', 'virtual', 'wchar_t',
1775 # alternative representations
1776 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1777 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001778 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001779 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001780 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001781 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1782 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001783 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001784 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001785
Amos Kong05dfb262014-06-10 19:25:53 +08001786eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001787pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001788
Eric Blake437db252015-09-29 16:21:02 -06001789
Michael Roth0f923be2011-07-19 14:50:39 -05001790def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001791 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001792 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001793 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001794 return ret
1795
1796indent_level = 0
1797
Eric Blake437db252015-09-29 16:21:02 -06001798
Michael Roth0f923be2011-07-19 14:50:39 -05001799def push_indent(indent_amount=4):
1800 global indent_level
1801 indent_level += indent_amount
1802
Eric Blake437db252015-09-29 16:21:02 -06001803
Michael Roth0f923be2011-07-19 14:50:39 -05001804def pop_indent(indent_amount=4):
1805 global indent_level
1806 indent_level -= indent_amount
1807
Eric Blake437db252015-09-29 16:21:02 -06001808
Markus Armbruster77e703b2015-06-24 19:27:32 +02001809# Generate @code with @kwds interpolated.
1810# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001811def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001812 raw = code % kwds
1813 if indent_level:
1814 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001815 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001816 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001817 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001818 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001819 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001820
Eric Blake437db252015-09-29 16:21:02 -06001821
Michael Roth0f923be2011-07-19 14:50:39 -05001822def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001823 if code[0] == '\n':
1824 code = code[1:]
1825 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001826
Michael Roth0f923be2011-07-19 14:50:39 -05001827
1828def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001829 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001830
Eric Blake437db252015-09-29 16:21:02 -06001831
Michael Rothc0afa9c2013-05-10 17:46:00 -05001832def guardstart(name):
1833 return mcgen('''
1834
1835#ifndef %(name)s
1836#define %(name)s
1837
1838''',
1839 name=guardname(name))
1840
Eric Blake437db252015-09-29 16:21:02 -06001841
Michael Rothc0afa9c2013-05-10 17:46:00 -05001842def guardend(name):
1843 return mcgen('''
1844
1845#endif /* %(name)s */
1846
1847''',
1848 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001849
Eric Blake437db252015-09-29 16:21:02 -06001850
Markus Armbrustere98859a2015-09-16 13:06:16 +02001851def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001852 ret = mcgen('''
1853
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001854const QEnumLookup %(c_name)s_lookup = {
1855 .array = (const char *const[]) {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001856''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001857 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001858 for value in values:
1859 index = c_enum_const(name, value, prefix)
1860 ret += mcgen('''
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001861 [%(index)s] = "%(value)s",
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001862''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001863 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001864
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001865 ret += mcgen('''
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001866 },
1867 .size = %(max_index)s
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001868};
1869''',
Marc-André Lureauebf677c2017-08-24 10:46:11 +02001870 max_index=c_enum_const(name, '_MAX', prefix))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001871 return ret
1872
Eric Blake437db252015-09-29 16:21:02 -06001873
Markus Armbrustere98859a2015-09-16 13:06:16 +02001874def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001875 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001876 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001877
1878 ret = mcgen('''
1879
1880typedef enum %(c_name)s {
1881''',
1882 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001883
1884 i = 0
1885 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001886 ret += mcgen('''
1887 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001888''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001889 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001890 i=i)
1891 i += 1
1892
Markus Armbrustere98859a2015-09-16 13:06:16 +02001893 ret += mcgen('''
1894} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001895''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001896 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001897
Markus Armbrustere98859a2015-09-16 13:06:16 +02001898 ret += mcgen('''
1899
Markus Armbruster5b5f8252017-08-24 10:46:07 +02001900#define %(c_name)s_str(val) \\
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001901 qapi_enum_lookup(&%(c_name)s_lookup, (val))
Markus Armbruster5b5f8252017-08-24 10:46:07 +02001902
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001903extern const QEnumLookup %(c_name)s_lookup;
Markus Armbrustere98859a2015-09-16 13:06:16 +02001904''',
1905 c_name=c_name(name))
1906 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001907
Eric Blake437db252015-09-29 16:21:02 -06001908
Marc-André Lureau086ee7a2017-06-01 16:41:41 +04001909def build_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001910 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001911 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001912 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001913 ret = ''
1914 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001915 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001916 ret += '%s arg' % arg_type.c_param_type()
1917 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001918 else:
1919 assert not arg_type.variants
1920 for memb in arg_type.members:
1921 ret += sep
1922 sep = ', '
1923 if memb.optional:
1924 ret += 'bool has_%s, ' % c_name(memb.name)
1925 ret += '%s %s' % (memb.type.c_param_type(),
1926 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001927 if extra:
1928 ret += sep + extra
1929 return ret
1930
Eric Blake1f353342015-09-29 16:21:13 -06001931
Markus Armbruster00e4b282015-06-10 10:04:36 +02001932#
1933# Common command line parsing
1934#
1935
Eric Blake437db252015-09-29 16:21:02 -06001936
Markus Armbrusteref801a92017-03-15 13:57:08 +01001937def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001938
1939 try:
1940 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001941 'chp:o:' + extra_options,
1942 ['source', 'header', 'prefix=',
1943 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001944 except getopt.GetoptError as err:
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001945 print("%s: %s" % (sys.argv[0], str(err)), file=sys.stderr)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001946 sys.exit(1)
1947
Markus Armbrusteref801a92017-03-15 13:57:08 +01001948 output_dir = ''
1949 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001950 do_c = False
1951 do_h = False
1952 extra_opts = []
1953
1954 for oa in opts:
1955 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001956 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001957 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001958 if match.end() != len(a):
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001959 print("%s: 'funny character '%s' in argument of --prefix" \
1960 % (sys.argv[0], a[match.end()]), file=sys.stderr)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001961 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001962 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001963 elif o in ('-o', '--output-dir'):
1964 output_dir = a + '/'
1965 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001966 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001967 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001968 do_h = True
1969 else:
1970 extra_opts.append(oa)
1971
1972 if not do_c and not do_h:
1973 do_c = True
1974 do_h = True
1975
Markus Armbruster16d80f62015-04-02 13:32:16 +02001976 if len(args) != 1:
Daniel P. Berrangeef9d9102018-01-16 13:42:04 +00001977 print("%s: need exactly one argument" % sys.argv[0], file=sys.stderr)
Markus Armbrusterb4540962015-04-02 13:17:34 +02001978 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02001979 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001980
Markus Armbruster54414042015-06-09 16:22:45 +02001981 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001982
Markus Armbruster00e4b282015-06-10 10:04:36 +02001983#
1984# Generate output files with boilerplate
1985#
1986
Eric Blake437db252015-09-29 16:21:02 -06001987
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001988def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1989 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001990 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001991 c_file = output_dir + prefix + c_file
1992 h_file = output_dir + prefix + h_file
1993
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001994 if output_dir:
1995 try:
1996 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01001997 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001998 if e.errno != errno.EEXIST:
1999 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002000
2001 def maybe_open(really, name, opt):
2002 if really:
2003 return open(name, opt)
2004 else:
Daniel P. Berrange5f90af82018-01-16 13:42:07 +00002005 return StringIO()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002006
2007 fdef = maybe_open(do_c, c_file, 'w')
2008 fdecl = maybe_open(do_h, h_file, 'w')
2009
2010 fdef.write(mcgen('''
2011/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2012%(comment)s
2013''',
Eric Blake437db252015-09-29 16:21:02 -06002014 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002015
2016 fdecl.write(mcgen('''
2017/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2018%(comment)s
2019#ifndef %(guard)s
2020#define %(guard)s
2021
2022''',
Eric Blake437db252015-09-29 16:21:02 -06002023 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002024
2025 return (fdef, fdecl)
2026
Eric Blake437db252015-09-29 16:21:02 -06002027
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002028def close_output(fdef, fdecl):
2029 fdecl.write('''
2030#endif
2031''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002032 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002033 fdef.close()