diff options
Diffstat (limited to 'scripts/qapi.py')
-rw-r--r-- | scripts/qapi.py | 244 |
1 files changed, 173 insertions, 71 deletions
diff --git a/scripts/qapi.py b/scripts/qapi.py index 166b74f644..f96a7772e5 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -13,8 +13,11 @@ import re from ordereddict import OrderedDict +import errno +import getopt import os import sys +import string builtin_types = { 'str': 'QTYPE_QSTRING', @@ -537,7 +540,7 @@ def check_union(expr, expr_info): # Otherwise, check for conflicts in the generated enum else: - c_key = _generate_enum_string(key) + c_key = camel_to_upper(key) if c_key in values: raise QAPIExprError(expr_info, "Union '%s' member '%s' clashes with '%s'" @@ -555,7 +558,7 @@ def check_alternate(expr, expr_info): check_name(expr_info, "Member of alternate '%s'" % name, key) # Check for conflicts in the generated enum - c_key = _generate_enum_string(key) + c_key = camel_to_upper(key) if c_key in values: raise QAPIExprError(expr_info, "Alternate '%s' member '%s' clashes with '%s'" @@ -586,7 +589,7 @@ def check_enum(expr, expr_info): for member in members: check_name(expr_info, "Member of enum '%s'" %name, member, enum_member=True) - key = _generate_enum_string(member) + key = camel_to_upper(member) if key in values: raise QAPIExprError(expr_info, "Enum '%s' member '%s' clashes with '%s'" @@ -728,17 +731,6 @@ def parse_args(typeinfo): # value of an optional argument. yield (argname, argentry, optional) -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 @@ -752,7 +744,43 @@ def camel_case(name): new_name += ch.lower() return new_name -def c_var(name, protect=True): +# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 +# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 +# ENUM24_Name -> ENUM24_NAME +def camel_to_upper(value): + c_fun_str = c_name(value, False) + if value.isupper(): + return c_fun_str + + new_name = '' + l = len(c_fun_str) + for i in range(l): + c = c_fun_str[i] + # When c is upper and no "_" appears before, do more checks + if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_": + # Case 1: next string is lower + # Case 2: previous string is digit + if (i < (l - 1) and c_fun_str[i + 1].islower()) or \ + c_fun_str[i - 1].isdigit(): + new_name += '_' + new_name += c + return new_name.lstrip('_').upper() + +def c_enum_const(type_name, const_name): + return camel_to_upper(type_name + '_' + const_name) + +c_name_trans = string.maketrans('.-', '__') + +# Map @name to a valid C identifier. +# If @protect, avoid returning certain ticklish identifiers (like +# C keywords) by prepending "q_". +# +# Used for converting 'name' from a 'name':'type' qapi definition +# into a generated struct member, as well as converting type names +# into substrings of a generated C function name. +# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo' +# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int' +def c_name(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', @@ -781,18 +809,27 @@ def c_var(name, protect=True): polluted_words = set(['unix', 'errno']) if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words): return "q_" + name - return name.replace('-', '_').lstrip("*") - -def c_fun(name, protect=True): - return c_var(name, protect).replace('.', '_') + return name.translate(c_name_trans) +# Map type @name to the C typedef name for the list form. +# +# ['Name'] -> 'NameList', ['x-Foo'] -> 'x_FooList', ['int'] -> 'intList' def c_list_type(name): - return '%sList' % name + return type_name(name) + 'List' -def type_name(name): - if type(name) == list: - return c_list_type(name[0]) - return name +# Map type @value to the C typedef form. +# +# Used for converting 'type' from a 'member':'type' qapi definition +# into the alphanumeric portion of the type for a generated C parameter, +# as well as generated C function names. See c_type() for the rest of +# the conversion such as adding '*' on pointer types. +# 'int' -> 'int', '[x-Foo]' -> 'x_FooList', '__a.b_c' -> '__a_b_c' +def type_name(value): + if type(value) == list: + return c_list_type(value[0]) + if value in builtin_types.keys(): + return value + return c_name(value) def add_name(name, info, meta, implicit = False): global all_names @@ -849,42 +886,48 @@ def is_enum(name): return find_enum(name) != None eatspace = '\033EATSPACE.' +pointer_suffix = ' *' + eatspace +# Map type @name to its C type expression. +# If @is_param, const-qualify the string type. +# +# This function is used for computing the full C type of 'member':'name'. # A special suffix is added in c_type() for pointer types, and it's # stripped in mcgen(). So please notice this when you check the return # value of c_type() outside mcgen(). -def c_type(name, is_param=False): - if name == 'str': +def c_type(value, is_param=False): + if value == 'str': if is_param: - return 'const char *' + eatspace - return 'char *' + eatspace + return 'const char' + pointer_suffix + return 'char' + pointer_suffix - elif name == 'int': + elif value == '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': + elif (value == 'int8' or value == 'int16' or value == 'int32' or + value == 'int64' or value == 'uint8' or value == 'uint16' or + value == 'uint32' or value == 'uint64'): + return value + '_t' + elif value == 'size': return 'uint64_t' - elif name == 'bool': + elif value == 'bool': return 'bool' - elif name == 'number': + elif value == 'number': return 'double' - elif type(name) == list: - return '%s *%s' % (c_list_type(name[0]), eatspace) - elif is_enum(name): - return name - elif name == None or len(name) == 0: + elif type(value) == list: + return c_list_type(value[0]) + pointer_suffix + elif is_enum(value): + return c_name(value) + elif value == None: return 'void' - elif name in events: - return '%sEvent *%s' % (camel_case(name), eatspace) + elif value in events: + return camel_case(value) + 'Event' + pointer_suffix else: - return '%s *%s' % (name, eatspace) + # complex type name + assert isinstance(value, str) and value != "" + return c_name(value) + pointer_suffix -def is_c_ptr(name): - suffix = "*" + eatspace - return c_type(name).endswith(suffix) +def is_c_ptr(value): + return c_type(value).endswith(pointer_suffix) def genindent(count): ret = "" @@ -938,29 +981,88 @@ def guardend(name): ''', name=guardname(name)) -# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 -# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 -# ENUM24_Name -> ENUM24_NAME -def _generate_enum_string(value): - c_fun_str = c_fun(value, False) - if value.isupper(): - return c_fun_str +def parse_command_line(extra_options = "", extra_long_options = []): - new_name = '' - l = len(c_fun_str) - for i in range(l): - c = c_fun_str[i] - # When c is upper and no "_" appears before, do more checks - if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_": - # Case 1: next string is lower - # Case 2: previous string is digit - if (i < (l - 1) and c_fun_str[i + 1].islower()) or \ - c_fun_str[i - 1].isdigit(): - new_name += '_' - new_name += c - return new_name.lstrip('_').upper() + try: + opts, args = getopt.gnu_getopt(sys.argv[1:], + "chp:o:" + extra_options, + ["source", "header", "prefix=", + "output-dir="] + extra_long_options) + except getopt.GetoptError, err: + print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err)) + sys.exit(1) + + output_dir = "" + prefix = "" + do_c = False + do_h = False + extra_opts = [] + + for oa in opts: + o, a = oa + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + elif o in ("-c", "--source"): + do_c = True + elif o in ("-h", "--header"): + do_h = True + else: + extra_opts.append(oa) + + if not do_c and not do_h: + do_c = True + do_h = True + + if len(args) != 1: + print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0] + sys.exit(1) + input_file = args[0] + + return (input_file, output_dir, do_c, do_h, prefix, extra_opts) + +def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, + c_comment, h_comment): + c_file = output_dir + prefix + c_file + h_file = output_dir + prefix + h_file + + try: + os.makedirs(output_dir) + except os.error, e: + if e.errno != errno.EEXIST: + raise + + def maybe_open(really, name, opt): + if really: + return open(name, opt) + else: + import StringIO + return StringIO.StringIO() + + fdef = maybe_open(do_c, c_file, 'w') + fdecl = maybe_open(do_h, h_file, 'w') + + fdef.write(mcgen(''' +/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ +%(comment)s +''', + comment = c_comment)) + + fdecl.write(mcgen(''' +/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ +%(comment)s +#ifndef %(guard)s +#define %(guard)s + +''', + comment = h_comment, guard = guardname(h_file))) + + return (fdef, fdecl) -def generate_enum_full_value(enum_name, enum_value): - abbrev_string = _generate_enum_string(enum_name) - value_string = _generate_enum_string(enum_value) - return "%s_%s" % (abbrev_string, value_string) +def close_output(fdef, fdecl): + fdecl.write(''' +#endif +''') + fdecl.close() + fdef.close() |