xref: /openbmc/qemu/scripts/qapi/common.py (revision b8eada54b2ad8a7d98d93d5ab4d3e888c5880097)
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