]> git.ipfire.org Git - thirdparty/qemu.git/blame - scripts/qapi/common.py
Merge tag 'pull-target-arm-20250701-1' of https://gitlab.com/pm215/qemu into staging
[thirdparty/qemu.git] / scripts / qapi / common.py
CommitLineData
0f923be2
MR
1#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
47a6ea9a 5# Copyright (c) 2013-2018 Red Hat Inc.
0f923be2
MR
6#
7# Authors:
8# Anthony Liguori <aliguori@us.ibm.com>
c7a3f252 9# Markus Armbruster <armbru@redhat.com>
0f923be2 10#
678e48a2
MA
11# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
0f923be2 13
c2613949 14import re
6cc2e481 15from typing import (
5d83b9a1
MAL
16 Any,
17 Dict,
6cc2e481
MAL
18 Match,
19 Optional,
ccea6a86 20 Sequence,
6cc2e481
MAL
21 Union,
22)
0f923be2 23
437db254 24
1cc7398d 25#: Magic string that gets removed along with all space to its right.
a7aa64a6
JS
26EATSPACE = '\033EATSPACE.'
27POINTER_SUFFIX = ' *' + EATSPACE
a7aa64a6
JS
28
29
d646b2a1 30def camel_to_upper(value: str) -> str:
1cc7398d
JS
31 """
32 Converts CamelCase to CAMEL_CASE.
33
34 Examples::
35
36 ENUMName -> ENUM_NAME
37 EnumName1 -> ENUM_NAME1
38 ENUM_NAME -> ENUM_NAME
39 ENUM_NAME1 -> ENUM_NAME1
40 ENUM_Name2 -> ENUM_NAME2
41 ENUM24_Name -> ENUM24_NAME
42 """
7b29353f
MA
43 ret = value[0]
44 upc = value[0].isupper()
45
46 # Copy remainder of ``value`` to ``ret`` with '_' inserted
47 for ch in value[1:]:
48 if ch.isupper() == upc:
49 pass
50 elif upc:
51 # ``ret`` ends in upper case, next char isn't: insert '_'
52 # before the last upper case char unless there is one
53 # already, or it's at the beginning
54 if len(ret) > 2 and ret[-2].isalnum():
55 ret = ret[:-1] + '_' + ret[-1]
56 else:
57 # ``ret`` doesn't end in upper case, next char is: insert
58 # '_' before it
59 if ret[-1].isalnum():
60 ret += '_'
61 ret += ch
62 upc = ch.isupper()
63
64 return c_name(ret.upper()).lstrip('_')
849bc538 65
437db254 66
d646b2a1
JS
67def c_enum_const(type_name: str,
68 const_name: str,
69 prefix: Optional[str] = None) -> str:
1cc7398d
JS
70 """
71 Generate a C enumeration constant name.
72
73 :param type_name: The name of the enumeration.
74 :param const_name: The name of this constant.
75 :param prefix: Optional, prefix that overrides the type_name.
76 """
7b29353f
MA
77 if prefix is None:
78 prefix = camel_to_upper(type_name)
79 return prefix + '_' + c_name(const_name, False).upper()
849bc538 80
b736e25a 81
d646b2a1 82def c_name(name: str, protect: bool = True) -> str:
1cc7398d
JS
83 """
84 Map ``name`` to a valid C identifier.
85
86 Used for converting 'name' from a 'name':'type' qapi definition
87 into a generated struct member, as well as converting type names
88 into substrings of a generated C function name.
89
90 '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
91 protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
92
93 :param name: The name to map.
94 :param protect: If true, avoid returning certain ticklish identifiers
95 (like C keywords) by prepending ``q_``.
96 """
427a1a2c
BS
97 # ANSI X3J11/88-090, 3.1.1
98 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
437db254
EB
99 'default', 'do', 'double', 'else', 'enum', 'extern',
100 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
101 'return', 'short', 'signed', 'sizeof', 'static',
102 'struct', 'switch', 'typedef', 'union', 'unsigned',
103 'void', 'volatile', 'while'])
427a1a2c
BS
104 # ISO/IEC 9899:1999, 6.4.1
105 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
106 # ISO/IEC 9899:2011, 6.4.1
437db254
EB
107 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
108 '_Noreturn', '_Static_assert', '_Thread_local'])
427a1a2c
BS
109 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
110 # excluding _.*
111 gcc_words = set(['asm', 'typeof'])
6f88009e
TS
112 # C++ ISO/IEC 14882:2003 2.11
113 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
114 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
115 'namespace', 'new', 'operator', 'private', 'protected',
116 'public', 'reinterpret_cast', 'static_cast', 'template',
117 'this', 'throw', 'true', 'try', 'typeid', 'typename',
118 'using', 'virtual', 'wchar_t',
119 # alternative representations
120 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
121 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1057725f 122 # namespace pollution:
fd89c8ab 123 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386', 'linux'])
5fbc78dd
MA
124 name = re.sub(r'[^A-Za-z0-9_]', '_', name)
125 if protect and (name in (c89_words | c99_words | c11_words | gcc_words
126 | cpp_words | polluted_words)
127 or name[0].isdigit()):
ef801a9b 128 return 'q_' + name
c43567c1 129 return name
0f923be2 130
b736e25a 131
cbe8f87f
JS
132class Indentation:
133 """
134 Indentation level management.
0f923be2 135
cbe8f87f
JS
136 :param initial: Initial number of spaces, default 0.
137 """
138 def __init__(self, initial: int = 0) -> None:
139 self._level = initial
b736e25a 140
cbe8f87f
JS
141 def __repr__(self) -> str:
142 return "{}({:d})".format(type(self).__name__, self._level)
437db254 143
cbe8f87f
JS
144 def __str__(self) -> str:
145 """Return the current indentation as a string of spaces."""
146 return ' ' * self._level
0f923be2 147
cbe8f87f
JS
148 def increase(self, amount: int = 4) -> None:
149 """Increase the indentation level by ``amount``, default 4."""
150 self._level += amount
151
152 def decrease(self, amount: int = 4) -> None:
153 """Decrease the indentation level by ``amount``, default 4."""
e2ff14a5 154 assert amount <= self._level
cbe8f87f
JS
155 self._level -= amount
156
157
1cc7398d 158#: Global, current indent level for code generation.
cbe8f87f 159indent = Indentation()
0f923be2 160
437db254 161
d646b2a1 162def cgen(code: str, **kwds: object) -> str:
1cc7398d
JS
163 """
164 Generate ``code`` with ``kwds`` interpolated.
165
166 Obey `indent`, and strip `EATSPACE`.
167 """
77e703b8 168 raw = code % kwds
916fca17
MA
169 pfx = str(indent)
170 if pfx:
171 raw = re.sub(r'^(?!(#|$))', pfx, raw, flags=re.MULTILINE)
a7aa64a6 172 return re.sub(re.escape(EATSPACE) + r' *', '', raw)
0f923be2 173
437db254 174
d646b2a1 175def mcgen(code: str, **kwds: object) -> str:
77e703b8
MA
176 if code[0] == '\n':
177 code = code[1:]
178 return cgen(code, **kwds)
0f923be2 179
0f923be2 180
d646b2a1 181def c_fname(filename: str) -> str:
709395f8 182 return re.sub(r'[^A-Za-z0-9_]', '_', filename)
c0afa9c5 183
437db254 184
d646b2a1 185def guardstart(name: str) -> str:
c0afa9c5 186 return mcgen('''
c0afa9c5
MR
187#ifndef %(name)s
188#define %(name)s
189
190''',
709395f8 191 name=c_fname(name).upper())
c0afa9c5 192
437db254 193
d646b2a1 194def guardend(name: str) -> str:
c0afa9c5
MR
195 return mcgen('''
196
197#endif /* %(name)s */
c0afa9c5 198''',
709395f8 199 name=c_fname(name).upper())
2114f5a9 200
437db254 201
ccea6a86
MA
202def gen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]],
203 cond_fmt: str, not_fmt: str,
204 all_operator: str, any_operator: str) -> str:
205
7b275cdd
MA
206 def do_gen(ifcond: Union[str, Dict[str, Any]],
207 need_parens: bool) -> str:
ccea6a86
MA
208 if isinstance(ifcond, str):
209 return cond_fmt % ifcond
210 assert isinstance(ifcond, dict) and len(ifcond) == 1
211 if 'not' in ifcond:
a7987799 212 return not_fmt % do_gen(ifcond['not'], True)
ccea6a86
MA
213 if 'all' in ifcond:
214 gen = gen_infix(all_operator, ifcond['all'])
215 else:
216 gen = gen_infix(any_operator, ifcond['any'])
a7987799
MA
217 if need_parens:
218 gen = '(' + gen + ')'
ccea6a86
MA
219 return gen
220
221 def gen_infix(operator: str, operands: Sequence[Any]) -> str:
a7987799 222 return operator.join([do_gen(o, True) for o in operands])
ccea6a86 223
6cc2e481
MAL
224 if not ifcond:
225 return ''
a7987799 226 return do_gen(ifcond, False)
6cc2e481 227
ccea6a86
MA
228
229def cgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
230 return gen_ifcond(ifcond, 'defined(%s)', '!%s', ' && ', ' || ')
6cc2e481 231
5d83b9a1 232
e46c930c 233def docgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
d806f89f 234 # TODO Doc generated for conditions needs polish
d0830ee4 235 return gen_ifcond(ifcond, '%s', 'not %s', ' and ', ' or ')
d806f89f
MAL
236
237
6cc2e481
MAL
238def gen_if(cond: str) -> str:
239 if not cond:
240 return ''
241 return mcgen('''
ded9fc28 242#if %(cond)s
6cc2e481 243''', cond=cond)
ded9fc28
MAL
244
245
6cc2e481
MAL
246def gen_endif(cond: str) -> str:
247 if not cond:
248 return ''
249 return mcgen('''
ded9fc28 250#endif /* %(cond)s */
6cc2e481 251''', cond=cond)
e0e8a0ac
JS
252
253
254def must_match(pattern: str, string: str) -> Match[str]:
255 match = re.match(pattern, string)
256 assert match is not None
257 return match