1fb0bc835SMarkus Armbruster# 2fb0bc835SMarkus Armbruster# QAPI helper library 3fb0bc835SMarkus Armbruster# 4fb0bc835SMarkus Armbruster# Copyright IBM, Corp. 2011 5fb0bc835SMarkus Armbruster# Copyright (c) 2013-2018 Red Hat Inc. 6fb0bc835SMarkus Armbruster# 7fb0bc835SMarkus Armbruster# Authors: 8fb0bc835SMarkus Armbruster# Anthony Liguori <aliguori@us.ibm.com> 9fb0bc835SMarkus Armbruster# Markus Armbruster <armbru@redhat.com> 10fb0bc835SMarkus Armbruster# 11fb0bc835SMarkus Armbruster# This work is licensed under the terms of the GNU GPL, version 2. 12fb0bc835SMarkus Armbruster# See the COPYING file in the top-level directory. 13fb0bc835SMarkus Armbruster 14fb0bc835SMarkus Armbrusterimport re 156cc2e481SMarc-André Lureaufrom typing import ( 165d83b9a1SMarc-André Lureau Any, 175d83b9a1SMarc-André Lureau Dict, 186cc2e481SMarc-André Lureau Match, 196cc2e481SMarc-André Lureau Optional, 20ccea6a86SMarkus Armbruster Sequence, 216cc2e481SMarc-André Lureau Union, 226cc2e481SMarc-André Lureau) 23fb0bc835SMarkus Armbruster 24fb0bc835SMarkus Armbruster 251cc7398dSJohn Snow#: Magic string that gets removed along with all space to its right. 26a7aa64a6SJohn SnowEATSPACE = '\033EATSPACE.' 27a7aa64a6SJohn SnowPOINTER_SUFFIX = ' *' + EATSPACE 28a7aa64a6SJohn Snow 29a7aa64a6SJohn Snow 30d646b2a1SJohn Snowdef camel_to_upper(value: str) -> str: 311cc7398dSJohn Snow """ 321cc7398dSJohn Snow Converts CamelCase to CAMEL_CASE. 331cc7398dSJohn Snow 341cc7398dSJohn Snow Examples:: 351cc7398dSJohn Snow 361cc7398dSJohn Snow ENUMName -> ENUM_NAME 371cc7398dSJohn Snow EnumName1 -> ENUM_NAME1 381cc7398dSJohn Snow ENUM_NAME -> ENUM_NAME 391cc7398dSJohn Snow ENUM_NAME1 -> ENUM_NAME1 401cc7398dSJohn Snow ENUM_Name2 -> ENUM_NAME2 411cc7398dSJohn Snow ENUM24_Name -> ENUM24_NAME 421cc7398dSJohn Snow """ 43*7b29353fSMarkus Armbruster ret = value[0] 44*7b29353fSMarkus Armbruster upc = value[0].isupper() 45fb0bc835SMarkus Armbruster 46*7b29353fSMarkus Armbruster # Copy remainder of ``value`` to ``ret`` with '_' inserted 47*7b29353fSMarkus Armbruster for ch in value[1:]: 48*7b29353fSMarkus Armbruster if ch.isupper() == upc: 49*7b29353fSMarkus Armbruster pass 50*7b29353fSMarkus Armbruster elif upc: 51*7b29353fSMarkus Armbruster # ``ret`` ends in upper case, next char isn't: insert '_' 52*7b29353fSMarkus Armbruster # before the last upper case char unless there is one 53*7b29353fSMarkus Armbruster # already, or it's at the beginning 54*7b29353fSMarkus Armbruster if len(ret) > 2 and ret[-2].isalnum(): 55*7b29353fSMarkus Armbruster ret = ret[:-1] + '_' + ret[-1] 56*7b29353fSMarkus Armbruster else: 57*7b29353fSMarkus Armbruster # ``ret`` doesn't end in upper case, next char is: insert 58*7b29353fSMarkus Armbruster # '_' before it 59*7b29353fSMarkus Armbruster if ret[-1].isalnum(): 60*7b29353fSMarkus Armbruster ret += '_' 61*7b29353fSMarkus Armbruster ret += ch 62*7b29353fSMarkus Armbruster upc = ch.isupper() 63*7b29353fSMarkus Armbruster 64*7b29353fSMarkus Armbruster return c_name(ret.upper()).lstrip('_') 65fb0bc835SMarkus Armbruster 66fb0bc835SMarkus Armbruster 67d646b2a1SJohn Snowdef c_enum_const(type_name: str, 68d646b2a1SJohn Snow const_name: str, 69d646b2a1SJohn Snow prefix: Optional[str] = None) -> str: 701cc7398dSJohn Snow """ 711cc7398dSJohn Snow Generate a C enumeration constant name. 721cc7398dSJohn Snow 731cc7398dSJohn Snow :param type_name: The name of the enumeration. 741cc7398dSJohn Snow :param const_name: The name of this constant. 751cc7398dSJohn Snow :param prefix: Optional, prefix that overrides the type_name. 761cc7398dSJohn Snow """ 77*7b29353fSMarkus Armbruster if prefix is None: 78*7b29353fSMarkus Armbruster prefix = camel_to_upper(type_name) 79*7b29353fSMarkus Armbruster return prefix + '_' + c_name(const_name, False).upper() 80fb0bc835SMarkus Armbruster 81b736e25aSMarkus Armbruster 82d646b2a1SJohn Snowdef c_name(name: str, protect: bool = True) -> str: 831cc7398dSJohn Snow """ 841cc7398dSJohn Snow Map ``name`` to a valid C identifier. 851cc7398dSJohn Snow 861cc7398dSJohn Snow Used for converting 'name' from a 'name':'type' qapi definition 871cc7398dSJohn Snow into a generated struct member, as well as converting type names 881cc7398dSJohn Snow into substrings of a generated C function name. 891cc7398dSJohn Snow 901cc7398dSJohn Snow '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo' 911cc7398dSJohn Snow protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int' 921cc7398dSJohn Snow 931cc7398dSJohn Snow :param name: The name to map. 941cc7398dSJohn Snow :param protect: If true, avoid returning certain ticklish identifiers 951cc7398dSJohn Snow (like C keywords) by prepending ``q_``. 961cc7398dSJohn Snow """ 97fb0bc835SMarkus Armbruster # ANSI X3J11/88-090, 3.1.1 98fb0bc835SMarkus Armbruster c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', 99fb0bc835SMarkus Armbruster 'default', 'do', 'double', 'else', 'enum', 'extern', 100fb0bc835SMarkus Armbruster 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 101fb0bc835SMarkus Armbruster 'return', 'short', 'signed', 'sizeof', 'static', 102fb0bc835SMarkus Armbruster 'struct', 'switch', 'typedef', 'union', 'unsigned', 103fb0bc835SMarkus Armbruster 'void', 'volatile', 'while']) 104fb0bc835SMarkus Armbruster # ISO/IEC 9899:1999, 6.4.1 105fb0bc835SMarkus Armbruster c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) 106fb0bc835SMarkus Armbruster # ISO/IEC 9899:2011, 6.4.1 107fb0bc835SMarkus Armbruster c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', 108fb0bc835SMarkus Armbruster '_Noreturn', '_Static_assert', '_Thread_local']) 109fb0bc835SMarkus Armbruster # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html 110fb0bc835SMarkus Armbruster # excluding _.* 111fb0bc835SMarkus Armbruster gcc_words = set(['asm', 'typeof']) 112fb0bc835SMarkus Armbruster # C++ ISO/IEC 14882:2003 2.11 113fb0bc835SMarkus Armbruster cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', 114fb0bc835SMarkus Armbruster 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', 115fb0bc835SMarkus Armbruster 'namespace', 'new', 'operator', 'private', 'protected', 116fb0bc835SMarkus Armbruster 'public', 'reinterpret_cast', 'static_cast', 'template', 117fb0bc835SMarkus Armbruster 'this', 'throw', 'true', 'try', 'typeid', 'typename', 118fb0bc835SMarkus Armbruster 'using', 'virtual', 'wchar_t', 119fb0bc835SMarkus Armbruster # alternative representations 120fb0bc835SMarkus Armbruster 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 121fb0bc835SMarkus Armbruster 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) 122fb0bc835SMarkus Armbruster # namespace pollution: 123fd89c8abSzhenwei pi polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386', 'linux']) 1245fbc78ddSMarkus Armbruster name = re.sub(r'[^A-Za-z0-9_]', '_', name) 1255fbc78ddSMarkus Armbruster if protect and (name in (c89_words | c99_words | c11_words | gcc_words 1265fbc78ddSMarkus Armbruster | cpp_words | polluted_words) 1275fbc78ddSMarkus Armbruster or name[0].isdigit()): 128fb0bc835SMarkus Armbruster return 'q_' + name 129fb0bc835SMarkus Armbruster return name 130fb0bc835SMarkus Armbruster 131b736e25aSMarkus Armbruster 132cbe8f87fSJohn Snowclass Indentation: 133cbe8f87fSJohn Snow """ 134cbe8f87fSJohn Snow Indentation level management. 135cbe8f87fSJohn Snow 136cbe8f87fSJohn Snow :param initial: Initial number of spaces, default 0. 137cbe8f87fSJohn Snow """ 138cbe8f87fSJohn Snow def __init__(self, initial: int = 0) -> None: 139cbe8f87fSJohn Snow self._level = initial 140cbe8f87fSJohn Snow 141cbe8f87fSJohn Snow def __repr__(self) -> str: 142cbe8f87fSJohn Snow return "{}({:d})".format(type(self).__name__, self._level) 143cbe8f87fSJohn Snow 144cbe8f87fSJohn Snow def __str__(self) -> str: 145cbe8f87fSJohn Snow """Return the current indentation as a string of spaces.""" 146cbe8f87fSJohn Snow return ' ' * self._level 147cbe8f87fSJohn Snow 148cbe8f87fSJohn Snow def increase(self, amount: int = 4) -> None: 149cbe8f87fSJohn Snow """Increase the indentation level by ``amount``, default 4.""" 150cbe8f87fSJohn Snow self._level += amount 151cbe8f87fSJohn Snow 152cbe8f87fSJohn Snow def decrease(self, amount: int = 4) -> None: 153cbe8f87fSJohn Snow """Decrease the indentation level by ``amount``, default 4.""" 154e2ff14a5SMarkus Armbruster assert amount <= self._level 155cbe8f87fSJohn Snow self._level -= amount 156fb0bc835SMarkus Armbruster 157b736e25aSMarkus Armbruster 1581cc7398dSJohn Snow#: Global, current indent level for code generation. 159cbe8f87fSJohn Snowindent = Indentation() 160fb0bc835SMarkus Armbruster 161fb0bc835SMarkus Armbruster 162d646b2a1SJohn Snowdef cgen(code: str, **kwds: object) -> str: 1631cc7398dSJohn Snow """ 1641cc7398dSJohn Snow Generate ``code`` with ``kwds`` interpolated. 1651cc7398dSJohn Snow 1661cc7398dSJohn Snow Obey `indent`, and strip `EATSPACE`. 1671cc7398dSJohn Snow """ 168fb0bc835SMarkus Armbruster raw = code % kwds 169916fca17SMarkus Armbruster pfx = str(indent) 170916fca17SMarkus Armbruster if pfx: 171916fca17SMarkus Armbruster raw = re.sub(r'^(?!(#|$))', pfx, raw, flags=re.MULTILINE) 172a7aa64a6SJohn Snow return re.sub(re.escape(EATSPACE) + r' *', '', raw) 173fb0bc835SMarkus Armbruster 174fb0bc835SMarkus Armbruster 175d646b2a1SJohn Snowdef mcgen(code: str, **kwds: object) -> str: 176fb0bc835SMarkus Armbruster if code[0] == '\n': 177fb0bc835SMarkus Armbruster code = code[1:] 178fb0bc835SMarkus Armbruster return cgen(code, **kwds) 179fb0bc835SMarkus Armbruster 180fb0bc835SMarkus Armbruster 181d646b2a1SJohn Snowdef c_fname(filename: str) -> str: 182709395f8SMarkus Armbruster return re.sub(r'[^A-Za-z0-9_]', '_', filename) 183fb0bc835SMarkus Armbruster 184fb0bc835SMarkus Armbruster 185d646b2a1SJohn Snowdef guardstart(name: str) -> str: 186fb0bc835SMarkus Armbruster return mcgen(''' 187fb0bc835SMarkus Armbruster#ifndef %(name)s 188fb0bc835SMarkus Armbruster#define %(name)s 189fb0bc835SMarkus Armbruster 190fb0bc835SMarkus Armbruster''', 191709395f8SMarkus Armbruster name=c_fname(name).upper()) 192fb0bc835SMarkus Armbruster 193fb0bc835SMarkus Armbruster 194d646b2a1SJohn Snowdef guardend(name: str) -> str: 195fb0bc835SMarkus Armbruster return mcgen(''' 196fb0bc835SMarkus Armbruster 197fb0bc835SMarkus Armbruster#endif /* %(name)s */ 198fb0bc835SMarkus Armbruster''', 199709395f8SMarkus Armbruster name=c_fname(name).upper()) 200fb0bc835SMarkus Armbruster 201fb0bc835SMarkus Armbruster 202ccea6a86SMarkus Armbrusterdef gen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]], 203ccea6a86SMarkus Armbruster cond_fmt: str, not_fmt: str, 204ccea6a86SMarkus Armbruster all_operator: str, any_operator: str) -> str: 205ccea6a86SMarkus Armbruster 2067b275cddSMarkus Armbruster def do_gen(ifcond: Union[str, Dict[str, Any]], 2077b275cddSMarkus Armbruster need_parens: bool) -> str: 208ccea6a86SMarkus Armbruster if isinstance(ifcond, str): 209ccea6a86SMarkus Armbruster return cond_fmt % ifcond 210ccea6a86SMarkus Armbruster assert isinstance(ifcond, dict) and len(ifcond) == 1 211ccea6a86SMarkus Armbruster if 'not' in ifcond: 212a7987799SMarkus Armbruster return not_fmt % do_gen(ifcond['not'], True) 213ccea6a86SMarkus Armbruster if 'all' in ifcond: 214ccea6a86SMarkus Armbruster gen = gen_infix(all_operator, ifcond['all']) 215ccea6a86SMarkus Armbruster else: 216ccea6a86SMarkus Armbruster gen = gen_infix(any_operator, ifcond['any']) 217a7987799SMarkus Armbruster if need_parens: 218a7987799SMarkus Armbruster gen = '(' + gen + ')' 219ccea6a86SMarkus Armbruster return gen 220ccea6a86SMarkus Armbruster 221ccea6a86SMarkus Armbruster def gen_infix(operator: str, operands: Sequence[Any]) -> str: 222a7987799SMarkus Armbruster return operator.join([do_gen(o, True) for o in operands]) 223ccea6a86SMarkus Armbruster 2246cc2e481SMarc-André Lureau if not ifcond: 2256cc2e481SMarc-André Lureau return '' 226a7987799SMarkus Armbruster return do_gen(ifcond, False) 2275d83b9a1SMarc-André Lureau 228ccea6a86SMarkus Armbruster 229ccea6a86SMarkus Armbrusterdef cgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str: 230ccea6a86SMarkus Armbruster return gen_ifcond(ifcond, 'defined(%s)', '!%s', ' && ', ' || ') 2316cc2e481SMarc-André Lureau 2326cc2e481SMarc-André Lureau 233e46c930cSMarkus Armbrusterdef docgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str: 234d806f89fSMarc-André Lureau # TODO Doc generated for conditions needs polish 235d0830ee4SMarkus Armbruster return gen_ifcond(ifcond, '%s', 'not %s', ' and ', ' or ') 236d806f89fSMarc-André Lureau 237d806f89fSMarc-André Lureau 2386cc2e481SMarc-André Lureaudef gen_if(cond: str) -> str: 2396cc2e481SMarc-André Lureau if not cond: 2406cc2e481SMarc-André Lureau return '' 2416cc2e481SMarc-André Lureau return mcgen(''' 242ded9fc28SMarc-André Lureau#if %(cond)s 2436cc2e481SMarc-André Lureau''', cond=cond) 244ded9fc28SMarc-André Lureau 245ded9fc28SMarc-André Lureau 2466cc2e481SMarc-André Lureaudef gen_endif(cond: str) -> str: 2476cc2e481SMarc-André Lureau if not cond: 2486cc2e481SMarc-André Lureau return '' 2496cc2e481SMarc-André Lureau return mcgen(''' 250ded9fc28SMarc-André Lureau#endif /* %(cond)s */ 2516cc2e481SMarc-André Lureau''', cond=cond) 252e0e8a0acSJohn Snow 253e0e8a0acSJohn Snow 254e0e8a0acSJohn Snowdef must_match(pattern: str, string: str) -> Match[str]: 255e0e8a0acSJohn Snow match = re.match(pattern, string) 256e0e8a0acSJohn Snow assert match is not None 257e0e8a0acSJohn Snow return match 258