]> Git Repo - qemu.git/blob - scripts/qapi.py
input: add qemu_input_handler_deactivate
[qemu.git] / scripts / qapi.py
1 #
2 # QAPI helper library
3 #
4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013 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 os
17 import sys
18
19 builtin_types = [
20     'str', 'int', 'number', 'bool',
21     'int8', 'int16', 'int32', 'int64',
22     'uint8', 'uint16', 'uint32', 'uint64'
23 ]
24
25 builtin_type_qtypes = {
26     'str':      'QTYPE_QSTRING',
27     'int':      'QTYPE_QINT',
28     'number':   'QTYPE_QFLOAT',
29     'bool':     'QTYPE_QBOOL',
30     'int8':     'QTYPE_QINT',
31     'int16':    'QTYPE_QINT',
32     'int32':    'QTYPE_QINT',
33     'int64':    'QTYPE_QINT',
34     'uint8':    'QTYPE_QINT',
35     'uint16':   'QTYPE_QINT',
36     'uint32':   'QTYPE_QINT',
37     'uint64':   'QTYPE_QINT',
38 }
39
40 def error_path(parent):
41     res = ""
42     while parent:
43         res = ("In file included from %s:%d:\n" % (parent['file'],
44                                                    parent['line'])) + res
45         parent = parent['parent']
46     return res
47
48 class QAPISchemaError(Exception):
49     def __init__(self, schema, msg):
50         self.input_file = schema.input_file
51         self.msg = msg
52         self.col = 1
53         self.line = schema.line
54         for ch in schema.src[schema.line_pos:schema.pos]:
55             if ch == '\t':
56                 self.col = (self.col + 7) % 8 + 1
57             else:
58                 self.col += 1
59         self.info = schema.parent_info
60
61     def __str__(self):
62         return error_path(self.info) + \
63             "%s:%d:%d: %s" % (self.input_file, self.line, self.col, self.msg)
64
65 class QAPIExprError(Exception):
66     def __init__(self, expr_info, msg):
67         self.info = expr_info
68         self.msg = msg
69
70     def __str__(self):
71         return error_path(self.info['parent']) + \
72             "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
73
74 class QAPISchema:
75
76     def __init__(self, fp, input_relname=None, include_hist=[], parent_info=None):
77         input_fname = os.path.abspath(fp.name)
78         if input_relname is None:
79             input_relname = fp.name
80         self.input_dir = os.path.dirname(input_fname)
81         self.input_file = input_relname
82         self.include_hist = include_hist + [(input_relname, input_fname)]
83         self.parent_info = parent_info
84         self.src = fp.read()
85         if self.src == '' or self.src[-1] != '\n':
86             self.src += '\n'
87         self.cursor = 0
88         self.line = 1
89         self.line_pos = 0
90         self.exprs = []
91         self.accept()
92
93         while self.tok != None:
94             expr_info = {'file': input_relname, 'line': self.line, 'parent': self.parent_info}
95             expr = self.get_expr(False)
96             if isinstance(expr, dict) and "include" in expr:
97                 if len(expr) != 1:
98                     raise QAPIExprError(expr_info, "Invalid 'include' directive")
99                 include = expr["include"]
100                 if not isinstance(include, str):
101                     raise QAPIExprError(expr_info,
102                                         'Expected a file name (string), got: %s'
103                                         % include)
104                 include_path = os.path.join(self.input_dir, include)
105                 if any(include_path == elem[1]
106                        for elem in self.include_hist):
107                     raise QAPIExprError(expr_info, "Inclusion loop for %s"
108                                         % include)
109                 try:
110                     fobj = open(include_path, 'r')
111                 except IOError as e:
112                     raise QAPIExprError(expr_info,
113                                         '%s: %s' % (e.strerror, include))
114                 exprs_include = QAPISchema(fobj, include,
115                                            self.include_hist, expr_info)
116                 self.exprs.extend(exprs_include.exprs)
117             else:
118                 expr_elem = {'expr': expr,
119                              'info': expr_info}
120                 self.exprs.append(expr_elem)
121
122     def accept(self):
123         while True:
124             self.tok = self.src[self.cursor]
125             self.pos = self.cursor
126             self.cursor += 1
127             self.val = None
128
129             if self.tok == '#':
130                 self.cursor = self.src.find('\n', self.cursor)
131             elif self.tok in ['{', '}', ':', ',', '[', ']']:
132                 return
133             elif self.tok == "'":
134                 string = ''
135                 esc = False
136                 while True:
137                     ch = self.src[self.cursor]
138                     self.cursor += 1
139                     if ch == '\n':
140                         raise QAPISchemaError(self,
141                                               'Missing terminating "\'"')
142                     if esc:
143                         string += ch
144                         esc = False
145                     elif ch == "\\":
146                         esc = True
147                     elif ch == "'":
148                         self.val = string
149                         return
150                     else:
151                         string += ch
152             elif self.tok == '\n':
153                 if self.cursor == len(self.src):
154                     self.tok = None
155                     return
156                 self.line += 1
157                 self.line_pos = self.cursor
158             elif not self.tok.isspace():
159                 raise QAPISchemaError(self, 'Stray "%s"' % self.tok)
160
161     def get_members(self):
162         expr = OrderedDict()
163         if self.tok == '}':
164             self.accept()
165             return expr
166         if self.tok != "'":
167             raise QAPISchemaError(self, 'Expected string or "}"')
168         while True:
169             key = self.val
170             self.accept()
171             if self.tok != ':':
172                 raise QAPISchemaError(self, 'Expected ":"')
173             self.accept()
174             if key in expr:
175                 raise QAPISchemaError(self, 'Duplicate key "%s"' % key)
176             expr[key] = self.get_expr(True)
177             if self.tok == '}':
178                 self.accept()
179                 return expr
180             if self.tok != ',':
181                 raise QAPISchemaError(self, 'Expected "," or "}"')
182             self.accept()
183             if self.tok != "'":
184                 raise QAPISchemaError(self, 'Expected string')
185
186     def get_values(self):
187         expr = []
188         if self.tok == ']':
189             self.accept()
190             return expr
191         if not self.tok in [ '{', '[', "'" ]:
192             raise QAPISchemaError(self, 'Expected "{", "[", "]" or string')
193         while True:
194             expr.append(self.get_expr(True))
195             if self.tok == ']':
196                 self.accept()
197                 return expr
198             if self.tok != ',':
199                 raise QAPISchemaError(self, 'Expected "," or "]"')
200             self.accept()
201
202     def get_expr(self, nested):
203         if self.tok != '{' and not nested:
204             raise QAPISchemaError(self, 'Expected "{"')
205         if self.tok == '{':
206             self.accept()
207             expr = self.get_members()
208         elif self.tok == '[':
209             self.accept()
210             expr = self.get_values()
211         elif self.tok == "'":
212             expr = self.val
213             self.accept()
214         else:
215             raise QAPISchemaError(self, 'Expected "{", "[" or string')
216         return expr
217
218 def find_base_fields(base):
219     base_struct_define = find_struct(base)
220     if not base_struct_define:
221         return None
222     return base_struct_define['data']
223
224 # Return the discriminator enum define if discriminator is specified as an
225 # enum type, otherwise return None.
226 def discriminator_find_enum_define(expr):
227     base = expr.get('base')
228     discriminator = expr.get('discriminator')
229
230     if not (discriminator and base):
231         return None
232
233     base_fields = find_base_fields(base)
234     if not base_fields:
235         return None
236
237     discriminator_type = base_fields.get(discriminator)
238     if not discriminator_type:
239         return None
240
241     return find_enum(discriminator_type)
242
243 def check_union(expr, expr_info):
244     name = expr['union']
245     base = expr.get('base')
246     discriminator = expr.get('discriminator')
247     members = expr['data']
248
249     # If the object has a member 'base', its value must name a complex type.
250     if base:
251         base_fields = find_base_fields(base)
252         if not base_fields:
253             raise QAPIExprError(expr_info,
254                                 "Base '%s' is not a valid type"
255                                 % base)
256
257     # If the union object has no member 'discriminator', it's an
258     # ordinary union.
259     if not discriminator:
260         enum_define = None
261
262     # Else if the value of member 'discriminator' is {}, it's an
263     # anonymous union.
264     elif discriminator == {}:
265         enum_define = None
266
267     # Else, it's a flat union.
268     else:
269         # The object must have a member 'base'.
270         if not base:
271             raise QAPIExprError(expr_info,
272                                 "Flat union '%s' must have a base field"
273                                 % name)
274         # The value of member 'discriminator' must name a member of the
275         # base type.
276         discriminator_type = base_fields.get(discriminator)
277         if not discriminator_type:
278             raise QAPIExprError(expr_info,
279                                 "Discriminator '%s' is not a member of base "
280                                 "type '%s'"
281                                 % (discriminator, base))
282         enum_define = find_enum(discriminator_type)
283         # Do not allow string discriminator
284         if not enum_define:
285             raise QAPIExprError(expr_info,
286                                 "Discriminator '%s' must be of enumeration "
287                                 "type" % discriminator)
288
289     # Check every branch
290     for (key, value) in members.items():
291         # If this named member's value names an enum type, then all members
292         # of 'data' must also be members of the enum type.
293         if enum_define and not key in enum_define['enum_values']:
294             raise QAPIExprError(expr_info,
295                                 "Discriminator value '%s' is not found in "
296                                 "enum '%s'" %
297                                 (key, enum_define["enum_name"]))
298         # Todo: add checking for values. Key is checked as above, value can be
299         # also checked here, but we need more functions to handle array case.
300
301 def check_exprs(schema):
302     for expr_elem in schema.exprs:
303         expr = expr_elem['expr']
304         if expr.has_key('union'):
305             check_union(expr, expr_elem['info'])
306
307 def parse_schema(input_file):
308     try:
309         schema = QAPISchema(open(input_file, "r"))
310     except (QAPISchemaError, QAPIExprError), e:
311         print >>sys.stderr, e
312         exit(1)
313
314     exprs = []
315
316     for expr_elem in schema.exprs:
317         expr = expr_elem['expr']
318         if expr.has_key('enum'):
319             add_enum(expr['enum'], expr['data'])
320         elif expr.has_key('union'):
321             add_union(expr)
322         elif expr.has_key('type'):
323             add_struct(expr)
324         exprs.append(expr)
325
326     # Try again for hidden UnionKind enum
327     for expr_elem in schema.exprs:
328         expr = expr_elem['expr']
329         if expr.has_key('union'):
330             if not discriminator_find_enum_define(expr):
331                 add_enum('%sKind' % expr['union'])
332
333     try:
334         check_exprs(schema)
335     except QAPIExprError, e:
336         print >>sys.stderr, e
337         exit(1)
338
339     return exprs
340
341 def parse_args(typeinfo):
342     if isinstance(typeinfo, basestring):
343         struct = find_struct(typeinfo)
344         assert struct != None
345         typeinfo = struct['data']
346
347     for member in typeinfo:
348         argname = member
349         argentry = typeinfo[member]
350         optional = False
351         structured = False
352         if member.startswith('*'):
353             argname = member[1:]
354             optional = True
355         if isinstance(argentry, OrderedDict):
356             structured = True
357         yield (argname, argentry, optional, structured)
358
359 def de_camel_case(name):
360     new_name = ''
361     for ch in name:
362         if ch.isupper() and new_name:
363             new_name += '_'
364         if ch == '-':
365             new_name += '_'
366         else:
367             new_name += ch.lower()
368     return new_name
369
370 def camel_case(name):
371     new_name = ''
372     first = True
373     for ch in name:
374         if ch in ['_', '-']:
375             first = True
376         elif first:
377             new_name += ch.upper()
378             first = False
379         else:
380             new_name += ch.lower()
381     return new_name
382
383 def c_var(name, protect=True):
384     # ANSI X3J11/88-090, 3.1.1
385     c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
386                      'default', 'do', 'double', 'else', 'enum', 'extern', 'float',
387                      'for', 'goto', 'if', 'int', 'long', 'register', 'return',
388                      'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
389                      'typedef', 'union', 'unsigned', 'void', 'volatile', 'while'])
390     # ISO/IEC 9899:1999, 6.4.1
391     c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
392     # ISO/IEC 9899:2011, 6.4.1
393     c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn',
394                      '_Static_assert', '_Thread_local'])
395     # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
396     # excluding _.*
397     gcc_words = set(['asm', 'typeof'])
398     # C++ ISO/IEC 14882:2003 2.11
399     cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
400                      'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
401                      'namespace', 'new', 'operator', 'private', 'protected',
402                      'public', 'reinterpret_cast', 'static_cast', 'template',
403                      'this', 'throw', 'true', 'try', 'typeid', 'typename',
404                      'using', 'virtual', 'wchar_t',
405                      # alternative representations
406                      'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
407                      'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
408     # namespace pollution:
409     polluted_words = set(['unix', 'errno'])
410     if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words):
411         return "q_" + name
412     return name.replace('-', '_').lstrip("*")
413
414 def c_fun(name, protect=True):
415     return c_var(name, protect).replace('.', '_')
416
417 def c_list_type(name):
418     return '%sList' % name
419
420 def type_name(name):
421     if type(name) == list:
422         return c_list_type(name[0])
423     return name
424
425 enum_types = []
426 struct_types = []
427 union_types = []
428
429 def add_struct(definition):
430     global struct_types
431     struct_types.append(definition)
432
433 def find_struct(name):
434     global struct_types
435     for struct in struct_types:
436         if struct['type'] == name:
437             return struct
438     return None
439
440 def add_union(definition):
441     global union_types
442     union_types.append(definition)
443
444 def find_union(name):
445     global union_types
446     for union in union_types:
447         if union['union'] == name:
448             return union
449     return None
450
451 def add_enum(name, enum_values = None):
452     global enum_types
453     enum_types.append({"enum_name": name, "enum_values": enum_values})
454
455 def find_enum(name):
456     global enum_types
457     for enum in enum_types:
458         if enum['enum_name'] == name:
459             return enum
460     return None
461
462 def is_enum(name):
463     return find_enum(name) != None
464
465 def c_type(name):
466     if name == 'str':
467         return 'char *'
468     elif name == 'int':
469         return 'int64_t'
470     elif (name == 'int8' or name == 'int16' or name == 'int32' or
471           name == 'int64' or name == 'uint8' or name == 'uint16' or
472           name == 'uint32' or name == 'uint64'):
473         return name + '_t'
474     elif name == 'size':
475         return 'uint64_t'
476     elif name == 'bool':
477         return 'bool'
478     elif name == 'number':
479         return 'double'
480     elif type(name) == list:
481         return '%s *' % c_list_type(name[0])
482     elif is_enum(name):
483         return name
484     elif name == None or len(name) == 0:
485         return 'void'
486     elif name == name.upper():
487         return '%sEvent *' % camel_case(name)
488     else:
489         return '%s *' % name
490
491 def genindent(count):
492     ret = ""
493     for i in range(count):
494         ret += " "
495     return ret
496
497 indent_level = 0
498
499 def push_indent(indent_amount=4):
500     global indent_level
501     indent_level += indent_amount
502
503 def pop_indent(indent_amount=4):
504     global indent_level
505     indent_level -= indent_amount
506
507 def cgen(code, **kwds):
508     indent = genindent(indent_level)
509     lines = code.split('\n')
510     lines = map(lambda x: indent + x, lines)
511     return '\n'.join(lines) % kwds + '\n'
512
513 def mcgen(code, **kwds):
514     return cgen('\n'.join(code.split('\n')[1:-1]), **kwds)
515
516 def basename(filename):
517     return filename.split("/")[-1]
518
519 def guardname(filename):
520     guard = basename(filename).rsplit(".", 1)[0]
521     for substr in [".", " ", "-"]:
522         guard = guard.replace(substr, "_")
523     return guard.upper() + '_H'
524
525 def guardstart(name):
526     return mcgen('''
527
528 #ifndef %(name)s
529 #define %(name)s
530
531 ''',
532                  name=guardname(name))
533
534 def guardend(name):
535     return mcgen('''
536
537 #endif /* %(name)s */
538
539 ''',
540                  name=guardname(name))
541
542 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
543 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
544 # ENUM24_Name -> ENUM24_NAME
545 def _generate_enum_string(value):
546     c_fun_str = c_fun(value, False)
547     if value.isupper():
548         return c_fun_str
549
550     new_name = ''
551     l = len(c_fun_str)
552     for i in range(l):
553         c = c_fun_str[i]
554         # When c is upper and no "_" appears before, do more checks
555         if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
556             # Case 1: next string is lower
557             # Case 2: previous string is digit
558             if (i < (l - 1) and c_fun_str[i + 1].islower()) or \
559             c_fun_str[i - 1].isdigit():
560                 new_name += '_'
561         new_name += c
562     return new_name.lstrip('_').upper()
563
564 def generate_enum_full_value(enum_name, enum_value):
565     abbrev_string = _generate_enum_string(enum_name)
566     value_string = _generate_enum_string(enum_value)
567     return "%s_%s" % (abbrev_string, value_string)
This page took 0.061847 seconds and 4 git commands to generate.