]> Git Repo - qemu.git/blob - scripts/qapi.py
qapi: Avoid assertion failure on union 'type' collision
[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         if not isinstance(base, str):
564             raise QAPIExprError(expr_info,
565                                 "Flat union '%s' must have a string base field"
566                                 % name)
567         base_fields = find_base_fields(base)
568         if not base_fields:
569             raise QAPIExprError(expr_info,
570                                 "Base '%s' is not a valid struct"
571                                 % base)
572
573         # The value of member 'discriminator' must name a non-optional
574         # member of the base struct.
575         check_name(expr_info, "Discriminator of flat union '%s'" % name,
576                    discriminator)
577         discriminator_type = base_fields.get(discriminator)
578         if not discriminator_type:
579             raise QAPIExprError(expr_info,
580                                 "Discriminator '%s' is not a member of base "
581                                 "struct '%s'"
582                                 % (discriminator, base))
583         enum_define = find_enum(discriminator_type)
584         allow_metas = ['struct']
585         # Do not allow string discriminator
586         if not enum_define:
587             raise QAPIExprError(expr_info,
588                                 "Discriminator '%s' must be of enumeration "
589                                 "type" % discriminator)
590
591     # Check every branch
592     for (key, value) in members.items():
593         check_name(expr_info, "Member of union '%s'" % name, key)
594
595         # Each value must name a known type; furthermore, in flat unions,
596         # branches must be a struct with no overlapping member names
597         check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
598                    value, allow_array=not base, allow_metas=allow_metas)
599         if base:
600             branch_struct = find_struct(value)
601             assert branch_struct
602             check_member_clash(expr_info, base, branch_struct['data'],
603                                " of branch '%s'" % key)
604
605         # If the discriminator names an enum type, then all members
606         # of 'data' must also be members of the enum type, which in turn
607         # must not collide with the discriminator name.
608         if enum_define:
609             if key not in enum_define['enum_values']:
610                 raise QAPIExprError(expr_info,
611                                     "Discriminator value '%s' is not found in "
612                                     "enum '%s'" %
613                                     (key, enum_define["enum_name"]))
614             if discriminator in enum_define['enum_values']:
615                 raise QAPIExprError(expr_info,
616                                     "Discriminator name '%s' collides with "
617                                     "enum value in '%s'" %
618                                     (discriminator, enum_define["enum_name"]))
619
620         # Otherwise, check for conflicts in the generated enum
621         else:
622             c_key = camel_to_upper(key)
623             if c_key in values:
624                 raise QAPIExprError(expr_info,
625                                     "Union '%s' member '%s' clashes with '%s'"
626                                     % (name, key, values[c_key]))
627             values[c_key] = key
628
629
630 def check_alternate(expr, expr_info):
631     name = expr['alternate']
632     members = expr['data']
633     values = {'MAX': '(automatic)'}
634     types_seen = {}
635
636     # Check every branch
637     for (key, value) in members.items():
638         check_name(expr_info, "Member of alternate '%s'" % name, key)
639
640         # Check for conflicts in the generated enum
641         c_key = camel_to_upper(key)
642         if c_key in values:
643             raise QAPIExprError(expr_info,
644                                 "Alternate '%s' member '%s' clashes with '%s'"
645                                 % (name, key, values[c_key]))
646         values[c_key] = key
647
648         # Ensure alternates have no type conflicts.
649         check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
650                    value,
651                    allow_metas=['built-in', 'union', 'struct', 'enum'])
652         qtype = find_alternate_member_qtype(value)
653         assert qtype
654         if qtype in types_seen:
655             raise QAPIExprError(expr_info,
656                                 "Alternate '%s' member '%s' can't "
657                                 "be distinguished from member '%s'"
658                                 % (name, key, types_seen[qtype]))
659         types_seen[qtype] = key
660
661
662 def check_enum(expr, expr_info):
663     name = expr['enum']
664     members = expr.get('data')
665     prefix = expr.get('prefix')
666     values = {'MAX': '(automatic)'}
667
668     if not isinstance(members, list):
669         raise QAPIExprError(expr_info,
670                             "Enum '%s' requires an array for 'data'" % name)
671     if prefix is not None and not isinstance(prefix, str):
672         raise QAPIExprError(expr_info,
673                             "Enum '%s' requires a string for 'prefix'" % name)
674     for member in members:
675         check_name(expr_info, "Member of enum '%s'" % name, member,
676                    enum_member=True)
677         key = camel_to_upper(member)
678         if key in values:
679             raise QAPIExprError(expr_info,
680                                 "Enum '%s' member '%s' clashes with '%s'"
681                                 % (name, member, values[key]))
682         values[key] = member
683
684
685 def check_struct(expr, expr_info):
686     name = expr['struct']
687     members = expr['data']
688
689     check_type(expr_info, "'data' for struct '%s'" % name, members,
690                allow_dict=True, allow_optional=True)
691     check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
692                allow_metas=['struct'])
693     if expr.get('base'):
694         check_member_clash(expr_info, expr['base'], expr['data'])
695
696
697 def check_keys(expr_elem, meta, required, optional=[]):
698     expr = expr_elem['expr']
699     info = expr_elem['info']
700     name = expr[meta]
701     if not isinstance(name, str):
702         raise QAPIExprError(info,
703                             "'%s' key must have a string value" % meta)
704     required = required + [meta]
705     for (key, value) in expr.items():
706         if key not in required and key not in optional:
707             raise QAPIExprError(info,
708                                 "Unknown key '%s' in %s '%s'"
709                                 % (key, meta, name))
710         if (key == 'gen' or key == 'success-response') and value is not False:
711             raise QAPIExprError(info,
712                                 "'%s' of %s '%s' should only use false value"
713                                 % (key, meta, name))
714     for key in required:
715         if key not in expr:
716             raise QAPIExprError(info,
717                                 "Key '%s' is missing from %s '%s'"
718                                 % (key, meta, name))
719
720
721 def check_exprs(exprs):
722     global all_names
723
724     # Learn the types and check for valid expression keys
725     for builtin in builtin_types.keys():
726         all_names[builtin] = 'built-in'
727     for expr_elem in exprs:
728         expr = expr_elem['expr']
729         info = expr_elem['info']
730         if 'enum' in expr:
731             check_keys(expr_elem, 'enum', ['data'], ['prefix'])
732             add_enum(expr['enum'], info, expr['data'])
733         elif 'union' in expr:
734             check_keys(expr_elem, 'union', ['data'],
735                        ['base', 'discriminator'])
736             add_union(expr, info)
737         elif 'alternate' in expr:
738             check_keys(expr_elem, 'alternate', ['data'])
739             add_name(expr['alternate'], info, 'alternate')
740         elif 'struct' in expr:
741             check_keys(expr_elem, 'struct', ['data'], ['base'])
742             add_struct(expr, info)
743         elif 'command' in expr:
744             check_keys(expr_elem, 'command', [],
745                        ['data', 'returns', 'gen', 'success-response'])
746             add_name(expr['command'], info, 'command')
747         elif 'event' in expr:
748             check_keys(expr_elem, 'event', [], ['data'])
749             add_name(expr['event'], info, 'event')
750         else:
751             raise QAPIExprError(expr_elem['info'],
752                                 "Expression is missing metatype")
753
754     # Try again for hidden UnionKind enum
755     for expr_elem in exprs:
756         expr = expr_elem['expr']
757         if 'union' in expr:
758             if not discriminator_find_enum_define(expr):
759                 add_enum('%sKind' % expr['union'], expr_elem['info'],
760                          implicit=True)
761         elif 'alternate' in expr:
762             add_enum('%sKind' % expr['alternate'], expr_elem['info'],
763                      implicit=True)
764
765     # Validate that exprs make sense
766     for expr_elem in exprs:
767         expr = expr_elem['expr']
768         info = expr_elem['info']
769
770         if 'enum' in expr:
771             check_enum(expr, info)
772         elif 'union' in expr:
773             check_union(expr, info)
774         elif 'alternate' in expr:
775             check_alternate(expr, info)
776         elif 'struct' in expr:
777             check_struct(expr, info)
778         elif 'command' in expr:
779             check_command(expr, info)
780         elif 'event' in expr:
781             check_event(expr, info)
782         else:
783             assert False, 'unexpected meta type'
784
785     return exprs
786
787
788 #
789 # Schema compiler frontend
790 #
791
792 class QAPISchemaEntity(object):
793     def __init__(self, name, info):
794         assert isinstance(name, str)
795         self.name = name
796         self.info = info
797
798     def c_name(self):
799         return c_name(self.name)
800
801     def check(self, schema):
802         pass
803
804     def visit(self, visitor):
805         pass
806
807
808 class QAPISchemaVisitor(object):
809     def visit_begin(self, schema):
810         pass
811
812     def visit_end(self):
813         pass
814
815     def visit_builtin_type(self, name, info, json_type):
816         pass
817
818     def visit_enum_type(self, name, info, values, prefix):
819         pass
820
821     def visit_array_type(self, name, info, element_type):
822         pass
823
824     def visit_object_type(self, name, info, base, members, variants):
825         pass
826
827     def visit_object_type_flat(self, name, info, members, variants):
828         pass
829
830     def visit_alternate_type(self, name, info, variants):
831         pass
832
833     def visit_command(self, name, info, arg_type, ret_type,
834                       gen, success_response):
835         pass
836
837     def visit_event(self, name, info, arg_type):
838         pass
839
840
841 class QAPISchemaType(QAPISchemaEntity):
842     def c_type(self, is_param=False):
843         return c_name(self.name) + pointer_suffix
844
845     def c_null(self):
846         return 'NULL'
847
848     def json_type(self):
849         pass
850
851     def alternate_qtype(self):
852         json2qtype = {
853             'string':  'QTYPE_QSTRING',
854             'number':  'QTYPE_QFLOAT',
855             'int':     'QTYPE_QINT',
856             'boolean': 'QTYPE_QBOOL',
857             'object':  'QTYPE_QDICT'
858         }
859         return json2qtype.get(self.json_type())
860
861
862 class QAPISchemaBuiltinType(QAPISchemaType):
863     def __init__(self, name, json_type, c_type, c_null):
864         QAPISchemaType.__init__(self, name, None)
865         assert not c_type or isinstance(c_type, str)
866         assert json_type in ('string', 'number', 'int', 'boolean', 'null',
867                              'value')
868         self._json_type_name = json_type
869         self._c_type_name = c_type
870         self._c_null_val = c_null
871
872     def c_name(self):
873         return self.name
874
875     def c_type(self, is_param=False):
876         if is_param and self.name == 'str':
877             return 'const ' + self._c_type_name
878         return self._c_type_name
879
880     def c_null(self):
881         return self._c_null_val
882
883     def json_type(self):
884         return self._json_type_name
885
886     def visit(self, visitor):
887         visitor.visit_builtin_type(self.name, self.info, self.json_type())
888
889
890 class QAPISchemaEnumType(QAPISchemaType):
891     def __init__(self, name, info, values, prefix):
892         QAPISchemaType.__init__(self, name, info)
893         for v in values:
894             assert isinstance(v, str)
895         assert prefix is None or isinstance(prefix, str)
896         self.values = values
897         self.prefix = prefix
898
899     def check(self, schema):
900         assert len(set(self.values)) == len(self.values)
901
902     def c_type(self, is_param=False):
903         return c_name(self.name)
904
905     def c_null(self):
906         return c_enum_const(self.name, (self.values + ['MAX'])[0],
907                             self.prefix)
908
909     def json_type(self):
910         return 'string'
911
912     def visit(self, visitor):
913         visitor.visit_enum_type(self.name, self.info,
914                                 self.values, self.prefix)
915
916
917 class QAPISchemaArrayType(QAPISchemaType):
918     def __init__(self, name, info, element_type):
919         QAPISchemaType.__init__(self, name, info)
920         assert isinstance(element_type, str)
921         self._element_type_name = element_type
922         self.element_type = None
923
924     def check(self, schema):
925         self.element_type = schema.lookup_type(self._element_type_name)
926         assert self.element_type
927
928     def json_type(self):
929         return 'array'
930
931     def visit(self, visitor):
932         visitor.visit_array_type(self.name, self.info, self.element_type)
933
934
935 class QAPISchemaObjectType(QAPISchemaType):
936     def __init__(self, name, info, base, local_members, variants):
937         QAPISchemaType.__init__(self, name, info)
938         assert base is None or isinstance(base, str)
939         for m in local_members:
940             assert isinstance(m, QAPISchemaObjectTypeMember)
941         assert (variants is None or
942                 isinstance(variants, QAPISchemaObjectTypeVariants))
943         self._base_name = base
944         self.base = None
945         self.local_members = local_members
946         self.variants = variants
947         self.members = None
948
949     def check(self, schema):
950         assert self.members is not False        # not running in cycles
951         if self.members:
952             return
953         self.members = False                    # mark as being checked
954         if self._base_name:
955             self.base = schema.lookup_type(self._base_name)
956             assert isinstance(self.base, QAPISchemaObjectType)
957             assert not self.base.variants       # not implemented
958             self.base.check(schema)
959             members = list(self.base.members)
960         else:
961             members = []
962         seen = {}
963         for m in members:
964             seen[m.name] = m
965         for m in self.local_members:
966             m.check(schema, members, seen)
967         if self.variants:
968             self.variants.check(schema, members, seen)
969         self.members = members
970
971     def c_name(self):
972         assert self.info
973         return QAPISchemaType.c_name(self)
974
975     def c_type(self, is_param=False):
976         assert self.info
977         return QAPISchemaType.c_type(self)
978
979     def json_type(self):
980         return 'object'
981
982     def visit(self, visitor):
983         visitor.visit_object_type(self.name, self.info,
984                                   self.base, self.local_members, self.variants)
985         visitor.visit_object_type_flat(self.name, self.info,
986                                        self.members, self.variants)
987
988
989 class QAPISchemaObjectTypeMember(object):
990     def __init__(self, name, typ, optional):
991         assert isinstance(name, str)
992         assert isinstance(typ, str)
993         assert isinstance(optional, bool)
994         self.name = name
995         self._type_name = typ
996         self.type = None
997         self.optional = optional
998
999     def check(self, schema, all_members, seen):
1000         assert self.name not in seen
1001         self.type = schema.lookup_type(self._type_name)
1002         assert self.type
1003         all_members.append(self)
1004         seen[self.name] = self
1005
1006
1007 class QAPISchemaObjectTypeVariants(object):
1008     def __init__(self, tag_name, tag_enum, variants):
1009         assert tag_name is None or isinstance(tag_name, str)
1010         assert tag_enum is None or isinstance(tag_enum, str)
1011         for v in variants:
1012             assert isinstance(v, QAPISchemaObjectTypeVariant)
1013         self.tag_name = tag_name
1014         if tag_name:
1015             assert not tag_enum
1016             self.tag_member = None
1017         else:
1018             self.tag_member = QAPISchemaObjectTypeMember('type', tag_enum,
1019                                                          False)
1020         self.variants = variants
1021
1022     def check(self, schema, members, seen):
1023         if self.tag_name:
1024             self.tag_member = seen[self.tag_name]
1025         else:
1026             self.tag_member.check(schema, members, seen)
1027         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1028         for v in self.variants:
1029             vseen = dict(seen)
1030             v.check(schema, self.tag_member.type, vseen)
1031
1032
1033 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1034     def __init__(self, name, typ):
1035         QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1036
1037     def check(self, schema, tag_type, seen):
1038         QAPISchemaObjectTypeMember.check(self, schema, [], seen)
1039         assert self.name in tag_type.values
1040
1041     # This function exists to support ugly simple union special cases
1042     # TODO get rid of them, and drop the function
1043     def simple_union_type(self):
1044         if isinstance(self.type, QAPISchemaObjectType) and not self.type.info:
1045             assert len(self.type.members) == 1
1046             assert not self.type.variants
1047             return self.type.members[0].type
1048         return None
1049
1050
1051 class QAPISchemaAlternateType(QAPISchemaType):
1052     def __init__(self, name, info, variants):
1053         QAPISchemaType.__init__(self, name, info)
1054         assert isinstance(variants, QAPISchemaObjectTypeVariants)
1055         assert not variants.tag_name
1056         self.variants = variants
1057
1058     def check(self, schema):
1059         self.variants.check(schema, [], {})
1060
1061     def json_type(self):
1062         return 'value'
1063
1064     def visit(self, visitor):
1065         visitor.visit_alternate_type(self.name, self.info, self.variants)
1066
1067
1068 class QAPISchemaCommand(QAPISchemaEntity):
1069     def __init__(self, name, info, arg_type, ret_type, gen, success_response):
1070         QAPISchemaEntity.__init__(self, name, info)
1071         assert not arg_type or isinstance(arg_type, str)
1072         assert not ret_type or isinstance(ret_type, str)
1073         self._arg_type_name = arg_type
1074         self.arg_type = None
1075         self._ret_type_name = ret_type
1076         self.ret_type = None
1077         self.gen = gen
1078         self.success_response = success_response
1079
1080     def check(self, schema):
1081         if self._arg_type_name:
1082             self.arg_type = schema.lookup_type(self._arg_type_name)
1083             assert isinstance(self.arg_type, QAPISchemaObjectType)
1084             assert not self.arg_type.variants   # not implemented
1085         if self._ret_type_name:
1086             self.ret_type = schema.lookup_type(self._ret_type_name)
1087             assert isinstance(self.ret_type, QAPISchemaType)
1088
1089     def visit(self, visitor):
1090         visitor.visit_command(self.name, self.info,
1091                               self.arg_type, self.ret_type,
1092                               self.gen, self.success_response)
1093
1094
1095 class QAPISchemaEvent(QAPISchemaEntity):
1096     def __init__(self, name, info, arg_type):
1097         QAPISchemaEntity.__init__(self, name, info)
1098         assert not arg_type or isinstance(arg_type, str)
1099         self._arg_type_name = arg_type
1100         self.arg_type = None
1101
1102     def check(self, schema):
1103         if self._arg_type_name:
1104             self.arg_type = schema.lookup_type(self._arg_type_name)
1105             assert isinstance(self.arg_type, QAPISchemaObjectType)
1106             assert not self.arg_type.variants   # not implemented
1107
1108     def visit(self, visitor):
1109         visitor.visit_event(self.name, self.info, self.arg_type)
1110
1111
1112 class QAPISchema(object):
1113     def __init__(self, fname):
1114         try:
1115             self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
1116         except (QAPISchemaError, QAPIExprError), err:
1117             print >>sys.stderr, err
1118             exit(1)
1119         self._entity_dict = {}
1120         self._def_predefineds()
1121         self._def_exprs()
1122         self.check()
1123
1124     def _def_entity(self, ent):
1125         assert ent.name not in self._entity_dict
1126         self._entity_dict[ent.name] = ent
1127
1128     def lookup_entity(self, name, typ=None):
1129         ent = self._entity_dict.get(name)
1130         if typ and not isinstance(ent, typ):
1131             return None
1132         return ent
1133
1134     def lookup_type(self, name):
1135         return self.lookup_entity(name, QAPISchemaType)
1136
1137     def _def_builtin_type(self, name, json_type, c_type, c_null):
1138         self._def_entity(QAPISchemaBuiltinType(name, json_type,
1139                                                c_type, c_null))
1140         self._make_array_type(name)     # TODO really needed?
1141
1142     def _def_predefineds(self):
1143         for t in [('str',    'string',  'char' + pointer_suffix, 'NULL'),
1144                   ('number', 'number',  'double',   '0'),
1145                   ('int',    'int',     'int64_t',  '0'),
1146                   ('int8',   'int',     'int8_t',   '0'),
1147                   ('int16',  'int',     'int16_t',  '0'),
1148                   ('int32',  'int',     'int32_t',  '0'),
1149                   ('int64',  'int',     'int64_t',  '0'),
1150                   ('uint8',  'int',     'uint8_t',  '0'),
1151                   ('uint16', 'int',     'uint16_t', '0'),
1152                   ('uint32', 'int',     'uint32_t', '0'),
1153                   ('uint64', 'int',     'uint64_t', '0'),
1154                   ('size',   'int',     'uint64_t', '0'),
1155                   ('bool',   'boolean', 'bool',     'false'),
1156                   ('any',    'value',   'QObject' + pointer_suffix, 'NULL')]:
1157             self._def_builtin_type(*t)
1158         self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
1159                                                           [], None)
1160         self._def_entity(self.the_empty_object_type)
1161
1162     def _make_implicit_enum_type(self, name, values):
1163         name = name + 'Kind'
1164         self._def_entity(QAPISchemaEnumType(name, None, values, None))
1165         return name
1166
1167     def _make_array_type(self, element_type):
1168         name = element_type + 'List'
1169         if not self.lookup_type(name):
1170             self._def_entity(QAPISchemaArrayType(name, None, element_type))
1171         return name
1172
1173     def _make_implicit_object_type(self, name, role, members):
1174         if not members:
1175             return None
1176         name = ':obj-%s-%s' % (name, role)
1177         if not self.lookup_entity(name, QAPISchemaObjectType):
1178             self._def_entity(QAPISchemaObjectType(name, None, None,
1179                                                   members, None))
1180         return name
1181
1182     def _def_enum_type(self, expr, info):
1183         name = expr['enum']
1184         data = expr['data']
1185         prefix = expr.get('prefix')
1186         self._def_entity(QAPISchemaEnumType(name, info, data, prefix))
1187         self._make_array_type(name)     # TODO really needed?
1188
1189     def _make_member(self, name, typ):
1190         optional = False
1191         if name.startswith('*'):
1192             name = name[1:]
1193             optional = True
1194         if isinstance(typ, list):
1195             assert len(typ) == 1
1196             typ = self._make_array_type(typ[0])
1197         return QAPISchemaObjectTypeMember(name, typ, optional)
1198
1199     def _make_members(self, data):
1200         return [self._make_member(key, value)
1201                 for (key, value) in data.iteritems()]
1202
1203     def _def_struct_type(self, expr, info):
1204         name = expr['struct']
1205         base = expr.get('base')
1206         data = expr['data']
1207         self._def_entity(QAPISchemaObjectType(name, info, base,
1208                                               self._make_members(data),
1209                                               None))
1210         self._make_array_type(name)     # TODO really needed?
1211
1212     def _make_variant(self, case, typ):
1213         return QAPISchemaObjectTypeVariant(case, typ)
1214
1215     def _make_simple_variant(self, case, typ):
1216         if isinstance(typ, list):
1217             assert len(typ) == 1
1218             typ = self._make_array_type(typ[0])
1219         typ = self._make_implicit_object_type(typ, 'wrapper',
1220                                               [self._make_member('data', typ)])
1221         return QAPISchemaObjectTypeVariant(case, typ)
1222
1223     def _make_tag_enum(self, type_name, variants):
1224         return self._make_implicit_enum_type(type_name,
1225                                              [v.name for v in variants])
1226
1227     def _def_union_type(self, expr, info):
1228         name = expr['union']
1229         data = expr['data']
1230         base = expr.get('base')
1231         tag_name = expr.get('discriminator')
1232         tag_enum = None
1233         if tag_name:
1234             variants = [self._make_variant(key, value)
1235                         for (key, value) in data.iteritems()]
1236         else:
1237             variants = [self._make_simple_variant(key, value)
1238                         for (key, value) in data.iteritems()]
1239             tag_enum = self._make_tag_enum(name, variants)
1240         self._def_entity(
1241             QAPISchemaObjectType(name, info, base,
1242                                  self._make_members(OrderedDict()),
1243                                  QAPISchemaObjectTypeVariants(tag_name,
1244                                                               tag_enum,
1245                                                               variants)))
1246         self._make_array_type(name)     # TODO really needed?
1247
1248     def _def_alternate_type(self, expr, info):
1249         name = expr['alternate']
1250         data = expr['data']
1251         variants = [self._make_variant(key, value)
1252                     for (key, value) in data.iteritems()]
1253         tag_enum = self._make_tag_enum(name, variants)
1254         self._def_entity(
1255             QAPISchemaAlternateType(name, info,
1256                                     QAPISchemaObjectTypeVariants(None,
1257                                                                  tag_enum,
1258                                                                  variants)))
1259         self._make_array_type(name)     # TODO really needed?
1260
1261     def _def_command(self, expr, info):
1262         name = expr['command']
1263         data = expr.get('data')
1264         rets = expr.get('returns')
1265         gen = expr.get('gen', True)
1266         success_response = expr.get('success-response', True)
1267         if isinstance(data, OrderedDict):
1268             data = self._make_implicit_object_type(name, 'arg',
1269                                                    self._make_members(data))
1270         if isinstance(rets, list):
1271             assert len(rets) == 1
1272             rets = self._make_array_type(rets[0])
1273         self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
1274                                            success_response))
1275
1276     def _def_event(self, expr, info):
1277         name = expr['event']
1278         data = expr.get('data')
1279         if isinstance(data, OrderedDict):
1280             data = self._make_implicit_object_type(name, 'arg',
1281                                                    self._make_members(data))
1282         self._def_entity(QAPISchemaEvent(name, info, data))
1283
1284     def _def_exprs(self):
1285         for expr_elem in self.exprs:
1286             expr = expr_elem['expr']
1287             info = expr_elem['info']
1288             if 'enum' in expr:
1289                 self._def_enum_type(expr, info)
1290             elif 'struct' in expr:
1291                 self._def_struct_type(expr, info)
1292             elif 'union' in expr:
1293                 self._def_union_type(expr, info)
1294             elif 'alternate' in expr:
1295                 self._def_alternate_type(expr, info)
1296             elif 'command' in expr:
1297                 self._def_command(expr, info)
1298             elif 'event' in expr:
1299                 self._def_event(expr, info)
1300             else:
1301                 assert False
1302
1303     def check(self):
1304         for ent in self._entity_dict.values():
1305             ent.check(self)
1306
1307     def visit(self, visitor):
1308         ignore = visitor.visit_begin(self)
1309         for name in sorted(self._entity_dict.keys()):
1310             if not ignore or not isinstance(self._entity_dict[name], ignore):
1311                 self._entity_dict[name].visit(visitor)
1312         visitor.visit_end()
1313
1314
1315 #
1316 # Code generation helpers
1317 #
1318
1319 def camel_case(name):
1320     new_name = ''
1321     first = True
1322     for ch in name:
1323         if ch in ['_', '-']:
1324             first = True
1325         elif first:
1326             new_name += ch.upper()
1327             first = False
1328         else:
1329             new_name += ch.lower()
1330     return new_name
1331
1332
1333 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1334 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1335 # ENUM24_Name -> ENUM24_NAME
1336 def camel_to_upper(value):
1337     c_fun_str = c_name(value, False)
1338     if value.isupper():
1339         return c_fun_str
1340
1341     new_name = ''
1342     l = len(c_fun_str)
1343     for i in range(l):
1344         c = c_fun_str[i]
1345         # When c is upper and no "_" appears before, do more checks
1346         if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
1347             if i < l - 1 and c_fun_str[i + 1].islower():
1348                 new_name += '_'
1349             elif c_fun_str[i - 1].isdigit():
1350                 new_name += '_'
1351         new_name += c
1352     return new_name.lstrip('_').upper()
1353
1354
1355 def c_enum_const(type_name, const_name, prefix=None):
1356     if prefix is not None:
1357         type_name = prefix
1358     return camel_to_upper(type_name + '_' + const_name)
1359
1360 c_name_trans = string.maketrans('.-', '__')
1361
1362
1363 # Map @name to a valid C identifier.
1364 # If @protect, avoid returning certain ticklish identifiers (like
1365 # C keywords) by prepending "q_".
1366 #
1367 # Used for converting 'name' from a 'name':'type' qapi definition
1368 # into a generated struct member, as well as converting type names
1369 # into substrings of a generated C function name.
1370 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1371 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1372 def c_name(name, protect=True):
1373     # ANSI X3J11/88-090, 3.1.1
1374     c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1375                      'default', 'do', 'double', 'else', 'enum', 'extern',
1376                      'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1377                      'return', 'short', 'signed', 'sizeof', 'static',
1378                      'struct', 'switch', 'typedef', 'union', 'unsigned',
1379                      'void', 'volatile', 'while'])
1380     # ISO/IEC 9899:1999, 6.4.1
1381     c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1382     # ISO/IEC 9899:2011, 6.4.1
1383     c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1384                      '_Noreturn', '_Static_assert', '_Thread_local'])
1385     # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1386     # excluding _.*
1387     gcc_words = set(['asm', 'typeof'])
1388     # C++ ISO/IEC 14882:2003 2.11
1389     cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1390                      'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1391                      'namespace', 'new', 'operator', 'private', 'protected',
1392                      'public', 'reinterpret_cast', 'static_cast', 'template',
1393                      'this', 'throw', 'true', 'try', 'typeid', 'typename',
1394                      'using', 'virtual', 'wchar_t',
1395                      # alternative representations
1396                      'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1397                      'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1398     # namespace pollution:
1399     polluted_words = set(['unix', 'errno'])
1400     if protect and (name in c89_words | c99_words | c11_words | gcc_words
1401                     | cpp_words | polluted_words):
1402         return "q_" + name
1403     return name.translate(c_name_trans)
1404
1405 eatspace = '\033EATSPACE.'
1406 pointer_suffix = ' *' + eatspace
1407
1408
1409 def genindent(count):
1410     ret = ""
1411     for _ in range(count):
1412         ret += " "
1413     return ret
1414
1415 indent_level = 0
1416
1417
1418 def push_indent(indent_amount=4):
1419     global indent_level
1420     indent_level += indent_amount
1421
1422
1423 def pop_indent(indent_amount=4):
1424     global indent_level
1425     indent_level -= indent_amount
1426
1427
1428 # Generate @code with @kwds interpolated.
1429 # Obey indent_level, and strip eatspace.
1430 def cgen(code, **kwds):
1431     raw = code % kwds
1432     if indent_level:
1433         indent = genindent(indent_level)
1434         # re.subn() lacks flags support before Python 2.7, use re.compile()
1435         raw = re.subn(re.compile("^.", re.MULTILINE),
1436                       indent + r'\g<0>', raw)
1437         raw = raw[0]
1438     return re.sub(re.escape(eatspace) + ' *', '', raw)
1439
1440
1441 def mcgen(code, **kwds):
1442     if code[0] == '\n':
1443         code = code[1:]
1444     return cgen(code, **kwds)
1445
1446
1447 def guardname(filename):
1448     return c_name(filename, protect=False).upper()
1449
1450
1451 def guardstart(name):
1452     return mcgen('''
1453
1454 #ifndef %(name)s
1455 #define %(name)s
1456
1457 ''',
1458                  name=guardname(name))
1459
1460
1461 def guardend(name):
1462     return mcgen('''
1463
1464 #endif /* %(name)s */
1465
1466 ''',
1467                  name=guardname(name))
1468
1469
1470 def gen_enum_lookup(name, values, prefix=None):
1471     ret = mcgen('''
1472
1473 const char *const %(c_name)s_lookup[] = {
1474 ''',
1475                 c_name=c_name(name))
1476     for value in values:
1477         index = c_enum_const(name, value, prefix)
1478         ret += mcgen('''
1479     [%(index)s] = "%(value)s",
1480 ''',
1481                      index=index, value=value)
1482
1483     max_index = c_enum_const(name, 'MAX', prefix)
1484     ret += mcgen('''
1485     [%(max_index)s] = NULL,
1486 };
1487 ''',
1488                  max_index=max_index)
1489     return ret
1490
1491
1492 def gen_enum(name, values, prefix=None):
1493     # append automatically generated _MAX value
1494     enum_values = values + ['MAX']
1495
1496     ret = mcgen('''
1497
1498 typedef enum %(c_name)s {
1499 ''',
1500                 c_name=c_name(name))
1501
1502     i = 0
1503     for value in enum_values:
1504         ret += mcgen('''
1505     %(c_enum)s = %(i)d,
1506 ''',
1507                      c_enum=c_enum_const(name, value, prefix),
1508                      i=i)
1509         i += 1
1510
1511     ret += mcgen('''
1512 } %(c_name)s;
1513 ''',
1514                  c_name=c_name(name))
1515
1516     ret += mcgen('''
1517
1518 extern const char *const %(c_name)s_lookup[];
1519 ''',
1520                  c_name=c_name(name))
1521     return ret
1522
1523
1524 def gen_params(arg_type, extra):
1525     if not arg_type:
1526         return extra
1527     assert not arg_type.variants
1528     ret = ''
1529     sep = ''
1530     for memb in arg_type.members:
1531         ret += sep
1532         sep = ', '
1533         if memb.optional:
1534             ret += 'bool has_%s, ' % c_name(memb.name)
1535         ret += '%s %s' % (memb.type.c_type(is_param=True), c_name(memb.name))
1536     if extra:
1537         ret += sep + extra
1538     return ret
1539
1540 #
1541 # Common command line parsing
1542 #
1543
1544
1545 def parse_command_line(extra_options="", extra_long_options=[]):
1546
1547     try:
1548         opts, args = getopt.gnu_getopt(sys.argv[1:],
1549                                        "chp:o:" + extra_options,
1550                                        ["source", "header", "prefix=",
1551                                         "output-dir="] + extra_long_options)
1552     except getopt.GetoptError, err:
1553         print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
1554         sys.exit(1)
1555
1556     output_dir = ""
1557     prefix = ""
1558     do_c = False
1559     do_h = False
1560     extra_opts = []
1561
1562     for oa in opts:
1563         o, a = oa
1564         if o in ("-p", "--prefix"):
1565             match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1566             if match.end() != len(a):
1567                 print >>sys.stderr, \
1568                     "%s: 'funny character '%s' in argument of --prefix" \
1569                     % (sys.argv[0], a[match.end()])
1570                 sys.exit(1)
1571             prefix = a
1572         elif o in ("-o", "--output-dir"):
1573             output_dir = a + "/"
1574         elif o in ("-c", "--source"):
1575             do_c = True
1576         elif o in ("-h", "--header"):
1577             do_h = True
1578         else:
1579             extra_opts.append(oa)
1580
1581     if not do_c and not do_h:
1582         do_c = True
1583         do_h = True
1584
1585     if len(args) != 1:
1586         print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
1587         sys.exit(1)
1588     fname = args[0]
1589
1590     return (fname, output_dir, do_c, do_h, prefix, extra_opts)
1591
1592 #
1593 # Generate output files with boilerplate
1594 #
1595
1596
1597 def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1598                 c_comment, h_comment):
1599     guard = guardname(prefix + h_file)
1600     c_file = output_dir + prefix + c_file
1601     h_file = output_dir + prefix + h_file
1602
1603     if output_dir:
1604         try:
1605             os.makedirs(output_dir)
1606         except os.error, e:
1607             if e.errno != errno.EEXIST:
1608                 raise
1609
1610     def maybe_open(really, name, opt):
1611         if really:
1612             return open(name, opt)
1613         else:
1614             import StringIO
1615             return StringIO.StringIO()
1616
1617     fdef = maybe_open(do_c, c_file, 'w')
1618     fdecl = maybe_open(do_h, h_file, 'w')
1619
1620     fdef.write(mcgen('''
1621 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1622 %(comment)s
1623 ''',
1624                      comment=c_comment))
1625
1626     fdecl.write(mcgen('''
1627 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1628 %(comment)s
1629 #ifndef %(guard)s
1630 #define %(guard)s
1631
1632 ''',
1633                       comment=h_comment, guard=guard))
1634
1635     return (fdef, fdecl)
1636
1637
1638 def close_output(fdef, fdecl):
1639     fdecl.write('''
1640 #endif
1641 ''')
1642     fdecl.close()
1643     fdef.close()
This page took 0.121135 seconds and 4 git commands to generate.