diff options
author | 2023-10-10 11:40:56 +0000 | |
---|---|---|
committer | 2023-10-10 11:40:56 +0000 | |
commit | e02cda008591317b1625707ff8e115a4841aa889 (patch) | |
tree | aee302e3cf8b59ec2d32ec481be3d1afddfc8968 /scripts/qapi/common.py | |
parent | cc668e6b7e0ffd8c9d130513d12053cf5eda1d3b (diff) |
Introduce Virtio-loopback epsilon release:
Epsilon release introduces a new compatibility layer which make virtio-loopback
design to work with QEMU and rust-vmm vhost-user backend without require any
changes.
Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
Change-Id: I52e57563e08a7d0bdc002f8e928ee61ba0c53dd9
Diffstat (limited to 'scripts/qapi/common.py')
-rw-r--r-- | scripts/qapi/common.py | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py new file mode 100644 index 000000000..489273574 --- /dev/null +++ b/scripts/qapi/common.py @@ -0,0 +1,251 @@ +# +# QAPI helper library +# +# Copyright IBM, Corp. 2011 +# Copyright (c) 2013-2018 Red Hat Inc. +# +# Authors: +# Anthony Liguori <aliguori@us.ibm.com> +# Markus Armbruster <armbru@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +import re +from typing import ( + Any, + Dict, + Match, + Optional, + Sequence, + Union, +) + + +#: Magic string that gets removed along with all space to its right. +EATSPACE = '\033EATSPACE.' +POINTER_SUFFIX = ' *' + EATSPACE + + +def camel_to_upper(value: str) -> str: + """ + Converts CamelCase to CAMEL_CASE. + + Examples:: + + ENUMName -> ENUM_NAME + EnumName1 -> ENUM_NAME1 + ENUM_NAME -> ENUM_NAME + ENUM_NAME1 -> ENUM_NAME1 + ENUM_Name2 -> ENUM_NAME2 + ENUM24_Name -> ENUM24_NAME + """ + c_fun_str = c_name(value, False) + if value.isupper(): + return c_fun_str + + new_name = '' + length = len(c_fun_str) + for i in range(length): + char = c_fun_str[i] + # When char is upper case and no '_' appears before, do more checks + if char.isupper() and (i > 0) and c_fun_str[i - 1] != '_': + if i < length - 1 and c_fun_str[i + 1].islower(): + new_name += '_' + elif c_fun_str[i - 1].isdigit(): + new_name += '_' + new_name += char + return new_name.lstrip('_').upper() + + +def c_enum_const(type_name: str, + const_name: str, + prefix: Optional[str] = None) -> str: + """ + Generate a C enumeration constant name. + + :param type_name: The name of the enumeration. + :param const_name: The name of this constant. + :param prefix: Optional, prefix that overrides the type_name. + """ + if prefix is not None: + type_name = prefix + return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() + + +def c_name(name: str, protect: bool = True) -> str: + """ + Map ``name`` to a valid C identifier. + + 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' + + :param name: The name to map. + :param protect: If true, avoid returning certain ticklish identifiers + (like C keywords) by prepending ``q_``. + """ + # 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']) + # C++ ISO/IEC 14882:2003 2.11 + cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', + 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', + 'namespace', 'new', 'operator', 'private', 'protected', + 'public', 'reinterpret_cast', 'static_cast', 'template', + 'this', 'throw', 'true', 'try', 'typeid', 'typename', + 'using', 'virtual', 'wchar_t', + # alternative representations + 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', + 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) + # namespace pollution: + polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) + name = re.sub(r'[^A-Za-z0-9_]', '_', name) + if protect and (name in (c89_words | c99_words | c11_words | gcc_words + | cpp_words | polluted_words) + or name[0].isdigit()): + return 'q_' + name + return name + + +class Indentation: + """ + Indentation level management. + + :param initial: Initial number of spaces, default 0. + """ + def __init__(self, initial: int = 0) -> None: + self._level = initial + + def __repr__(self) -> str: + return "{}({:d})".format(type(self).__name__, self._level) + + def __str__(self) -> str: + """Return the current indentation as a string of spaces.""" + return ' ' * self._level + + def increase(self, amount: int = 4) -> None: + """Increase the indentation level by ``amount``, default 4.""" + self._level += amount + + def decrease(self, amount: int = 4) -> None: + """Decrease the indentation level by ``amount``, default 4.""" + assert amount <= self._level + self._level -= amount + + +#: Global, current indent level for code generation. +indent = Indentation() + + +def cgen(code: str, **kwds: object) -> str: + """ + Generate ``code`` with ``kwds`` interpolated. + + Obey `indent`, and strip `EATSPACE`. + """ + raw = code % kwds + pfx = str(indent) + if pfx: + raw = re.sub(r'^(?!(#|$))', pfx, raw, flags=re.MULTILINE) + return re.sub(re.escape(EATSPACE) + r' *', '', raw) + + +def mcgen(code: str, **kwds: object) -> str: + if code[0] == '\n': + code = code[1:] + return cgen(code, **kwds) + + +def c_fname(filename: str) -> str: + return re.sub(r'[^A-Za-z0-9_]', '_', filename) + + +def guardstart(name: str) -> str: + return mcgen(''' +#ifndef %(name)s +#define %(name)s + +''', + name=c_fname(name).upper()) + + +def guardend(name: str) -> str: + return mcgen(''' + +#endif /* %(name)s */ +''', + name=c_fname(name).upper()) + + +def gen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]], + cond_fmt: str, not_fmt: str, + all_operator: str, any_operator: str) -> str: + + def do_gen(ifcond: Union[str, Dict[str, Any]], + need_parens: bool) -> str: + if isinstance(ifcond, str): + return cond_fmt % ifcond + assert isinstance(ifcond, dict) and len(ifcond) == 1 + if 'not' in ifcond: + return not_fmt % do_gen(ifcond['not'], True) + if 'all' in ifcond: + gen = gen_infix(all_operator, ifcond['all']) + else: + gen = gen_infix(any_operator, ifcond['any']) + if need_parens: + gen = '(' + gen + ')' + return gen + + def gen_infix(operator: str, operands: Sequence[Any]) -> str: + return operator.join([do_gen(o, True) for o in operands]) + + if not ifcond: + return '' + return do_gen(ifcond, False) + + +def cgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str: + return gen_ifcond(ifcond, 'defined(%s)', '!%s', ' && ', ' || ') + + +def docgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str: + # TODO Doc generated for conditions needs polish + return gen_ifcond(ifcond, '%s', 'not %s', ' and ', ' or ') + + +def gen_if(cond: str) -> str: + if not cond: + return '' + return mcgen(''' +#if %(cond)s +''', cond=cond) + + +def gen_endif(cond: str) -> str: + if not cond: + return '' + return mcgen(''' +#endif /* %(cond)s */ +''', cond=cond) + + +def must_match(pattern: str, string: str) -> Match[str]: + match = re.match(pattern, string) + assert match is not None + return match |