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