qapi: Simplify QAPISchemaObjectTypeVariants.check()
[qemu/ar7.git] / scripts / qapi.py
blobc6cb17b14a7021fc6bc5a7ae7c7d3b85b73aad9c
2 # QAPI helper library
4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2015 Red Hat Inc.
7 # Authors:
8 # Anthony Liguori <aliguori@us.ibm.com>
9 # Markus Armbruster <armbru@redhat.com>
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
14 import re
15 from ordereddict import OrderedDict
16 import errno
17 import getopt
18 import os
19 import sys
20 import string
22 builtin_types = {
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 'size': 'QTYPE_QINT',
36 'any': None, # any qtype_code possible, actually
39 # Whitelist of commands allowed to return a non-dictionary
40 returns_whitelist = [
41 # From QMP:
42 'human-monitor-command',
43 'qom-get',
44 'query-migrate-cache-size',
45 'query-tpm-models',
46 'query-tpm-types',
47 'ringbuf-read',
49 # From QGA:
50 'guest-file-open',
51 'guest-fsfreeze-freeze',
52 'guest-fsfreeze-freeze-list',
53 'guest-fsfreeze-status',
54 'guest-fsfreeze-thaw',
55 'guest-get-time',
56 'guest-set-vcpus',
57 'guest-sync',
58 'guest-sync-delimited',
61 enum_types = []
62 struct_types = []
63 union_types = []
64 events = []
65 all_names = {}
68 # Parsing the schema into expressions
72 def 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
81 class QAPISchemaError(Exception):
82 def __init__(self, schema, msg):
83 Exception.__init__(self)
84 self.fname = schema.fname
85 self.msg = msg
86 self.col = 1
87 self.line = schema.line
88 for ch in schema.src[schema.line_pos:schema.pos]:
89 if ch == '\t':
90 self.col = (self.col + 7) % 8 + 1
91 else:
92 self.col += 1
93 self.info = schema.incl_info
95 def __str__(self):
96 return error_path(self.info) + \
97 "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
100 class QAPIExprError(Exception):
101 def __init__(self, expr_info, msg):
102 Exception.__init__(self)
103 assert expr_info
104 self.info = expr_info
105 self.msg = msg
107 def __str__(self):
108 return error_path(self.info['parent']) + \
109 "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
112 class QAPISchemaParser(object):
114 def __init__(self, fp, previously_included=[], incl_info=None):
115 abs_fname = os.path.abspath(fp.name)
116 fname = fp.name
117 self.fname = fname
118 previously_included.append(abs_fname)
119 self.incl_info = incl_info
120 self.src = fp.read()
121 if self.src == '' or self.src[-1] != '\n':
122 self.src += '\n'
123 self.cursor = 0
124 self.line = 1
125 self.line_pos = 0
126 self.exprs = []
127 self.accept()
129 while self.tok is not None:
130 expr_info = {'file': fname, 'line': self.line,
131 'parent': self.incl_info}
132 expr = self.get_expr(False)
133 if isinstance(expr, dict) and "include" in expr:
134 if len(expr) != 1:
135 raise QAPIExprError(expr_info,
136 "Invalid 'include' directive")
137 include = expr["include"]
138 if not isinstance(include, str):
139 raise QAPIExprError(expr_info,
140 "Value of 'include' must be a string")
141 incl_abs_fname = os.path.join(os.path.dirname(abs_fname),
142 include)
143 # catch inclusion cycle
144 inf = expr_info
145 while inf:
146 if incl_abs_fname == os.path.abspath(inf['file']):
147 raise QAPIExprError(expr_info, "Inclusion loop for %s"
148 % include)
149 inf = inf['parent']
150 # skip multiple include of the same file
151 if incl_abs_fname in previously_included:
152 continue
153 try:
154 fobj = open(incl_abs_fname, 'r')
155 except IOError, e:
156 raise QAPIExprError(expr_info,
157 '%s: %s' % (e.strerror, include))
158 exprs_include = QAPISchemaParser(fobj, previously_included,
159 expr_info)
160 self.exprs.extend(exprs_include.exprs)
161 else:
162 expr_elem = {'expr': expr,
163 'info': expr_info}
164 self.exprs.append(expr_elem)
166 def accept(self):
167 while True:
168 self.tok = self.src[self.cursor]
169 self.pos = self.cursor
170 self.cursor += 1
171 self.val = None
173 if self.tok == '#':
174 self.cursor = self.src.find('\n', self.cursor)
175 elif self.tok in "{}:,[]":
176 return
177 elif self.tok == "'":
178 string = ''
179 esc = False
180 while True:
181 ch = self.src[self.cursor]
182 self.cursor += 1
183 if ch == '\n':
184 raise QAPISchemaError(self,
185 'Missing terminating "\'"')
186 if esc:
187 if ch == 'b':
188 string += '\b'
189 elif ch == 'f':
190 string += '\f'
191 elif ch == 'n':
192 string += '\n'
193 elif ch == 'r':
194 string += '\r'
195 elif ch == 't':
196 string += '\t'
197 elif ch == 'u':
198 value = 0
199 for _ in range(0, 4):
200 ch = self.src[self.cursor]
201 self.cursor += 1
202 if ch not in "0123456789abcdefABCDEF":
203 raise QAPISchemaError(self,
204 '\\u escape needs 4 '
205 'hex digits')
206 value = (value << 4) + int(ch, 16)
207 # If Python 2 and 3 didn't disagree so much on
208 # how to handle Unicode, then we could allow
209 # Unicode string defaults. But most of QAPI is
210 # ASCII-only, so we aren't losing much for now.
211 if not value or value > 0x7f:
212 raise QAPISchemaError(self,
213 'For now, \\u escape '
214 'only supports non-zero '
215 'values up to \\u007f')
216 string += chr(value)
217 elif ch in "\\/'\"":
218 string += ch
219 else:
220 raise QAPISchemaError(self,
221 "Unknown escape \\%s" % ch)
222 esc = False
223 elif ch == "\\":
224 esc = True
225 elif ch == "'":
226 self.val = string
227 return
228 else:
229 string += ch
230 elif self.src.startswith("true", self.pos):
231 self.val = True
232 self.cursor += 3
233 return
234 elif self.src.startswith("false", self.pos):
235 self.val = False
236 self.cursor += 4
237 return
238 elif self.src.startswith("null", self.pos):
239 self.val = None
240 self.cursor += 3
241 return
242 elif self.tok == '\n':
243 if self.cursor == len(self.src):
244 self.tok = None
245 return
246 self.line += 1
247 self.line_pos = self.cursor
248 elif not self.tok.isspace():
249 raise QAPISchemaError(self, 'Stray "%s"' % self.tok)
251 def get_members(self):
252 expr = OrderedDict()
253 if self.tok == '}':
254 self.accept()
255 return expr
256 if self.tok != "'":
257 raise QAPISchemaError(self, 'Expected string or "}"')
258 while True:
259 key = self.val
260 self.accept()
261 if self.tok != ':':
262 raise QAPISchemaError(self, 'Expected ":"')
263 self.accept()
264 if key in expr:
265 raise QAPISchemaError(self, 'Duplicate key "%s"' % key)
266 expr[key] = self.get_expr(True)
267 if self.tok == '}':
268 self.accept()
269 return expr
270 if self.tok != ',':
271 raise QAPISchemaError(self, 'Expected "," or "}"')
272 self.accept()
273 if self.tok != "'":
274 raise QAPISchemaError(self, 'Expected string')
276 def get_values(self):
277 expr = []
278 if self.tok == ']':
279 self.accept()
280 return expr
281 if self.tok not in "{['tfn":
282 raise QAPISchemaError(self, 'Expected "{", "[", "]", string, '
283 'boolean or "null"')
284 while True:
285 expr.append(self.get_expr(True))
286 if self.tok == ']':
287 self.accept()
288 return expr
289 if self.tok != ',':
290 raise QAPISchemaError(self, 'Expected "," or "]"')
291 self.accept()
293 def get_expr(self, nested):
294 if self.tok != '{' and not nested:
295 raise QAPISchemaError(self, 'Expected "{"')
296 if self.tok == '{':
297 self.accept()
298 expr = self.get_members()
299 elif self.tok == '[':
300 self.accept()
301 expr = self.get_values()
302 elif self.tok in "'tfn":
303 expr = self.val
304 self.accept()
305 else:
306 raise QAPISchemaError(self, 'Expected "{", "[" or string')
307 return expr
310 # Semantic analysis of schema expressions
311 # TODO fold into QAPISchema
312 # TODO catching name collisions in generated code would be nice
316 def find_base_fields(base):
317 base_struct_define = find_struct(base)
318 if not base_struct_define:
319 return None
320 return base_struct_define['data']
323 # Return the qtype of an alternate branch, or None on error.
324 def find_alternate_member_qtype(qapi_type):
325 if qapi_type in builtin_types:
326 return builtin_types[qapi_type]
327 elif find_struct(qapi_type):
328 return "QTYPE_QDICT"
329 elif find_enum(qapi_type):
330 return "QTYPE_QSTRING"
331 elif find_union(qapi_type):
332 return "QTYPE_QDICT"
333 return None
336 # Return the discriminator enum define if discriminator is specified as an
337 # enum type, otherwise return None.
338 def discriminator_find_enum_define(expr):
339 base = expr.get('base')
340 discriminator = expr.get('discriminator')
342 if not (discriminator and base):
343 return None
345 base_fields = find_base_fields(base)
346 if not base_fields:
347 return None
349 discriminator_type = base_fields.get(discriminator)
350 if not discriminator_type:
351 return None
353 return find_enum(discriminator_type)
356 # FIXME should enforce "other than downstream extensions [...], all
357 # names should begin with a letter".
358 valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$')
361 def check_name(expr_info, source, name, allow_optional=False,
362 enum_member=False):
363 global valid_name
364 membername = name
366 if not isinstance(name, str):
367 raise QAPIExprError(expr_info,
368 "%s requires a string name" % source)
369 if name.startswith('*'):
370 membername = name[1:]
371 if not allow_optional:
372 raise QAPIExprError(expr_info,
373 "%s does not allow optional name '%s'"
374 % (source, name))
375 # Enum members can start with a digit, because the generated C
376 # code always prefixes it with the enum name
377 if enum_member:
378 membername = '_' + membername
379 # Reserve the entire 'q_' namespace for c_name()
380 if not valid_name.match(membername) or \
381 c_name(membername, False).startswith('q_'):
382 raise QAPIExprError(expr_info,
383 "%s uses invalid name '%s'" % (source, name))
386 def add_name(name, info, meta, implicit=False):
387 global all_names
388 check_name(info, "'%s'" % meta, name)
389 # FIXME should reject names that differ only in '_' vs. '.'
390 # vs. '-', because they're liable to clash in generated C.
391 if name in all_names:
392 raise QAPIExprError(info,
393 "%s '%s' is already defined"
394 % (all_names[name], name))
395 if not implicit and (name.endswith('Kind') or name.endswith('List')):
396 raise QAPIExprError(info,
397 "%s '%s' should not end in '%s'"
398 % (meta, name, name[-4:]))
399 all_names[name] = meta
402 def add_struct(definition, info):
403 global struct_types
404 name = definition['struct']
405 add_name(name, info, 'struct')
406 struct_types.append(definition)
409 def find_struct(name):
410 global struct_types
411 for struct in struct_types:
412 if struct['struct'] == name:
413 return struct
414 return None
417 def add_union(definition, info):
418 global union_types
419 name = definition['union']
420 add_name(name, info, 'union')
421 union_types.append(definition)
424 def find_union(name):
425 global union_types
426 for union in union_types:
427 if union['union'] == name:
428 return union
429 return None
432 def add_enum(name, info, enum_values=None, implicit=False):
433 global enum_types
434 add_name(name, info, 'enum', implicit)
435 enum_types.append({"enum_name": name, "enum_values": enum_values})
438 def find_enum(name):
439 global enum_types
440 for enum in enum_types:
441 if enum['enum_name'] == name:
442 return enum
443 return None
446 def is_enum(name):
447 return find_enum(name) is not None
450 def check_type(expr_info, source, value, allow_array=False,
451 allow_dict=False, allow_optional=False,
452 allow_metas=[]):
453 global all_names
455 if value is None:
456 return
458 # Check if array type for value is okay
459 if isinstance(value, list):
460 if not allow_array:
461 raise QAPIExprError(expr_info,
462 "%s cannot be an array" % source)
463 if len(value) != 1 or not isinstance(value[0], str):
464 raise QAPIExprError(expr_info,
465 "%s: array type must contain single type name"
466 % source)
467 value = value[0]
469 # Check if type name for value is okay
470 if isinstance(value, str):
471 if value not in all_names:
472 raise QAPIExprError(expr_info,
473 "%s uses unknown type '%s'"
474 % (source, value))
475 if not all_names[value] in allow_metas:
476 raise QAPIExprError(expr_info,
477 "%s cannot use %s type '%s'"
478 % (source, all_names[value], value))
479 return
481 if not allow_dict:
482 raise QAPIExprError(expr_info,
483 "%s should be a type name" % source)
485 if not isinstance(value, OrderedDict):
486 raise QAPIExprError(expr_info,
487 "%s should be a dictionary or type name" % source)
489 # value is a dictionary, check that each member is okay
490 for (key, arg) in value.items():
491 check_name(expr_info, "Member of %s" % source, key,
492 allow_optional=allow_optional)
493 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
494 raise QAPIExprError(expr_info,
495 "Member of %s uses reserved name '%s'"
496 % (source, key))
497 # Todo: allow dictionaries to represent default values of
498 # an optional argument.
499 check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
500 allow_array=True,
501 allow_metas=['built-in', 'union', 'alternate', 'struct',
502 'enum'])
505 def check_member_clash(expr_info, base_name, data, source=""):
506 base = find_struct(base_name)
507 assert base
508 base_members = base['data']
509 for key in data.keys():
510 if key.startswith('*'):
511 key = key[1:]
512 if key in base_members or "*" + key in base_members:
513 raise QAPIExprError(expr_info,
514 "Member name '%s'%s clashes with base '%s'"
515 % (key, source, base_name))
516 if base.get('base'):
517 check_member_clash(expr_info, base['base'], data, source)
520 def check_command(expr, expr_info):
521 name = expr['command']
523 check_type(expr_info, "'data' for command '%s'" % name,
524 expr.get('data'), allow_dict=True, allow_optional=True,
525 allow_metas=['struct'])
526 returns_meta = ['union', 'struct']
527 if name in returns_whitelist:
528 returns_meta += ['built-in', 'alternate', 'enum']
529 check_type(expr_info, "'returns' for command '%s'" % name,
530 expr.get('returns'), allow_array=True,
531 allow_optional=True, allow_metas=returns_meta)
534 def check_event(expr, expr_info):
535 global events
536 name = expr['event']
538 if name.upper() == 'MAX':
539 raise QAPIExprError(expr_info, "Event name 'MAX' cannot be created")
540 events.append(name)
541 check_type(expr_info, "'data' for event '%s'" % name,
542 expr.get('data'), allow_dict=True, allow_optional=True,
543 allow_metas=['struct'])
546 def check_union(expr, expr_info):
547 name = expr['union']
548 base = expr.get('base')
549 discriminator = expr.get('discriminator')
550 members = expr['data']
551 values = {'MAX': '(automatic)', 'KIND': '(automatic)',
552 'TYPE': '(automatic)'}
554 # Two types of unions, determined by discriminator.
556 # With no discriminator it is a simple union.
557 if discriminator is None:
558 enum_define = None
559 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
560 if base is not None:
561 raise QAPIExprError(expr_info,
562 "Simple union '%s' must not have a base"
563 % name)
565 # Else, it's a flat union.
566 else:
567 # The object must have a string member 'base'.
568 check_type(expr_info, "'base' for union '%s'" % name,
569 base, allow_metas=['struct'])
570 if not base:
571 raise QAPIExprError(expr_info,
572 "Flat union '%s' must have a base"
573 % name)
574 base_fields = find_base_fields(base)
575 assert base_fields
577 # The value of member 'discriminator' must name a non-optional
578 # member of the base struct.
579 check_name(expr_info, "Discriminator of flat union '%s'" % name,
580 discriminator)
581 discriminator_type = base_fields.get(discriminator)
582 if not discriminator_type:
583 raise QAPIExprError(expr_info,
584 "Discriminator '%s' is not a member of base "
585 "struct '%s'"
586 % (discriminator, base))
587 enum_define = find_enum(discriminator_type)
588 allow_metas = ['struct']
589 # Do not allow string discriminator
590 if not enum_define:
591 raise QAPIExprError(expr_info,
592 "Discriminator '%s' must be of enumeration "
593 "type" % discriminator)
595 # Check every branch
596 for (key, value) in members.items():
597 check_name(expr_info, "Member of union '%s'" % name, key)
599 # Each value must name a known type; furthermore, in flat unions,
600 # branches must be a struct with no overlapping member names
601 check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
602 value, allow_array=not base, allow_metas=allow_metas)
603 if base:
604 branch_struct = find_struct(value)
605 assert branch_struct
606 check_member_clash(expr_info, base, branch_struct['data'],
607 " of branch '%s'" % key)
609 # If the discriminator names an enum type, then all members
610 # of 'data' must also be members of the enum type, which in turn
611 # must not collide with the discriminator name.
612 if enum_define:
613 if key not in enum_define['enum_values']:
614 raise QAPIExprError(expr_info,
615 "Discriminator value '%s' is not found in "
616 "enum '%s'" %
617 (key, enum_define["enum_name"]))
618 if discriminator in enum_define['enum_values']:
619 raise QAPIExprError(expr_info,
620 "Discriminator name '%s' collides with "
621 "enum value in '%s'" %
622 (discriminator, enum_define["enum_name"]))
624 # Otherwise, check for conflicts in the generated enum
625 else:
626 c_key = camel_to_upper(key)
627 if c_key in values:
628 raise QAPIExprError(expr_info,
629 "Union '%s' member '%s' clashes with '%s'"
630 % (name, key, values[c_key]))
631 values[c_key] = key
634 def check_alternate(expr, expr_info):
635 name = expr['alternate']
636 members = expr['data']
637 values = {'MAX': '(automatic)'}
638 types_seen = {}
640 # Check every branch
641 for (key, value) in members.items():
642 check_name(expr_info, "Member of alternate '%s'" % name, key)
644 # Check for conflicts in the generated enum
645 c_key = camel_to_upper(key)
646 if c_key in values:
647 raise QAPIExprError(expr_info,
648 "Alternate '%s' member '%s' clashes with '%s'"
649 % (name, key, values[c_key]))
650 values[c_key] = key
652 # Ensure alternates have no type conflicts.
653 check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
654 value,
655 allow_metas=['built-in', 'union', 'struct', 'enum'])
656 qtype = find_alternate_member_qtype(value)
657 assert qtype
658 if qtype in types_seen:
659 raise QAPIExprError(expr_info,
660 "Alternate '%s' member '%s' can't "
661 "be distinguished from member '%s'"
662 % (name, key, types_seen[qtype]))
663 types_seen[qtype] = key
666 def check_enum(expr, expr_info):
667 name = expr['enum']
668 members = expr.get('data')
669 prefix = expr.get('prefix')
670 values = {'MAX': '(automatic)'}
672 if not isinstance(members, list):
673 raise QAPIExprError(expr_info,
674 "Enum '%s' requires an array for 'data'" % name)
675 if prefix is not None and not isinstance(prefix, str):
676 raise QAPIExprError(expr_info,
677 "Enum '%s' requires a string for 'prefix'" % name)
678 for member in members:
679 check_name(expr_info, "Member of enum '%s'" % name, member,
680 enum_member=True)
681 key = camel_to_upper(member)
682 if key in values:
683 raise QAPIExprError(expr_info,
684 "Enum '%s' member '%s' clashes with '%s'"
685 % (name, member, values[key]))
686 values[key] = member
689 def check_struct(expr, expr_info):
690 name = expr['struct']
691 members = expr['data']
693 check_type(expr_info, "'data' for struct '%s'" % name, members,
694 allow_dict=True, allow_optional=True)
695 check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
696 allow_metas=['struct'])
697 if expr.get('base'):
698 check_member_clash(expr_info, expr['base'], expr['data'])
701 def check_keys(expr_elem, meta, required, optional=[]):
702 expr = expr_elem['expr']
703 info = expr_elem['info']
704 name = expr[meta]
705 if not isinstance(name, str):
706 raise QAPIExprError(info,
707 "'%s' key must have a string value" % meta)
708 required = required + [meta]
709 for (key, value) in expr.items():
710 if key not in required and key not in optional:
711 raise QAPIExprError(info,
712 "Unknown key '%s' in %s '%s'"
713 % (key, meta, name))
714 if (key == 'gen' or key == 'success-response') and value is not False:
715 raise QAPIExprError(info,
716 "'%s' of %s '%s' should only use false value"
717 % (key, meta, name))
718 for key in required:
719 if key not in expr:
720 raise QAPIExprError(info,
721 "Key '%s' is missing from %s '%s'"
722 % (key, meta, name))
725 def check_exprs(exprs):
726 global all_names
728 # Learn the types and check for valid expression keys
729 for builtin in builtin_types.keys():
730 all_names[builtin] = 'built-in'
731 for expr_elem in exprs:
732 expr = expr_elem['expr']
733 info = expr_elem['info']
734 if 'enum' in expr:
735 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
736 add_enum(expr['enum'], info, expr['data'])
737 elif 'union' in expr:
738 check_keys(expr_elem, 'union', ['data'],
739 ['base', 'discriminator'])
740 add_union(expr, info)
741 elif 'alternate' in expr:
742 check_keys(expr_elem, 'alternate', ['data'])
743 add_name(expr['alternate'], info, 'alternate')
744 elif 'struct' in expr:
745 check_keys(expr_elem, 'struct', ['data'], ['base'])
746 add_struct(expr, info)
747 elif 'command' in expr:
748 check_keys(expr_elem, 'command', [],
749 ['data', 'returns', 'gen', 'success-response'])
750 add_name(expr['command'], info, 'command')
751 elif 'event' in expr:
752 check_keys(expr_elem, 'event', [], ['data'])
753 add_name(expr['event'], info, 'event')
754 else:
755 raise QAPIExprError(expr_elem['info'],
756 "Expression is missing metatype")
758 # Try again for hidden UnionKind enum
759 for expr_elem in exprs:
760 expr = expr_elem['expr']
761 if 'union' in expr:
762 if not discriminator_find_enum_define(expr):
763 add_enum('%sKind' % expr['union'], expr_elem['info'],
764 implicit=True)
765 elif 'alternate' in expr:
766 add_enum('%sKind' % expr['alternate'], expr_elem['info'],
767 implicit=True)
769 # Validate that exprs make sense
770 for expr_elem in exprs:
771 expr = expr_elem['expr']
772 info = expr_elem['info']
774 if 'enum' in expr:
775 check_enum(expr, info)
776 elif 'union' in expr:
777 check_union(expr, info)
778 elif 'alternate' in expr:
779 check_alternate(expr, info)
780 elif 'struct' in expr:
781 check_struct(expr, info)
782 elif 'command' in expr:
783 check_command(expr, info)
784 elif 'event' in expr:
785 check_event(expr, info)
786 else:
787 assert False, 'unexpected meta type'
789 return exprs
793 # Schema compiler frontend
796 class QAPISchemaEntity(object):
797 def __init__(self, name, info):
798 assert isinstance(name, str)
799 self.name = name
800 # For explicitly defined entities, info points to the (explicit)
801 # definition. For builtins (and their arrays), info is None.
802 # For implicitly defined entities, info points to a place that
803 # triggered the implicit definition (there may be more than one
804 # such place).
805 self.info = info
807 def c_name(self):
808 return c_name(self.name)
810 def check(self, schema):
811 pass
813 def is_implicit(self):
814 return not self.info
816 def visit(self, visitor):
817 pass
820 class QAPISchemaVisitor(object):
821 def visit_begin(self, schema):
822 pass
824 def visit_end(self):
825 pass
827 def visit_needed(self, entity):
828 # Default to visiting everything
829 return True
831 def visit_builtin_type(self, name, info, json_type):
832 pass
834 def visit_enum_type(self, name, info, values, prefix):
835 pass
837 def visit_array_type(self, name, info, element_type):
838 pass
840 def visit_object_type(self, name, info, base, members, variants):
841 pass
843 def visit_object_type_flat(self, name, info, members, variants):
844 pass
846 def visit_alternate_type(self, name, info, variants):
847 pass
849 def visit_command(self, name, info, arg_type, ret_type,
850 gen, success_response):
851 pass
853 def visit_event(self, name, info, arg_type):
854 pass
857 class QAPISchemaType(QAPISchemaEntity):
858 def c_type(self, is_param=False):
859 return c_name(self.name) + pointer_suffix
861 def c_null(self):
862 return 'NULL'
864 def json_type(self):
865 pass
867 def alternate_qtype(self):
868 json2qtype = {
869 'string': 'QTYPE_QSTRING',
870 'number': 'QTYPE_QFLOAT',
871 'int': 'QTYPE_QINT',
872 'boolean': 'QTYPE_QBOOL',
873 'object': 'QTYPE_QDICT'
875 return json2qtype.get(self.json_type())
878 class QAPISchemaBuiltinType(QAPISchemaType):
879 def __init__(self, name, json_type, c_type, c_null):
880 QAPISchemaType.__init__(self, name, None)
881 assert not c_type or isinstance(c_type, str)
882 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
883 'value')
884 self._json_type_name = json_type
885 self._c_type_name = c_type
886 self._c_null_val = c_null
888 def c_name(self):
889 return self.name
891 def c_type(self, is_param=False):
892 if is_param and self.name == 'str':
893 return 'const ' + self._c_type_name
894 return self._c_type_name
896 def c_null(self):
897 return self._c_null_val
899 def json_type(self):
900 return self._json_type_name
902 def visit(self, visitor):
903 visitor.visit_builtin_type(self.name, self.info, self.json_type())
906 class QAPISchemaEnumType(QAPISchemaType):
907 def __init__(self, name, info, values, prefix):
908 QAPISchemaType.__init__(self, name, info)
909 for v in values:
910 assert isinstance(v, str)
911 assert prefix is None or isinstance(prefix, str)
912 self.values = values
913 self.prefix = prefix
915 def check(self, schema):
916 assert len(set(self.values)) == len(self.values)
918 def is_implicit(self):
919 # See QAPISchema._make_implicit_enum_type()
920 return self.name.endswith('Kind')
922 def c_type(self, is_param=False):
923 return c_name(self.name)
925 def c_null(self):
926 return c_enum_const(self.name, (self.values + ['MAX'])[0],
927 self.prefix)
929 def json_type(self):
930 return 'string'
932 def visit(self, visitor):
933 visitor.visit_enum_type(self.name, self.info,
934 self.values, self.prefix)
937 class QAPISchemaArrayType(QAPISchemaType):
938 def __init__(self, name, info, element_type):
939 QAPISchemaType.__init__(self, name, info)
940 assert isinstance(element_type, str)
941 self._element_type_name = element_type
942 self.element_type = None
944 def check(self, schema):
945 self.element_type = schema.lookup_type(self._element_type_name)
946 assert self.element_type
948 def is_implicit(self):
949 return True
951 def json_type(self):
952 return 'array'
954 def visit(self, visitor):
955 visitor.visit_array_type(self.name, self.info, self.element_type)
958 class QAPISchemaObjectType(QAPISchemaType):
959 def __init__(self, name, info, base, local_members, variants):
960 # struct has local_members, optional base, and no variants
961 # flat union has base, variants, and no local_members
962 # simple union has local_members, variants, and no base
963 QAPISchemaType.__init__(self, name, info)
964 assert base is None or isinstance(base, str)
965 for m in local_members:
966 assert isinstance(m, QAPISchemaObjectTypeMember)
967 assert (variants is None or
968 isinstance(variants, QAPISchemaObjectTypeVariants))
969 self._base_name = base
970 self.base = None
971 self.local_members = local_members
972 self.variants = variants
973 self.members = None
975 def check(self, schema):
976 assert self.members is not False # not running in cycles
977 if self.members:
978 return
979 self.members = False # mark as being checked
980 seen = OrderedDict()
981 if self._base_name:
982 self.base = schema.lookup_type(self._base_name)
983 assert isinstance(self.base, QAPISchemaObjectType)
984 assert not self.base.variants # not implemented
985 self.base.check(schema)
986 for m in self.base.members:
987 m.check_clash(seen)
988 for m in self.local_members:
989 m.check(schema)
990 m.check_clash(seen)
991 self.members = seen.values()
992 if self.variants:
993 self.variants.check(schema, seen)
994 assert self.variants.tag_member in self.members
996 def is_implicit(self):
997 # See QAPISchema._make_implicit_object_type()
998 return self.name[0] == ':'
1000 def c_name(self):
1001 assert not self.is_implicit()
1002 return QAPISchemaType.c_name(self)
1004 def c_type(self, is_param=False):
1005 assert not self.is_implicit()
1006 return QAPISchemaType.c_type(self)
1008 def json_type(self):
1009 return 'object'
1011 def visit(self, visitor):
1012 visitor.visit_object_type(self.name, self.info,
1013 self.base, self.local_members, self.variants)
1014 visitor.visit_object_type_flat(self.name, self.info,
1015 self.members, self.variants)
1018 class QAPISchemaObjectTypeMember(object):
1019 def __init__(self, name, typ, optional):
1020 assert isinstance(name, str)
1021 assert isinstance(typ, str)
1022 assert isinstance(optional, bool)
1023 self.name = name
1024 self._type_name = typ
1025 self.type = None
1026 self.optional = optional
1028 def check(self, schema):
1029 self.type = schema.lookup_type(self._type_name)
1030 assert self.type
1032 def check_clash(self, seen):
1033 # TODO change key of seen from QAPI name to C name
1034 assert self.name not in seen
1035 seen[self.name] = self
1038 class QAPISchemaObjectTypeVariants(object):
1039 def __init__(self, tag_name, tag_member, variants):
1040 # Flat unions pass tag_name but not tag_member.
1041 # Simple unions and alternates pass tag_member but not tag_name.
1042 # After check(), tag_member is always set, and tag_name remains
1043 # a reliable witness of being used by a flat union.
1044 assert bool(tag_member) != bool(tag_name)
1045 assert (isinstance(tag_name, str) or
1046 isinstance(tag_member, QAPISchemaObjectTypeMember))
1047 for v in variants:
1048 assert isinstance(v, QAPISchemaObjectTypeVariant)
1049 self.tag_name = tag_name
1050 self.tag_member = tag_member
1051 self.variants = variants
1053 def check(self, schema, seen):
1054 if not self.tag_member: # flat union
1055 self.tag_member = seen[self.tag_name]
1056 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1057 for v in self.variants:
1058 v.check(schema, self.tag_member.type)
1061 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1062 def __init__(self, name, typ):
1063 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1065 def check(self, schema, tag_type):
1066 QAPISchemaObjectTypeMember.check(self, schema)
1067 assert self.name in tag_type.values
1069 # This function exists to support ugly simple union special cases
1070 # TODO get rid of them, and drop the function
1071 def simple_union_type(self):
1072 if (self.type.is_implicit() and
1073 isinstance(self.type, QAPISchemaObjectType)):
1074 assert len(self.type.members) == 1
1075 assert not self.type.variants
1076 return self.type.members[0].type
1077 return None
1080 class QAPISchemaAlternateType(QAPISchemaType):
1081 def __init__(self, name, info, variants):
1082 QAPISchemaType.__init__(self, name, info)
1083 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1084 assert not variants.tag_name
1085 self.variants = variants
1087 def check(self, schema):
1088 self.variants.tag_member.check(schema)
1089 self.variants.check(schema, {})
1091 def json_type(self):
1092 return 'value'
1094 def visit(self, visitor):
1095 visitor.visit_alternate_type(self.name, self.info, self.variants)
1098 class QAPISchemaCommand(QAPISchemaEntity):
1099 def __init__(self, name, info, arg_type, ret_type, gen, success_response):
1100 QAPISchemaEntity.__init__(self, name, info)
1101 assert not arg_type or isinstance(arg_type, str)
1102 assert not ret_type or isinstance(ret_type, str)
1103 self._arg_type_name = arg_type
1104 self.arg_type = None
1105 self._ret_type_name = ret_type
1106 self.ret_type = None
1107 self.gen = gen
1108 self.success_response = success_response
1110 def check(self, schema):
1111 if self._arg_type_name:
1112 self.arg_type = schema.lookup_type(self._arg_type_name)
1113 assert isinstance(self.arg_type, QAPISchemaObjectType)
1114 assert not self.arg_type.variants # not implemented
1115 if self._ret_type_name:
1116 self.ret_type = schema.lookup_type(self._ret_type_name)
1117 assert isinstance(self.ret_type, QAPISchemaType)
1119 def visit(self, visitor):
1120 visitor.visit_command(self.name, self.info,
1121 self.arg_type, self.ret_type,
1122 self.gen, self.success_response)
1125 class QAPISchemaEvent(QAPISchemaEntity):
1126 def __init__(self, name, info, arg_type):
1127 QAPISchemaEntity.__init__(self, name, info)
1128 assert not arg_type or isinstance(arg_type, str)
1129 self._arg_type_name = arg_type
1130 self.arg_type = None
1132 def check(self, schema):
1133 if self._arg_type_name:
1134 self.arg_type = schema.lookup_type(self._arg_type_name)
1135 assert isinstance(self.arg_type, QAPISchemaObjectType)
1136 assert not self.arg_type.variants # not implemented
1138 def visit(self, visitor):
1139 visitor.visit_event(self.name, self.info, self.arg_type)
1142 class QAPISchema(object):
1143 def __init__(self, fname):
1144 try:
1145 self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
1146 self._entity_dict = {}
1147 self._predefining = True
1148 self._def_predefineds()
1149 self._predefining = False
1150 self._def_exprs()
1151 self.check()
1152 except (QAPISchemaError, QAPIExprError), err:
1153 print >>sys.stderr, err
1154 exit(1)
1156 def _def_entity(self, ent):
1157 # Only the predefined types are allowed to not have info
1158 assert ent.info or self._predefining
1159 assert ent.name not in self._entity_dict
1160 self._entity_dict[ent.name] = ent
1162 def lookup_entity(self, name, typ=None):
1163 ent = self._entity_dict.get(name)
1164 if typ and not isinstance(ent, typ):
1165 return None
1166 return ent
1168 def lookup_type(self, name):
1169 return self.lookup_entity(name, QAPISchemaType)
1171 def _def_builtin_type(self, name, json_type, c_type, c_null):
1172 self._def_entity(QAPISchemaBuiltinType(name, json_type,
1173 c_type, c_null))
1174 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1175 # qapi-types.h from a single .c, all arrays of builtins must be
1176 # declared in the first file whether or not they are used. Nicer
1177 # would be to use lazy instantiation, while figuring out how to
1178 # avoid compilation issues with multiple qapi-types.h.
1179 self._make_array_type(name, None)
1181 def _def_predefineds(self):
1182 for t in [('str', 'string', 'char' + pointer_suffix, 'NULL'),
1183 ('number', 'number', 'double', '0'),
1184 ('int', 'int', 'int64_t', '0'),
1185 ('int8', 'int', 'int8_t', '0'),
1186 ('int16', 'int', 'int16_t', '0'),
1187 ('int32', 'int', 'int32_t', '0'),
1188 ('int64', 'int', 'int64_t', '0'),
1189 ('uint8', 'int', 'uint8_t', '0'),
1190 ('uint16', 'int', 'uint16_t', '0'),
1191 ('uint32', 'int', 'uint32_t', '0'),
1192 ('uint64', 'int', 'uint64_t', '0'),
1193 ('size', 'int', 'uint64_t', '0'),
1194 ('bool', 'boolean', 'bool', 'false'),
1195 ('any', 'value', 'QObject' + pointer_suffix, 'NULL')]:
1196 self._def_builtin_type(*t)
1197 self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
1198 [], None)
1199 self._def_entity(self.the_empty_object_type)
1201 def _make_implicit_enum_type(self, name, info, values):
1202 name = name + 'Kind' # Use namespace reserved by add_name()
1203 self._def_entity(QAPISchemaEnumType(name, info, values, None))
1204 return name
1206 def _make_array_type(self, element_type, info):
1207 name = element_type + 'List' # Use namespace reserved by add_name()
1208 if not self.lookup_type(name):
1209 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1210 return name
1212 def _make_implicit_object_type(self, name, info, role, members):
1213 if not members:
1214 return None
1215 name = ':obj-%s-%s' % (name, role)
1216 if not self.lookup_entity(name, QAPISchemaObjectType):
1217 self._def_entity(QAPISchemaObjectType(name, info, None,
1218 members, None))
1219 return name
1221 def _def_enum_type(self, expr, info):
1222 name = expr['enum']
1223 data = expr['data']
1224 prefix = expr.get('prefix')
1225 self._def_entity(QAPISchemaEnumType(name, info, data, prefix))
1227 def _make_member(self, name, typ, info):
1228 optional = False
1229 if name.startswith('*'):
1230 name = name[1:]
1231 optional = True
1232 if isinstance(typ, list):
1233 assert len(typ) == 1
1234 typ = self._make_array_type(typ[0], info)
1235 return QAPISchemaObjectTypeMember(name, typ, optional)
1237 def _make_members(self, data, info):
1238 return [self._make_member(key, value, info)
1239 for (key, value) in data.iteritems()]
1241 def _def_struct_type(self, expr, info):
1242 name = expr['struct']
1243 base = expr.get('base')
1244 data = expr['data']
1245 self._def_entity(QAPISchemaObjectType(name, info, base,
1246 self._make_members(data, info),
1247 None))
1249 def _make_variant(self, case, typ):
1250 return QAPISchemaObjectTypeVariant(case, typ)
1252 def _make_simple_variant(self, case, typ, info):
1253 if isinstance(typ, list):
1254 assert len(typ) == 1
1255 typ = self._make_array_type(typ[0], info)
1256 typ = self._make_implicit_object_type(
1257 typ, info, 'wrapper', [self._make_member('data', typ, info)])
1258 return QAPISchemaObjectTypeVariant(case, typ)
1260 def _make_implicit_tag(self, type_name, info, variants):
1261 typ = self._make_implicit_enum_type(type_name, info,
1262 [v.name for v in variants])
1263 return QAPISchemaObjectTypeMember('type', typ, False)
1265 def _def_union_type(self, expr, info):
1266 name = expr['union']
1267 data = expr['data']
1268 base = expr.get('base')
1269 tag_name = expr.get('discriminator')
1270 tag_member = None
1271 if tag_name:
1272 variants = [self._make_variant(key, value)
1273 for (key, value) in data.iteritems()]
1274 members = []
1275 else:
1276 variants = [self._make_simple_variant(key, value, info)
1277 for (key, value) in data.iteritems()]
1278 tag_member = self._make_implicit_tag(name, info, variants)
1279 members = [tag_member]
1280 self._def_entity(
1281 QAPISchemaObjectType(name, info, base, members,
1282 QAPISchemaObjectTypeVariants(tag_name,
1283 tag_member,
1284 variants)))
1286 def _def_alternate_type(self, expr, info):
1287 name = expr['alternate']
1288 data = expr['data']
1289 variants = [self._make_variant(key, value)
1290 for (key, value) in data.iteritems()]
1291 tag_member = self._make_implicit_tag(name, info, variants)
1292 self._def_entity(
1293 QAPISchemaAlternateType(name, info,
1294 QAPISchemaObjectTypeVariants(None,
1295 tag_member,
1296 variants)))
1298 def _def_command(self, expr, info):
1299 name = expr['command']
1300 data = expr.get('data')
1301 rets = expr.get('returns')
1302 gen = expr.get('gen', True)
1303 success_response = expr.get('success-response', True)
1304 if isinstance(data, OrderedDict):
1305 data = self._make_implicit_object_type(
1306 name, info, 'arg', self._make_members(data, info))
1307 if isinstance(rets, list):
1308 assert len(rets) == 1
1309 rets = self._make_array_type(rets[0], info)
1310 self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
1311 success_response))
1313 def _def_event(self, expr, info):
1314 name = expr['event']
1315 data = expr.get('data')
1316 if isinstance(data, OrderedDict):
1317 data = self._make_implicit_object_type(
1318 name, info, 'arg', self._make_members(data, info))
1319 self._def_entity(QAPISchemaEvent(name, info, data))
1321 def _def_exprs(self):
1322 for expr_elem in self.exprs:
1323 expr = expr_elem['expr']
1324 info = expr_elem['info']
1325 if 'enum' in expr:
1326 self._def_enum_type(expr, info)
1327 elif 'struct' in expr:
1328 self._def_struct_type(expr, info)
1329 elif 'union' in expr:
1330 self._def_union_type(expr, info)
1331 elif 'alternate' in expr:
1332 self._def_alternate_type(expr, info)
1333 elif 'command' in expr:
1334 self._def_command(expr, info)
1335 elif 'event' in expr:
1336 self._def_event(expr, info)
1337 else:
1338 assert False
1340 def check(self):
1341 for ent in self._entity_dict.values():
1342 ent.check(self)
1344 def visit(self, visitor):
1345 visitor.visit_begin(self)
1346 for (name, entity) in sorted(self._entity_dict.items()):
1347 if visitor.visit_needed(entity):
1348 entity.visit(visitor)
1349 visitor.visit_end()
1353 # Code generation helpers
1356 def camel_case(name):
1357 new_name = ''
1358 first = True
1359 for ch in name:
1360 if ch in ['_', '-']:
1361 first = True
1362 elif first:
1363 new_name += ch.upper()
1364 first = False
1365 else:
1366 new_name += ch.lower()
1367 return new_name
1370 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1371 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1372 # ENUM24_Name -> ENUM24_NAME
1373 def camel_to_upper(value):
1374 c_fun_str = c_name(value, False)
1375 if value.isupper():
1376 return c_fun_str
1378 new_name = ''
1379 l = len(c_fun_str)
1380 for i in range(l):
1381 c = c_fun_str[i]
1382 # When c is upper and no "_" appears before, do more checks
1383 if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
1384 if i < l - 1 and c_fun_str[i + 1].islower():
1385 new_name += '_'
1386 elif c_fun_str[i - 1].isdigit():
1387 new_name += '_'
1388 new_name += c
1389 return new_name.lstrip('_').upper()
1392 def c_enum_const(type_name, const_name, prefix=None):
1393 if prefix is not None:
1394 type_name = prefix
1395 return camel_to_upper(type_name + '_' + const_name)
1397 c_name_trans = string.maketrans('.-', '__')
1400 # Map @name to a valid C identifier.
1401 # If @protect, avoid returning certain ticklish identifiers (like
1402 # C keywords) by prepending "q_".
1404 # Used for converting 'name' from a 'name':'type' qapi definition
1405 # into a generated struct member, as well as converting type names
1406 # into substrings of a generated C function name.
1407 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1408 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1409 def c_name(name, protect=True):
1410 # ANSI X3J11/88-090, 3.1.1
1411 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1412 'default', 'do', 'double', 'else', 'enum', 'extern',
1413 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1414 'return', 'short', 'signed', 'sizeof', 'static',
1415 'struct', 'switch', 'typedef', 'union', 'unsigned',
1416 'void', 'volatile', 'while'])
1417 # ISO/IEC 9899:1999, 6.4.1
1418 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1419 # ISO/IEC 9899:2011, 6.4.1
1420 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1421 '_Noreturn', '_Static_assert', '_Thread_local'])
1422 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1423 # excluding _.*
1424 gcc_words = set(['asm', 'typeof'])
1425 # C++ ISO/IEC 14882:2003 2.11
1426 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1427 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1428 'namespace', 'new', 'operator', 'private', 'protected',
1429 'public', 'reinterpret_cast', 'static_cast', 'template',
1430 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1431 'using', 'virtual', 'wchar_t',
1432 # alternative representations
1433 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1434 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1435 # namespace pollution:
1436 polluted_words = set(['unix', 'errno'])
1437 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1438 | cpp_words | polluted_words):
1439 return "q_" + name
1440 return name.translate(c_name_trans)
1442 eatspace = '\033EATSPACE.'
1443 pointer_suffix = ' *' + eatspace
1446 def genindent(count):
1447 ret = ""
1448 for _ in range(count):
1449 ret += " "
1450 return ret
1452 indent_level = 0
1455 def push_indent(indent_amount=4):
1456 global indent_level
1457 indent_level += indent_amount
1460 def pop_indent(indent_amount=4):
1461 global indent_level
1462 indent_level -= indent_amount
1465 # Generate @code with @kwds interpolated.
1466 # Obey indent_level, and strip eatspace.
1467 def cgen(code, **kwds):
1468 raw = code % kwds
1469 if indent_level:
1470 indent = genindent(indent_level)
1471 # re.subn() lacks flags support before Python 2.7, use re.compile()
1472 raw = re.subn(re.compile("^.", re.MULTILINE),
1473 indent + r'\g<0>', raw)
1474 raw = raw[0]
1475 return re.sub(re.escape(eatspace) + ' *', '', raw)
1478 def mcgen(code, **kwds):
1479 if code[0] == '\n':
1480 code = code[1:]
1481 return cgen(code, **kwds)
1484 def guardname(filename):
1485 return c_name(filename, protect=False).upper()
1488 def guardstart(name):
1489 return mcgen('''
1491 #ifndef %(name)s
1492 #define %(name)s
1494 ''',
1495 name=guardname(name))
1498 def guardend(name):
1499 return mcgen('''
1501 #endif /* %(name)s */
1503 ''',
1504 name=guardname(name))
1507 def gen_enum_lookup(name, values, prefix=None):
1508 ret = mcgen('''
1510 const char *const %(c_name)s_lookup[] = {
1511 ''',
1512 c_name=c_name(name))
1513 for value in values:
1514 index = c_enum_const(name, value, prefix)
1515 ret += mcgen('''
1516 [%(index)s] = "%(value)s",
1517 ''',
1518 index=index, value=value)
1520 max_index = c_enum_const(name, 'MAX', prefix)
1521 ret += mcgen('''
1522 [%(max_index)s] = NULL,
1524 ''',
1525 max_index=max_index)
1526 return ret
1529 def gen_enum(name, values, prefix=None):
1530 # append automatically generated _MAX value
1531 enum_values = values + ['MAX']
1533 ret = mcgen('''
1535 typedef enum %(c_name)s {
1536 ''',
1537 c_name=c_name(name))
1539 i = 0
1540 for value in enum_values:
1541 ret += mcgen('''
1542 %(c_enum)s = %(i)d,
1543 ''',
1544 c_enum=c_enum_const(name, value, prefix),
1545 i=i)
1546 i += 1
1548 ret += mcgen('''
1549 } %(c_name)s;
1550 ''',
1551 c_name=c_name(name))
1553 ret += mcgen('''
1555 extern const char *const %(c_name)s_lookup[];
1556 ''',
1557 c_name=c_name(name))
1558 return ret
1561 def gen_params(arg_type, extra):
1562 if not arg_type:
1563 return extra
1564 assert not arg_type.variants
1565 ret = ''
1566 sep = ''
1567 for memb in arg_type.members:
1568 ret += sep
1569 sep = ', '
1570 if memb.optional:
1571 ret += 'bool has_%s, ' % c_name(memb.name)
1572 ret += '%s %s' % (memb.type.c_type(is_param=True), c_name(memb.name))
1573 if extra:
1574 ret += sep + extra
1575 return ret
1578 def gen_err_check(label='out', skiperr=False):
1579 if skiperr:
1580 return ''
1581 return mcgen('''
1582 if (err) {
1583 goto %(label)s;
1585 ''',
1586 label=label)
1589 def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False):
1590 ret = ''
1591 if skiperr:
1592 errparg = 'NULL'
1593 else:
1594 errparg = '&err'
1596 for memb in members:
1597 if memb.optional:
1598 ret += mcgen('''
1599 visit_optional(v, &%(prefix)shas_%(c_name)s, "%(name)s", %(errp)s);
1600 ''',
1601 prefix=prefix, c_name=c_name(memb.name),
1602 name=memb.name, errp=errparg)
1603 ret += gen_err_check(skiperr=skiperr)
1604 ret += mcgen('''
1605 if (%(prefix)shas_%(c_name)s) {
1606 ''',
1607 prefix=prefix, c_name=c_name(memb.name))
1608 push_indent()
1610 # Ugly: sometimes we need to cast away const
1611 if need_cast and memb.type.name == 'str':
1612 cast = '(char **)'
1613 else:
1614 cast = ''
1616 ret += mcgen('''
1617 visit_type_%(c_type)s(v, %(cast)s&%(prefix)s%(c_name)s, "%(name)s", %(errp)s);
1618 ''',
1619 c_type=memb.type.c_name(), prefix=prefix, cast=cast,
1620 c_name=c_name(memb.name), name=memb.name,
1621 errp=errparg)
1622 ret += gen_err_check(skiperr=skiperr)
1624 if memb.optional:
1625 pop_indent()
1626 ret += mcgen('''
1628 ''')
1629 return ret
1633 # Common command line parsing
1637 def parse_command_line(extra_options="", extra_long_options=[]):
1639 try:
1640 opts, args = getopt.gnu_getopt(sys.argv[1:],
1641 "chp:o:" + extra_options,
1642 ["source", "header", "prefix=",
1643 "output-dir="] + extra_long_options)
1644 except getopt.GetoptError, err:
1645 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
1646 sys.exit(1)
1648 output_dir = ""
1649 prefix = ""
1650 do_c = False
1651 do_h = False
1652 extra_opts = []
1654 for oa in opts:
1655 o, a = oa
1656 if o in ("-p", "--prefix"):
1657 match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1658 if match.end() != len(a):
1659 print >>sys.stderr, \
1660 "%s: 'funny character '%s' in argument of --prefix" \
1661 % (sys.argv[0], a[match.end()])
1662 sys.exit(1)
1663 prefix = a
1664 elif o in ("-o", "--output-dir"):
1665 output_dir = a + "/"
1666 elif o in ("-c", "--source"):
1667 do_c = True
1668 elif o in ("-h", "--header"):
1669 do_h = True
1670 else:
1671 extra_opts.append(oa)
1673 if not do_c and not do_h:
1674 do_c = True
1675 do_h = True
1677 if len(args) != 1:
1678 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
1679 sys.exit(1)
1680 fname = args[0]
1682 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
1685 # Generate output files with boilerplate
1689 def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1690 c_comment, h_comment):
1691 guard = guardname(prefix + h_file)
1692 c_file = output_dir + prefix + c_file
1693 h_file = output_dir + prefix + h_file
1695 if output_dir:
1696 try:
1697 os.makedirs(output_dir)
1698 except os.error, e:
1699 if e.errno != errno.EEXIST:
1700 raise
1702 def maybe_open(really, name, opt):
1703 if really:
1704 return open(name, opt)
1705 else:
1706 import StringIO
1707 return StringIO.StringIO()
1709 fdef = maybe_open(do_c, c_file, 'w')
1710 fdecl = maybe_open(do_h, h_file, 'w')
1712 fdef.write(mcgen('''
1713 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1714 %(comment)s
1715 ''',
1716 comment=c_comment))
1718 fdecl.write(mcgen('''
1719 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1720 %(comment)s
1721 #ifndef %(guard)s
1722 #define %(guard)s
1724 ''',
1725 comment=h_comment, guard=guard))
1727 return (fdef, fdecl)
1730 def close_output(fdef, fdecl):
1731 fdecl.write('''
1732 #endif
1733 ''')
1734 fdecl.close()
1735 fdef.close()