|  | # | 
|  | # 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 | 
|  | """ | 
|  | ret = value[0] | 
|  | upc = value[0].isupper() | 
|  |  | 
|  | # Copy remainder of ``value`` to ``ret`` with '_' inserted | 
|  | for ch in value[1:]: | 
|  | if ch.isupper() == upc: | 
|  | pass | 
|  | elif upc: | 
|  | # ``ret`` ends in upper case, next char isn't: insert '_' | 
|  | # before the last upper case char unless there is one | 
|  | # already, or it's at the beginning | 
|  | if len(ret) > 2 and ret[-2].isalnum(): | 
|  | ret = ret[:-1] + '_' + ret[-1] | 
|  | else: | 
|  | # ``ret`` doesn't end in upper case, next char is: insert | 
|  | # '_' before it | 
|  | if ret[-1].isalnum(): | 
|  | ret += '_' | 
|  | ret += ch | 
|  | upc = ch.isupper() | 
|  |  | 
|  | return c_name(ret.upper()).lstrip('_') | 
|  |  | 
|  |  | 
|  | 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 None: | 
|  | prefix = camel_to_upper(type_name) | 
|  | return prefix + '_' + 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', 'linux']) | 
|  | 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 |