]> git.proxmox.com Git - mirror_qemu.git/blob - scripts/qapi.py
qapi: rename input buttons
[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 'InputAxis', # TODO: drop when x-input-send-event is fixed
70 'QapiErrorClass', # all members, visible through errors
71 'UuidInfo', # UUID, visible through query-uuid
72 'X86CPURegister32', # all members, visible indirectly through qom-get
73 ]
74
75 enum_types = []
76 struct_types = []
77 union_types = []
78 events = []
79 all_names = {}
80
81 #
82 # Parsing the schema into expressions
83 #
84
85
86 def error_path(parent):
87 res = ""
88 while parent:
89 res = ("In file included from %s:%d:\n" % (parent['file'],
90 parent['line'])) + res
91 parent = parent['parent']
92 return res
93
94
95 class QAPISchemaError(Exception):
96 def __init__(self, schema, msg):
97 Exception.__init__(self)
98 self.fname = schema.fname
99 self.msg = msg
100 self.col = 1
101 self.line = schema.line
102 for ch in schema.src[schema.line_pos:schema.pos]:
103 if ch == '\t':
104 self.col = (self.col + 7) % 8 + 1
105 else:
106 self.col += 1
107 self.info = schema.incl_info
108
109 def __str__(self):
110 return error_path(self.info) + \
111 "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
112
113
114 class QAPIExprError(Exception):
115 def __init__(self, expr_info, msg):
116 Exception.__init__(self)
117 assert expr_info
118 self.info = expr_info
119 self.msg = msg
120
121 def __str__(self):
122 return error_path(self.info['parent']) + \
123 "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
124
125
126 class QAPISchemaParser(object):
127
128 def __init__(self, fp, previously_included=[], incl_info=None):
129 abs_fname = os.path.abspath(fp.name)
130 fname = fp.name
131 self.fname = fname
132 previously_included.append(abs_fname)
133 self.incl_info = incl_info
134 self.src = fp.read()
135 if self.src == '' or self.src[-1] != '\n':
136 self.src += '\n'
137 self.cursor = 0
138 self.line = 1
139 self.line_pos = 0
140 self.exprs = []
141 self.accept()
142
143 while self.tok is not None:
144 expr_info = {'file': fname, 'line': self.line,
145 'parent': self.incl_info}
146 expr = self.get_expr(False)
147 if isinstance(expr, dict) and "include" in expr:
148 if len(expr) != 1:
149 raise QAPIExprError(expr_info,
150 "Invalid 'include' directive")
151 include = expr["include"]
152 if not isinstance(include, str):
153 raise QAPIExprError(expr_info,
154 "Value of 'include' must be a string")
155 incl_abs_fname = os.path.join(os.path.dirname(abs_fname),
156 include)
157 # catch inclusion cycle
158 inf = expr_info
159 while inf:
160 if incl_abs_fname == os.path.abspath(inf['file']):
161 raise QAPIExprError(expr_info, "Inclusion loop for %s"
162 % include)
163 inf = inf['parent']
164 # skip multiple include of the same file
165 if incl_abs_fname in previously_included:
166 continue
167 try:
168 fobj = open(incl_abs_fname, 'r')
169 except IOError as e:
170 raise QAPIExprError(expr_info,
171 '%s: %s' % (e.strerror, include))
172 exprs_include = QAPISchemaParser(fobj, previously_included,
173 expr_info)
174 self.exprs.extend(exprs_include.exprs)
175 else:
176 expr_elem = {'expr': expr,
177 'info': expr_info}
178 self.exprs.append(expr_elem)
179
180 def accept(self):
181 while True:
182 self.tok = self.src[self.cursor]
183 self.pos = self.cursor
184 self.cursor += 1
185 self.val = None
186
187 if self.tok == '#':
188 self.cursor = self.src.find('\n', self.cursor)
189 elif self.tok in "{}:,[]":
190 return
191 elif self.tok == "'":
192 string = ''
193 esc = False
194 while True:
195 ch = self.src[self.cursor]
196 self.cursor += 1
197 if ch == '\n':
198 raise QAPISchemaError(self,
199 'Missing terminating "\'"')
200 if esc:
201 if ch == 'b':
202 string += '\b'
203 elif ch == 'f':
204 string += '\f'
205 elif ch == 'n':
206 string += '\n'
207 elif ch == 'r':
208 string += '\r'
209 elif ch == 't':
210 string += '\t'
211 elif ch == 'u':
212 value = 0
213 for _ in range(0, 4):
214 ch = self.src[self.cursor]
215 self.cursor += 1
216 if ch not in "0123456789abcdefABCDEF":
217 raise QAPISchemaError(self,
218 '\\u escape needs 4 '
219 'hex digits')
220 value = (value << 4) + int(ch, 16)
221 # If Python 2 and 3 didn't disagree so much on
222 # how to handle Unicode, then we could allow
223 # Unicode string defaults. But most of QAPI is
224 # ASCII-only, so we aren't losing much for now.
225 if not value or value > 0x7f:
226 raise QAPISchemaError(self,
227 'For now, \\u escape '
228 'only supports non-zero '
229 'values up to \\u007f')
230 string += chr(value)
231 elif ch in "\\/'\"":
232 string += ch
233 else:
234 raise QAPISchemaError(self,
235 "Unknown escape \\%s" % ch)
236 esc = False
237 elif ch == "\\":
238 esc = True
239 elif ch == "'":
240 self.val = string
241 return
242 else:
243 string += ch
244 elif self.src.startswith("true", self.pos):
245 self.val = True
246 self.cursor += 3
247 return
248 elif self.src.startswith("false", self.pos):
249 self.val = False
250 self.cursor += 4
251 return
252 elif self.src.startswith("null", self.pos):
253 self.val = None
254 self.cursor += 3
255 return
256 elif self.tok == '\n':
257 if self.cursor == len(self.src):
258 self.tok = None
259 return
260 self.line += 1
261 self.line_pos = self.cursor
262 elif not self.tok.isspace():
263 raise QAPISchemaError(self, 'Stray "%s"' % self.tok)
264
265 def get_members(self):
266 expr = OrderedDict()
267 if self.tok == '}':
268 self.accept()
269 return expr
270 if self.tok != "'":
271 raise QAPISchemaError(self, 'Expected string or "}"')
272 while True:
273 key = self.val
274 self.accept()
275 if self.tok != ':':
276 raise QAPISchemaError(self, 'Expected ":"')
277 self.accept()
278 if key in expr:
279 raise QAPISchemaError(self, 'Duplicate key "%s"' % key)
280 expr[key] = self.get_expr(True)
281 if self.tok == '}':
282 self.accept()
283 return expr
284 if self.tok != ',':
285 raise QAPISchemaError(self, 'Expected "," or "}"')
286 self.accept()
287 if self.tok != "'":
288 raise QAPISchemaError(self, 'Expected string')
289
290 def get_values(self):
291 expr = []
292 if self.tok == ']':
293 self.accept()
294 return expr
295 if self.tok not in "{['tfn":
296 raise QAPISchemaError(self, 'Expected "{", "[", "]", string, '
297 'boolean or "null"')
298 while True:
299 expr.append(self.get_expr(True))
300 if self.tok == ']':
301 self.accept()
302 return expr
303 if self.tok != ',':
304 raise QAPISchemaError(self, 'Expected "," or "]"')
305 self.accept()
306
307 def get_expr(self, nested):
308 if self.tok != '{' and not nested:
309 raise QAPISchemaError(self, 'Expected "{"')
310 if self.tok == '{':
311 self.accept()
312 expr = self.get_members()
313 elif self.tok == '[':
314 self.accept()
315 expr = self.get_values()
316 elif self.tok in "'tfn":
317 expr = self.val
318 self.accept()
319 else:
320 raise QAPISchemaError(self, 'Expected "{", "[" or string')
321 return expr
322
323 #
324 # Semantic analysis of schema expressions
325 # TODO fold into QAPISchema
326 # TODO catching name collisions in generated code would be nice
327 #
328
329
330 def find_base_fields(base):
331 base_struct_define = find_struct(base)
332 if not base_struct_define:
333 return None
334 return base_struct_define['data']
335
336
337 # Return the qtype of an alternate branch, or None on error.
338 def find_alternate_member_qtype(qapi_type):
339 if qapi_type in builtin_types:
340 return builtin_types[qapi_type]
341 elif find_struct(qapi_type):
342 return "QTYPE_QDICT"
343 elif find_enum(qapi_type):
344 return "QTYPE_QSTRING"
345 elif find_union(qapi_type):
346 return "QTYPE_QDICT"
347 return None
348
349
350 # Return the discriminator enum define if discriminator is specified as an
351 # enum type, otherwise return None.
352 def discriminator_find_enum_define(expr):
353 base = expr.get('base')
354 discriminator = expr.get('discriminator')
355
356 if not (discriminator and base):
357 return None
358
359 base_fields = find_base_fields(base)
360 if not base_fields:
361 return None
362
363 discriminator_type = base_fields.get(discriminator)
364 if not discriminator_type:
365 return None
366
367 return find_enum(discriminator_type)
368
369
370 # Names must be letters, numbers, -, and _. They must start with letter,
371 # except for downstream extensions which must start with __RFQDN_.
372 # Dots are only valid in the downstream extension prefix.
373 valid_name = re.compile('^(__[a-zA-Z0-9.-]+_)?'
374 '[a-zA-Z][a-zA-Z0-9_-]*$')
375
376
377 def check_name(expr_info, source, name, allow_optional=False,
378 enum_member=False):
379 global valid_name
380 membername = name
381
382 if not isinstance(name, str):
383 raise QAPIExprError(expr_info,
384 "%s requires a string name" % source)
385 if name.startswith('*'):
386 membername = name[1:]
387 if not allow_optional:
388 raise QAPIExprError(expr_info,
389 "%s does not allow optional name '%s'"
390 % (source, name))
391 # Enum members can start with a digit, because the generated C
392 # code always prefixes it with the enum name
393 if enum_member and membername[0].isdigit():
394 membername = 'D' + membername
395 # Reserve the entire 'q_' namespace for c_name()
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_fields = find_base_fields(base)
572 assert base_fields
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_fields.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 def c_type(self, is_param=False, is_unboxed=False):
827 return c_name(self.name) + pointer_suffix
828
829 def c_null(self):
830 return 'NULL'
831
832 def json_type(self):
833 pass
834
835 def alternate_qtype(self):
836 json2qtype = {
837 'string': 'QTYPE_QSTRING',
838 'number': 'QTYPE_QFLOAT',
839 'int': 'QTYPE_QINT',
840 'boolean': 'QTYPE_QBOOL',
841 'object': 'QTYPE_QDICT'
842 }
843 return json2qtype.get(self.json_type())
844
845
846 class QAPISchemaBuiltinType(QAPISchemaType):
847 def __init__(self, name, json_type, c_type, c_null):
848 QAPISchemaType.__init__(self, name, None)
849 assert not c_type or isinstance(c_type, str)
850 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
851 'value')
852 self._json_type_name = json_type
853 self._c_type_name = c_type
854 self._c_null_val = c_null
855
856 def c_name(self):
857 return self.name
858
859 def c_type(self, is_param=False, is_unboxed=False):
860 if is_param and self.name == 'str':
861 return 'const ' + self._c_type_name
862 return self._c_type_name
863
864 def c_null(self):
865 return self._c_null_val
866
867 def json_type(self):
868 return self._json_type_name
869
870 def visit(self, visitor):
871 visitor.visit_builtin_type(self.name, self.info, self.json_type())
872
873
874 class QAPISchemaEnumType(QAPISchemaType):
875 def __init__(self, name, info, values, prefix):
876 QAPISchemaType.__init__(self, name, info)
877 for v in values:
878 assert isinstance(v, QAPISchemaMember)
879 v.set_owner(name)
880 assert prefix is None or isinstance(prefix, str)
881 self.values = values
882 self.prefix = prefix
883
884 def check(self, schema):
885 seen = {}
886 for v in self.values:
887 v.check_clash(self.info, seen)
888
889 def is_implicit(self):
890 # See QAPISchema._make_implicit_enum_type()
891 return self.name.endswith('Kind')
892
893 def c_type(self, is_param=False, is_unboxed=False):
894 return c_name(self.name)
895
896 def member_names(self):
897 return [v.name for v in self.values]
898
899 def c_null(self):
900 return c_enum_const(self.name, (self.member_names() + ['_MAX'])[0],
901 self.prefix)
902
903 def json_type(self):
904 return 'string'
905
906 def visit(self, visitor):
907 visitor.visit_enum_type(self.name, self.info,
908 self.member_names(), self.prefix)
909
910
911 class QAPISchemaArrayType(QAPISchemaType):
912 def __init__(self, name, info, element_type):
913 QAPISchemaType.__init__(self, name, info)
914 assert isinstance(element_type, str)
915 self._element_type_name = element_type
916 self.element_type = None
917
918 def check(self, schema):
919 self.element_type = schema.lookup_type(self._element_type_name)
920 assert self.element_type
921
922 def is_implicit(self):
923 return True
924
925 def json_type(self):
926 return 'array'
927
928 def visit(self, visitor):
929 visitor.visit_array_type(self.name, self.info, self.element_type)
930
931
932 class QAPISchemaObjectType(QAPISchemaType):
933 def __init__(self, name, info, base, local_members, variants):
934 # struct has local_members, optional base, and no variants
935 # flat union has base, variants, and no local_members
936 # simple union has local_members, variants, and no base
937 QAPISchemaType.__init__(self, name, info)
938 assert base is None or isinstance(base, str)
939 for m in local_members:
940 assert isinstance(m, QAPISchemaObjectTypeMember)
941 m.set_owner(name)
942 if variants is not None:
943 assert isinstance(variants, QAPISchemaObjectTypeVariants)
944 variants.set_owner(name)
945 self._base_name = base
946 self.base = None
947 self.local_members = local_members
948 self.variants = variants
949 self.members = None
950
951 def check(self, schema):
952 if self.members is False: # check for cycles
953 raise QAPIExprError(self.info,
954 "Object %s contains itself" % self.name)
955 if self.members:
956 return
957 self.members = False # mark as being checked
958 seen = OrderedDict()
959 if self._base_name:
960 self.base = schema.lookup_type(self._base_name)
961 assert isinstance(self.base, QAPISchemaObjectType)
962 self.base.check(schema)
963 self.base.check_clash(schema, self.info, seen)
964 for m in self.local_members:
965 m.check(schema)
966 m.check_clash(self.info, seen)
967 self.members = seen.values()
968 if self.variants:
969 self.variants.check(schema, seen)
970 assert self.variants.tag_member in self.members
971 self.variants.check_clash(schema, self.info, seen)
972
973 # Check that the members of this type do not cause duplicate JSON fields,
974 # and update seen to track the members seen so far. Report any errors
975 # on behalf of info, which is not necessarily self.info
976 def check_clash(self, schema, info, seen):
977 assert not self.variants # not implemented
978 for m in self.members:
979 m.check_clash(info, seen)
980
981 def is_implicit(self):
982 # See QAPISchema._make_implicit_object_type()
983 return self.name[0] == ':'
984
985 def c_name(self):
986 assert not self.is_implicit()
987 return QAPISchemaType.c_name(self)
988
989 def c_type(self, is_param=False, is_unboxed=False):
990 assert not self.is_implicit()
991 if is_unboxed:
992 return c_name(self.name)
993 return c_name(self.name) + pointer_suffix
994
995 def json_type(self):
996 return 'object'
997
998 def visit(self, visitor):
999 visitor.visit_object_type(self.name, self.info,
1000 self.base, self.local_members, self.variants)
1001 visitor.visit_object_type_flat(self.name, self.info,
1002 self.members, self.variants)
1003
1004
1005 class QAPISchemaMember(object):
1006 role = 'member'
1007
1008 def __init__(self, name):
1009 assert isinstance(name, str)
1010 self.name = name
1011 self.owner = None
1012
1013 def set_owner(self, name):
1014 assert not self.owner
1015 self.owner = name
1016
1017 def check_clash(self, info, seen):
1018 cname = c_name(self.name)
1019 if cname.lower() != cname and self.owner not in case_whitelist:
1020 raise QAPIExprError(info,
1021 "%s should not use uppercase" % self.describe())
1022 if cname in seen:
1023 raise QAPIExprError(info,
1024 "%s collides with %s"
1025 % (self.describe(), seen[cname].describe()))
1026 seen[cname] = self
1027
1028 def _pretty_owner(self):
1029 owner = self.owner
1030 if owner.startswith(':obj-'):
1031 # See QAPISchema._make_implicit_object_type() - reverse the
1032 # mapping there to create a nice human-readable description
1033 owner = owner[5:]
1034 if owner.endswith('-arg'):
1035 return '(parameter of %s)' % owner[:-4]
1036 else:
1037 assert owner.endswith('-wrapper')
1038 # Unreachable and not implemented
1039 assert False
1040 if owner.endswith('Kind'):
1041 # See QAPISchema._make_implicit_enum_type()
1042 return '(branch of %s)' % owner[:-4]
1043 return '(%s of %s)' % (self.role, owner)
1044
1045 def describe(self):
1046 return "'%s' %s" % (self.name, self._pretty_owner())
1047
1048
1049 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1050 def __init__(self, name, typ, optional):
1051 QAPISchemaMember.__init__(self, name)
1052 assert isinstance(typ, str)
1053 assert isinstance(optional, bool)
1054 self._type_name = typ
1055 self.type = None
1056 self.optional = optional
1057
1058 def check(self, schema):
1059 assert self.owner
1060 self.type = schema.lookup_type(self._type_name)
1061 assert self.type
1062
1063
1064 class QAPISchemaObjectTypeVariants(object):
1065 def __init__(self, tag_name, tag_member, variants):
1066 # Flat unions pass tag_name but not tag_member.
1067 # Simple unions and alternates pass tag_member but not tag_name.
1068 # After check(), tag_member is always set, and tag_name remains
1069 # a reliable witness of being used by a flat union.
1070 assert bool(tag_member) != bool(tag_name)
1071 assert (isinstance(tag_name, str) or
1072 isinstance(tag_member, QAPISchemaObjectTypeMember))
1073 assert len(variants) > 0
1074 for v in variants:
1075 assert isinstance(v, QAPISchemaObjectTypeVariant)
1076 self.tag_name = tag_name
1077 self.tag_member = tag_member
1078 self.variants = variants
1079
1080 def set_owner(self, name):
1081 for v in self.variants:
1082 v.set_owner(name)
1083
1084 def check(self, schema, seen):
1085 if not self.tag_member: # flat union
1086 self.tag_member = seen[c_name(self.tag_name)]
1087 assert self.tag_name == self.tag_member.name
1088 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1089 for v in self.variants:
1090 v.check(schema)
1091 # Union names must match enum values; alternate names are
1092 # checked separately. Use 'seen' to tell the two apart.
1093 if seen:
1094 assert v.name in self.tag_member.type.member_names()
1095 assert isinstance(v.type, QAPISchemaObjectType)
1096 v.type.check(schema)
1097
1098 def check_clash(self, schema, info, seen):
1099 for v in self.variants:
1100 # Reset seen map for each variant, since qapi names from one
1101 # branch do not affect another branch
1102 assert isinstance(v.type, QAPISchemaObjectType)
1103 v.type.check_clash(schema, info, dict(seen))
1104
1105
1106 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1107 role = 'branch'
1108
1109 def __init__(self, name, typ):
1110 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1111
1112 # This function exists to support ugly simple union special cases
1113 # TODO get rid of them, and drop the function
1114 def simple_union_type(self):
1115 if (self.type.is_implicit() and
1116 isinstance(self.type, QAPISchemaObjectType)):
1117 assert len(self.type.members) == 1
1118 assert not self.type.variants
1119 return self.type.members[0].type
1120 return None
1121
1122
1123 class QAPISchemaAlternateType(QAPISchemaType):
1124 def __init__(self, name, info, variants):
1125 QAPISchemaType.__init__(self, name, info)
1126 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1127 assert not variants.tag_name
1128 variants.set_owner(name)
1129 variants.tag_member.set_owner(self.name)
1130 self.variants = variants
1131
1132 def check(self, schema):
1133 self.variants.tag_member.check(schema)
1134 # Not calling self.variants.check_clash(), because there's nothing
1135 # to clash with
1136 self.variants.check(schema, {})
1137 # Alternate branch names have no relation to the tag enum values;
1138 # so we have to check for potential name collisions ourselves.
1139 seen = {}
1140 for v in self.variants.variants:
1141 v.check_clash(self.info, seen)
1142
1143 def json_type(self):
1144 return 'value'
1145
1146 def visit(self, visitor):
1147 visitor.visit_alternate_type(self.name, self.info, self.variants)
1148
1149
1150 class QAPISchemaCommand(QAPISchemaEntity):
1151 def __init__(self, name, info, arg_type, ret_type, gen, success_response):
1152 QAPISchemaEntity.__init__(self, name, info)
1153 assert not arg_type or isinstance(arg_type, str)
1154 assert not ret_type or isinstance(ret_type, str)
1155 self._arg_type_name = arg_type
1156 self.arg_type = None
1157 self._ret_type_name = ret_type
1158 self.ret_type = None
1159 self.gen = gen
1160 self.success_response = success_response
1161
1162 def check(self, schema):
1163 if self._arg_type_name:
1164 self.arg_type = schema.lookup_type(self._arg_type_name)
1165 assert isinstance(self.arg_type, QAPISchemaObjectType)
1166 assert not self.arg_type.variants # not implemented
1167 if self._ret_type_name:
1168 self.ret_type = schema.lookup_type(self._ret_type_name)
1169 assert isinstance(self.ret_type, QAPISchemaType)
1170
1171 def visit(self, visitor):
1172 visitor.visit_command(self.name, self.info,
1173 self.arg_type, self.ret_type,
1174 self.gen, self.success_response)
1175
1176
1177 class QAPISchemaEvent(QAPISchemaEntity):
1178 def __init__(self, name, info, arg_type):
1179 QAPISchemaEntity.__init__(self, name, info)
1180 assert not arg_type or isinstance(arg_type, str)
1181 self._arg_type_name = arg_type
1182 self.arg_type = None
1183
1184 def check(self, schema):
1185 if self._arg_type_name:
1186 self.arg_type = schema.lookup_type(self._arg_type_name)
1187 assert isinstance(self.arg_type, QAPISchemaObjectType)
1188 assert not self.arg_type.variants # not implemented
1189
1190 def visit(self, visitor):
1191 visitor.visit_event(self.name, self.info, self.arg_type)
1192
1193
1194 class QAPISchema(object):
1195 def __init__(self, fname):
1196 try:
1197 self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
1198 self._entity_dict = {}
1199 self._predefining = True
1200 self._def_predefineds()
1201 self._predefining = False
1202 self._def_exprs()
1203 self.check()
1204 except (QAPISchemaError, QAPIExprError) as err:
1205 print >>sys.stderr, err
1206 exit(1)
1207
1208 def _def_entity(self, ent):
1209 # Only the predefined types are allowed to not have info
1210 assert ent.info or self._predefining
1211 assert ent.name not in self._entity_dict
1212 self._entity_dict[ent.name] = ent
1213
1214 def lookup_entity(self, name, typ=None):
1215 ent = self._entity_dict.get(name)
1216 if typ and not isinstance(ent, typ):
1217 return None
1218 return ent
1219
1220 def lookup_type(self, name):
1221 return self.lookup_entity(name, QAPISchemaType)
1222
1223 def _def_builtin_type(self, name, json_type, c_type, c_null):
1224 self._def_entity(QAPISchemaBuiltinType(name, json_type,
1225 c_type, c_null))
1226 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1227 # qapi-types.h from a single .c, all arrays of builtins must be
1228 # declared in the first file whether or not they are used. Nicer
1229 # would be to use lazy instantiation, while figuring out how to
1230 # avoid compilation issues with multiple qapi-types.h.
1231 self._make_array_type(name, None)
1232
1233 def _def_predefineds(self):
1234 for t in [('str', 'string', 'char' + pointer_suffix, 'NULL'),
1235 ('number', 'number', 'double', '0'),
1236 ('int', 'int', 'int64_t', '0'),
1237 ('int8', 'int', 'int8_t', '0'),
1238 ('int16', 'int', 'int16_t', '0'),
1239 ('int32', 'int', 'int32_t', '0'),
1240 ('int64', 'int', 'int64_t', '0'),
1241 ('uint8', 'int', 'uint8_t', '0'),
1242 ('uint16', 'int', 'uint16_t', '0'),
1243 ('uint32', 'int', 'uint32_t', '0'),
1244 ('uint64', 'int', 'uint64_t', '0'),
1245 ('size', 'int', 'uint64_t', '0'),
1246 ('bool', 'boolean', 'bool', 'false'),
1247 ('any', 'value', 'QObject' + pointer_suffix, 'NULL')]:
1248 self._def_builtin_type(*t)
1249 self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
1250 [], None)
1251 self._def_entity(self.the_empty_object_type)
1252 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1253 'qstring', 'qdict', 'qlist',
1254 'qfloat', 'qbool'])
1255 self._def_entity(QAPISchemaEnumType('QType', None, qtype_values,
1256 'QTYPE'))
1257
1258 def _make_enum_members(self, values):
1259 return [QAPISchemaMember(v) for v in values]
1260
1261 def _make_implicit_enum_type(self, name, info, values):
1262 # See also QAPISchemaObjectTypeMember._pretty_owner()
1263 name = name + 'Kind' # Use namespace reserved by add_name()
1264 self._def_entity(QAPISchemaEnumType(
1265 name, info, self._make_enum_members(values), None))
1266 return name
1267
1268 def _make_array_type(self, element_type, info):
1269 name = element_type + 'List' # Use namespace reserved by add_name()
1270 if not self.lookup_type(name):
1271 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1272 return name
1273
1274 def _make_implicit_object_type(self, name, info, role, members):
1275 if not members:
1276 return None
1277 # See also QAPISchemaObjectTypeMember._pretty_owner()
1278 name = ':obj-%s-%s' % (name, role)
1279 if not self.lookup_entity(name, QAPISchemaObjectType):
1280 self._def_entity(QAPISchemaObjectType(name, info, None,
1281 members, None))
1282 return name
1283
1284 def _def_enum_type(self, expr, info):
1285 name = expr['enum']
1286 data = expr['data']
1287 prefix = expr.get('prefix')
1288 self._def_entity(QAPISchemaEnumType(
1289 name, info, self._make_enum_members(data), prefix))
1290
1291 def _make_member(self, name, typ, info):
1292 optional = False
1293 if name.startswith('*'):
1294 name = name[1:]
1295 optional = True
1296 if isinstance(typ, list):
1297 assert len(typ) == 1
1298 typ = self._make_array_type(typ[0], info)
1299 return QAPISchemaObjectTypeMember(name, typ, optional)
1300
1301 def _make_members(self, data, info):
1302 return [self._make_member(key, value, info)
1303 for (key, value) in data.iteritems()]
1304
1305 def _def_struct_type(self, expr, info):
1306 name = expr['struct']
1307 base = expr.get('base')
1308 data = expr['data']
1309 self._def_entity(QAPISchemaObjectType(name, info, base,
1310 self._make_members(data, info),
1311 None))
1312
1313 def _make_variant(self, case, typ):
1314 return QAPISchemaObjectTypeVariant(case, typ)
1315
1316 def _make_simple_variant(self, case, typ, info):
1317 if isinstance(typ, list):
1318 assert len(typ) == 1
1319 typ = self._make_array_type(typ[0], info)
1320 typ = self._make_implicit_object_type(
1321 typ, info, 'wrapper', [self._make_member('data', typ, info)])
1322 return QAPISchemaObjectTypeVariant(case, typ)
1323
1324 def _def_union_type(self, expr, info):
1325 name = expr['union']
1326 data = expr['data']
1327 base = expr.get('base')
1328 tag_name = expr.get('discriminator')
1329 tag_member = None
1330 if tag_name:
1331 variants = [self._make_variant(key, value)
1332 for (key, value) in data.iteritems()]
1333 members = []
1334 else:
1335 variants = [self._make_simple_variant(key, value, info)
1336 for (key, value) in data.iteritems()]
1337 typ = self._make_implicit_enum_type(name, info,
1338 [v.name for v in variants])
1339 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1340 members = [tag_member]
1341 self._def_entity(
1342 QAPISchemaObjectType(name, info, base, members,
1343 QAPISchemaObjectTypeVariants(tag_name,
1344 tag_member,
1345 variants)))
1346
1347 def _def_alternate_type(self, expr, info):
1348 name = expr['alternate']
1349 data = expr['data']
1350 variants = [self._make_variant(key, value)
1351 for (key, value) in data.iteritems()]
1352 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1353 self._def_entity(
1354 QAPISchemaAlternateType(name, info,
1355 QAPISchemaObjectTypeVariants(None,
1356 tag_member,
1357 variants)))
1358
1359 def _def_command(self, expr, info):
1360 name = expr['command']
1361 data = expr.get('data')
1362 rets = expr.get('returns')
1363 gen = expr.get('gen', True)
1364 success_response = expr.get('success-response', True)
1365 if isinstance(data, OrderedDict):
1366 data = self._make_implicit_object_type(
1367 name, info, 'arg', self._make_members(data, info))
1368 if isinstance(rets, list):
1369 assert len(rets) == 1
1370 rets = self._make_array_type(rets[0], info)
1371 self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
1372 success_response))
1373
1374 def _def_event(self, expr, info):
1375 name = expr['event']
1376 data = expr.get('data')
1377 if isinstance(data, OrderedDict):
1378 data = self._make_implicit_object_type(
1379 name, info, 'arg', self._make_members(data, info))
1380 self._def_entity(QAPISchemaEvent(name, info, data))
1381
1382 def _def_exprs(self):
1383 for expr_elem in self.exprs:
1384 expr = expr_elem['expr']
1385 info = expr_elem['info']
1386 if 'enum' in expr:
1387 self._def_enum_type(expr, info)
1388 elif 'struct' in expr:
1389 self._def_struct_type(expr, info)
1390 elif 'union' in expr:
1391 self._def_union_type(expr, info)
1392 elif 'alternate' in expr:
1393 self._def_alternate_type(expr, info)
1394 elif 'command' in expr:
1395 self._def_command(expr, info)
1396 elif 'event' in expr:
1397 self._def_event(expr, info)
1398 else:
1399 assert False
1400
1401 def check(self):
1402 for ent in self._entity_dict.values():
1403 ent.check(self)
1404
1405 def visit(self, visitor):
1406 visitor.visit_begin(self)
1407 for (name, entity) in sorted(self._entity_dict.items()):
1408 if visitor.visit_needed(entity):
1409 entity.visit(visitor)
1410 visitor.visit_end()
1411
1412
1413 #
1414 # Code generation helpers
1415 #
1416
1417 def camel_case(name):
1418 new_name = ''
1419 first = True
1420 for ch in name:
1421 if ch in ['_', '-']:
1422 first = True
1423 elif first:
1424 new_name += ch.upper()
1425 first = False
1426 else:
1427 new_name += ch.lower()
1428 return new_name
1429
1430
1431 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1432 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1433 # ENUM24_Name -> ENUM24_NAME
1434 def camel_to_upper(value):
1435 c_fun_str = c_name(value, False)
1436 if value.isupper():
1437 return c_fun_str
1438
1439 new_name = ''
1440 l = len(c_fun_str)
1441 for i in range(l):
1442 c = c_fun_str[i]
1443 # When c is upper and no "_" appears before, do more checks
1444 if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
1445 if i < l - 1 and c_fun_str[i + 1].islower():
1446 new_name += '_'
1447 elif c_fun_str[i - 1].isdigit():
1448 new_name += '_'
1449 new_name += c
1450 return new_name.lstrip('_').upper()
1451
1452
1453 def c_enum_const(type_name, const_name, prefix=None):
1454 if prefix is not None:
1455 type_name = prefix
1456 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1457
1458 c_name_trans = string.maketrans('.-', '__')
1459
1460
1461 # Map @name to a valid C identifier.
1462 # If @protect, avoid returning certain ticklish identifiers (like
1463 # C keywords) by prepending "q_".
1464 #
1465 # Used for converting 'name' from a 'name':'type' qapi definition
1466 # into a generated struct member, as well as converting type names
1467 # into substrings of a generated C function name.
1468 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1469 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1470 def c_name(name, protect=True):
1471 # ANSI X3J11/88-090, 3.1.1
1472 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1473 'default', 'do', 'double', 'else', 'enum', 'extern',
1474 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1475 'return', 'short', 'signed', 'sizeof', 'static',
1476 'struct', 'switch', 'typedef', 'union', 'unsigned',
1477 'void', 'volatile', 'while'])
1478 # ISO/IEC 9899:1999, 6.4.1
1479 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1480 # ISO/IEC 9899:2011, 6.4.1
1481 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1482 '_Noreturn', '_Static_assert', '_Thread_local'])
1483 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1484 # excluding _.*
1485 gcc_words = set(['asm', 'typeof'])
1486 # C++ ISO/IEC 14882:2003 2.11
1487 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1488 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1489 'namespace', 'new', 'operator', 'private', 'protected',
1490 'public', 'reinterpret_cast', 'static_cast', 'template',
1491 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1492 'using', 'virtual', 'wchar_t',
1493 # alternative representations
1494 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1495 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1496 # namespace pollution:
1497 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
1498 name = name.translate(c_name_trans)
1499 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1500 | cpp_words | polluted_words):
1501 return "q_" + name
1502 return name
1503
1504 eatspace = '\033EATSPACE.'
1505 pointer_suffix = ' *' + eatspace
1506
1507
1508 def genindent(count):
1509 ret = ""
1510 for _ in range(count):
1511 ret += " "
1512 return ret
1513
1514 indent_level = 0
1515
1516
1517 def push_indent(indent_amount=4):
1518 global indent_level
1519 indent_level += indent_amount
1520
1521
1522 def pop_indent(indent_amount=4):
1523 global indent_level
1524 indent_level -= indent_amount
1525
1526
1527 # Generate @code with @kwds interpolated.
1528 # Obey indent_level, and strip eatspace.
1529 def cgen(code, **kwds):
1530 raw = code % kwds
1531 if indent_level:
1532 indent = genindent(indent_level)
1533 # re.subn() lacks flags support before Python 2.7, use re.compile()
1534 raw = re.subn(re.compile("^.", re.MULTILINE),
1535 indent + r'\g<0>', raw)
1536 raw = raw[0]
1537 return re.sub(re.escape(eatspace) + ' *', '', raw)
1538
1539
1540 def mcgen(code, **kwds):
1541 if code[0] == '\n':
1542 code = code[1:]
1543 return cgen(code, **kwds)
1544
1545
1546 def guardname(filename):
1547 return c_name(filename, protect=False).upper()
1548
1549
1550 def guardstart(name):
1551 return mcgen('''
1552
1553 #ifndef %(name)s
1554 #define %(name)s
1555
1556 ''',
1557 name=guardname(name))
1558
1559
1560 def guardend(name):
1561 return mcgen('''
1562
1563 #endif /* %(name)s */
1564
1565 ''',
1566 name=guardname(name))
1567
1568
1569 def gen_enum_lookup(name, values, prefix=None):
1570 ret = mcgen('''
1571
1572 const char *const %(c_name)s_lookup[] = {
1573 ''',
1574 c_name=c_name(name))
1575 for value in values:
1576 index = c_enum_const(name, value, prefix)
1577 ret += mcgen('''
1578 [%(index)s] = "%(value)s",
1579 ''',
1580 index=index, value=value)
1581
1582 max_index = c_enum_const(name, '_MAX', prefix)
1583 ret += mcgen('''
1584 [%(max_index)s] = NULL,
1585 };
1586 ''',
1587 max_index=max_index)
1588 return ret
1589
1590
1591 def gen_enum(name, values, prefix=None):
1592 # append automatically generated _MAX value
1593 enum_values = values + ['_MAX']
1594
1595 ret = mcgen('''
1596
1597 typedef enum %(c_name)s {
1598 ''',
1599 c_name=c_name(name))
1600
1601 i = 0
1602 for value in enum_values:
1603 ret += mcgen('''
1604 %(c_enum)s = %(i)d,
1605 ''',
1606 c_enum=c_enum_const(name, value, prefix),
1607 i=i)
1608 i += 1
1609
1610 ret += mcgen('''
1611 } %(c_name)s;
1612 ''',
1613 c_name=c_name(name))
1614
1615 ret += mcgen('''
1616
1617 extern const char *const %(c_name)s_lookup[];
1618 ''',
1619 c_name=c_name(name))
1620 return ret
1621
1622
1623 def gen_params(arg_type, extra):
1624 if not arg_type:
1625 return extra
1626 assert not arg_type.variants
1627 ret = ''
1628 sep = ''
1629 for memb in arg_type.members:
1630 ret += sep
1631 sep = ', '
1632 if memb.optional:
1633 ret += 'bool has_%s, ' % c_name(memb.name)
1634 ret += '%s %s' % (memb.type.c_type(is_param=True), c_name(memb.name))
1635 if extra:
1636 ret += sep + extra
1637 return ret
1638
1639
1640 def gen_err_check(label='out', skiperr=False):
1641 if skiperr:
1642 return ''
1643 return mcgen('''
1644 if (err) {
1645 goto %(label)s;
1646 }
1647 ''',
1648 label=label)
1649
1650
1651 def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False,
1652 label='out'):
1653 ret = ''
1654 if skiperr:
1655 errparg = 'NULL'
1656 else:
1657 errparg = '&err'
1658
1659 for memb in members:
1660 if memb.optional:
1661 ret += mcgen('''
1662 if (visit_optional(v, "%(name)s", &%(prefix)shas_%(c_name)s)) {
1663 ''',
1664 prefix=prefix, c_name=c_name(memb.name),
1665 name=memb.name)
1666 push_indent()
1667
1668 # Ugly: sometimes we need to cast away const
1669 if need_cast and memb.type.name == 'str':
1670 cast = '(char **)'
1671 else:
1672 cast = ''
1673
1674 ret += mcgen('''
1675 visit_type_%(c_type)s(v, "%(name)s", %(cast)s&%(prefix)s%(c_name)s, %(errp)s);
1676 ''',
1677 c_type=memb.type.c_name(), prefix=prefix, cast=cast,
1678 c_name=c_name(memb.name), name=memb.name,
1679 errp=errparg)
1680 ret += gen_err_check(skiperr=skiperr, label=label)
1681
1682 if memb.optional:
1683 pop_indent()
1684 ret += mcgen('''
1685 }
1686 ''')
1687 return ret
1688
1689
1690 #
1691 # Common command line parsing
1692 #
1693
1694
1695 def parse_command_line(extra_options="", extra_long_options=[]):
1696
1697 try:
1698 opts, args = getopt.gnu_getopt(sys.argv[1:],
1699 "chp:o:" + extra_options,
1700 ["source", "header", "prefix=",
1701 "output-dir="] + extra_long_options)
1702 except getopt.GetoptError as err:
1703 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
1704 sys.exit(1)
1705
1706 output_dir = ""
1707 prefix = ""
1708 do_c = False
1709 do_h = False
1710 extra_opts = []
1711
1712 for oa in opts:
1713 o, a = oa
1714 if o in ("-p", "--prefix"):
1715 match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1716 if match.end() != len(a):
1717 print >>sys.stderr, \
1718 "%s: 'funny character '%s' in argument of --prefix" \
1719 % (sys.argv[0], a[match.end()])
1720 sys.exit(1)
1721 prefix = a
1722 elif o in ("-o", "--output-dir"):
1723 output_dir = a + "/"
1724 elif o in ("-c", "--source"):
1725 do_c = True
1726 elif o in ("-h", "--header"):
1727 do_h = True
1728 else:
1729 extra_opts.append(oa)
1730
1731 if not do_c and not do_h:
1732 do_c = True
1733 do_h = True
1734
1735 if len(args) != 1:
1736 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
1737 sys.exit(1)
1738 fname = args[0]
1739
1740 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
1741
1742 #
1743 # Generate output files with boilerplate
1744 #
1745
1746
1747 def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1748 c_comment, h_comment):
1749 guard = guardname(prefix + h_file)
1750 c_file = output_dir + prefix + c_file
1751 h_file = output_dir + prefix + h_file
1752
1753 if output_dir:
1754 try:
1755 os.makedirs(output_dir)
1756 except os.error as e:
1757 if e.errno != errno.EEXIST:
1758 raise
1759
1760 def maybe_open(really, name, opt):
1761 if really:
1762 return open(name, opt)
1763 else:
1764 import StringIO
1765 return StringIO.StringIO()
1766
1767 fdef = maybe_open(do_c, c_file, 'w')
1768 fdecl = maybe_open(do_h, h_file, 'w')
1769
1770 fdef.write(mcgen('''
1771 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1772 %(comment)s
1773 ''',
1774 comment=c_comment))
1775
1776 fdecl.write(mcgen('''
1777 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1778 %(comment)s
1779 #ifndef %(guard)s
1780 #define %(guard)s
1781
1782 ''',
1783 comment=h_comment, guard=guard))
1784
1785 return (fdef, fdecl)
1786
1787
1788 def close_output(fdef, fdecl):
1789 fdecl.write('''
1790 #endif
1791 ''')
1792 fdecl.close()
1793 fdef.close()