summaryrefslogtreecommitdiffstats
path: root/scripts/qapi/common.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/qapi/common.py')
-rw-r--r--scripts/qapi/common.py164
1 files changed, 92 insertions, 72 deletions
diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index ba35abea47..11b86beeab 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -12,12 +12,28 @@
# See the COPYING file in the top-level directory.
import re
+from typing import Optional, Sequence
-# 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):
+#: Magic string that gets removed along with all space to its right.
+EATSPACE = '\033EATSPACE.'
+POINTER_SUFFIX = ' *' + EATSPACE
+_C_NAME_TRANS = str.maketrans('.-', '__')
+
+
+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
@@ -25,36 +41,47 @@ def camel_to_upper(value):
new_name = ''
length = len(c_fun_str)
for i in range(length):
- 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] != '_':
+ 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 += c
+ new_name += char
return new_name.lstrip('_').upper()
-def c_enum_const(type_name, const_name, prefix=None):
+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()
-c_name_trans = str.maketrans('.-', '__')
+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.
-# 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):
+ '__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',
@@ -82,61 +109,75 @@ def c_name(name, protect=True):
'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
# namespace pollution:
polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
- name = name.translate(c_name_trans)
+ name = name.translate(_C_NAME_TRANS)
if protect and (name in c89_words | c99_words | c11_words | gcc_words
| cpp_words | polluted_words):
return 'q_' + name
return name
-eatspace = '\033EATSPACE.'
-pointer_suffix = ' *' + eatspace
+class Indentation:
+ """
+ Indentation level management.
+ :param initial: Initial number of spaces, default 0.
+ """
+ def __init__(self, initial: int = 0) -> None:
+ self._level = initial
-def genindent(count):
- ret = ''
- for _ in range(count):
- ret += ' '
- return ret
+ def __int__(self) -> int:
+ return self._level
+
+ 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 __bool__(self) -> bool:
+ """True when there is a non-zero indentation."""
+ return bool(self._level)
-indent_level = 0
+ 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."""
+ if self._level < amount:
+ raise ArithmeticError(
+ f"Can't remove {amount:d} spaces from {self!r}")
+ self._level -= amount
-def push_indent(indent_amount=4):
- global indent_level
- indent_level += indent_amount
+#: Global, current indent level for code generation.
+indent = Indentation()
-def pop_indent(indent_amount=4):
- global indent_level
- indent_level -= indent_amount
+def cgen(code: str, **kwds: object) -> str:
+ """
+ Generate ``code`` with ``kwds`` interpolated.
-# Generate @code with @kwds interpolated.
-# Obey indent_level, and strip eatspace.
-def cgen(code, **kwds):
+ Obey `indent`, and strip `EATSPACE`.
+ """
raw = code % kwds
- if indent_level:
- indent = genindent(indent_level)
- # re.subn() lacks flags support before Python 2.7, use re.compile()
- raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
- indent, raw)
- raw = raw[0]
- return re.sub(re.escape(eatspace) + r' *', '', raw)
+ if indent:
+ raw = re.sub(r'^(?!(#|$))', str(indent), raw, flags=re.MULTILINE)
+ return re.sub(re.escape(EATSPACE) + r' *', '', raw)
-def mcgen(code, **kwds):
+def mcgen(code: str, **kwds: object) -> str:
if code[0] == '\n':
code = code[1:]
return cgen(code, **kwds)
-def c_fname(filename):
+def c_fname(filename: str) -> str:
return re.sub(r'[^A-Za-z0-9_]', '_', filename)
-def guardstart(name):
+def guardstart(name: str) -> str:
return mcgen('''
#ifndef %(name)s
#define %(name)s
@@ -145,7 +186,7 @@ def guardstart(name):
name=c_fname(name).upper())
-def guardend(name):
+def guardend(name: str) -> str:
return mcgen('''
#endif /* %(name)s */
@@ -153,7 +194,7 @@ def guardend(name):
name=c_fname(name).upper())
-def gen_if(ifcond):
+def gen_if(ifcond: Sequence[str]) -> str:
ret = ''
for ifc in ifcond:
ret += mcgen('''
@@ -162,31 +203,10 @@ def gen_if(ifcond):
return ret
-def gen_endif(ifcond):
+def gen_endif(ifcond: Sequence[str]) -> str:
ret = ''
for ifc in reversed(ifcond):
ret += mcgen('''
#endif /* %(cond)s */
''', cond=ifc)
return ret
-
-
-def build_params(arg_type, boxed, extra=None):
- ret = ''
- sep = ''
- if boxed:
- assert arg_type
- ret += '%s arg' % arg_type.c_param_type()
- sep = ', '
- elif arg_type:
- assert not arg_type.variants
- for memb in arg_type.members:
- ret += sep
- sep = ', '
- if memb.optional:
- ret += 'bool has_%s, ' % c_name(memb.name)
- ret += '%s %s' % (memb.type.c_param_type(),
- c_name(memb.name))
- if extra:
- ret += sep + extra
- return ret if ret else 'void'