- Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathtemplates.py
266 lines (193 loc) · 8.96 KB
/
templates.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# SPDX-License-Identifier: BSD-2-Clause
# Copyright (c) 2024 Phil Thompson <phil@riverbankcomputing.com>
fromcopyimportcopy
from .scoped_nameimportScopedName
from .specificationimportArgumentType, IfaceFileType
from .utilsimportappend_iface_file, argument_as_str, same_base_type
defencoded_template_name(template):
""" Return the encoded name of a template. """
snd=ScopedName(template.cpp_name)
foradintemplate.types.args:
flags=0
ifad.is_const:
flags|=1
ifad.is_reference:
flags|=2
# We use numbers so they don't conflict with names.
encoding='{:02d}{}{}'.format(ad.type.value, flags, len(ad.derefs))
ifad.typeisArgumentType.DEFINED:
arg_snd=ScopedName(ad.definition)
elifad.typeisArgumentType.TEMPLATE:
arg_snd=encoded_template_name(ad.definition)
elifad.typeisArgumentType.STRUCT:
arg_snd=ScopedName(ad.definition)
else:
arg_snd=None
ifarg_sndisNone:
snd.append(encoding)
else:
# Replace the first element of the argument name with a copy with
# the encoding prepended.
arg_snd[0] =encoding+arg_snd[0]
fornameinarg_snd:
snd.append(name)
returnsnd
defsame_template_signature(sig1, sig2, deep=False):
""" Return True if the template signatures are the same. A deep comparison
is used for mapped type templates where we want to recurse into any nested
templates.
"""
iflen(sig1.args) !=len(sig2.args):
returnFalse
fortype1, type2inzip(sig1.args, sig2.args):
# If we are doing a shallow comparision (ie. for class templates) then
# a type name in the first signature matches anything in the second
# signature.
iftype1.typeisArgumentType.DEFINEDandnotdeep:
continue
# For type names only compare the references and pointers, and do the
# same for any nested templates.
iftype1.typeisArgumentType.DEFINEDandtype2.typeisArgumentType.DEFINED:
iftype1.is_reference!=type2.is_referenceorlen(type1.derefs) !=len(type2.derefs):
returnFalse
eliftype1.typeisArgumentType.TEMPLATEandtype2.typeisArgumentType.TEMPLATE:
ifnotsame_template_signature(type1.definition.types, type2.definition.types, deep=deep):
returnFalse
elifnotsame_base_type(type1, type2):
returnFalse
returnTrue
deftemplate_code(spec, used, proto_code, expansions):
""" Return a copy of an optional CodeBlock object with sub-strings replaced
by corresponding values.
"""
# Handle the trivial case.
ifproto_codeisNone:
returnNone
return_template_code_block(spec, used, proto_code, expansions)
deftemplate_code_blocks(spec, used, proto_code_blocks, expansions):
""" Return a copy of a list of CodeBlock objects with sub-strings replaced
by corresponding values.
"""
return [_template_code_block(spec, used, pc, expansions)
forpcinproto_code_blocks]
deftemplate_expansions(template_names, instantiation_values,
declared_names=None):
""" Return a dict of expansions to be applied when instantiating mapped
type of class templates (including handwritten code). The key is the
symbolic name of a template argument and the value is the replacement to be
used in a particular instantiation.
"""
# TODO: This is broken (or possibly just over-complicated). The
# declaration of template parameters is used for two purposes: firstly to
# allow real types to be used to distinguish between overloaded templates;
# secondly to define the names of the parameters that will be replaced by
# values provided at instantiation. It's possible that only the latter
# will ever be expanded.
expansions= {}
forarg_nr, name_arginenumerate(template_names.args):
ifname_arg.typeisArgumentType.DEFINED:
# If the type names have been declared (as they are with a mapped
# type template) check that this is one of them.
ifdeclared_namesisnotNone:
# Only consider unscoped names.
ifnotname_arg.definition.is_simple:
continue
fordeclaredindeclared_names.args:
# Skip anything but simple names.
ifdeclared.typeisnotArgumentType.DEFINED:
continue
ifname_arg.definition.base_name==declared.definition.base_name:
name=name_arg.definition.base_name
break
else:
continue
else:
name=name_arg.definition.base_name
# Get the corresponding value. For defined types we don't want any
# indirection or references.
value_arg=instantiation_values.args[arg_nr]
ifvalue_arg.typeisArgumentType.DEFINED:
value=str(value_arg.definition)
else:
value=argument_as_str(value_arg)
# We do want const.
ifvalue_arg.is_const:
value='const '+value;
expansions[name] =value
elifname_arg.typeisArgumentType.TEMPLATE:
value_arg=instantiation_values.args[arg_nr]
# These checks shouldn't be necessary, but...
ifvalue_arg.typeisArgumentType.TEMPLATEandlen(name_arg.definition.types.args) ==len(value_arg.definition.types.args):
expansions.update(
template_expansions(name_arg.definition.types,
value_arg.definition.types, declared_names))
returnexpansions
deftemplate_string(proto_str, expansions, scope_replacement=None):
""" Return a copy of a string with sub-strings replaced by corresponding
values.
"""
forname, valueinexpansions.items():
value=_strip_const(value)
# Translate any C++ scoping.
ifscope_replacementisnotNone:
value=value.replace('::', scope_replacement)
# Perform any replacement.
proto_str=proto_str.replace(name, value)
returnproto_str
def_strip_const(s):
""" Strip any leading 'const' from a string. """
ifs.startswith('const '):
s=s[6:]
returns
def_template_code_block(spec, used, proto_code, expansions):
""" Return a copy of a CodeBlock object with sub-strings replaced by
corresponding values or the original if there were no substitutions.
"""
i_code=copy(proto_code)
i_lines= []
forproto_lineinproto_code.text.split('\n'):
i_line=proto_line
# Don't do any substitution in lines that appear to be preprocessor
# directives. This prevents #include'd file names being broken.
ifnotproto_line.lstrip().startswith('#'):
# Go through each expansion.
forname, valueinexpansions.items():
# Look for the name at the current position in the current
# line.
pos=i_line.find(name)
whilepos>=0:
# See if the name is referring to a generated type
# structure.
forgen_typein ('sipType_', 'sipException_'):
ifi_line[:pos].endswith(gen_type):
value=_strip_const(value)
_add_used_from_code(spec, used, value)
# Convert the value to the rest of the name of the
# generated type structure.
ifvalue.startswith('::'):
value=value[2:]
value=value.replace('::', '_')
break
# Perform the substitution and update the current position.
i_line=i_line[0:pos] +value+i_line[pos+len(name):]
pos=i_line.find(name, pos+len(value))
i_lines.append(i_line)
i_code.text='\n'.join(i_lines)
# Return the prototype itself if nothing changed.
ifproto_code.text==i_code.text:
returnproto_code
returni_code
def_add_used_from_code(spec, used, name):
""" Add any interface files to a used list that are defined for a name. """
name=ScopedName.parse(name)
name.make_absolute()
foriface_fileinspec.iface_files:
ififace_file.typein (IfaceFileType.CLASS, IfaceFileType.EXCEPTION):
ififace_file.fq_cpp_name==name:
append_iface_file(used, iface_file)
return
forenuminspec.enums:
ifenum.scopeisnotNone:
ifenum.fq_cpp_name==name:
append_iface_file(used, enum.scope.iface_file)
return