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