b35284ea20
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>
287 lines
7.2 KiB
Python
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))
|