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