qemu/scripts/qapi.py
Kevin Wolf b35284ea20 qapi.py: Allow top-level type reference for command definitions
If 'data' for a command definition isn't a dict, but a string, it is
taken as a (struct) type name and the fields of this struct are directly
used as parameters.

This is useful for transactionable commands that can use the same type
definition for both the transaction action and the arguments of the
standalone command.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2013-07-10 13:39:37 -04:00

287 lines
7.2 KiB
Python

#
# QAPI helper library
#
# Copyright IBM, Corp. 2011
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.com>
#
# This work is licensed under the terms of the GNU GPLv2.
# See the COPYING.LIB file in the top-level directory.
from ordereddict import OrderedDict
builtin_types = [
'str', 'int', 'number', 'bool',
'int8', 'int16', 'int32', 'int64',
'uint8', 'uint16', 'uint32', 'uint64'
]
def tokenize(data):
while len(data):
ch = data[0]
data = data[1:]
if ch in ['{', '}', ':', ',', '[', ']']:
yield ch
elif ch in ' \n':
None
elif ch == "'":
string = ''
esc = False
while True:
if (data == ''):
raise Exception("Mismatched quotes")
ch = data[0]
data = data[1:]
if esc:
string += ch
esc = False
elif ch == "\\":
esc = True
elif ch == "'":
break
else:
string += ch
yield string
def parse(tokens):
if tokens[0] == '{':
ret = OrderedDict()
tokens = tokens[1:]
while tokens[0] != '}':
key = tokens[0]
tokens = tokens[1:]
tokens = tokens[1:] # :
value, tokens = parse(tokens)
if tokens[0] == ',':
tokens = tokens[1:]
ret[key] = value
tokens = tokens[1:]
return ret, tokens
elif tokens[0] == '[':
ret = []
tokens = tokens[1:]
while tokens[0] != ']':
value, tokens = parse(tokens)
if tokens[0] == ',':
tokens = tokens[1:]
ret.append(value)
tokens = tokens[1:]
return ret, tokens
else:
return tokens[0], tokens[1:]
def evaluate(string):
return parse(map(lambda x: x, tokenize(string)))[0]
def get_expr(fp):
expr = ''
for line in fp:
if line.startswith('#') or line == '\n':
continue
if line.startswith(' '):
expr += line
elif expr:
yield expr
expr = line
else:
expr += line
if expr:
yield expr
def parse_schema(fp):
exprs = []
for expr in get_expr(fp):
expr_eval = evaluate(expr)
if expr_eval.has_key('enum'):
add_enum(expr_eval['enum'])
elif expr_eval.has_key('union'):
add_enum('%sKind' % expr_eval['union'])
elif expr_eval.has_key('type'):
add_struct(expr_eval)
exprs.append(expr_eval)
return exprs
def parse_args(typeinfo):
if isinstance(typeinfo, basestring):
struct = find_struct(typeinfo)
assert struct != None
typeinfo = struct['data']
for member in typeinfo:
argname = member
argentry = typeinfo[member]
optional = False
structured = False
if member.startswith('*'):
argname = member[1:]
optional = True
if isinstance(argentry, OrderedDict):
structured = True
yield (argname, argentry, optional, structured)
def de_camel_case(name):
new_name = ''
for ch in name:
if ch.isupper() and new_name:
new_name += '_'
if ch == '-':
new_name += '_'
else:
new_name += ch.lower()
return new_name
def camel_case(name):
new_name = ''
first = True
for ch in name:
if ch in ['_', '-']:
first = True
elif first:
new_name += ch.upper()
first = False
else:
new_name += ch.lower()
return new_name
def c_var(name, protect=True):
# ANSI X3J11/88-090, 3.1.1
c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
'default', 'do', 'double', 'else', 'enum', 'extern', 'float',
'for', 'goto', 'if', 'int', 'long', 'register', 'return',
'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
'typedef', 'union', 'unsigned', 'void', 'volatile', 'while'])
# ISO/IEC 9899:1999, 6.4.1
c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
# ISO/IEC 9899:2011, 6.4.1
c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn',
'_Static_assert', '_Thread_local'])
# GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
# excluding _.*
gcc_words = set(['asm', 'typeof'])
# namespace pollution:
polluted_words = set(['unix'])
if protect and (name in c89_words | c99_words | c11_words | gcc_words | polluted_words):
return "q_" + name
return name.replace('-', '_').lstrip("*")
def c_fun(name, protect=True):
return c_var(name, protect).replace('.', '_')
def c_list_type(name):
return '%sList' % name
def type_name(name):
if type(name) == list:
return c_list_type(name[0])
return name
enum_types = []
struct_types = []
def add_struct(definition):
global struct_types
struct_types.append(definition)
def find_struct(name):
global struct_types
for struct in struct_types:
if struct['type'] == name:
return struct
return None
def add_enum(name):
global enum_types
enum_types.append(name)
def is_enum(name):
global enum_types
return (name in enum_types)
def c_type(name):
if name == 'str':
return 'char *'
elif name == 'int':
return 'int64_t'
elif (name == 'int8' or name == 'int16' or name == 'int32' or
name == 'int64' or name == 'uint8' or name == 'uint16' or
name == 'uint32' or name == 'uint64'):
return name + '_t'
elif name == 'size':
return 'uint64_t'
elif name == 'bool':
return 'bool'
elif name == 'number':
return 'double'
elif type(name) == list:
return '%s *' % c_list_type(name[0])
elif is_enum(name):
return name
elif name == None or len(name) == 0:
return 'void'
elif name == name.upper():
return '%sEvent *' % camel_case(name)
else:
return '%s *' % name
def genindent(count):
ret = ""
for i in range(count):
ret += " "
return ret
indent_level = 0
def push_indent(indent_amount=4):
global indent_level
indent_level += indent_amount
def pop_indent(indent_amount=4):
global indent_level
indent_level -= indent_amount
def cgen(code, **kwds):
indent = genindent(indent_level)
lines = code.split('\n')
lines = map(lambda x: indent + x, lines)
return '\n'.join(lines) % kwds + '\n'
def mcgen(code, **kwds):
return cgen('\n'.join(code.split('\n')[1:-1]), **kwds)
def basename(filename):
return filename.split("/")[-1]
def guardname(filename):
guard = basename(filename).rsplit(".", 1)[0]
for substr in [".", " ", "-"]:
guard = guard.replace(substr, "_")
return guard.upper() + '_H'
def guardstart(name):
return mcgen('''
#ifndef %(name)s
#define %(name)s
''',
name=guardname(name))
def guardend(name):
return mcgen('''
#endif /* %(name)s */
''',
name=guardname(name))