]> git.proxmox.com Git - mirror_qemu.git/blame - scripts/qapi.py
tests/qapi-schema: Cover non-string, non-dictionary members
[mirror_qemu.git] / scripts / qapi.py
CommitLineData
0f923be2
MR
1#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
fe2a9303 5# Copyright (c) 2013-2015 Red Hat Inc.
0f923be2
MR
6#
7# Authors:
8# Anthony Liguori <aliguori@us.ibm.com>
c7a3f252 9# Markus Armbruster <armbru@redhat.com>
0f923be2 10#
678e48a2
MA
11# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
0f923be2 13
a719a27c 14import re
0f923be2 15from ordereddict import OrderedDict
12f8e1b9 16import errno
2114f5a9 17import getopt
33aaad52 18import os
2caba36c 19import sys
47299262 20import string
0f923be2 21
b52c4b9c 22builtin_types = {
69dd62df
KW
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',
cb17f79e 35 'size': 'QTYPE_QINT',
69dd62df
KW
36}
37
10d4d997
EB
38# Whitelist of commands allowed to return a non-dictionary
39returns_whitelist = [
40 # From QMP:
41 'human-monitor-command',
42 'query-migrate-cache-size',
43 'query-tpm-models',
44 'query-tpm-types',
45 'ringbuf-read',
46
47 # From QGA:
48 'guest-file-open',
49 'guest-fsfreeze-freeze',
50 'guest-fsfreeze-freeze-list',
51 'guest-fsfreeze-status',
52 'guest-fsfreeze-thaw',
53 'guest-get-time',
54 'guest-set-vcpus',
55 'guest-sync',
56 'guest-sync-delimited',
57
58 # From qapi-schema-test:
59 'user_def_cmd3',
60]
61
4dc2e690
EB
62enum_types = []
63struct_types = []
64union_types = []
65events = []
66all_names = {}
67
00e4b285
MA
68#
69# Parsing the schema into expressions
70#
71
a719a27c
LV
72def error_path(parent):
73 res = ""
74 while parent:
75 res = ("In file included from %s:%d:\n" % (parent['file'],
76 parent['line'])) + res
77 parent = parent['parent']
78 return res
79
2caba36c
MA
80class QAPISchemaError(Exception):
81 def __init__(self, schema, msg):
54414047 82 self.fname = schema.fname
2caba36c 83 self.msg = msg
515b943a
WX
84 self.col = 1
85 self.line = schema.line
86 for ch in schema.src[schema.line_pos:schema.pos]:
87 if ch == '\t':
2caba36c
MA
88 self.col = (self.col + 7) % 8 + 1
89 else:
90 self.col += 1
54414047 91 self.info = schema.incl_info
2caba36c
MA
92
93 def __str__(self):
a719a27c 94 return error_path(self.info) + \
54414047 95 "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
2caba36c 96
b86b05ed
WX
97class QAPIExprError(Exception):
98 def __init__(self, expr_info, msg):
a719a27c 99 self.info = expr_info
b86b05ed
WX
100 self.msg = msg
101
102 def __str__(self):
a719a27c
LV
103 return error_path(self.info['parent']) + \
104 "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
b86b05ed 105
c7a3f252
MA
106class QAPISchema:
107
a1366087 108 def __init__(self, fp, previously_included = [], incl_info = None):
54414047 109 abs_fname = os.path.abspath(fp.name)
8608d252 110 fname = fp.name
54414047 111 self.fname = fname
54414047
MA
112 previously_included.append(abs_fname)
113 self.incl_info = incl_info
c7a3f252
MA
114 self.src = fp.read()
115 if self.src == '' or self.src[-1] != '\n':
116 self.src += '\n'
117 self.cursor = 0
515b943a
WX
118 self.line = 1
119 self.line_pos = 0
c7a3f252
MA
120 self.exprs = []
121 self.accept()
122
123 while self.tok != None:
54414047
MA
124 expr_info = {'file': fname, 'line': self.line,
125 'parent': self.incl_info}
a719a27c
LV
126 expr = self.get_expr(False)
127 if isinstance(expr, dict) and "include" in expr:
128 if len(expr) != 1:
129 raise QAPIExprError(expr_info, "Invalid 'include' directive")
130 include = expr["include"]
131 if not isinstance(include, str):
132 raise QAPIExprError(expr_info,
133 'Expected a file name (string), got: %s'
134 % include)
54414047
MA
135 incl_abs_fname = os.path.join(os.path.dirname(abs_fname),
136 include)
a1366087
MA
137 # catch inclusion cycle
138 inf = expr_info
139 while inf:
140 if incl_abs_fname == os.path.abspath(inf['file']):
7ac9a9d6
SH
141 raise QAPIExprError(expr_info, "Inclusion loop for %s"
142 % include)
a1366087 143 inf = inf['parent']
24fd8489 144 # skip multiple include of the same file
54414047 145 if incl_abs_fname in previously_included:
24fd8489 146 continue
a719a27c 147 try:
54414047 148 fobj = open(incl_abs_fname, 'r')
34788811 149 except IOError, e:
a719a27c
LV
150 raise QAPIExprError(expr_info,
151 '%s: %s' % (e.strerror, include))
a1366087
MA
152 exprs_include = QAPISchema(fobj, previously_included,
153 expr_info)
a719a27c
LV
154 self.exprs.extend(exprs_include.exprs)
155 else:
156 expr_elem = {'expr': expr,
157 'info': expr_info}
158 self.exprs.append(expr_elem)
c7a3f252
MA
159
160 def accept(self):
161 while True:
c7a3f252 162 self.tok = self.src[self.cursor]
2caba36c 163 self.pos = self.cursor
c7a3f252
MA
164 self.cursor += 1
165 self.val = None
166
f1a145e1 167 if self.tok == '#':
c7a3f252
MA
168 self.cursor = self.src.find('\n', self.cursor)
169 elif self.tok in ['{', '}', ':', ',', '[', ']']:
170 return
171 elif self.tok == "'":
172 string = ''
173 esc = False
174 while True:
175 ch = self.src[self.cursor]
176 self.cursor += 1
177 if ch == '\n':
2caba36c
MA
178 raise QAPISchemaError(self,
179 'Missing terminating "\'"')
c7a3f252 180 if esc:
a7f5966b
EB
181 if ch == 'b':
182 string += '\b'
183 elif ch == 'f':
184 string += '\f'
185 elif ch == 'n':
186 string += '\n'
187 elif ch == 'r':
188 string += '\r'
189 elif ch == 't':
190 string += '\t'
191 elif ch == 'u':
192 value = 0
193 for x in range(0, 4):
194 ch = self.src[self.cursor]
195 self.cursor += 1
196 if ch not in "0123456789abcdefABCDEF":
197 raise QAPISchemaError(self,
198 '\\u escape needs 4 '
199 'hex digits')
200 value = (value << 4) + int(ch, 16)
201 # If Python 2 and 3 didn't disagree so much on
202 # how to handle Unicode, then we could allow
203 # Unicode string defaults. But most of QAPI is
204 # ASCII-only, so we aren't losing much for now.
205 if not value or value > 0x7f:
206 raise QAPISchemaError(self,
207 'For now, \\u escape '
208 'only supports non-zero '
209 'values up to \\u007f')
210 string += chr(value)
211 elif ch in "\\/'\"":
212 string += ch
213 else:
214 raise QAPISchemaError(self,
215 "Unknown escape \\%s" %ch)
c7a3f252
MA
216 esc = False
217 elif ch == "\\":
218 esc = True
219 elif ch == "'":
220 self.val = string
221 return
222 else:
223 string += ch
e565d934
MA
224 elif self.src.startswith("true", self.pos):
225 self.val = True
226 self.cursor += 3
227 return
228 elif self.src.startswith("false", self.pos):
229 self.val = False
230 self.cursor += 4
231 return
232 elif self.src.startswith("null", self.pos):
233 self.val = None
234 self.cursor += 3
235 return
c7a3f252
MA
236 elif self.tok == '\n':
237 if self.cursor == len(self.src):
238 self.tok = None
239 return
515b943a
WX
240 self.line += 1
241 self.line_pos = self.cursor
9213aa53
MA
242 elif not self.tok.isspace():
243 raise QAPISchemaError(self, 'Stray "%s"' % self.tok)
c7a3f252
MA
244
245 def get_members(self):
246 expr = OrderedDict()
6974ccd5
MA
247 if self.tok == '}':
248 self.accept()
249 return expr
250 if self.tok != "'":
251 raise QAPISchemaError(self, 'Expected string or "}"')
252 while True:
c7a3f252
MA
253 key = self.val
254 self.accept()
6974ccd5
MA
255 if self.tok != ':':
256 raise QAPISchemaError(self, 'Expected ":"')
257 self.accept()
4b35991a
WX
258 if key in expr:
259 raise QAPISchemaError(self, 'Duplicate key "%s"' % key)
5f3cd2b7 260 expr[key] = self.get_expr(True)
6974ccd5 261 if self.tok == '}':
c7a3f252 262 self.accept()
6974ccd5
MA
263 return expr
264 if self.tok != ',':
265 raise QAPISchemaError(self, 'Expected "," or "}"')
266 self.accept()
267 if self.tok != "'":
268 raise QAPISchemaError(self, 'Expected string')
c7a3f252
MA
269
270 def get_values(self):
271 expr = []
6974ccd5
MA
272 if self.tok == ']':
273 self.accept()
274 return expr
e53188ad
FZ
275 if not self.tok in "{['tfn":
276 raise QAPISchemaError(self, 'Expected "{", "[", "]", string, '
277 'boolean or "null"')
6974ccd5 278 while True:
5f3cd2b7 279 expr.append(self.get_expr(True))
6974ccd5 280 if self.tok == ']':
c7a3f252 281 self.accept()
6974ccd5
MA
282 return expr
283 if self.tok != ',':
284 raise QAPISchemaError(self, 'Expected "," or "]"')
285 self.accept()
c7a3f252 286
5f3cd2b7
MA
287 def get_expr(self, nested):
288 if self.tok != '{' and not nested:
289 raise QAPISchemaError(self, 'Expected "{"')
c7a3f252
MA
290 if self.tok == '{':
291 self.accept()
292 expr = self.get_members()
293 elif self.tok == '[':
294 self.accept()
295 expr = self.get_values()
e53188ad 296 elif self.tok in "'tfn":
c7a3f252
MA
297 expr = self.val
298 self.accept()
6974ccd5
MA
299 else:
300 raise QAPISchemaError(self, 'Expected "{", "[" or string')
c7a3f252 301 return expr
bd9927fe 302
00e4b285
MA
303#
304# Semantic analysis of schema expressions
305#
306
b86b05ed
WX
307def find_base_fields(base):
308 base_struct_define = find_struct(base)
309 if not base_struct_define:
310 return None
311 return base_struct_define['data']
312
811d04fd
EB
313# Return the qtype of an alternate branch, or None on error.
314def find_alternate_member_qtype(qapi_type):
44bd1276
EB
315 if builtin_types.has_key(qapi_type):
316 return builtin_types[qapi_type]
317 elif find_struct(qapi_type):
318 return "QTYPE_QDICT"
319 elif find_enum(qapi_type):
320 return "QTYPE_QSTRING"
811d04fd
EB
321 elif find_union(qapi_type):
322 return "QTYPE_QDICT"
44bd1276
EB
323 return None
324
bceae769
WX
325# Return the discriminator enum define if discriminator is specified as an
326# enum type, otherwise return None.
327def discriminator_find_enum_define(expr):
328 base = expr.get('base')
329 discriminator = expr.get('discriminator')
330
331 if not (discriminator and base):
332 return None
333
334 base_fields = find_base_fields(base)
335 if not base_fields:
336 return None
337
338 discriminator_type = base_fields.get(discriminator)
339 if not discriminator_type:
340 return None
341
342 return find_enum(discriminator_type)
343
d90675fa
MA
344# FIXME should enforce "other than downstream extensions [...], all
345# names should begin with a letter".
c9e0a798
EB
346valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$')
347def check_name(expr_info, source, name, allow_optional = False,
348 enum_member = False):
349 global valid_name
350 membername = name
351
352 if not isinstance(name, str):
353 raise QAPIExprError(expr_info,
354 "%s requires a string name" % source)
355 if name.startswith('*'):
356 membername = name[1:]
357 if not allow_optional:
358 raise QAPIExprError(expr_info,
359 "%s does not allow optional name '%s'"
360 % (source, name))
361 # Enum members can start with a digit, because the generated C
362 # code always prefixes it with the enum name
363 if enum_member:
364 membername = '_' + membername
365 if not valid_name.match(membername):
366 raise QAPIExprError(expr_info,
367 "%s uses invalid name '%s'" % (source, name))
368
00e4b285
MA
369def add_name(name, info, meta, implicit = False):
370 global all_names
371 check_name(info, "'%s'" % meta, name)
d90675fa
MA
372 # FIXME should reject names that differ only in '_' vs. '.'
373 # vs. '-', because they're liable to clash in generated C.
00e4b285
MA
374 if name in all_names:
375 raise QAPIExprError(info,
376 "%s '%s' is already defined"
377 % (all_names[name], name))
378 if not implicit and name[-4:] == 'Kind':
379 raise QAPIExprError(info,
380 "%s '%s' should not end in 'Kind'"
381 % (meta, name))
382 all_names[name] = meta
383
384def add_struct(definition, info):
385 global struct_types
386 name = definition['struct']
387 add_name(name, info, 'struct')
388 struct_types.append(definition)
389
390def find_struct(name):
391 global struct_types
392 for struct in struct_types:
393 if struct['struct'] == name:
394 return struct
395 return None
396
397def add_union(definition, info):
398 global union_types
399 name = definition['union']
400 add_name(name, info, 'union')
401 union_types.append(definition)
402
403def find_union(name):
404 global union_types
405 for union in union_types:
406 if union['union'] == name:
407 return union
408 return None
409
410def add_enum(name, info, enum_values = None, implicit = False):
411 global enum_types
412 add_name(name, info, 'enum', implicit)
413 enum_types.append({"enum_name": name, "enum_values": enum_values})
414
415def find_enum(name):
416 global enum_types
417 for enum in enum_types:
418 if enum['enum_name'] == name:
419 return enum
420 return None
421
422def is_enum(name):
423 return find_enum(name) != None
424
dd883c6f 425def check_type(expr_info, source, value, allow_array = False,
2cbf0992
EB
426 allow_dict = False, allow_optional = False,
427 allow_star = False, allow_metas = []):
dd883c6f
EB
428 global all_names
429 orig_value = value
430
431 if value is None:
432 return
433
2cbf0992 434 if allow_star and value == '**':
dd883c6f
EB
435 return
436
437 # Check if array type for value is okay
438 if isinstance(value, list):
439 if not allow_array:
440 raise QAPIExprError(expr_info,
441 "%s cannot be an array" % source)
442 if len(value) != 1 or not isinstance(value[0], str):
443 raise QAPIExprError(expr_info,
444 "%s: array type must contain single type name"
445 % source)
446 value = value[0]
447 orig_value = "array of %s" %value
448
449 # Check if type name for value is okay
450 if isinstance(value, str):
2cbf0992
EB
451 if value == '**':
452 raise QAPIExprError(expr_info,
453 "%s uses '**' but did not request 'gen':false"
454 % source)
dd883c6f
EB
455 if not value in all_names:
456 raise QAPIExprError(expr_info,
457 "%s uses unknown type '%s'"
458 % (source, orig_value))
459 if not all_names[value] in allow_metas:
460 raise QAPIExprError(expr_info,
461 "%s cannot use %s type '%s'"
462 % (source, all_names[value], orig_value))
463 return
464
465 # value is a dictionary, check that each member is okay
466 if not isinstance(value, OrderedDict):
467 raise QAPIExprError(expr_info,
468 "%s should be a dictionary" % source)
469 if not allow_dict:
470 raise QAPIExprError(expr_info,
471 "%s should be a type name" % source)
472 for (key, arg) in value.items():
c9e0a798
EB
473 check_name(expr_info, "Member of %s" % source, key,
474 allow_optional=allow_optional)
6b5abc7d
EB
475 # Todo: allow dictionaries to represent default values of
476 # an optional argument.
dd883c6f 477 check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
6b5abc7d 478 allow_array=True, allow_star=allow_star,
dd883c6f 479 allow_metas=['built-in', 'union', 'alternate', 'struct',
6b5abc7d 480 'enum'])
dd883c6f 481
ff55d72e
EB
482def check_member_clash(expr_info, base_name, data, source = ""):
483 base = find_struct(base_name)
484 assert base
485 base_members = base['data']
486 for key in data.keys():
487 if key.startswith('*'):
488 key = key[1:]
489 if key in base_members or "*" + key in base_members:
490 raise QAPIExprError(expr_info,
491 "Member name '%s'%s clashes with base '%s'"
492 % (key, source, base_name))
493 if base.get('base'):
494 check_member_clash(expr_info, base['base'], data, source)
495
dd883c6f
EB
496def check_command(expr, expr_info):
497 name = expr['command']
2cbf0992
EB
498 allow_star = expr.has_key('gen')
499
dd883c6f 500 check_type(expr_info, "'data' for command '%s'" % name,
c9e0a798 501 expr.get('data'), allow_dict=True, allow_optional=True,
315932b5 502 allow_metas=['struct'], allow_star=allow_star)
10d4d997
EB
503 returns_meta = ['union', 'struct']
504 if name in returns_whitelist:
505 returns_meta += ['built-in', 'alternate', 'enum']
dd883c6f 506 check_type(expr_info, "'returns' for command '%s'" % name,
9b090d42 507 expr.get('returns'), allow_array=True,
2cbf0992
EB
508 allow_optional=True, allow_metas=returns_meta,
509 allow_star=allow_star)
dd883c6f 510
21cd70df 511def check_event(expr, expr_info):
4dc2e690
EB
512 global events
513 name = expr['event']
4dc2e690
EB
514
515 if name.upper() == 'MAX':
516 raise QAPIExprError(expr_info, "Event name 'MAX' cannot be created")
517 events.append(name)
dd883c6f 518 check_type(expr_info, "'data' for event '%s'" % name,
c9e0a798 519 expr.get('data'), allow_dict=True, allow_optional=True,
315932b5 520 allow_metas=['struct'])
21cd70df 521
b86b05ed
WX
522def check_union(expr, expr_info):
523 name = expr['union']
524 base = expr.get('base')
525 discriminator = expr.get('discriminator')
526 members = expr['data']
44bd1276 527 values = { 'MAX': '(automatic)' }
b86b05ed 528
811d04fd 529 # Two types of unions, determined by discriminator.
811d04fd
EB
530
531 # With no discriminator it is a simple union.
532 if discriminator is None:
b86b05ed 533 enum_define = None
dd883c6f 534 allow_metas=['built-in', 'union', 'alternate', 'struct', 'enum']
44bd1276
EB
535 if base is not None:
536 raise QAPIExprError(expr_info,
811d04fd 537 "Simple union '%s' must not have a base"
44bd1276 538 % name)
b86b05ed
WX
539
540 # Else, it's a flat union.
541 else:
44bd1276
EB
542 # The object must have a string member 'base'.
543 if not isinstance(base, str):
b86b05ed 544 raise QAPIExprError(expr_info,
44bd1276 545 "Flat union '%s' must have a string base field"
b86b05ed 546 % name)
44bd1276
EB
547 base_fields = find_base_fields(base)
548 if not base_fields:
549 raise QAPIExprError(expr_info,
fd41dd4e 550 "Base '%s' is not a valid struct"
44bd1276
EB
551 % base)
552
c9e0a798 553 # The value of member 'discriminator' must name a non-optional
fd41dd4e 554 # member of the base struct.
c9e0a798
EB
555 check_name(expr_info, "Discriminator of flat union '%s'" % name,
556 discriminator)
b86b05ed
WX
557 discriminator_type = base_fields.get(discriminator)
558 if not discriminator_type:
559 raise QAPIExprError(expr_info,
560 "Discriminator '%s' is not a member of base "
fd41dd4e 561 "struct '%s'"
b86b05ed
WX
562 % (discriminator, base))
563 enum_define = find_enum(discriminator_type)
dd883c6f 564 allow_metas=['struct']
5223070c
WX
565 # Do not allow string discriminator
566 if not enum_define:
567 raise QAPIExprError(expr_info,
568 "Discriminator '%s' must be of enumeration "
569 "type" % discriminator)
b86b05ed
WX
570
571 # Check every branch
572 for (key, value) in members.items():
c9e0a798
EB
573 check_name(expr_info, "Member of union '%s'" % name, key)
574
dd883c6f 575 # Each value must name a known type; furthermore, in flat unions,
ff55d72e 576 # branches must be a struct with no overlapping member names
dd883c6f 577 check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
f9a14273 578 value, allow_array=not base, allow_metas=allow_metas)
ff55d72e
EB
579 if base:
580 branch_struct = find_struct(value)
581 assert branch_struct
582 check_member_clash(expr_info, base, branch_struct['data'],
583 " of branch '%s'" % key)
dd883c6f 584
44bd1276 585 # If the discriminator names an enum type, then all members
b86b05ed 586 # of 'data' must also be members of the enum type.
44bd1276
EB
587 if enum_define:
588 if not key in enum_define['enum_values']:
589 raise QAPIExprError(expr_info,
590 "Discriminator value '%s' is not found in "
591 "enum '%s'" %
592 (key, enum_define["enum_name"]))
593
594 # Otherwise, check for conflicts in the generated enum
595 else:
fa6068a1 596 c_key = camel_to_upper(key)
44bd1276
EB
597 if c_key in values:
598 raise QAPIExprError(expr_info,
599 "Union '%s' member '%s' clashes with '%s'"
600 % (name, key, values[c_key]))
601 values[c_key] = key
602
811d04fd 603def check_alternate(expr, expr_info):
ab916fad 604 name = expr['alternate']
811d04fd
EB
605 members = expr['data']
606 values = { 'MAX': '(automatic)' }
607 types_seen = {}
608
811d04fd
EB
609 # Check every branch
610 for (key, value) in members.items():
c9e0a798
EB
611 check_name(expr_info, "Member of alternate '%s'" % name, key)
612
811d04fd 613 # Check for conflicts in the generated enum
fa6068a1 614 c_key = camel_to_upper(key)
811d04fd
EB
615 if c_key in values:
616 raise QAPIExprError(expr_info,
ab916fad
EB
617 "Alternate '%s' member '%s' clashes with '%s'"
618 % (name, key, values[c_key]))
811d04fd 619 values[c_key] = key
44bd1276 620
811d04fd 621 # Ensure alternates have no type conflicts.
dd883c6f
EB
622 check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
623 value,
624 allow_metas=['built-in', 'union', 'struct', 'enum'])
811d04fd 625 qtype = find_alternate_member_qtype(value)
dd883c6f 626 assert qtype
811d04fd
EB
627 if qtype in types_seen:
628 raise QAPIExprError(expr_info,
ab916fad 629 "Alternate '%s' member '%s' can't "
811d04fd
EB
630 "be distinguished from member '%s'"
631 % (name, key, types_seen[qtype]))
632 types_seen[qtype] = key
b86b05ed 633
cf393590
EB
634def check_enum(expr, expr_info):
635 name = expr['enum']
636 members = expr.get('data')
637 values = { 'MAX': '(automatic)' }
638
639 if not isinstance(members, list):
640 raise QAPIExprError(expr_info,
641 "Enum '%s' requires an array for 'data'" % name)
642 for member in members:
c9e0a798
EB
643 check_name(expr_info, "Member of enum '%s'" %name, member,
644 enum_member=True)
fa6068a1 645 key = camel_to_upper(member)
cf393590
EB
646 if key in values:
647 raise QAPIExprError(expr_info,
648 "Enum '%s' member '%s' clashes with '%s'"
649 % (name, member, values[key]))
650 values[key] = member
651
dd883c6f 652def check_struct(expr, expr_info):
fd41dd4e 653 name = expr['struct']
dd883c6f
EB
654 members = expr['data']
655
fd41dd4e 656 check_type(expr_info, "'data' for struct '%s'" % name, members,
c9e0a798 657 allow_dict=True, allow_optional=True)
fd41dd4e 658 check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
dd883c6f 659 allow_metas=['struct'])
ff55d72e
EB
660 if expr.get('base'):
661 check_member_clash(expr_info, expr['base'], expr['data'])
dd883c6f 662
0545f6b8
EB
663def check_keys(expr_elem, meta, required, optional=[]):
664 expr = expr_elem['expr']
665 info = expr_elem['info']
666 name = expr[meta]
667 if not isinstance(name, str):
668 raise QAPIExprError(info,
669 "'%s' key must have a string value" % meta)
670 required = required + [ meta ]
671 for (key, value) in expr.items():
672 if not key in required and not key in optional:
673 raise QAPIExprError(info,
674 "Unknown key '%s' in %s '%s'"
675 % (key, meta, name))
2cbf0992
EB
676 if (key == 'gen' or key == 'success-response') and value != False:
677 raise QAPIExprError(info,
678 "'%s' of %s '%s' should only use false value"
679 % (key, meta, name))
0545f6b8
EB
680 for key in required:
681 if not expr.has_key(key):
682 raise QAPIExprError(info,
683 "Key '%s' is missing from %s '%s'"
684 % (key, meta, name))
685
4d076d67 686def check_exprs(exprs):
4dc2e690 687 global all_names
4dc2e690 688
4d076d67
MA
689 # Learn the types and check for valid expression keys
690 for builtin in builtin_types.keys():
691 all_names[builtin] = 'built-in'
692 for expr_elem in exprs:
693 expr = expr_elem['expr']
694 info = expr_elem['info']
695 if expr.has_key('enum'):
696 check_keys(expr_elem, 'enum', ['data'])
697 add_enum(expr['enum'], info, expr['data'])
698 elif expr.has_key('union'):
699 check_keys(expr_elem, 'union', ['data'],
700 ['base', 'discriminator'])
701 add_union(expr, info)
702 elif expr.has_key('alternate'):
703 check_keys(expr_elem, 'alternate', ['data'])
704 add_name(expr['alternate'], info, 'alternate')
705 elif expr.has_key('struct'):
706 check_keys(expr_elem, 'struct', ['data'], ['base'])
707 add_struct(expr, info)
708 elif expr.has_key('command'):
709 check_keys(expr_elem, 'command', [],
710 ['data', 'returns', 'gen', 'success-response'])
711 add_name(expr['command'], info, 'command')
712 elif expr.has_key('event'):
713 check_keys(expr_elem, 'event', [], ['data'])
714 add_name(expr['event'], info, 'event')
715 else:
716 raise QAPIExprError(expr_elem['info'],
717 "Expression is missing metatype")
2caba36c 718
4d076d67
MA
719 # Try again for hidden UnionKind enum
720 for expr_elem in exprs:
721 expr = expr_elem['expr']
722 if expr.has_key('union'):
723 if not discriminator_find_enum_define(expr):
724 add_enum('%sKind' % expr['union'], expr_elem['info'],
4dc2e690 725 implicit=True)
4d076d67
MA
726 elif expr.has_key('alternate'):
727 add_enum('%sKind' % expr['alternate'], expr_elem['info'],
728 implicit=True)
729
730 # Validate that exprs make sense
731 for expr_elem in exprs:
732 expr = expr_elem['expr']
733 info = expr_elem['info']
268a1c5e 734
4d076d67
MA
735 if expr.has_key('enum'):
736 check_enum(expr, info)
737 elif expr.has_key('union'):
738 check_union(expr, info)
739 elif expr.has_key('alternate'):
740 check_alternate(expr, info)
741 elif expr.has_key('struct'):
742 check_struct(expr, info)
743 elif expr.has_key('command'):
744 check_command(expr, info)
745 elif expr.has_key('event'):
746 check_event(expr, info)
747 else:
748 assert False, 'unexpected meta type'
749
750 return map(lambda expr_elem: expr_elem['expr'], exprs)
751
752def parse_schema(fname):
753 try:
754 schema = QAPISchema(open(fname, "r"))
755 return check_exprs(schema.exprs)
756 except (QAPISchemaError, QAPIExprError), e:
b86b05ed
WX
757 print >>sys.stderr, e
758 exit(1)
759
00e4b285
MA
760#
761# Code generation helpers
762#
763
0f923be2 764def parse_args(typeinfo):
fe2a9303 765 if isinstance(typeinfo, str):
b35284ea
KW
766 struct = find_struct(typeinfo)
767 assert struct != None
768 typeinfo = struct['data']
769
0f923be2
MR
770 for member in typeinfo:
771 argname = member
772 argentry = typeinfo[member]
773 optional = False
0f923be2
MR
774 if member.startswith('*'):
775 argname = member[1:]
776 optional = True
6b5abc7d
EB
777 # Todo: allow argentry to be OrderedDict, for providing the
778 # value of an optional argument.
779 yield (argname, argentry, optional)
0f923be2 780
0f923be2
MR
781def camel_case(name):
782 new_name = ''
783 first = True
784 for ch in name:
785 if ch in ['_', '-']:
786 first = True
787 elif first:
788 new_name += ch.upper()
789 first = False
790 else:
791 new_name += ch.lower()
792 return new_name
793
849bc538
MA
794# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
795# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
796# ENUM24_Name -> ENUM24_NAME
797def camel_to_upper(value):
798 c_fun_str = c_name(value, False)
799 if value.isupper():
800 return c_fun_str
801
802 new_name = ''
803 l = len(c_fun_str)
804 for i in range(l):
805 c = c_fun_str[i]
806 # When c is upper and no "_" appears before, do more checks
807 if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
808 # Case 1: next string is lower
809 # Case 2: previous string is digit
810 if (i < (l - 1) and c_fun_str[i + 1].islower()) or \
811 c_fun_str[i - 1].isdigit():
812 new_name += '_'
813 new_name += c
814 return new_name.lstrip('_').upper()
815
816def c_enum_const(type_name, const_name):
817 return camel_to_upper(type_name + '_' + const_name)
818
18df515e 819c_name_trans = string.maketrans('.-', '__')
47299262 820
c6405b54
EB
821# Map @name to a valid C identifier.
822# If @protect, avoid returning certain ticklish identifiers (like
823# C keywords) by prepending "q_".
824#
825# Used for converting 'name' from a 'name':'type' qapi definition
826# into a generated struct member, as well as converting type names
827# into substrings of a generated C function name.
828# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
829# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
18df515e 830def c_name(name, protect=True):
427a1a2c
BS
831 # ANSI X3J11/88-090, 3.1.1
832 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
833 'default', 'do', 'double', 'else', 'enum', 'extern', 'float',
834 'for', 'goto', 'if', 'int', 'long', 'register', 'return',
835 'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
836 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while'])
837 # ISO/IEC 9899:1999, 6.4.1
838 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
839 # ISO/IEC 9899:2011, 6.4.1
840 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn',
841 '_Static_assert', '_Thread_local'])
842 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
843 # excluding _.*
844 gcc_words = set(['asm', 'typeof'])
6f88009e
TS
845 # C++ ISO/IEC 14882:2003 2.11
846 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
847 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
848 'namespace', 'new', 'operator', 'private', 'protected',
849 'public', 'reinterpret_cast', 'static_cast', 'template',
850 'this', 'throw', 'true', 'try', 'typeid', 'typename',
851 'using', 'virtual', 'wchar_t',
852 # alternative representations
853 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
854 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1057725f 855 # namespace pollution:
8592a545 856 polluted_words = set(['unix', 'errno'])
6f88009e 857 if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words):
427a1a2c 858 return "q_" + name
18df515e 859 return name.translate(c_name_trans)
0f923be2 860
c6405b54
EB
861# Map type @name to the C typedef name for the list form.
862#
863# ['Name'] -> 'NameList', ['x-Foo'] -> 'x_FooList', ['int'] -> 'intList'
0f923be2 864def c_list_type(name):
c6405b54 865 return type_name(name) + 'List'
0f923be2 866
c6405b54
EB
867# Map type @value to the C typedef form.
868#
869# Used for converting 'type' from a 'member':'type' qapi definition
870# into the alphanumeric portion of the type for a generated C parameter,
871# as well as generated C function names. See c_type() for the rest of
872# the conversion such as adding '*' on pointer types.
873# 'int' -> 'int', '[x-Foo]' -> 'x_FooList', '__a.b_c' -> '__a_b_c'
d5573446
EB
874def type_name(value):
875 if type(value) == list:
876 return c_list_type(value[0])
c6405b54
EB
877 if value in builtin_types.keys():
878 return value
879 return c_name(value)
0f923be2 880
05dfb26c 881eatspace = '\033EATSPACE.'
d5573446 882pointer_suffix = ' *' + eatspace
05dfb26c 883
c6405b54
EB
884# Map type @name to its C type expression.
885# If @is_param, const-qualify the string type.
886#
887# This function is used for computing the full C type of 'member':'name'.
05dfb26c
AK
888# A special suffix is added in c_type() for pointer types, and it's
889# stripped in mcgen(). So please notice this when you check the return
890# value of c_type() outside mcgen().
d5573446
EB
891def c_type(value, is_param=False):
892 if value == 'str':
0d14eeb2 893 if is_param:
d5573446
EB
894 return 'const char' + pointer_suffix
895 return 'char' + pointer_suffix
05dfb26c 896
d5573446 897 elif value == 'int':
0f923be2 898 return 'int64_t'
d5573446
EB
899 elif (value == 'int8' or value == 'int16' or value == 'int32' or
900 value == 'int64' or value == 'uint8' or value == 'uint16' or
901 value == 'uint32' or value == 'uint64'):
902 return value + '_t'
903 elif value == 'size':
092705d4 904 return 'uint64_t'
d5573446 905 elif value == 'bool':
0f923be2 906 return 'bool'
d5573446 907 elif value == 'number':
0f923be2 908 return 'double'
d5573446
EB
909 elif type(value) == list:
910 return c_list_type(value[0]) + pointer_suffix
911 elif is_enum(value):
c6405b54 912 return c_name(value)
d5573446 913 elif value == None:
0f923be2 914 return 'void'
d5573446
EB
915 elif value in events:
916 return camel_case(value) + 'Event' + pointer_suffix
0f923be2 917 else:
d5573446
EB
918 # complex type name
919 assert isinstance(value, str) and value != ""
c6405b54 920 return c_name(value) + pointer_suffix
05dfb26c 921
d5573446
EB
922def is_c_ptr(value):
923 return c_type(value).endswith(pointer_suffix)
0f923be2
MR
924
925def genindent(count):
926 ret = ""
927 for i in range(count):
928 ret += " "
929 return ret
930
931indent_level = 0
932
933def push_indent(indent_amount=4):
934 global indent_level
935 indent_level += indent_amount
936
937def pop_indent(indent_amount=4):
938 global indent_level
939 indent_level -= indent_amount
940
77e703b8
MA
941# Generate @code with @kwds interpolated.
942# Obey indent_level, and strip eatspace.
0f923be2 943def cgen(code, **kwds):
77e703b8
MA
944 raw = code % kwds
945 if indent_level:
946 indent = genindent(indent_level)
947 raw = re.subn("^.", indent + r'\g<0>', raw, 0, re.MULTILINE)
948 raw = raw[0]
949 return re.sub(re.escape(eatspace) + ' *', '', raw)
0f923be2
MR
950
951def mcgen(code, **kwds):
77e703b8
MA
952 if code[0] == '\n':
953 code = code[1:]
954 return cgen(code, **kwds)
0f923be2 955
0f923be2
MR
956
957def guardname(filename):
00dfc3b2 958 return c_name(filename, protect=False).upper()
c0afa9c5
MR
959
960def guardstart(name):
961 return mcgen('''
962
963#ifndef %(name)s
964#define %(name)s
965
966''',
967 name=guardname(name))
968
969def guardend(name):
970 return mcgen('''
971
972#endif /* %(name)s */
973
974''',
975 name=guardname(name))
2114f5a9 976
00e4b285
MA
977#
978# Common command line parsing
979#
980
2114f5a9
MA
981def parse_command_line(extra_options = "", extra_long_options = []):
982
983 try:
984 opts, args = getopt.gnu_getopt(sys.argv[1:],
16d80f61 985 "chp:o:" + extra_options,
2114f5a9 986 ["source", "header", "prefix=",
16d80f61 987 "output-dir="] + extra_long_options)
2114f5a9 988 except getopt.GetoptError, err:
b4540968 989 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
2114f5a9
MA
990 sys.exit(1)
991
992 output_dir = ""
993 prefix = ""
994 do_c = False
995 do_h = False
996 extra_opts = []
997
998 for oa in opts:
999 o, a = oa
1000 if o in ("-p", "--prefix"):
1cf47a15
MA
1001 match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1002 if match.end() != len(a):
1003 print >>sys.stderr, \
1004 "%s: 'funny character '%s' in argument of --prefix" \
1005 % (sys.argv[0], a[match.end()])
1006 sys.exit(1)
2114f5a9 1007 prefix = a
2114f5a9
MA
1008 elif o in ("-o", "--output-dir"):
1009 output_dir = a + "/"
1010 elif o in ("-c", "--source"):
1011 do_c = True
1012 elif o in ("-h", "--header"):
1013 do_h = True
1014 else:
1015 extra_opts.append(oa)
1016
1017 if not do_c and not do_h:
1018 do_c = True
1019 do_h = True
1020
16d80f61
MA
1021 if len(args) != 1:
1022 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
b4540968 1023 sys.exit(1)
54414047 1024 fname = args[0]
b4540968 1025
54414047 1026 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
12f8e1b9 1027
00e4b285
MA
1028#
1029# Generate output files with boilerplate
1030#
1031
12f8e1b9
MA
1032def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1033 c_comment, h_comment):
00dfc3b2 1034 guard = guardname(prefix + h_file)
12f8e1b9
MA
1035 c_file = output_dir + prefix + c_file
1036 h_file = output_dir + prefix + h_file
1037
1038 try:
1039 os.makedirs(output_dir)
1040 except os.error, e:
1041 if e.errno != errno.EEXIST:
1042 raise
1043
1044 def maybe_open(really, name, opt):
1045 if really:
1046 return open(name, opt)
1047 else:
1048 import StringIO
1049 return StringIO.StringIO()
1050
1051 fdef = maybe_open(do_c, c_file, 'w')
1052 fdecl = maybe_open(do_h, h_file, 'w')
1053
1054 fdef.write(mcgen('''
1055/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1056%(comment)s
1057''',
1058 comment = c_comment))
1059
1060 fdecl.write(mcgen('''
1061/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1062%(comment)s
1063#ifndef %(guard)s
1064#define %(guard)s
1065
1066''',
00dfc3b2 1067 comment = h_comment, guard = guard))
12f8e1b9
MA
1068
1069 return (fdef, fdecl)
1070
1071def close_output(fdef, fdecl):
1072 fdecl.write('''
1073#endif
1074''')
12f8e1b9 1075 fdecl.close()
12f8e1b9 1076 fdef.close()