blob: 58e315bf3a8f5f74d06b11487cc462b2211326e6 [file] [log] [blame]
Michael Roth0f923be2011-07-19 14:50:39 -05001#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
Markus Armbrusterc7a3f252013-07-27 17:41:55 +02005# Copyright (c) 2013 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#
11# This work is licensed under the terms of the GNU GPLv2.
12# See the COPYING.LIB file in the top-level directory.
13
14from ordereddict import OrderedDict
15
Michael Rothc0afa9c2013-05-10 17:46:00 -050016builtin_types = [
17 'str', 'int', 'number', 'bool',
18 'int8', 'int16', 'int32', 'int64',
19 'uint8', 'uint16', 'uint32', 'uint64'
20]
21
Kevin Wolf69dd62d2013-07-08 16:14:21 +020022builtin_type_qtypes = {
23 'str': 'QTYPE_QSTRING',
24 'int': 'QTYPE_QINT',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
27 'int8': 'QTYPE_QINT',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
35}
36
Markus Armbrusterc7a3f252013-07-27 17:41:55 +020037class QAPISchema:
Michael Roth0f923be2011-07-19 14:50:39 -050038
Markus Armbrusterc7a3f252013-07-27 17:41:55 +020039 def __init__(self, fp):
40 self.fp = fp
41 self.src = fp.read()
42 if self.src == '' or self.src[-1] != '\n':
43 self.src += '\n'
44 self.cursor = 0
45 self.exprs = []
46 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -050047
Markus Armbrusterc7a3f252013-07-27 17:41:55 +020048 while self.tok != None:
49 self.exprs.append(self.get_expr())
Michael Roth0f923be2011-07-19 14:50:39 -050050
Markus Armbrusterc7a3f252013-07-27 17:41:55 +020051 def accept(self):
52 while True:
53 bol = self.cursor == 0 or self.src[self.cursor-1] == '\n'
54 self.tok = self.src[self.cursor]
55 self.cursor += 1
56 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -050057
Markus Armbrusterc7a3f252013-07-27 17:41:55 +020058 if self.tok == '#' and bol:
59 self.cursor = self.src.find('\n', self.cursor)
60 elif self.tok in ['{', '}', ':', ',', '[', ']']:
61 return
62 elif self.tok == "'":
63 string = ''
64 esc = False
65 while True:
66 ch = self.src[self.cursor]
67 self.cursor += 1
68 if ch == '\n':
69 raise Exception("Mismatched quotes")
70 if esc:
71 string += ch
72 esc = False
73 elif ch == "\\":
74 esc = True
75 elif ch == "'":
76 self.val = string
77 return
78 else:
79 string += ch
80 elif self.tok == '\n':
81 if self.cursor == len(self.src):
82 self.tok = None
83 return
Michael Roth0f923be2011-07-19 14:50:39 -050084
Markus Armbrusterc7a3f252013-07-27 17:41:55 +020085 def get_members(self):
86 expr = OrderedDict()
87 while self.tok != '}':
88 key = self.val
89 self.accept()
90 self.accept() # :
91 expr[key] = self.get_expr()
92 if self.tok == ',':
93 self.accept()
94 self.accept()
95 return expr
Michael Roth0f923be2011-07-19 14:50:39 -050096
Markus Armbrusterc7a3f252013-07-27 17:41:55 +020097 def get_values(self):
98 expr = []
99 while self.tok != ']':
100 expr.append(self.get_expr())
101 if self.tok == ',':
102 self.accept()
103 self.accept()
104 return expr
Michael Roth0f923be2011-07-19 14:50:39 -0500105
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200106 def get_expr(self):
107 if self.tok == '{':
108 self.accept()
109 expr = self.get_members()
110 elif self.tok == '[':
111 self.accept()
112 expr = self.get_values()
Michael Roth0f923be2011-07-19 14:50:39 -0500113 else:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200114 expr = self.val
115 self.accept()
116 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200117
118def parse_schema(fp):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200119 schema = QAPISchema(fp)
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200120 exprs = []
121
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200122 for expr_eval in schema.exprs:
Michael Roth0f923be2011-07-19 14:50:39 -0500123 if expr_eval.has_key('enum'):
124 add_enum(expr_eval['enum'])
125 elif expr_eval.has_key('union'):
Kevin Wolfea66c6d2013-07-16 10:49:41 +0200126 add_union(expr_eval)
Michael Roth0f923be2011-07-19 14:50:39 -0500127 add_enum('%sKind' % expr_eval['union'])
Kevin Wolfb35284e2013-07-01 16:31:51 +0200128 elif expr_eval.has_key('type'):
129 add_struct(expr_eval)
Michael Roth0f923be2011-07-19 14:50:39 -0500130 exprs.append(expr_eval)
131
132 return exprs
133
134def parse_args(typeinfo):
Kevin Wolfb35284e2013-07-01 16:31:51 +0200135 if isinstance(typeinfo, basestring):
136 struct = find_struct(typeinfo)
137 assert struct != None
138 typeinfo = struct['data']
139
Michael Roth0f923be2011-07-19 14:50:39 -0500140 for member in typeinfo:
141 argname = member
142 argentry = typeinfo[member]
143 optional = False
144 structured = False
145 if member.startswith('*'):
146 argname = member[1:]
147 optional = True
148 if isinstance(argentry, OrderedDict):
149 structured = True
150 yield (argname, argentry, optional, structured)
151
152def de_camel_case(name):
153 new_name = ''
154 for ch in name:
155 if ch.isupper() and new_name:
156 new_name += '_'
157 if ch == '-':
158 new_name += '_'
159 else:
160 new_name += ch.lower()
161 return new_name
162
163def camel_case(name):
164 new_name = ''
165 first = True
166 for ch in name:
167 if ch in ['_', '-']:
168 first = True
169 elif first:
170 new_name += ch.upper()
171 first = False
172 else:
173 new_name += ch.lower()
174 return new_name
175
Paolo Bonzinieda50a62012-09-19 16:31:06 +0200176def c_var(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +0000177 # ANSI X3J11/88-090, 3.1.1
178 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
179 'default', 'do', 'double', 'else', 'enum', 'extern', 'float',
180 'for', 'goto', 'if', 'int', 'long', 'register', 'return',
181 'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
182 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while'])
183 # ISO/IEC 9899:1999, 6.4.1
184 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
185 # ISO/IEC 9899:2011, 6.4.1
186 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn',
187 '_Static_assert', '_Thread_local'])
188 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
189 # excluding _.*
190 gcc_words = set(['asm', 'typeof'])
Paolo Bonzini10577252012-09-19 16:31:07 +0200191 # namespace pollution:
192 polluted_words = set(['unix'])
193 if protect and (name in c89_words | c99_words | c11_words | gcc_words | polluted_words):
Blue Swirl427a1a22012-07-30 15:46:55 +0000194 return "q_" + name
Federico Simoncellic9da2282012-03-20 13:54:35 +0000195 return name.replace('-', '_').lstrip("*")
196
Paolo Bonzinieda50a62012-09-19 16:31:06 +0200197def c_fun(name, protect=True):
198 return c_var(name, protect).replace('.', '_')
Michael Roth0f923be2011-07-19 14:50:39 -0500199
200def c_list_type(name):
201 return '%sList' % name
202
203def type_name(name):
204 if type(name) == list:
205 return c_list_type(name[0])
206 return name
207
208enum_types = []
Kevin Wolfb35284e2013-07-01 16:31:51 +0200209struct_types = []
Kevin Wolfea66c6d2013-07-16 10:49:41 +0200210union_types = []
Kevin Wolfb35284e2013-07-01 16:31:51 +0200211
212def add_struct(definition):
213 global struct_types
214 struct_types.append(definition)
215
216def find_struct(name):
217 global struct_types
218 for struct in struct_types:
219 if struct['type'] == name:
220 return struct
221 return None
Michael Roth0f923be2011-07-19 14:50:39 -0500222
Kevin Wolfea66c6d2013-07-16 10:49:41 +0200223def add_union(definition):
224 global union_types
225 union_types.append(definition)
226
227def find_union(name):
228 global union_types
229 for union in union_types:
230 if union['union'] == name:
231 return union
232 return None
233
Michael Roth0f923be2011-07-19 14:50:39 -0500234def add_enum(name):
235 global enum_types
236 enum_types.append(name)
237
238def is_enum(name):
239 global enum_types
240 return (name in enum_types)
241
242def c_type(name):
243 if name == 'str':
244 return 'char *'
245 elif name == 'int':
246 return 'int64_t'
Laszlo Ersekc46f18c2012-07-17 16:17:06 +0200247 elif (name == 'int8' or name == 'int16' or name == 'int32' or
248 name == 'int64' or name == 'uint8' or name == 'uint16' or
249 name == 'uint32' or name == 'uint64'):
250 return name + '_t'
Laszlo Ersek092705d2012-07-17 16:17:07 +0200251 elif name == 'size':
252 return 'uint64_t'
Michael Roth0f923be2011-07-19 14:50:39 -0500253 elif name == 'bool':
254 return 'bool'
255 elif name == 'number':
256 return 'double'
257 elif type(name) == list:
258 return '%s *' % c_list_type(name[0])
259 elif is_enum(name):
260 return name
261 elif name == None or len(name) == 0:
262 return 'void'
263 elif name == name.upper():
264 return '%sEvent *' % camel_case(name)
265 else:
266 return '%s *' % name
267
268def genindent(count):
269 ret = ""
270 for i in range(count):
271 ret += " "
272 return ret
273
274indent_level = 0
275
276def push_indent(indent_amount=4):
277 global indent_level
278 indent_level += indent_amount
279
280def pop_indent(indent_amount=4):
281 global indent_level
282 indent_level -= indent_amount
283
284def cgen(code, **kwds):
285 indent = genindent(indent_level)
286 lines = code.split('\n')
287 lines = map(lambda x: indent + x, lines)
288 return '\n'.join(lines) % kwds + '\n'
289
290def mcgen(code, **kwds):
291 return cgen('\n'.join(code.split('\n')[1:-1]), **kwds)
292
293def basename(filename):
294 return filename.split("/")[-1]
295
296def guardname(filename):
Michael Rothd8e1f212011-11-29 16:47:48 -0600297 guard = basename(filename).rsplit(".", 1)[0]
298 for substr in [".", " ", "-"]:
299 guard = guard.replace(substr, "_")
300 return guard.upper() + '_H'
Michael Rothc0afa9c2013-05-10 17:46:00 -0500301
302def guardstart(name):
303 return mcgen('''
304
305#ifndef %(name)s
306#define %(name)s
307
308''',
309 name=guardname(name))
310
311def guardend(name):
312 return mcgen('''
313
314#endif /* %(name)s */
315
316''',
317 name=guardname(name))