Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Tools/c-analyzer/c_parser/parser/_func_body.py
12 views
1
import re
2
3
from ._regexes import (
4
LOCAL as _LOCAL,
5
LOCAL_STATICS as _LOCAL_STATICS,
6
)
7
from ._common import (
8
log_match,
9
parse_var_decl,
10
set_capture_groups,
11
match_paren,
12
)
13
from ._compound_decl_body import DECL_BODY_PARSERS
14
15
16
LOCAL = set_capture_groups(_LOCAL, (
17
'EMPTY',
18
'INLINE_LEADING',
19
'INLINE_PRE',
20
'INLINE_KIND',
21
'INLINE_NAME',
22
'STORAGE',
23
'VAR_DECL',
24
'VAR_INIT',
25
'VAR_ENDING',
26
'COMPOUND_BARE',
27
'COMPOUND_LABELED',
28
'COMPOUND_PAREN',
29
'BLOCK_LEADING',
30
'BLOCK_OPEN',
31
'SIMPLE_STMT',
32
'SIMPLE_ENDING',
33
'BLOCK_CLOSE',
34
))
35
LOCAL_RE = re.compile(rf'^ \s* {LOCAL}', re.VERBOSE)
36
37
38
# Note that parse_function_body() still has trouble with a few files
39
# in the CPython codebase.
40
41
def parse_function_body(source, name, anon_name):
42
# XXX
43
raise NotImplementedError
44
45
46
def parse_function_body(name, text, resolve, source, anon_name, parent):
47
raise NotImplementedError
48
# For now we do not worry about locals declared in for loop "headers".
49
depth = 1;
50
while depth > 0:
51
m = LOCAL_RE.match(text)
52
while not m:
53
text, resolve = continue_text(source, text or '{', resolve)
54
m = LOCAL_RE.match(text)
55
text = text[m.end():]
56
(
57
empty,
58
inline_leading, inline_pre, inline_kind, inline_name,
59
storage, decl,
60
var_init, var_ending,
61
compound_bare, compound_labeled, compound_paren,
62
block_leading, block_open,
63
simple_stmt, simple_ending,
64
block_close,
65
) = m.groups()
66
67
if empty:
68
log_match('', m, depth)
69
resolve(None, None, None, text)
70
yield None, text
71
elif inline_kind:
72
log_match('', m, depth)
73
kind = inline_kind
74
name = inline_name or anon_name('inline-')
75
data = [] # members
76
# We must set the internal "text" from _iter_source() to the
77
# start of the inline compound body,
78
# Note that this is effectively like a forward reference that
79
# we do not emit.
80
resolve(kind, None, name, text, None)
81
_parse_body = DECL_BODY_PARSERS[kind]
82
before = []
83
ident = f'{kind} {name}'
84
for member, inline, text in _parse_body(text, resolve, source, anon_name, ident):
85
if member:
86
data.append(member)
87
if inline:
88
yield from inline
89
# un-inline the decl. Note that it might not actually be inline.
90
# We handle the case in the "maybe_inline_actual" branch.
91
text = f'{inline_leading or ""} {inline_pre or ""} {kind} {name} {text}'
92
# XXX Should "parent" really be None for inline type decls?
93
yield resolve(kind, data, name, text, None), text
94
elif block_close:
95
log_match('', m, depth)
96
depth -= 1
97
resolve(None, None, None, text)
98
# XXX This isn't great. Calling resolve() should have
99
# cleared the closing bracket. However, some code relies
100
# on the yielded value instead of the resolved one. That
101
# needs to be fixed.
102
yield None, text
103
elif compound_bare:
104
log_match('', m, depth)
105
yield resolve('statement', compound_bare, None, text, parent), text
106
elif compound_labeled:
107
log_match('', m, depth)
108
yield resolve('statement', compound_labeled, None, text, parent), text
109
elif compound_paren:
110
log_match('', m, depth)
111
try:
112
pos = match_paren(text)
113
except ValueError:
114
text = f'{compound_paren} {text}'
115
#resolve(None, None, None, text)
116
text, resolve = continue_text(source, text, resolve)
117
yield None, text
118
else:
119
head = text[:pos]
120
text = text[pos:]
121
if compound_paren == 'for':
122
# XXX Parse "head" as a compound statement.
123
stmt1, stmt2, stmt3 = head.split(';', 2)
124
data = {
125
'compound': compound_paren,
126
'statements': (stmt1, stmt2, stmt3),
127
}
128
else:
129
data = {
130
'compound': compound_paren,
131
'statement': head,
132
}
133
yield resolve('statement', data, None, text, parent), text
134
elif block_open:
135
log_match('', m, depth)
136
depth += 1
137
if block_leading:
138
# An inline block: the last evaluated expression is used
139
# in place of the block.
140
# XXX Combine it with the remainder after the block close.
141
stmt = f'{block_open}{{<expr>}}...;'
142
yield resolve('statement', stmt, None, text, parent), text
143
else:
144
resolve(None, None, None, text)
145
yield None, text
146
elif simple_ending:
147
log_match('', m, depth)
148
yield resolve('statement', simple_stmt, None, text, parent), text
149
elif var_ending:
150
log_match('', m, depth)
151
kind = 'variable'
152
_, name, vartype = parse_var_decl(decl)
153
data = {
154
'storage': storage,
155
'vartype': vartype,
156
}
157
after = ()
158
if var_ending == ',':
159
# It was a multi-declaration, so queue up the next one.
160
_, qual, typespec, _ = vartype.values()
161
text = f'{storage or ""} {qual or ""} {typespec} {text}'
162
yield resolve(kind, data, name, text, parent), text
163
if var_init:
164
_data = f'{name} = {var_init.strip()}'
165
yield resolve('statement', _data, None, text, parent), text
166
else:
167
# This should be unreachable.
168
raise NotImplementedError
169
170
171
#############################
172
# static local variables
173
174
LOCAL_STATICS = set_capture_groups(_LOCAL_STATICS, (
175
'INLINE_LEADING',
176
'INLINE_PRE',
177
'INLINE_KIND',
178
'INLINE_NAME',
179
'STATIC_DECL',
180
'STATIC_INIT',
181
'STATIC_ENDING',
182
'DELIM_LEADING',
183
'BLOCK_OPEN',
184
'BLOCK_CLOSE',
185
'STMT_END',
186
))
187
LOCAL_STATICS_RE = re.compile(rf'^ \s* {LOCAL_STATICS}', re.VERBOSE)
188
189
190
def parse_function_statics(source, func, anon_name):
191
# For now we do not worry about locals declared in for loop "headers".
192
depth = 1;
193
while depth > 0:
194
for srcinfo in source:
195
m = LOCAL_STATICS_RE.match(srcinfo.text)
196
if m:
197
break
198
else:
199
# We ran out of lines.
200
if srcinfo is not None:
201
srcinfo.done()
202
return
203
for item, depth in _parse_next_local_static(m, srcinfo,
204
anon_name, func, depth):
205
if callable(item):
206
parse_body = item
207
yield from parse_body(source)
208
elif item is not None:
209
yield item
210
211
212
def _parse_next_local_static(m, srcinfo, anon_name, func, depth):
213
(inline_leading, inline_pre, inline_kind, inline_name,
214
static_decl, static_init, static_ending,
215
_delim_leading,
216
block_open,
217
block_close,
218
stmt_end,
219
) = m.groups()
220
remainder = srcinfo.text[m.end():]
221
222
if inline_kind:
223
log_match('func inline', m, depth, depth)
224
kind = inline_kind
225
name = inline_name or anon_name('inline-')
226
# Immediately emit a forward declaration.
227
yield srcinfo.resolve(kind, name=name, data=None), depth
228
229
# un-inline the decl. Note that it might not actually be inline.
230
# We handle the case in the "maybe_inline_actual" branch.
231
srcinfo.nest(
232
remainder,
233
f'{inline_leading or ""} {inline_pre or ""} {kind} {name}'
234
)
235
def parse_body(source):
236
_parse_body = DECL_BODY_PARSERS[kind]
237
238
data = [] # members
239
ident = f'{kind} {name}'
240
for item in _parse_body(source, anon_name, ident):
241
if item.kind == 'field':
242
data.append(item)
243
else:
244
yield item
245
# XXX Should "parent" really be None for inline type decls?
246
yield srcinfo.resolve(kind, data, name, parent=None)
247
248
srcinfo.resume()
249
yield parse_body, depth
250
251
elif static_decl:
252
log_match('local variable', m, depth, depth)
253
_, name, data = parse_var_decl(static_decl)
254
255
yield srcinfo.resolve('variable', data, name, parent=func), depth
256
257
if static_init:
258
srcinfo.advance(f'{name} {static_init} {remainder}')
259
elif static_ending == ',':
260
# It was a multi-declaration, so queue up the next one.
261
_, qual, typespec, _ = data.values()
262
srcinfo.advance(f'static {qual or ""} {typespec} {remainder}')
263
else:
264
srcinfo.advance('')
265
266
else:
267
log_match('func other', m)
268
if block_open:
269
log_match('func other', None, depth, depth + 1)
270
depth += 1
271
elif block_close:
272
log_match('func other', None, depth, depth - 1)
273
depth -= 1
274
elif stmt_end:
275
log_match('func other', None, depth, depth)
276
pass
277
else:
278
# This should be unreachable.
279
raise NotImplementedError
280
srcinfo.advance(remainder)
281
yield None, depth
282
283