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