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