]> git.proxmox.com Git - mirror_qemu.git/blob - scripts/qapi.py
68ee31903edfbcec9928e312b4756ee118e6ba6c
[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):
830 pass
831
832 def visit_event(self, name, info, arg_type):
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 c_name(self):
1003 assert self.name != 'q_empty'
1004 return QAPISchemaType.c_name(self)
1005
1006 def c_type(self):
1007 assert not self.is_implicit()
1008 return c_name(self.name) + pointer_suffix
1009
1010 def c_unboxed_type(self):
1011 return c_name(self.name)
1012
1013 def json_type(self):
1014 return 'object'
1015
1016 def visit(self, visitor):
1017 visitor.visit_object_type(self.name, self.info,
1018 self.base, self.local_members, self.variants)
1019 visitor.visit_object_type_flat(self.name, self.info,
1020 self.members, self.variants)
1021
1022
1023 class QAPISchemaMember(object):
1024 role = 'member'
1025
1026 def __init__(self, name):
1027 assert isinstance(name, str)
1028 self.name = name
1029 self.owner = None
1030
1031 def set_owner(self, name):
1032 assert not self.owner
1033 self.owner = name
1034
1035 def check_clash(self, info, seen):
1036 cname = c_name(self.name)
1037 if cname.lower() != cname and self.owner not in case_whitelist:
1038 raise QAPIExprError(info,
1039 "%s should not use uppercase" % self.describe())
1040 if cname in seen:
1041 raise QAPIExprError(info,
1042 "%s collides with %s"
1043 % (self.describe(), seen[cname].describe()))
1044 seen[cname] = self
1045
1046 def _pretty_owner(self):
1047 owner = self.owner
1048 if owner.startswith('q_obj_'):
1049 # See QAPISchema._make_implicit_object_type() - reverse the
1050 # mapping there to create a nice human-readable description
1051 owner = owner[6:]
1052 if owner.endswith('-arg'):
1053 return '(parameter of %s)' % owner[:-4]
1054 elif owner.endswith('-base'):
1055 return '(base of %s)' % owner[:-5]
1056 else:
1057 assert owner.endswith('-wrapper')
1058 # Unreachable and not implemented
1059 assert False
1060 if owner.endswith('Kind'):
1061 # See QAPISchema._make_implicit_enum_type()
1062 return '(branch of %s)' % owner[:-4]
1063 return '(%s of %s)' % (self.role, owner)
1064
1065 def describe(self):
1066 return "'%s' %s" % (self.name, self._pretty_owner())
1067
1068
1069 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1070 def __init__(self, name, typ, optional):
1071 QAPISchemaMember.__init__(self, name)
1072 assert isinstance(typ, str)
1073 assert isinstance(optional, bool)
1074 self._type_name = typ
1075 self.type = None
1076 self.optional = optional
1077
1078 def check(self, schema):
1079 assert self.owner
1080 self.type = schema.lookup_type(self._type_name)
1081 assert self.type
1082
1083
1084 class QAPISchemaObjectTypeVariants(object):
1085 def __init__(self, tag_name, tag_member, variants):
1086 # Flat unions pass tag_name but not tag_member.
1087 # Simple unions and alternates pass tag_member but not tag_name.
1088 # After check(), tag_member is always set, and tag_name remains
1089 # a reliable witness of being used by a flat union.
1090 assert bool(tag_member) != bool(tag_name)
1091 assert (isinstance(tag_name, str) or
1092 isinstance(tag_member, QAPISchemaObjectTypeMember))
1093 assert len(variants) > 0
1094 for v in variants:
1095 assert isinstance(v, QAPISchemaObjectTypeVariant)
1096 self.tag_name = tag_name
1097 self.tag_member = tag_member
1098 self.variants = variants
1099
1100 def set_owner(self, name):
1101 for v in self.variants:
1102 v.set_owner(name)
1103
1104 def check(self, schema, seen):
1105 if not self.tag_member: # flat union
1106 self.tag_member = seen[c_name(self.tag_name)]
1107 assert self.tag_name == self.tag_member.name
1108 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1109 for v in self.variants:
1110 v.check(schema)
1111 # Union names must match enum values; alternate names are
1112 # checked separately. Use 'seen' to tell the two apart.
1113 if seen:
1114 assert v.name in self.tag_member.type.member_names()
1115 assert isinstance(v.type, QAPISchemaObjectType)
1116 v.type.check(schema)
1117
1118 def check_clash(self, schema, info, seen):
1119 for v in self.variants:
1120 # Reset seen map for each variant, since qapi names from one
1121 # branch do not affect another branch
1122 assert isinstance(v.type, QAPISchemaObjectType)
1123 v.type.check_clash(schema, info, dict(seen))
1124
1125
1126 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1127 role = 'branch'
1128
1129 def __init__(self, name, typ):
1130 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1131
1132
1133 class QAPISchemaAlternateType(QAPISchemaType):
1134 def __init__(self, name, info, variants):
1135 QAPISchemaType.__init__(self, name, info)
1136 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1137 assert not variants.tag_name
1138 variants.set_owner(name)
1139 variants.tag_member.set_owner(self.name)
1140 self.variants = variants
1141
1142 def check(self, schema):
1143 self.variants.tag_member.check(schema)
1144 # Not calling self.variants.check_clash(), because there's nothing
1145 # to clash with
1146 self.variants.check(schema, {})
1147 # Alternate branch names have no relation to the tag enum values;
1148 # so we have to check for potential name collisions ourselves.
1149 seen = {}
1150 for v in self.variants.variants:
1151 v.check_clash(self.info, seen)
1152
1153 def c_type(self):
1154 return c_name(self.name) + pointer_suffix
1155
1156 def json_type(self):
1157 return 'value'
1158
1159 def visit(self, visitor):
1160 visitor.visit_alternate_type(self.name, self.info, self.variants)
1161
1162
1163 class QAPISchemaCommand(QAPISchemaEntity):
1164 def __init__(self, name, info, arg_type, ret_type, gen, success_response):
1165 QAPISchemaEntity.__init__(self, name, info)
1166 assert not arg_type or isinstance(arg_type, str)
1167 assert not ret_type or isinstance(ret_type, str)
1168 self._arg_type_name = arg_type
1169 self.arg_type = None
1170 self._ret_type_name = ret_type
1171 self.ret_type = None
1172 self.gen = gen
1173 self.success_response = success_response
1174
1175 def check(self, schema):
1176 if self._arg_type_name:
1177 self.arg_type = schema.lookup_type(self._arg_type_name)
1178 assert isinstance(self.arg_type, QAPISchemaObjectType)
1179 assert not self.arg_type.variants # not implemented
1180 if self._ret_type_name:
1181 self.ret_type = schema.lookup_type(self._ret_type_name)
1182 assert isinstance(self.ret_type, QAPISchemaType)
1183
1184 def visit(self, visitor):
1185 visitor.visit_command(self.name, self.info,
1186 self.arg_type, self.ret_type,
1187 self.gen, self.success_response)
1188
1189
1190 class QAPISchemaEvent(QAPISchemaEntity):
1191 def __init__(self, name, info, arg_type):
1192 QAPISchemaEntity.__init__(self, name, info)
1193 assert not arg_type or isinstance(arg_type, str)
1194 self._arg_type_name = arg_type
1195 self.arg_type = None
1196
1197 def check(self, schema):
1198 if self._arg_type_name:
1199 self.arg_type = schema.lookup_type(self._arg_type_name)
1200 assert isinstance(self.arg_type, QAPISchemaObjectType)
1201 assert not self.arg_type.variants # not implemented
1202
1203 def visit(self, visitor):
1204 visitor.visit_event(self.name, self.info, self.arg_type)
1205
1206
1207 class QAPISchema(object):
1208 def __init__(self, fname):
1209 try:
1210 self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
1211 self._entity_dict = {}
1212 self._predefining = True
1213 self._def_predefineds()
1214 self._predefining = False
1215 self._def_exprs()
1216 self.check()
1217 except (QAPISchemaError, QAPIExprError) as err:
1218 print >>sys.stderr, err
1219 exit(1)
1220
1221 def _def_entity(self, ent):
1222 # Only the predefined types are allowed to not have info
1223 assert ent.info or self._predefining
1224 assert ent.name not in self._entity_dict
1225 self._entity_dict[ent.name] = ent
1226
1227 def lookup_entity(self, name, typ=None):
1228 ent = self._entity_dict.get(name)
1229 if typ and not isinstance(ent, typ):
1230 return None
1231 return ent
1232
1233 def lookup_type(self, name):
1234 return self.lookup_entity(name, QAPISchemaType)
1235
1236 def _def_builtin_type(self, name, json_type, c_type):
1237 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1238 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1239 # qapi-types.h from a single .c, all arrays of builtins must be
1240 # declared in the first file whether or not they are used. Nicer
1241 # would be to use lazy instantiation, while figuring out how to
1242 # avoid compilation issues with multiple qapi-types.h.
1243 self._make_array_type(name, None)
1244
1245 def _def_predefineds(self):
1246 for t in [('str', 'string', 'char' + pointer_suffix),
1247 ('number', 'number', 'double'),
1248 ('int', 'int', 'int64_t'),
1249 ('int8', 'int', 'int8_t'),
1250 ('int16', 'int', 'int16_t'),
1251 ('int32', 'int', 'int32_t'),
1252 ('int64', 'int', 'int64_t'),
1253 ('uint8', 'int', 'uint8_t'),
1254 ('uint16', 'int', 'uint16_t'),
1255 ('uint32', 'int', 'uint32_t'),
1256 ('uint64', 'int', 'uint64_t'),
1257 ('size', 'int', 'uint64_t'),
1258 ('bool', 'boolean', 'bool'),
1259 ('any', 'value', 'QObject' + pointer_suffix)]:
1260 self._def_builtin_type(*t)
1261 self.the_empty_object_type = QAPISchemaObjectType('q_empty', None,
1262 None, [], None)
1263 self._def_entity(self.the_empty_object_type)
1264 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1265 'qstring', 'qdict', 'qlist',
1266 'qfloat', 'qbool'])
1267 self._def_entity(QAPISchemaEnumType('QType', None, qtype_values,
1268 'QTYPE'))
1269
1270 def _make_enum_members(self, values):
1271 return [QAPISchemaMember(v) for v in values]
1272
1273 def _make_implicit_enum_type(self, name, info, values):
1274 # See also QAPISchemaObjectTypeMember._pretty_owner()
1275 name = name + 'Kind' # Use namespace reserved by add_name()
1276 self._def_entity(QAPISchemaEnumType(
1277 name, info, self._make_enum_members(values), None))
1278 return name
1279
1280 def _make_array_type(self, element_type, info):
1281 name = element_type + 'List' # Use namespace reserved by add_name()
1282 if not self.lookup_type(name):
1283 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1284 return name
1285
1286 def _make_implicit_object_type(self, name, info, role, members):
1287 if not members:
1288 return None
1289 # See also QAPISchemaObjectTypeMember._pretty_owner()
1290 name = 'q_obj_%s-%s' % (name, role)
1291 if not self.lookup_entity(name, QAPISchemaObjectType):
1292 self._def_entity(QAPISchemaObjectType(name, info, None,
1293 members, None))
1294 return name
1295
1296 def _def_enum_type(self, expr, info):
1297 name = expr['enum']
1298 data = expr['data']
1299 prefix = expr.get('prefix')
1300 self._def_entity(QAPISchemaEnumType(
1301 name, info, self._make_enum_members(data), prefix))
1302
1303 def _make_member(self, name, typ, info):
1304 optional = False
1305 if name.startswith('*'):
1306 name = name[1:]
1307 optional = True
1308 if isinstance(typ, list):
1309 assert len(typ) == 1
1310 typ = self._make_array_type(typ[0], info)
1311 return QAPISchemaObjectTypeMember(name, typ, optional)
1312
1313 def _make_members(self, data, info):
1314 return [self._make_member(key, value, info)
1315 for (key, value) in data.iteritems()]
1316
1317 def _def_struct_type(self, expr, info):
1318 name = expr['struct']
1319 base = expr.get('base')
1320 data = expr['data']
1321 self._def_entity(QAPISchemaObjectType(name, info, base,
1322 self._make_members(data, info),
1323 None))
1324
1325 def _make_variant(self, case, typ):
1326 return QAPISchemaObjectTypeVariant(case, typ)
1327
1328 def _make_simple_variant(self, case, typ, info):
1329 if isinstance(typ, list):
1330 assert len(typ) == 1
1331 typ = self._make_array_type(typ[0], info)
1332 typ = self._make_implicit_object_type(
1333 typ, info, 'wrapper', [self._make_member('data', typ, info)])
1334 return QAPISchemaObjectTypeVariant(case, typ)
1335
1336 def _def_union_type(self, expr, info):
1337 name = expr['union']
1338 data = expr['data']
1339 base = expr.get('base')
1340 tag_name = expr.get('discriminator')
1341 tag_member = None
1342 if isinstance(base, dict):
1343 base = (self._make_implicit_object_type(
1344 name, info, 'base', self._make_members(base, info)))
1345 if tag_name:
1346 variants = [self._make_variant(key, value)
1347 for (key, value) in data.iteritems()]
1348 members = []
1349 else:
1350 variants = [self._make_simple_variant(key, value, info)
1351 for (key, value) in data.iteritems()]
1352 typ = self._make_implicit_enum_type(name, info,
1353 [v.name for v in variants])
1354 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1355 members = [tag_member]
1356 self._def_entity(
1357 QAPISchemaObjectType(name, info, base, members,
1358 QAPISchemaObjectTypeVariants(tag_name,
1359 tag_member,
1360 variants)))
1361
1362 def _def_alternate_type(self, expr, info):
1363 name = expr['alternate']
1364 data = expr['data']
1365 variants = [self._make_variant(key, value)
1366 for (key, value) in data.iteritems()]
1367 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1368 self._def_entity(
1369 QAPISchemaAlternateType(name, info,
1370 QAPISchemaObjectTypeVariants(None,
1371 tag_member,
1372 variants)))
1373
1374 def _def_command(self, expr, info):
1375 name = expr['command']
1376 data = expr.get('data')
1377 rets = expr.get('returns')
1378 gen = expr.get('gen', True)
1379 success_response = expr.get('success-response', True)
1380 if isinstance(data, OrderedDict):
1381 data = self._make_implicit_object_type(
1382 name, info, 'arg', self._make_members(data, info))
1383 if isinstance(rets, list):
1384 assert len(rets) == 1
1385 rets = self._make_array_type(rets[0], info)
1386 self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
1387 success_response))
1388
1389 def _def_event(self, expr, info):
1390 name = expr['event']
1391 data = expr.get('data')
1392 if isinstance(data, OrderedDict):
1393 data = self._make_implicit_object_type(
1394 name, info, 'arg', self._make_members(data, info))
1395 self._def_entity(QAPISchemaEvent(name, info, data))
1396
1397 def _def_exprs(self):
1398 for expr_elem in self.exprs:
1399 expr = expr_elem['expr']
1400 info = expr_elem['info']
1401 if 'enum' in expr:
1402 self._def_enum_type(expr, info)
1403 elif 'struct' in expr:
1404 self._def_struct_type(expr, info)
1405 elif 'union' in expr:
1406 self._def_union_type(expr, info)
1407 elif 'alternate' in expr:
1408 self._def_alternate_type(expr, info)
1409 elif 'command' in expr:
1410 self._def_command(expr, info)
1411 elif 'event' in expr:
1412 self._def_event(expr, info)
1413 else:
1414 assert False
1415
1416 def check(self):
1417 for ent in self._entity_dict.values():
1418 ent.check(self)
1419
1420 def visit(self, visitor):
1421 visitor.visit_begin(self)
1422 for (name, entity) in sorted(self._entity_dict.items()):
1423 if visitor.visit_needed(entity):
1424 entity.visit(visitor)
1425 visitor.visit_end()
1426
1427
1428 #
1429 # Code generation helpers
1430 #
1431
1432 def camel_case(name):
1433 new_name = ''
1434 first = True
1435 for ch in name:
1436 if ch in ['_', '-']:
1437 first = True
1438 elif first:
1439 new_name += ch.upper()
1440 first = False
1441 else:
1442 new_name += ch.lower()
1443 return new_name
1444
1445
1446 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1447 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1448 # ENUM24_Name -> ENUM24_NAME
1449 def camel_to_upper(value):
1450 c_fun_str = c_name(value, False)
1451 if value.isupper():
1452 return c_fun_str
1453
1454 new_name = ''
1455 l = len(c_fun_str)
1456 for i in range(l):
1457 c = c_fun_str[i]
1458 # When c is upper and no "_" appears before, do more checks
1459 if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
1460 if i < l - 1 and c_fun_str[i + 1].islower():
1461 new_name += '_'
1462 elif c_fun_str[i - 1].isdigit():
1463 new_name += '_'
1464 new_name += c
1465 return new_name.lstrip('_').upper()
1466
1467
1468 def c_enum_const(type_name, const_name, prefix=None):
1469 if prefix is not None:
1470 type_name = prefix
1471 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1472
1473 c_name_trans = string.maketrans('.-', '__')
1474
1475
1476 # Map @name to a valid C identifier.
1477 # If @protect, avoid returning certain ticklish identifiers (like
1478 # C keywords) by prepending "q_".
1479 #
1480 # Used for converting 'name' from a 'name':'type' qapi definition
1481 # into a generated struct member, as well as converting type names
1482 # into substrings of a generated C function name.
1483 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1484 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1485 def c_name(name, protect=True):
1486 # ANSI X3J11/88-090, 3.1.1
1487 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1488 'default', 'do', 'double', 'else', 'enum', 'extern',
1489 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1490 'return', 'short', 'signed', 'sizeof', 'static',
1491 'struct', 'switch', 'typedef', 'union', 'unsigned',
1492 'void', 'volatile', 'while'])
1493 # ISO/IEC 9899:1999, 6.4.1
1494 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1495 # ISO/IEC 9899:2011, 6.4.1
1496 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1497 '_Noreturn', '_Static_assert', '_Thread_local'])
1498 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1499 # excluding _.*
1500 gcc_words = set(['asm', 'typeof'])
1501 # C++ ISO/IEC 14882:2003 2.11
1502 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1503 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1504 'namespace', 'new', 'operator', 'private', 'protected',
1505 'public', 'reinterpret_cast', 'static_cast', 'template',
1506 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1507 'using', 'virtual', 'wchar_t',
1508 # alternative representations
1509 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1510 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1511 # namespace pollution:
1512 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
1513 name = name.translate(c_name_trans)
1514 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1515 | cpp_words | polluted_words):
1516 return "q_" + name
1517 return name
1518
1519 eatspace = '\033EATSPACE.'
1520 pointer_suffix = ' *' + eatspace
1521
1522
1523 def genindent(count):
1524 ret = ""
1525 for _ in range(count):
1526 ret += " "
1527 return ret
1528
1529 indent_level = 0
1530
1531
1532 def push_indent(indent_amount=4):
1533 global indent_level
1534 indent_level += indent_amount
1535
1536
1537 def pop_indent(indent_amount=4):
1538 global indent_level
1539 indent_level -= indent_amount
1540
1541
1542 # Generate @code with @kwds interpolated.
1543 # Obey indent_level, and strip eatspace.
1544 def cgen(code, **kwds):
1545 raw = code % kwds
1546 if indent_level:
1547 indent = genindent(indent_level)
1548 # re.subn() lacks flags support before Python 2.7, use re.compile()
1549 raw = re.subn(re.compile("^.", re.MULTILINE),
1550 indent + r'\g<0>', raw)
1551 raw = raw[0]
1552 return re.sub(re.escape(eatspace) + ' *', '', raw)
1553
1554
1555 def mcgen(code, **kwds):
1556 if code[0] == '\n':
1557 code = code[1:]
1558 return cgen(code, **kwds)
1559
1560
1561 def guardname(filename):
1562 return c_name(filename, protect=False).upper()
1563
1564
1565 def guardstart(name):
1566 return mcgen('''
1567
1568 #ifndef %(name)s
1569 #define %(name)s
1570
1571 ''',
1572 name=guardname(name))
1573
1574
1575 def guardend(name):
1576 return mcgen('''
1577
1578 #endif /* %(name)s */
1579
1580 ''',
1581 name=guardname(name))
1582
1583
1584 def gen_enum_lookup(name, values, prefix=None):
1585 ret = mcgen('''
1586
1587 const char *const %(c_name)s_lookup[] = {
1588 ''',
1589 c_name=c_name(name))
1590 for value in values:
1591 index = c_enum_const(name, value, prefix)
1592 ret += mcgen('''
1593 [%(index)s] = "%(value)s",
1594 ''',
1595 index=index, value=value)
1596
1597 max_index = c_enum_const(name, '_MAX', prefix)
1598 ret += mcgen('''
1599 [%(max_index)s] = NULL,
1600 };
1601 ''',
1602 max_index=max_index)
1603 return ret
1604
1605
1606 def gen_enum(name, values, prefix=None):
1607 # append automatically generated _MAX value
1608 enum_values = values + ['_MAX']
1609
1610 ret = mcgen('''
1611
1612 typedef enum %(c_name)s {
1613 ''',
1614 c_name=c_name(name))
1615
1616 i = 0
1617 for value in enum_values:
1618 ret += mcgen('''
1619 %(c_enum)s = %(i)d,
1620 ''',
1621 c_enum=c_enum_const(name, value, prefix),
1622 i=i)
1623 i += 1
1624
1625 ret += mcgen('''
1626 } %(c_name)s;
1627 ''',
1628 c_name=c_name(name))
1629
1630 ret += mcgen('''
1631
1632 extern const char *const %(c_name)s_lookup[];
1633 ''',
1634 c_name=c_name(name))
1635 return ret
1636
1637
1638 def gen_params(arg_type, extra):
1639 if not arg_type:
1640 return extra
1641 assert not arg_type.variants
1642 ret = ''
1643 sep = ''
1644 for memb in arg_type.members:
1645 ret += sep
1646 sep = ', '
1647 if memb.optional:
1648 ret += 'bool has_%s, ' % c_name(memb.name)
1649 ret += '%s %s' % (memb.type.c_param_type(), c_name(memb.name))
1650 if extra:
1651 ret += sep + extra
1652 return ret
1653
1654
1655 def gen_err_check():
1656 return mcgen('''
1657 if (err) {
1658 goto out;
1659 }
1660 ''')
1661
1662
1663 #
1664 # Common command line parsing
1665 #
1666
1667
1668 def parse_command_line(extra_options="", extra_long_options=[]):
1669
1670 try:
1671 opts, args = getopt.gnu_getopt(sys.argv[1:],
1672 "chp:o:" + extra_options,
1673 ["source", "header", "prefix=",
1674 "output-dir="] + extra_long_options)
1675 except getopt.GetoptError as err:
1676 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
1677 sys.exit(1)
1678
1679 output_dir = ""
1680 prefix = ""
1681 do_c = False
1682 do_h = False
1683 extra_opts = []
1684
1685 for oa in opts:
1686 o, a = oa
1687 if o in ("-p", "--prefix"):
1688 match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1689 if match.end() != len(a):
1690 print >>sys.stderr, \
1691 "%s: 'funny character '%s' in argument of --prefix" \
1692 % (sys.argv[0], a[match.end()])
1693 sys.exit(1)
1694 prefix = a
1695 elif o in ("-o", "--output-dir"):
1696 output_dir = a + "/"
1697 elif o in ("-c", "--source"):
1698 do_c = True
1699 elif o in ("-h", "--header"):
1700 do_h = True
1701 else:
1702 extra_opts.append(oa)
1703
1704 if not do_c and not do_h:
1705 do_c = True
1706 do_h = True
1707
1708 if len(args) != 1:
1709 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
1710 sys.exit(1)
1711 fname = args[0]
1712
1713 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
1714
1715 #
1716 # Generate output files with boilerplate
1717 #
1718
1719
1720 def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1721 c_comment, h_comment):
1722 guard = guardname(prefix + h_file)
1723 c_file = output_dir + prefix + c_file
1724 h_file = output_dir + prefix + h_file
1725
1726 if output_dir:
1727 try:
1728 os.makedirs(output_dir)
1729 except os.error as e:
1730 if e.errno != errno.EEXIST:
1731 raise
1732
1733 def maybe_open(really, name, opt):
1734 if really:
1735 return open(name, opt)
1736 else:
1737 import StringIO
1738 return StringIO.StringIO()
1739
1740 fdef = maybe_open(do_c, c_file, 'w')
1741 fdecl = maybe_open(do_h, h_file, 'w')
1742
1743 fdef.write(mcgen('''
1744 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1745 %(comment)s
1746 ''',
1747 comment=c_comment))
1748
1749 fdecl.write(mcgen('''
1750 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1751 %(comment)s
1752 #ifndef %(guard)s
1753 #define %(guard)s
1754
1755 ''',
1756 comment=h_comment, guard=guard))
1757
1758 return (fdef, fdecl)
1759
1760
1761 def close_output(fdef, fdecl):
1762 fdecl.write('''
1763 #endif
1764 ''')
1765 fdecl.close()
1766 fdef.close()