blob: 43a54bf40fcf0ba5cdf55a5b8ba25f0c9e265740 [file] [log] [blame]
Michael Roth0f923be2011-07-19 14:50:39 -05001#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
Eric Blakee4083112016-01-29 06:48:41 -07005# Copyright (c) 2013-2016 Red Hat Inc.
Michael Roth0f923be2011-07-19 14:50:39 -05006#
7# Authors:
8# Anthony Liguori <aliguori@us.ibm.com>
Markus Armbrusterc7a3f252013-07-27 17:41:55 +02009# Markus Armbruster <armbru@redhat.com>
Michael Roth0f923be2011-07-19 14:50:39 -050010#
Markus Armbruster678e48a2014-03-01 08:40:34 +010011# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
Michael Roth0f923be2011-07-19 14:50:39 -050013
Markus Armbruster12f8e1b2015-04-02 14:46:39 +020014import errno
Markus Armbruster2114f5a2015-04-02 13:12:21 +020015import getopt
Lluís Vilanova33aaad52014-05-02 15:52:35 +020016import os
Markus Armbrusterc2613942017-03-15 13:57:35 +010017import re
Markus Armbruster47299262015-05-14 06:50:47 -060018import string
Markus Armbrusterc2613942017-03-15 13:57:35 +010019import sys
20from ordereddict import OrderedDict
Michael Roth0f923be2011-07-19 14:50:39 -050021
Eric Blakeb52c4b92015-05-04 09:05:00 -060022builtin_types = {
Markus Armbruster4d2d5c42017-06-26 19:25:14 +020023 'null': 'QTYPE_QNULL',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020024 'str': 'QTYPE_QSTRING',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +040025 'int': 'QTYPE_QNUM',
26 'number': 'QTYPE_QNUM',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020027 'bool': 'QTYPE_QBOOL',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +040028 'int8': 'QTYPE_QNUM',
29 'int16': 'QTYPE_QNUM',
30 'int32': 'QTYPE_QNUM',
31 'int64': 'QTYPE_QNUM',
32 'uint8': 'QTYPE_QNUM',
33 'uint16': 'QTYPE_QNUM',
34 'uint32': 'QTYPE_QNUM',
35 'uint64': 'QTYPE_QNUM',
36 'size': 'QTYPE_QNUM',
Eric Blake1310a3d2015-12-01 22:20:46 -070037 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070038 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020039}
40
Markus Armbrusterbc52d032017-03-15 13:56:51 +010041# Are documentation comments required?
42doc_required = False
43
Eric Blake10d4d992015-05-04 09:05:23 -060044# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010045returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060046
Eric Blake893e1f22015-12-01 22:20:57 -070047# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010048name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070049
Markus Armbruster5f018442017-03-15 13:57:31 +010050enum_types = {}
Markus Armbrustered285bf2017-03-15 13:57:32 +010051struct_types = {}
Markus Armbruster768562d2017-03-15 13:57:33 +010052union_types = {}
Eric Blake4dc2e692015-05-04 09:05:17 -060053all_names = {}
54
Markus Armbruster00e4b282015-06-10 10:04:36 +020055#
56# Parsing the schema into expressions
57#
58
Eric Blake437db252015-09-29 16:21:02 -060059
Lluís Vilanovaa719a272014-05-07 20:46:15 +020060def error_path(parent):
Markus Armbrusteref801a92017-03-15 13:57:08 +010061 res = ''
Lluís Vilanovaa719a272014-05-07 20:46:15 +020062 while parent:
Markus Armbrusteref801a92017-03-15 13:57:08 +010063 res = ('In file included from %s:%d:\n' % (parent['file'],
Lluís Vilanovaa719a272014-05-07 20:46:15 +020064 parent['line'])) + res
65 parent = parent['parent']
66 return res
67
Eric Blake437db252015-09-29 16:21:02 -060068
Marc-André Lureau4148c292017-01-13 15:41:25 +010069class QAPIError(Exception):
70 def __init__(self, fname, line, col, incl_info, msg):
Eric Blake59b00542015-09-29 16:21:01 -060071 Exception.__init__(self)
Marc-André Lureau4148c292017-01-13 15:41:25 +010072 self.fname = fname
73 self.line = line
74 self.col = col
75 self.info = incl_info
Markus Armbruster2caba362013-07-27 17:41:56 +020076 self.msg = msg
Marc-André Lureau4148c292017-01-13 15:41:25 +010077
78 def __str__(self):
Markus Armbrusteref801a92017-03-15 13:57:08 +010079 loc = '%s:%d' % (self.fname, self.line)
Marc-André Lureau4148c292017-01-13 15:41:25 +010080 if self.col is not None:
Markus Armbrusteref801a92017-03-15 13:57:08 +010081 loc += ':%s' % self.col
82 return error_path(self.info) + '%s: %s' % (loc, self.msg)
Marc-André Lureau4148c292017-01-13 15:41:25 +010083
84
85class QAPIParseError(QAPIError):
86 def __init__(self, parser, msg):
87 col = 1
88 for ch in parser.src[parser.line_pos:parser.pos]:
Wenchao Xia515b9432014-03-04 18:44:33 -080089 if ch == '\t':
Marc-André Lureau4148c292017-01-13 15:41:25 +010090 col = (col + 7) % 8 + 1
Markus Armbruster2caba362013-07-27 17:41:56 +020091 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +010092 col += 1
93 QAPIError.__init__(self, parser.fname, parser.line, col,
94 parser.incl_info, msg)
Markus Armbruster2caba362013-07-27 17:41:56 +020095
Eric Blake437db252015-09-29 16:21:02 -060096
Marc-André Lureau4148c292017-01-13 15:41:25 +010097class QAPISemError(QAPIError):
98 def __init__(self, info, msg):
99 QAPIError.__init__(self, info['file'], info['line'], None,
100 info['parent'], msg)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800101
Eric Blake437db252015-09-29 16:21:02 -0600102
Marc-André Lureau3313b612017-01-13 15:41:29 +0100103class QAPIDoc(object):
104 class Section(object):
105 def __init__(self, name=None):
106 # optional section name (argument/member or section name)
107 self.name = name
108 # the list of lines for this section
Markus Armbruster09331fc2017-10-02 16:13:38 +0200109 self.text = ''
Marc-André Lureau3313b612017-01-13 15:41:29 +0100110
111 def append(self, line):
Markus Armbruster09331fc2017-10-02 16:13:38 +0200112 self.text += line.rstrip() + '\n'
Marc-André Lureau3313b612017-01-13 15:41:29 +0100113
114 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100115 def __init__(self, name):
116 QAPIDoc.Section.__init__(self, name)
117 self.member = None
118
119 def connect(self, member):
120 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100121
122 def __init__(self, parser, info):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200123 # self._parser is used to report errors with QAPIParseError. The
Marc-André Lureau3313b612017-01-13 15:41:29 +0100124 # resulting error position depends on the state of the parser.
125 # It happens to be the beginning of the comment. More or less
126 # servicable, but action at a distance.
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200127 self._parser = parser
Marc-André Lureau3313b612017-01-13 15:41:29 +0100128 self.info = info
129 self.symbol = None
130 self.body = QAPIDoc.Section()
131 # dict mapping parameter name to ArgSection
132 self.args = OrderedDict()
133 # a list of Section
134 self.sections = []
135 # the current section
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200136 self._section = self.body
Marc-André Lureau3313b612017-01-13 15:41:29 +0100137
138 def has_section(self, name):
139 """Return True if we have a section with this name."""
140 for i in self.sections:
141 if i.name == name:
142 return True
143 return False
144
145 def append(self, line):
146 """Parse a comment line and add it to the documentation."""
147 line = line[1:]
148 if not line:
149 self._append_freeform(line)
150 return
151
152 if line[0] != ' ':
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200153 raise QAPIParseError(self._parser, "Missing space after #")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100154 line = line[1:]
155
156 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
157 # recognized, and get silently treated as ordinary text
158 if self.symbol:
159 self._append_symbol_line(line)
Markus Armbruster09331fc2017-10-02 16:13:38 +0200160 elif not self.body.text and line.startswith('@'):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100161 if not line.endswith(':'):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200162 raise QAPIParseError(self._parser, "Line should end with :")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100163 self.symbol = line[1:-1]
164 # FIXME invalid names other than the empty string aren't flagged
165 if not self.symbol:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200166 raise QAPIParseError(self._parser, "Invalid name")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100167 else:
168 self._append_freeform(line)
169
Markus Armbruster4ea71482017-03-15 13:57:23 +0100170 def end_comment(self):
171 self._end_section()
172
Marc-André Lureau3313b612017-01-13 15:41:29 +0100173 def _append_symbol_line(self, line):
174 name = line.split(' ', 1)[0]
175
Markus Armbrusteref801a92017-03-15 13:57:08 +0100176 if name.startswith('@') and name.endswith(':'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100177 line = line[len(name)+1:]
178 self._start_args_section(name[1:-1])
Markus Armbrusteref801a92017-03-15 13:57:08 +0100179 elif name in ('Returns:', 'Since:',
Marc-André Lureau3313b612017-01-13 15:41:29 +0100180 # those are often singular or plural
Markus Armbrusteref801a92017-03-15 13:57:08 +0100181 'Note:', 'Notes:',
182 'Example:', 'Examples:',
183 'TODO:'):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100184 line = line[len(name)+1:]
185 self._start_section(name[:-1])
186
187 self._append_freeform(line)
188
189 def _start_args_section(self, name):
190 # FIXME invalid names other than the empty string aren't flagged
191 if not name:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200192 raise QAPIParseError(self._parser, "Invalid parameter name")
Marc-André Lureau3313b612017-01-13 15:41:29 +0100193 if name in self.args:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200194 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100195 "'%s' parameter name duplicated" % name)
196 if self.sections:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200197 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100198 "'@%s:' can't follow '%s' section"
199 % (name, self.sections[0].name))
Markus Armbruster4ea71482017-03-15 13:57:23 +0100200 self._end_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200201 self._section = QAPIDoc.ArgSection(name)
202 self.args[name] = self._section
Marc-André Lureau3313b612017-01-13 15:41:29 +0100203
Markus Armbrusterfc3f0df2017-10-02 16:13:37 +0200204 def _start_section(self, name=None):
Markus Armbrusteref801a92017-03-15 13:57:08 +0100205 if name in ('Returns', 'Since') and self.has_section(name):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200206 raise QAPIParseError(self._parser,
Marc-André Lureau3313b612017-01-13 15:41:29 +0100207 "Duplicated '%s' section" % name)
Markus Armbruster4ea71482017-03-15 13:57:23 +0100208 self._end_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200209 self._section = QAPIDoc.Section(name)
210 self.sections.append(self._section)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100211
Markus Armbruster4ea71482017-03-15 13:57:23 +0100212 def _end_section(self):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200213 if self._section:
214 text = self._section.text = self._section.text.strip()
215 if self._section.name and (not text or text.isspace()):
216 raise QAPIParseError(self._parser, "Empty doc section '%s'"
217 % self._section.name)
218 self._section = None
Markus Armbruster4ea71482017-03-15 13:57:23 +0100219
Marc-André Lureau3313b612017-01-13 15:41:29 +0100220 def _append_freeform(self, line):
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200221 in_arg = isinstance(self._section, QAPIDoc.ArgSection)
222 if (in_arg and self._section.text.endswith('\n\n')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100223 and line and not line[0].isspace()):
224 self._start_section()
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200225 if (in_arg or not self._section.name
226 or not self._section.name.startswith('Example')):
Marc-André Lureau3313b612017-01-13 15:41:29 +0100227 line = line.strip()
Markus Armbruster2d433232017-03-15 13:57:22 +0100228 match = re.match(r'(@\S+:)', line)
229 if match:
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200230 raise QAPIParseError(self._parser,
Markus Armbruster2d433232017-03-15 13:57:22 +0100231 "'%s' not allowed in free-form documentation"
232 % match.group(1))
Markus Armbruster8cbf1a52017-10-02 16:13:40 +0200233 self._section.append(line)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100234
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100235 def connect_member(self, member):
236 if member.name not in self.args:
237 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100238 self.args[member.name] = QAPIDoc.ArgSection(member.name)
239 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100240
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100241 def check_expr(self, expr):
242 if self.has_section('Returns') and 'command' not in expr:
243 raise QAPISemError(self.info,
244 "'Returns:' is only valid for commands")
245
Markus Armbruster816a57c2017-03-15 13:57:26 +0100246 def check(self):
247 bogus = [name for name, section in self.args.iteritems()
248 if not section.member]
249 if bogus:
250 raise QAPISemError(
251 self.info,
252 "The following documented members are not in "
253 "the declaration: %s" % ", ".join(bogus))
254
Marc-André Lureau3313b612017-01-13 15:41:29 +0100255
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200256class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500257
Eric Blake437db252015-09-29 16:21:02 -0600258 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200259 abs_fname = os.path.abspath(fp.name)
Markus Armbruster2281d002017-10-02 16:13:33 +0200260 self.fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200261 previously_included.append(abs_fname)
262 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200263 self.src = fp.read()
264 if self.src == '' or self.src[-1] != '\n':
265 self.src += '\n'
266 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800267 self.line = 1
268 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200269 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100270 self.docs = []
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200271 self.accept()
Markus Armbruster64d60332017-10-02 16:13:34 +0200272 cur_doc = None
Michael Roth0f923be2011-07-19 14:50:39 -0500273
Eric Blake437db252015-09-29 16:21:02 -0600274 while self.tok is not None:
Markus Armbruster2281d002017-10-02 16:13:33 +0200275 info = {'file': self.fname, 'line': self.line,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100276 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100277 if self.tok == '#':
Markus Armbruster64d60332017-10-02 16:13:34 +0200278 self.reject_expr_doc(cur_doc)
279 cur_doc = self.get_doc(info)
280 self.docs.append(cur_doc)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100281 continue
282
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200283 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100284 if 'include' in expr:
Markus Armbruster64d60332017-10-02 16:13:34 +0200285 self.reject_expr_doc(cur_doc)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200286 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100287 raise QAPISemError(info, "Invalid 'include' directive")
Markus Armbrusteref801a92017-03-15 13:57:08 +0100288 include = expr['include']
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200289 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100290 raise QAPISemError(info,
291 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100292 self._include(include, info, os.path.dirname(abs_fname),
293 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100294 elif "pragma" in expr:
Markus Armbruster64d60332017-10-02 16:13:34 +0200295 self.reject_expr_doc(cur_doc)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100296 if len(expr) != 1:
297 raise QAPISemError(info, "Invalid 'pragma' directive")
298 pragma = expr['pragma']
299 if not isinstance(pragma, dict):
300 raise QAPISemError(
301 info, "Value of 'pragma' must be a dictionary")
302 for name, value in pragma.iteritems():
303 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200304 else:
305 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100306 'info': info}
Markus Armbruster64d60332017-10-02 16:13:34 +0200307 if cur_doc:
308 if not cur_doc.symbol:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100309 raise QAPISemError(
Markus Armbruster64d60332017-10-02 16:13:34 +0200310 cur_doc.info, "Expression documentation required")
311 expr_elem['doc'] = cur_doc
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200312 self.exprs.append(expr_elem)
Markus Armbruster64d60332017-10-02 16:13:34 +0200313 cur_doc = None
314 self.reject_expr_doc(cur_doc)
Markus Armbrustere7823a22017-03-15 13:57:20 +0100315
Markus Armbruster64d60332017-10-02 16:13:34 +0200316 @staticmethod
317 def reject_expr_doc(doc):
318 if doc and doc.symbol:
Markus Armbrustere7823a22017-03-15 13:57:20 +0100319 raise QAPISemError(
Markus Armbruster64d60332017-10-02 16:13:34 +0200320 doc.info,
Markus Armbrustere7823a22017-03-15 13:57:20 +0100321 "Documentation for '%s' is not followed by the definition"
Markus Armbruster64d60332017-10-02 16:13:34 +0200322 % doc.symbol)
Michael Roth0f923be2011-07-19 14:50:39 -0500323
Markus Armbrustere04dea82017-03-15 13:56:50 +0100324 def _include(self, include, info, base_dir, previously_included):
325 incl_abs_fname = os.path.join(base_dir, include)
326 # catch inclusion cycle
327 inf = info
328 while inf:
329 if incl_abs_fname == os.path.abspath(inf['file']):
330 raise QAPISemError(info, "Inclusion loop for %s" % include)
331 inf = inf['parent']
332
333 # skip multiple include of the same file
334 if incl_abs_fname in previously_included:
335 return
336 try:
337 fobj = open(incl_abs_fname, 'r')
338 except IOError as e:
339 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
340 exprs_include = QAPISchemaParser(fobj, previously_included, info)
341 self.exprs.extend(exprs_include.exprs)
342 self.docs.extend(exprs_include.docs)
343
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100344 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100345 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100346 if name == 'doc-required':
347 if not isinstance(value, bool):
348 raise QAPISemError(info,
349 "Pragma 'doc-required' must be boolean")
350 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100351 elif name == 'returns-whitelist':
352 if (not isinstance(value, list)
353 or any([not isinstance(elt, str) for elt in value])):
354 raise QAPISemError(info,
355 "Pragma returns-whitelist must be"
356 " a list of strings")
357 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100358 elif name == 'name-case-whitelist':
359 if (not isinstance(value, list)
360 or any([not isinstance(elt, str) for elt in value])):
361 raise QAPISemError(info,
362 "Pragma name-case-whitelist must be"
363 " a list of strings")
364 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100365 else:
366 raise QAPISemError(info, "Unknown pragma '%s'" % name)
367
Marc-André Lureau3313b612017-01-13 15:41:29 +0100368 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200369 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200370 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200371 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200372 self.cursor += 1
373 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500374
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200375 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100376 if self.src[self.cursor] == '#':
377 # Start of doc comment
378 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200379 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100380 if not skip_comment:
381 self.val = self.src[self.pos:self.cursor]
382 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100383 elif self.tok in '{}:,[]':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200384 return
385 elif self.tok == "'":
386 string = ''
387 esc = False
388 while True:
389 ch = self.src[self.cursor]
390 self.cursor += 1
391 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100392 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200393 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600394 if ch == 'b':
395 string += '\b'
396 elif ch == 'f':
397 string += '\f'
398 elif ch == 'n':
399 string += '\n'
400 elif ch == 'r':
401 string += '\r'
402 elif ch == 't':
403 string += '\t'
404 elif ch == 'u':
405 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600406 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600407 ch = self.src[self.cursor]
408 self.cursor += 1
Markus Armbrusteref801a92017-03-15 13:57:08 +0100409 if ch not in '0123456789abcdefABCDEF':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100410 raise QAPIParseError(self,
411 '\\u escape needs 4 '
412 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600413 value = (value << 4) + int(ch, 16)
414 # If Python 2 and 3 didn't disagree so much on
415 # how to handle Unicode, then we could allow
416 # Unicode string defaults. But most of QAPI is
417 # ASCII-only, so we aren't losing much for now.
418 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100419 raise QAPIParseError(self,
420 'For now, \\u escape '
421 'only supports non-zero '
422 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600423 string += chr(value)
Markus Armbrusteref801a92017-03-15 13:57:08 +0100424 elif ch in '\\/\'"':
Eric Blakea7f59662015-05-04 09:05:36 -0600425 string += ch
426 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100427 raise QAPIParseError(self,
428 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200429 esc = False
Markus Armbrusteref801a92017-03-15 13:57:08 +0100430 elif ch == '\\':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200431 esc = True
432 elif ch == "'":
433 self.val = string
434 return
435 else:
436 string += ch
Markus Armbrusteref801a92017-03-15 13:57:08 +0100437 elif self.src.startswith('true', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200438 self.val = True
439 self.cursor += 3
440 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100441 elif self.src.startswith('false', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200442 self.val = False
443 self.cursor += 4
444 return
Markus Armbrusteref801a92017-03-15 13:57:08 +0100445 elif self.src.startswith('null', self.pos):
Markus Armbrustere565d932015-06-10 08:24:58 +0200446 self.val = None
447 self.cursor += 3
448 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200449 elif self.tok == '\n':
450 if self.cursor == len(self.src):
451 self.tok = None
452 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800453 self.line += 1
454 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200455 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100456 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500457
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200458 def get_members(self):
459 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200460 if self.tok == '}':
461 self.accept()
462 return expr
463 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100464 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200465 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200466 key = self.val
467 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200468 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100469 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200470 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800471 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100472 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200473 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200474 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200475 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200476 return expr
477 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100478 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200479 self.accept()
480 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100481 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500482
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200483 def get_values(self):
484 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200485 if self.tok == ']':
486 self.accept()
487 return expr
Eric Blake437db252015-09-29 16:21:02 -0600488 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100489 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
490 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200491 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200492 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200493 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200494 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200495 return expr
496 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100497 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200498 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500499
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200500 def get_expr(self, nested):
501 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100502 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200503 if self.tok == '{':
504 self.accept()
505 expr = self.get_members()
506 elif self.tok == '[':
507 self.accept()
508 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600509 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200510 expr = self.val
511 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200512 else:
Markus Armbruster012b1262017-03-15 13:57:36 +0100513 raise QAPIParseError(self, 'Expected "{", "[", string, '
514 'boolean or "null"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200515 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200516
Marc-André Lureau3313b612017-01-13 15:41:29 +0100517 def get_doc(self, info):
518 if self.val != '##':
519 raise QAPIParseError(self, "Junk after '##' at start of "
520 "documentation comment")
521
522 doc = QAPIDoc(self, info)
523 self.accept(False)
524 while self.tok == '#':
525 if self.val.startswith('##'):
526 # End of doc comment
527 if self.val != '##':
528 raise QAPIParseError(self, "Junk after '##' at end of "
529 "documentation comment")
Markus Armbruster4ea71482017-03-15 13:57:23 +0100530 doc.end_comment()
Marc-André Lureau3313b612017-01-13 15:41:29 +0100531 self.accept()
532 return doc
533 else:
534 doc.append(self.val)
535 self.accept(False)
536
537 raise QAPIParseError(self, "Documentation comment must end with '##'")
538
539
Markus Armbruster00e4b282015-06-10 10:04:36 +0200540#
541# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200542# TODO fold into QAPISchema
543# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200544#
545
Eric Blake437db252015-09-29 16:21:02 -0600546
Eric Blake14f00c62016-03-03 09:16:43 -0700547def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600548 if isinstance(base, dict):
549 return base
Markus Armbrustered285bf2017-03-15 13:57:32 +0100550 base_struct_define = struct_types.get(base)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800551 if not base_struct_define:
552 return None
553 return base_struct_define['data']
554
Eric Blake437db252015-09-29 16:21:02 -0600555
Eric Blake811d04f2015-05-04 09:05:10 -0600556# Return the qtype of an alternate branch, or None on error.
557def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600558 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600559 return builtin_types[qapi_type]
Markus Armbrustered285bf2017-03-15 13:57:32 +0100560 elif qapi_type in struct_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100561 return 'QTYPE_QDICT'
Markus Armbruster5f018442017-03-15 13:57:31 +0100562 elif qapi_type in enum_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100563 return 'QTYPE_QSTRING'
Markus Armbruster768562d2017-03-15 13:57:33 +0100564 elif qapi_type in union_types:
Markus Armbrusteref801a92017-03-15 13:57:08 +0100565 return 'QTYPE_QDICT'
Eric Blake44bd1272015-05-04 09:05:08 -0600566 return None
567
Eric Blake437db252015-09-29 16:21:02 -0600568
Wenchao Xiabceae762014-03-06 17:08:56 -0800569# Return the discriminator enum define if discriminator is specified as an
570# enum type, otherwise return None.
571def discriminator_find_enum_define(expr):
572 base = expr.get('base')
573 discriminator = expr.get('discriminator')
574
575 if not (discriminator and base):
576 return None
577
Eric Blake14f00c62016-03-03 09:16:43 -0700578 base_members = find_base_members(base)
579 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800580 return None
581
Eric Blake14f00c62016-03-03 09:16:43 -0700582 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800583 if not discriminator_type:
584 return None
585
Markus Armbruster5f018442017-03-15 13:57:31 +0100586 return enum_types.get(discriminator_type)
Wenchao Xiabceae762014-03-06 17:08:56 -0800587
Eric Blake437db252015-09-29 16:21:02 -0600588
Eric Blake59a92fe2015-11-18 01:52:56 -0700589# Names must be letters, numbers, -, and _. They must start with letter,
590# except for downstream extensions which must start with __RFQDN_.
591# Dots are only valid in the downstream extension prefix.
Markus Armbruster0fe675a2017-03-15 13:57:07 +0100592valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
Eric Blake59a92fe2015-11-18 01:52:56 -0700593 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600594
595
Marc-André Lureau4148c292017-01-13 15:41:25 +0100596def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600597 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600598 global valid_name
599 membername = name
600
601 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100602 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600603 if name.startswith('*'):
604 membername = name[1:]
605 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100606 raise QAPISemError(info, "%s does not allow optional name '%s'"
607 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600608 # Enum members can start with a digit, because the generated C
609 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700610 if enum_member and membername[0].isdigit():
611 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600612 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
613 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600614 if not valid_name.match(membername) or \
615 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100616 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600617
Eric Blake437db252015-09-29 16:21:02 -0600618
619def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200620 global all_names
621 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200622 # FIXME should reject names that differ only in '_' vs. '.'
623 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200624 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100625 raise QAPISemError(info, "%s '%s' is already defined"
626 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600627 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100628 raise QAPISemError(info, "%s '%s' should not end in '%s'"
629 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200630 all_names[name] = meta
631
Eric Blake437db252015-09-29 16:21:02 -0600632
Marc-André Lureau4148c292017-01-13 15:41:25 +0100633def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600634 allow_dict=False, allow_optional=False,
635 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600636 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600637
638 if value is None:
639 return
640
Eric Blakedd883c62015-05-04 09:05:21 -0600641 # Check if array type for value is okay
642 if isinstance(value, list):
643 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100644 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600645 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100646 raise QAPISemError(info,
647 "%s: array type must contain single type name" %
648 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600649 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600650
651 # Check if type name for value is okay
652 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600653 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100654 raise QAPISemError(info, "%s uses unknown type '%s'"
655 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600656 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100657 raise QAPISemError(info, "%s cannot use %s type '%s'" %
658 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600659 return
660
Eric Blakedd883c62015-05-04 09:05:21 -0600661 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100662 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200663
664 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100665 raise QAPISemError(info,
666 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200667
668 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600669 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100670 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600671 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600672 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100673 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
674 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600675 # Todo: allow dictionaries to represent default values of
676 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100677 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200678 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600679 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600680 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600681
Eric Blake437db252015-09-29 16:21:02 -0600682
Marc-André Lureau4148c292017-01-13 15:41:25 +0100683def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600684 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600685 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600686
Eric Blakec8184082016-07-13 21:50:20 -0600687 args_meta = ['struct']
688 if boxed:
689 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100690 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600691 expr.get('data'), allow_dict=not boxed, allow_optional=True,
692 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600693 returns_meta = ['union', 'struct']
694 if name in returns_whitelist:
695 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100696 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200697 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200698 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600699
Eric Blake437db252015-09-29 16:21:02 -0600700
Marc-André Lureau4148c292017-01-13 15:41:25 +0100701def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600702 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600703 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600704
Eric Blakec8184082016-07-13 21:50:20 -0600705 meta = ['struct']
706 if boxed:
707 meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100708 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600709 expr.get('data'), allow_dict=not boxed, allow_optional=True,
710 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200711
Eric Blake437db252015-09-29 16:21:02 -0600712
Marc-André Lureau4148c292017-01-13 15:41:25 +0100713def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800714 name = expr['union']
715 base = expr.get('base')
716 discriminator = expr.get('discriminator')
717 members = expr['data']
718
Eric Blake811d04f2015-05-04 09:05:10 -0600719 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600720
721 # With no discriminator it is a simple union.
722 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600723 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600724 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600725 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100726 raise QAPISemError(info, "Simple union '%s' must not have a base" %
727 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600728
729 # Else, it's a flat union.
730 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600731 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100732 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600733 base, allow_dict=True, allow_optional=True,
734 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600735 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100736 raise QAPISemError(info, "Flat union '%s' must have a base"
737 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700738 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100739 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800740
Eric Blakec9e0a792015-05-04 09:05:22 -0600741 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600742 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100743 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600744 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700745 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800746 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100747 raise QAPISemError(info,
748 "Discriminator '%s' is not a member of base "
749 "struct '%s'"
750 % (discriminator, base))
Markus Armbruster5f018442017-03-15 13:57:31 +0100751 enum_define = enum_types.get(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600752 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800753 # Do not allow string discriminator
754 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100755 raise QAPISemError(info,
756 "Discriminator '%s' must be of enumeration "
757 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800758
Eric Blake02a57ae2016-02-17 23:48:16 -0700759 # Check every branch; don't allow an empty union
760 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100761 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800762 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100763 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600764
Eric Blake01cfbaa2015-12-01 22:20:58 -0700765 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100766 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200767 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600768
Eric Blake44bd1272015-05-04 09:05:08 -0600769 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700770 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600771 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100772 if key not in enum_define['data']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100773 raise QAPISemError(info,
774 "Discriminator value '%s' is not found in "
775 "enum '%s'"
Markus Armbrustereda43c62017-03-15 13:57:29 +0100776 % (key, enum_define['enum']))
Eric Blake44bd1272015-05-04 09:05:08 -0600777
Eric Blaked0b18232016-07-13 21:50:13 -0600778 # If discriminator is user-defined, ensure all values are covered
779 if enum_define:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100780 for value in enum_define['data']:
Eric Blaked0b18232016-07-13 21:50:13 -0600781 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100782 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
783 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600784
Eric Blake437db252015-09-29 16:21:02 -0600785
Marc-André Lureau4148c292017-01-13 15:41:25 +0100786def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600787 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600788 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600789 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600790
Eric Blake02a57ae2016-02-17 23:48:16 -0700791 # Check every branch; require at least two branches
792 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100793 raise QAPISemError(info,
794 "Alternate '%s' should have at least two branches "
795 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600796 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100797 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600798
Eric Blake811d04f2015-05-04 09:05:10 -0600799 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100800 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600801 value,
802 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600803 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700804 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100805 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
806 "type '%s'" % (name, key, value))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200807 conflicting = set([qtype])
808 if qtype == 'QTYPE_QSTRING':
809 enum_expr = enum_types.get(value)
810 if enum_expr:
811 for v in enum_expr['data']:
812 if v in ['on', 'off']:
813 conflicting.add('QTYPE_QBOOL')
814 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +0400815 conflicting.add('QTYPE_QNUM')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200816 else:
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +0400817 conflicting.add('QTYPE_QNUM')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200818 conflicting.add('QTYPE_QBOOL')
Markus Armbrusterc0644772017-05-22 18:42:15 +0200819 for qt in conflicting:
Eduardo Habkostfda72ab2017-07-17 15:09:26 -0300820 if qt in types_seen:
821 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
822 "be distinguished from member '%s'"
823 % (name, key, types_seen[qt]))
Markus Armbrusterc0644772017-05-22 18:42:15 +0200824 types_seen[qt] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800825
Eric Blake437db252015-09-29 16:21:02 -0600826
Marc-André Lureau4148c292017-01-13 15:41:25 +0100827def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600828 name = expr['enum']
829 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100830 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600831
832 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100833 raise QAPISemError(info,
834 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100835 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100836 raise QAPISemError(info,
837 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600838 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100839 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600840 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600841
Eric Blake437db252015-09-29 16:21:02 -0600842
Marc-André Lureau4148c292017-01-13 15:41:25 +0100843def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600844 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600845 members = expr['data']
846
Marc-André Lureau4148c292017-01-13 15:41:25 +0100847 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600848 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100849 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600850 allow_metas=['struct'])
851
Eric Blake437db252015-09-29 16:21:02 -0600852
Eric Blake0545f6b2015-05-04 09:05:15 -0600853def check_keys(expr_elem, meta, required, optional=[]):
854 expr = expr_elem['expr']
855 info = expr_elem['info']
856 name = expr[meta]
857 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100858 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600859 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600860 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600861 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100862 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
863 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600864 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100865 raise QAPISemError(info,
866 "'%s' of %s '%s' should only use false value"
867 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600868 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100869 raise QAPISemError(info,
870 "'%s' of %s '%s' should only use true value"
871 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600872 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600873 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100874 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
875 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600876
Eric Blake437db252015-09-29 16:21:02 -0600877
Markus Armbruster4d076d62015-06-10 08:55:21 +0200878def check_exprs(exprs):
879 global all_names
880
Markus Armbruster79470162017-03-15 13:57:21 +0100881 # Populate name table with names of built-in types
Markus Armbruster4d076d62015-06-10 08:55:21 +0200882 for builtin in builtin_types.keys():
883 all_names[builtin] = 'built-in'
Markus Armbruster79470162017-03-15 13:57:21 +0100884
885 # Learn the types and check for valid expression keys
Markus Armbruster4d076d62015-06-10 08:55:21 +0200886 for expr_elem in exprs:
887 expr = expr_elem['expr']
888 info = expr_elem['info']
Markus Armbruster79470162017-03-15 13:57:21 +0100889 doc = expr_elem.get('doc')
Marc-André Lureau3313b612017-01-13 15:41:29 +0100890
Markus Armbruster79470162017-03-15 13:57:21 +0100891 if not doc and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100892 raise QAPISemError(info,
893 "Expression missing documentation comment")
894
Eric Blake437db252015-09-29 16:21:02 -0600895 if 'enum' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100896 meta = 'enum'
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100897 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster5f018442017-03-15 13:57:31 +0100898 enum_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600899 elif 'union' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100900 meta = 'union'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200901 check_keys(expr_elem, 'union', ['data'],
902 ['base', 'discriminator'])
Markus Armbruster768562d2017-03-15 13:57:33 +0100903 union_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600904 elif 'alternate' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100905 meta = 'alternate'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200906 check_keys(expr_elem, 'alternate', ['data'])
Eric Blake437db252015-09-29 16:21:02 -0600907 elif 'struct' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100908 meta = 'struct'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200909 check_keys(expr_elem, 'struct', ['data'], ['base'])
Markus Armbrustered285bf2017-03-15 13:57:32 +0100910 struct_types[expr[meta]] = expr
Eric Blake437db252015-09-29 16:21:02 -0600911 elif 'command' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100912 meta = 'command'
Markus Armbruster4d076d62015-06-10 08:55:21 +0200913 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600914 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Eric Blake437db252015-09-29 16:21:02 -0600915 elif 'event' in expr:
Markus Armbruster6f053452017-03-15 13:57:30 +0100916 meta = 'event'
Eric Blakec8184082016-07-13 21:50:20 -0600917 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200918 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100919 raise QAPISemError(expr_elem['info'],
920 "Expression is missing metatype")
Markus Armbruster6f053452017-03-15 13:57:30 +0100921 name = expr[meta]
922 add_name(name, info, meta)
Markus Armbruster79470162017-03-15 13:57:21 +0100923 if doc and doc.symbol != name:
924 raise QAPISemError(info, "Definition of '%s' follows documentation"
925 " for '%s'" % (name, doc.symbol))
Markus Armbruster4d076d62015-06-10 08:55:21 +0200926
927 # Try again for hidden UnionKind enum
928 for expr_elem in exprs:
929 expr = expr_elem['expr']
Markus Armbrustereda43c62017-03-15 13:57:29 +0100930 if 'union' in expr and not discriminator_find_enum_define(expr):
931 name = '%sKind' % expr['union']
Eric Blake437db252015-09-29 16:21:02 -0600932 elif 'alternate' in expr:
Markus Armbrustereda43c62017-03-15 13:57:29 +0100933 name = '%sKind' % expr['alternate']
934 else:
935 continue
Markus Armbruster5f018442017-03-15 13:57:31 +0100936 enum_types[name] = {'enum': name}
Markus Armbruster6f053452017-03-15 13:57:30 +0100937 add_name(name, info, 'enum', implicit=True)
Markus Armbruster4d076d62015-06-10 08:55:21 +0200938
939 # Validate that exprs make sense
940 for expr_elem in exprs:
941 expr = expr_elem['expr']
942 info = expr_elem['info']
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100943 doc = expr_elem.get('doc')
Markus Armbruster4d076d62015-06-10 08:55:21 +0200944
Eric Blake437db252015-09-29 16:21:02 -0600945 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200946 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600947 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200948 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600949 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200950 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600951 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200952 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600953 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200954 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600955 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200956 check_event(expr, info)
957 else:
958 assert False, 'unexpected meta type'
959
Markus Armbrustera9f396b2017-03-15 13:57:27 +0100960 if doc:
961 doc.check_expr(expr)
962
Markus Armbrusterac882192015-09-16 13:06:05 +0200963 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600964
Markus Armbrusterac882192015-09-16 13:06:05 +0200965
966#
967# Schema compiler frontend
968#
969
970class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100971 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +0200972 assert isinstance(name, str)
973 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -0600974 # For explicitly defined entities, info points to the (explicit)
975 # definition. For builtins (and their arrays), info is None.
976 # For implicitly defined entities, info points to a place that
977 # triggered the implicit definition (there may be more than one
978 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +0200979 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100980 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +0200981
Markus Armbrusterf51d8c32015-09-16 13:06:06 +0200982 def c_name(self):
983 return c_name(self.name)
984
Markus Armbrusterac882192015-09-16 13:06:05 +0200985 def check(self, schema):
986 pass
987
Eric Blake49823c42015-10-12 22:22:27 -0600988 def is_implicit(self):
989 return not self.info
990
Markus Armbruster3f7dc212015-09-16 13:06:07 +0200991 def visit(self, visitor):
992 pass
993
994
995class QAPISchemaVisitor(object):
996 def visit_begin(self, schema):
997 pass
998
999 def visit_end(self):
1000 pass
1001
Eric Blake25a0d9c2015-10-12 22:22:21 -06001002 def visit_needed(self, entity):
1003 # Default to visiting everything
1004 return True
1005
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001006 def visit_builtin_type(self, name, info, json_type):
1007 pass
1008
1009 def visit_enum_type(self, name, info, values, prefix):
1010 pass
1011
1012 def visit_array_type(self, name, info, element_type):
1013 pass
1014
1015 def visit_object_type(self, name, info, base, members, variants):
1016 pass
1017
Markus Armbruster39a18152015-09-16 13:06:28 +02001018 def visit_object_type_flat(self, name, info, members, variants):
1019 pass
1020
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001021 def visit_alternate_type(self, name, info, variants):
1022 pass
1023
1024 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001025 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001026 pass
1027
Eric Blake48825ca2016-07-13 21:50:19 -06001028 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001029 pass
1030
Markus Armbrusterac882192015-09-16 13:06:05 +02001031
1032class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001033 # Return the C type for common use.
1034 # For the types we commonly box, this is a pointer type.
1035 def c_type(self):
1036 pass
1037
1038 # Return the C type to be used in a parameter list.
1039 def c_param_type(self):
1040 return self.c_type()
1041
1042 # Return the C type to be used where we suppress boxing.
1043 def c_unboxed_type(self):
1044 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001045
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001046 def json_type(self):
1047 pass
1048
1049 def alternate_qtype(self):
1050 json2qtype = {
Markus Armbruster4d2d5c42017-06-26 19:25:14 +02001051 'null': 'QTYPE_QNULL',
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001052 'string': 'QTYPE_QSTRING',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001053 'number': 'QTYPE_QNUM',
1054 'int': 'QTYPE_QNUM',
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001055 'boolean': 'QTYPE_QBOOL',
1056 'object': 'QTYPE_QDICT'
1057 }
1058 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001059
Markus Armbruster691e0312017-03-15 13:57:14 +01001060 def doc_type(self):
1061 if self.is_implicit():
1062 return None
1063 return self.name
1064
Markus Armbrusterac882192015-09-16 13:06:05 +02001065
1066class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001067 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001068 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001069 assert not c_type or isinstance(c_type, str)
1070 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1071 'value')
1072 self._json_type_name = json_type
1073 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001074
1075 def c_name(self):
1076 return self.name
1077
Eric Blake4040d992016-03-17 16:48:28 -06001078 def c_type(self):
1079 return self._c_type_name
1080
1081 def c_param_type(self):
1082 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001083 return 'const ' + self._c_type_name
1084 return self._c_type_name
1085
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001086 def json_type(self):
1087 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001088
Markus Armbruster691e0312017-03-15 13:57:14 +01001089 def doc_type(self):
1090 return self.json_type()
1091
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001092 def visit(self, visitor):
1093 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1094
Markus Armbrusterac882192015-09-16 13:06:05 +02001095
1096class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001097 def __init__(self, name, info, doc, values, prefix):
1098 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001099 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001100 assert isinstance(v, QAPISchemaMember)
1101 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001102 assert prefix is None or isinstance(prefix, str)
1103 self.values = values
1104 self.prefix = prefix
1105
1106 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001107 seen = {}
1108 for v in self.values:
1109 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001110 if self.doc:
1111 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001112
Eric Blake99df5282015-10-12 22:22:32 -06001113 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001114 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1115 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001116
Eric Blake4040d992016-03-17 16:48:28 -06001117 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001118 return c_name(self.name)
1119
Eric Blake93bda4d2015-12-01 22:20:55 -07001120 def member_names(self):
1121 return [v.name for v in self.values]
1122
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001123 def json_type(self):
1124 return 'string'
1125
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001126 def visit(self, visitor):
1127 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001128 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001129
Markus Armbrusterac882192015-09-16 13:06:05 +02001130
1131class QAPISchemaArrayType(QAPISchemaType):
1132 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001133 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001134 assert isinstance(element_type, str)
1135 self._element_type_name = element_type
1136 self.element_type = None
1137
1138 def check(self, schema):
1139 self.element_type = schema.lookup_type(self._element_type_name)
1140 assert self.element_type
1141
Eric Blake99df5282015-10-12 22:22:32 -06001142 def is_implicit(self):
1143 return True
1144
Eric Blake4040d992016-03-17 16:48:28 -06001145 def c_type(self):
1146 return c_name(self.name) + pointer_suffix
1147
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001148 def json_type(self):
1149 return 'array'
1150
Markus Armbruster691e0312017-03-15 13:57:14 +01001151 def doc_type(self):
1152 elt_doc_type = self.element_type.doc_type()
1153 if not elt_doc_type:
1154 return None
1155 return 'array of ' + elt_doc_type
1156
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001157 def visit(self, visitor):
1158 visitor.visit_array_type(self.name, self.info, self.element_type)
1159
Markus Armbrusterac882192015-09-16 13:06:05 +02001160
1161class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001162 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001163 # struct has local_members, optional base, and no variants
1164 # flat union has base, variants, and no local_members
1165 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001166 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001167 assert base is None or isinstance(base, str)
1168 for m in local_members:
1169 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001170 m.set_owner(name)
1171 if variants is not None:
1172 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1173 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001174 self._base_name = base
1175 self.base = None
1176 self.local_members = local_members
1177 self.variants = variants
1178 self.members = None
1179
1180 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001181 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001182 raise QAPISemError(self.info,
1183 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001184 if self.members:
1185 return
1186 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001187 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001188 if self._base_name:
1189 self.base = schema.lookup_type(self._base_name)
1190 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001191 self.base.check(schema)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001192 self.base.check_clash(self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001193 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001194 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001195 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001196 if self.doc:
1197 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001198 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001199 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001200 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001201 assert self.variants.tag_member in self.members
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001202 self.variants.check_clash(self.info, seen)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001203 if self.doc:
1204 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001205
Eric Blake14f00c62016-03-03 09:16:43 -07001206 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001207 # and update seen to track the members seen so far. Report any errors
1208 # on behalf of info, which is not necessarily self.info
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001209 def check_clash(self, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001210 assert not self.variants # not implemented
1211 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001212 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001213
Eric Blake99df5282015-10-12 22:22:32 -06001214 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001215 # See QAPISchema._make_implicit_object_type(), as well as
1216 # _def_predefineds()
1217 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001218
Eric Blakeb6167702016-07-13 21:50:16 -06001219 def is_empty(self):
1220 assert self.members is not None
1221 return not self.members and not self.variants
1222
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001223 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001224 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001225 return QAPISchemaType.c_name(self)
1226
Eric Blake4040d992016-03-17 16:48:28 -06001227 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001228 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001229 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001230
Eric Blake4040d992016-03-17 16:48:28 -06001231 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001232 return c_name(self.name)
1233
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001234 def json_type(self):
1235 return 'object'
1236
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001237 def visit(self, visitor):
1238 visitor.visit_object_type(self.name, self.info,
1239 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001240 visitor.visit_object_type_flat(self.name, self.info,
1241 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001242
Markus Armbrusterac882192015-09-16 13:06:05 +02001243
Eric Blaked44f9ac2015-12-01 22:20:54 -07001244class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001245 role = 'member'
1246
Eric Blaked44f9ac2015-12-01 22:20:54 -07001247 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001248 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001249 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001250 self.owner = None
1251
1252 def set_owner(self, name):
1253 assert not self.owner
1254 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001255
Eric Blake27b60ab2015-11-18 01:52:51 -07001256 def check_clash(self, info, seen):
1257 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001258 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001259 raise QAPISemError(info,
1260 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001261 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001262 raise QAPISemError(info, "%s collides with %s" %
1263 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001264 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001265
Eric Blake88d4ef82015-11-18 01:52:50 -07001266 def _pretty_owner(self):
1267 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001268 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001269 # See QAPISchema._make_implicit_object_type() - reverse the
1270 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001271 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001272 if owner.endswith('-arg'):
1273 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001274 elif owner.endswith('-base'):
1275 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001276 else:
1277 assert owner.endswith('-wrapper')
1278 # Unreachable and not implemented
1279 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001280 if owner.endswith('Kind'):
1281 # See QAPISchema._make_implicit_enum_type()
1282 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001283 return '(%s of %s)' % (self.role, owner)
1284
1285 def describe(self):
1286 return "'%s' %s" % (self.name, self._pretty_owner())
1287
Markus Armbrusterac882192015-09-16 13:06:05 +02001288
Eric Blaked44f9ac2015-12-01 22:20:54 -07001289class QAPISchemaObjectTypeMember(QAPISchemaMember):
1290 def __init__(self, name, typ, optional):
1291 QAPISchemaMember.__init__(self, name)
1292 assert isinstance(typ, str)
1293 assert isinstance(optional, bool)
1294 self._type_name = typ
1295 self.type = None
1296 self.optional = optional
1297
1298 def check(self, schema):
1299 assert self.owner
1300 self.type = schema.lookup_type(self._type_name)
1301 assert self.type
1302
1303
Markus Armbrusterac882192015-09-16 13:06:05 +02001304class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001305 def __init__(self, tag_name, tag_member, variants):
1306 # Flat unions pass tag_name but not tag_member.
1307 # Simple unions and alternates pass tag_member but not tag_name.
1308 # After check(), tag_member is always set, and tag_name remains
1309 # a reliable witness of being used by a flat union.
1310 assert bool(tag_member) != bool(tag_name)
1311 assert (isinstance(tag_name, str) or
1312 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001313 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001314 for v in variants:
1315 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001316 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001317 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001318 self.variants = variants
1319
Eric Blake88d4ef82015-11-18 01:52:50 -07001320 def set_owner(self, name):
1321 for v in self.variants:
1322 v.set_owner(name)
1323
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001324 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001325 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001326 self.tag_member = seen[c_name(self._tag_name)]
1327 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001328 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1329 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001330 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001331 # Union names must match enum values; alternate names are
1332 # checked separately. Use 'seen' to tell the two apart.
1333 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001334 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001335 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001336 v.type.check(schema)
1337
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001338 def check_clash(self, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001339 for v in self.variants:
1340 # Reset seen map for each variant, since qapi names from one
1341 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001342 assert isinstance(v.type, QAPISchemaObjectType)
Markus Armbruster6bbfb122017-03-15 13:57:34 +01001343 v.type.check_clash(info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001344
Eric Blake437db252015-09-29 16:21:02 -06001345
Markus Armbrusterac882192015-09-16 13:06:05 +02001346class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001347 role = 'branch'
1348
Markus Armbrusterac882192015-09-16 13:06:05 +02001349 def __init__(self, name, typ):
1350 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1351
Markus Armbrusterac882192015-09-16 13:06:05 +02001352
1353class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001354 def __init__(self, name, info, doc, variants):
1355 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001356 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001357 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001358 variants.set_owner(name)
1359 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001360 self.variants = variants
1361
1362 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001363 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001364 # Not calling self.variants.check_clash(), because there's nothing
1365 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001366 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001367 # Alternate branch names have no relation to the tag enum values;
1368 # so we have to check for potential name collisions ourselves.
1369 seen = {}
1370 for v in self.variants.variants:
1371 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001372 if self.doc:
1373 self.doc.connect_member(v)
Markus Armbruster816a57c2017-03-15 13:57:26 +01001374 if self.doc:
1375 self.doc.check()
Markus Armbrusterac882192015-09-16 13:06:05 +02001376
Eric Blake4040d992016-03-17 16:48:28 -06001377 def c_type(self):
1378 return c_name(self.name) + pointer_suffix
1379
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001380 def json_type(self):
1381 return 'value'
1382
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001383 def visit(self, visitor):
1384 visitor.visit_alternate_type(self.name, self.info, self.variants)
1385
Eric Blakec8184082016-07-13 21:50:20 -06001386 def is_empty(self):
1387 return False
1388
Markus Armbrusterac882192015-09-16 13:06:05 +02001389
1390class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001391 def __init__(self, name, info, doc, arg_type, ret_type,
1392 gen, success_response, boxed):
1393 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001394 assert not arg_type or isinstance(arg_type, str)
1395 assert not ret_type or isinstance(ret_type, str)
1396 self._arg_type_name = arg_type
1397 self.arg_type = None
1398 self._ret_type_name = ret_type
1399 self.ret_type = None
1400 self.gen = gen
1401 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001402 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001403
1404 def check(self, schema):
1405 if self._arg_type_name:
1406 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001407 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1408 isinstance(self.arg_type, QAPISchemaAlternateType))
1409 self.arg_type.check(schema)
1410 if self.boxed:
1411 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001412 raise QAPISemError(self.info,
1413 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001414 else:
1415 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1416 assert not self.arg_type.variants
1417 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001418 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001419 if self._ret_type_name:
1420 self.ret_type = schema.lookup_type(self._ret_type_name)
1421 assert isinstance(self.ret_type, QAPISchemaType)
1422
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001423 def visit(self, visitor):
1424 visitor.visit_command(self.name, self.info,
1425 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001426 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001427
Markus Armbrusterac882192015-09-16 13:06:05 +02001428
1429class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001430 def __init__(self, name, info, doc, arg_type, boxed):
1431 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001432 assert not arg_type or isinstance(arg_type, str)
1433 self._arg_type_name = arg_type
1434 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001435 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001436
1437 def check(self, schema):
1438 if self._arg_type_name:
1439 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001440 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1441 isinstance(self.arg_type, QAPISchemaAlternateType))
1442 self.arg_type.check(schema)
1443 if self.boxed:
1444 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001445 raise QAPISemError(self.info,
1446 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001447 else:
1448 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1449 assert not self.arg_type.variants
1450 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001451 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001452
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001453 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001454 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001455
Markus Armbrusterac882192015-09-16 13:06:05 +02001456
1457class QAPISchema(object):
1458 def __init__(self, fname):
1459 try:
Markus Armbrusteref801a92017-03-15 13:57:08 +01001460 parser = QAPISchemaParser(open(fname, 'r'))
Marc-André Lureau3313b612017-01-13 15:41:29 +01001461 self.exprs = check_exprs(parser.exprs)
Markus Armbrustera9f396b2017-03-15 13:57:27 +01001462 self.docs = parser.docs
Eric Blake7618b912015-10-12 22:22:22 -06001463 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001464 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001465 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001466 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001467 self._def_exprs()
1468 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001469 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001470 print >>sys.stderr, err
1471 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001472
Markus Armbrusterac882192015-09-16 13:06:05 +02001473 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001474 # Only the predefined types are allowed to not have info
1475 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001476 assert ent.name not in self._entity_dict
1477 self._entity_dict[ent.name] = ent
1478
1479 def lookup_entity(self, name, typ=None):
1480 ent = self._entity_dict.get(name)
1481 if typ and not isinstance(ent, typ):
1482 return None
1483 return ent
1484
1485 def lookup_type(self, name):
1486 return self.lookup_entity(name, QAPISchemaType)
1487
Eric Blake861877a2016-03-17 16:48:36 -06001488 def _def_builtin_type(self, name, json_type, c_type):
1489 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001490 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1491 # qapi-types.h from a single .c, all arrays of builtins must be
1492 # declared in the first file whether or not they are used. Nicer
1493 # would be to use lazy instantiation, while figuring out how to
1494 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001495 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001496
1497 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001498 for t in [('str', 'string', 'char' + pointer_suffix),
1499 ('number', 'number', 'double'),
1500 ('int', 'int', 'int64_t'),
1501 ('int8', 'int', 'int8_t'),
1502 ('int16', 'int', 'int16_t'),
1503 ('int32', 'int', 'int32_t'),
1504 ('int64', 'int', 'int64_t'),
1505 ('uint8', 'int', 'uint8_t'),
1506 ('uint16', 'int', 'uint16_t'),
1507 ('uint32', 'int', 'uint32_t'),
1508 ('uint64', 'int', 'uint64_t'),
1509 ('size', 'int', 'uint64_t'),
1510 ('bool', 'boolean', 'bool'),
Markus Armbruster4d2d5c42017-06-26 19:25:14 +02001511 ('any', 'value', 'QObject' + pointer_suffix),
1512 ('null', 'null', 'QNull' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001513 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001514 self.the_empty_object_type = QAPISchemaObjectType(
1515 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001516 self._def_entity(self.the_empty_object_type)
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001517 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
Eric Blake93bda4d2015-12-01 22:20:55 -07001518 'qstring', 'qdict', 'qlist',
Marc-André Lureau01b2ffc2017-06-07 20:35:58 +04001519 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001520 self._def_entity(QAPISchemaEnumType('QType', None, None,
1521 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001522
Eric Blake93bda4d2015-12-01 22:20:55 -07001523 def _make_enum_members(self, values):
1524 return [QAPISchemaMember(v) for v in values]
1525
Eric Blake99df5282015-10-12 22:22:32 -06001526 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001527 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001528 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001529 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001530 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001531 return name
1532
Eric Blake99df5282015-10-12 22:22:32 -06001533 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001534 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001535 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001536 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001537 return name
1538
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001539 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001540 if not members:
1541 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001542 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001543 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001544 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001545 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001546 members, None))
1547 return name
1548
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001549 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001550 name = expr['enum']
1551 data = expr['data']
1552 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001553 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001554 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001555
Eric Blake99df5282015-10-12 22:22:32 -06001556 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001557 optional = False
1558 if name.startswith('*'):
1559 name = name[1:]
1560 optional = True
1561 if isinstance(typ, list):
1562 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001563 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001564 return QAPISchemaObjectTypeMember(name, typ, optional)
1565
Eric Blake99df5282015-10-12 22:22:32 -06001566 def _make_members(self, data, info):
1567 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001568 for (key, value) in data.iteritems()]
1569
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001570 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001571 name = expr['struct']
1572 base = expr.get('base')
1573 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001574 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001575 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001576 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001577
1578 def _make_variant(self, case, typ):
1579 return QAPISchemaObjectTypeVariant(case, typ)
1580
Eric Blake99df5282015-10-12 22:22:32 -06001581 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001582 if isinstance(typ, list):
1583 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001584 typ = self._make_array_type(typ[0], info)
1585 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001586 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001587 return QAPISchemaObjectTypeVariant(case, typ)
1588
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001589 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001590 name = expr['union']
1591 data = expr['data']
1592 base = expr.get('base')
1593 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001594 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001595 if isinstance(base, dict):
1596 base = (self._make_implicit_object_type(
Markus Armbrusterc2613942017-03-15 13:57:35 +01001597 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001598 if tag_name:
1599 variants = [self._make_variant(key, value)
1600 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001601 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001602 else:
Eric Blake99df5282015-10-12 22:22:32 -06001603 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001604 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001605 typ = self._make_implicit_enum_type(name, info,
1606 [v.name for v in variants])
1607 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001608 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001609 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001610 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001611 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001612 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001613 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001614
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001615 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001616 name = expr['alternate']
1617 data = expr['data']
1618 variants = [self._make_variant(key, value)
1619 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001620 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001621 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001622 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001623 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001624 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001625 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001626
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001627 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001628 name = expr['command']
1629 data = expr.get('data')
1630 rets = expr.get('returns')
1631 gen = expr.get('gen', True)
1632 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001633 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001634 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001635 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001636 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001637 if isinstance(rets, list):
1638 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001639 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001640 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1641 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001642
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001643 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001644 name = expr['event']
1645 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001646 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001647 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001648 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001649 name, info, doc, 'arg', self._make_members(data, info))
1650 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001651
1652 def _def_exprs(self):
1653 for expr_elem in self.exprs:
1654 expr = expr_elem['expr']
1655 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001656 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001657 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001658 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001659 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001660 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001661 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001662 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001663 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001664 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001665 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001666 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001667 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001668 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001669 else:
1670 assert False
1671
1672 def check(self):
1673 for ent in self._entity_dict.values():
1674 ent.check(self)
1675
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001676 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001677 visitor.visit_begin(self)
1678 for (name, entity) in sorted(self._entity_dict.items()):
1679 if visitor.visit_needed(entity):
1680 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001681 visitor.visit_end()
1682
Markus Armbruster2caba362013-07-27 17:41:56 +02001683
Markus Armbruster00e4b282015-06-10 10:04:36 +02001684#
1685# Code generation helpers
1686#
1687
Michael Roth0f923be2011-07-19 14:50:39 -05001688def camel_case(name):
1689 new_name = ''
1690 first = True
1691 for ch in name:
1692 if ch in ['_', '-']:
1693 first = True
1694 elif first:
1695 new_name += ch.upper()
1696 first = False
1697 else:
1698 new_name += ch.lower()
1699 return new_name
1700
Eric Blake437db252015-09-29 16:21:02 -06001701
Markus Armbruster849bc532015-05-14 06:50:53 -06001702# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1703# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1704# ENUM24_Name -> ENUM24_NAME
1705def camel_to_upper(value):
1706 c_fun_str = c_name(value, False)
1707 if value.isupper():
1708 return c_fun_str
1709
1710 new_name = ''
1711 l = len(c_fun_str)
1712 for i in range(l):
1713 c = c_fun_str[i]
Markus Armbrusteref801a92017-03-15 13:57:08 +01001714 # When c is upper and no '_' appears before, do more checks
1715 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
Eric Blake437db252015-09-29 16:21:02 -06001716 if i < l - 1 and c_fun_str[i + 1].islower():
1717 new_name += '_'
1718 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001719 new_name += '_'
1720 new_name += c
1721 return new_name.lstrip('_').upper()
1722
Eric Blake437db252015-09-29 16:21:02 -06001723
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001724def c_enum_const(type_name, const_name, prefix=None):
1725 if prefix is not None:
1726 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001727 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001728
Eric Blake18df5152015-05-14 06:50:48 -06001729c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001730
Eric Blake437db252015-09-29 16:21:02 -06001731
Eric Blakec6405b52015-05-14 06:50:55 -06001732# Map @name to a valid C identifier.
1733# If @protect, avoid returning certain ticklish identifiers (like
Markus Armbrusteref801a92017-03-15 13:57:08 +01001734# C keywords) by prepending 'q_'.
Eric Blakec6405b52015-05-14 06:50:55 -06001735#
1736# Used for converting 'name' from a 'name':'type' qapi definition
1737# into a generated struct member, as well as converting type names
1738# into substrings of a generated C function name.
1739# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1740# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001741def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001742 # ANSI X3J11/88-090, 3.1.1
1743 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001744 'default', 'do', 'double', 'else', 'enum', 'extern',
1745 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1746 'return', 'short', 'signed', 'sizeof', 'static',
1747 'struct', 'switch', 'typedef', 'union', 'unsigned',
1748 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001749 # ISO/IEC 9899:1999, 6.4.1
1750 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1751 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001752 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1753 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001754 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1755 # excluding _.*
1756 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001757 # C++ ISO/IEC 14882:2003 2.11
1758 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1759 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1760 'namespace', 'new', 'operator', 'private', 'protected',
1761 'public', 'reinterpret_cast', 'static_cast', 'template',
1762 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1763 'using', 'virtual', 'wchar_t',
1764 # alternative representations
1765 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1766 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001767 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001768 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001769 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001770 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1771 | cpp_words | polluted_words):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001772 return 'q_' + name
Eric Blakec43567c2015-11-18 01:52:52 -07001773 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001774
Amos Kong05dfb262014-06-10 19:25:53 +08001775eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001776pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001777
Eric Blake437db252015-09-29 16:21:02 -06001778
Michael Roth0f923be2011-07-19 14:50:39 -05001779def genindent(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001780 ret = ''
Eric Blake437db252015-09-29 16:21:02 -06001781 for _ in range(count):
Markus Armbrusteref801a92017-03-15 13:57:08 +01001782 ret += ' '
Michael Roth0f923be2011-07-19 14:50:39 -05001783 return ret
1784
1785indent_level = 0
1786
Eric Blake437db252015-09-29 16:21:02 -06001787
Michael Roth0f923be2011-07-19 14:50:39 -05001788def push_indent(indent_amount=4):
1789 global indent_level
1790 indent_level += indent_amount
1791
Eric Blake437db252015-09-29 16:21:02 -06001792
Michael Roth0f923be2011-07-19 14:50:39 -05001793def pop_indent(indent_amount=4):
1794 global indent_level
1795 indent_level -= indent_amount
1796
Eric Blake437db252015-09-29 16:21:02 -06001797
Markus Armbruster77e703b2015-06-24 19:27:32 +02001798# Generate @code with @kwds interpolated.
1799# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001800def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001801 raw = code % kwds
1802 if indent_level:
1803 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001804 # re.subn() lacks flags support before Python 2.7, use re.compile()
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001805 raw = re.subn(re.compile(r'^.', re.MULTILINE),
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001806 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001807 raw = raw[0]
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001808 return re.sub(re.escape(eatspace) + r' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001809
Eric Blake437db252015-09-29 16:21:02 -06001810
Michael Roth0f923be2011-07-19 14:50:39 -05001811def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001812 if code[0] == '\n':
1813 code = code[1:]
1814 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001815
Michael Roth0f923be2011-07-19 14:50:39 -05001816
1817def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001818 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001819
Eric Blake437db252015-09-29 16:21:02 -06001820
Michael Rothc0afa9c2013-05-10 17:46:00 -05001821def guardstart(name):
1822 return mcgen('''
1823
1824#ifndef %(name)s
1825#define %(name)s
1826
1827''',
1828 name=guardname(name))
1829
Eric Blake437db252015-09-29 16:21:02 -06001830
Michael Rothc0afa9c2013-05-10 17:46:00 -05001831def guardend(name):
1832 return mcgen('''
1833
1834#endif /* %(name)s */
1835
1836''',
1837 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001838
Eric Blake437db252015-09-29 16:21:02 -06001839
Markus Armbrustere98859a2015-09-16 13:06:16 +02001840def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001841 ret = mcgen('''
1842
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001843const QEnumLookup %(c_name)s_lookup = {
1844 .array = (const char *const[]) {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001845''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001846 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001847 for value in values:
1848 index = c_enum_const(name, value, prefix)
1849 ret += mcgen('''
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001850 [%(index)s] = "%(value)s",
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001851''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001852 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001853
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001854 ret += mcgen('''
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001855 },
1856 .size = %(max_index)s
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001857};
1858''',
Marc-André Lureauebf677c2017-08-24 10:46:11 +02001859 max_index=c_enum_const(name, '_MAX', prefix))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001860 return ret
1861
Eric Blake437db252015-09-29 16:21:02 -06001862
Markus Armbrustere98859a2015-09-16 13:06:16 +02001863def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001864 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001865 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001866
1867 ret = mcgen('''
1868
1869typedef enum %(c_name)s {
1870''',
1871 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001872
1873 i = 0
1874 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001875 ret += mcgen('''
1876 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001877''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001878 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001879 i=i)
1880 i += 1
1881
Markus Armbrustere98859a2015-09-16 13:06:16 +02001882 ret += mcgen('''
1883} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001884''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001885 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001886
Markus Armbrustere98859a2015-09-16 13:06:16 +02001887 ret += mcgen('''
1888
Markus Armbruster5b5f8252017-08-24 10:46:07 +02001889#define %(c_name)s_str(val) \\
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001890 qapi_enum_lookup(&%(c_name)s_lookup, (val))
Markus Armbruster5b5f8252017-08-24 10:46:07 +02001891
Marc-André Lureauf7abe0e2017-08-24 10:46:10 +02001892extern const QEnumLookup %(c_name)s_lookup;
Markus Armbrustere98859a2015-09-16 13:06:16 +02001893''',
1894 c_name=c_name(name))
1895 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001896
Eric Blake437db252015-09-29 16:21:02 -06001897
Marc-André Lureau086ee7a2017-06-01 16:41:41 +04001898def build_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001899 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001900 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001901 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001902 ret = ''
1903 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001904 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001905 ret += '%s arg' % arg_type.c_param_type()
1906 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001907 else:
1908 assert not arg_type.variants
1909 for memb in arg_type.members:
1910 ret += sep
1911 sep = ', '
1912 if memb.optional:
1913 ret += 'bool has_%s, ' % c_name(memb.name)
1914 ret += '%s %s' % (memb.type.c_param_type(),
1915 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001916 if extra:
1917 ret += sep + extra
1918 return ret
1919
Eric Blake1f353342015-09-29 16:21:13 -06001920
Markus Armbruster00e4b282015-06-10 10:04:36 +02001921#
1922# Common command line parsing
1923#
1924
Eric Blake437db252015-09-29 16:21:02 -06001925
Markus Armbrusteref801a92017-03-15 13:57:08 +01001926def parse_command_line(extra_options='', extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001927
1928 try:
1929 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbrusteref801a92017-03-15 13:57:08 +01001930 'chp:o:' + extra_options,
1931 ['source', 'header', 'prefix=',
1932 'output-dir='] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001933 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001934 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001935 sys.exit(1)
1936
Markus Armbrusteref801a92017-03-15 13:57:08 +01001937 output_dir = ''
1938 prefix = ''
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001939 do_c = False
1940 do_h = False
1941 extra_opts = []
1942
1943 for oa in opts:
1944 o, a = oa
Markus Armbrusteref801a92017-03-15 13:57:08 +01001945 if o in ('-p', '--prefix'):
Markus Armbruster0fe675a2017-03-15 13:57:07 +01001946 match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001947 if match.end() != len(a):
1948 print >>sys.stderr, \
1949 "%s: 'funny character '%s' in argument of --prefix" \
1950 % (sys.argv[0], a[match.end()])
1951 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001952 prefix = a
Markus Armbrusteref801a92017-03-15 13:57:08 +01001953 elif o in ('-o', '--output-dir'):
1954 output_dir = a + '/'
1955 elif o in ('-c', '--source'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001956 do_c = True
Markus Armbrusteref801a92017-03-15 13:57:08 +01001957 elif o in ('-h', '--header'):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001958 do_h = True
1959 else:
1960 extra_opts.append(oa)
1961
1962 if not do_c and not do_h:
1963 do_c = True
1964 do_h = True
1965
Markus Armbruster16d80f62015-04-02 13:32:16 +02001966 if len(args) != 1:
1967 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001968 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02001969 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02001970
Markus Armbruster54414042015-06-09 16:22:45 +02001971 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001972
Markus Armbruster00e4b282015-06-10 10:04:36 +02001973#
1974# Generate output files with boilerplate
1975#
1976
Eric Blake437db252015-09-29 16:21:02 -06001977
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001978def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1979 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001980 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001981 c_file = output_dir + prefix + c_file
1982 h_file = output_dir + prefix + h_file
1983
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001984 if output_dir:
1985 try:
1986 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01001987 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02001988 if e.errno != errno.EEXIST:
1989 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02001990
1991 def maybe_open(really, name, opt):
1992 if really:
1993 return open(name, opt)
1994 else:
1995 import StringIO
1996 return StringIO.StringIO()
1997
1998 fdef = maybe_open(do_c, c_file, 'w')
1999 fdecl = maybe_open(do_h, h_file, 'w')
2000
2001 fdef.write(mcgen('''
2002/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2003%(comment)s
2004''',
Eric Blake437db252015-09-29 16:21:02 -06002005 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002006
2007 fdecl.write(mcgen('''
2008/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2009%(comment)s
2010#ifndef %(guard)s
2011#define %(guard)s
2012
2013''',
Eric Blake437db252015-09-29 16:21:02 -06002014 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002015
2016 return (fdef, fdecl)
2017
Eric Blake437db252015-09-29 16:21:02 -06002018
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002019def close_output(fdef, fdecl):
2020 fdecl.write('''
2021#endif
2022''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002023 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002024 fdef.close()