• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import re
2
3from ._regexes import (
4    LOCAL as _LOCAL,
5    LOCAL_STATICS as _LOCAL_STATICS,
6)
7from ._common import (
8    log_match,
9    parse_var_decl,
10    set_capture_groups,
11    match_paren,
12)
13from ._compound_decl_body import DECL_BODY_PARSERS
14
15
16LOCAL = 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))
35LOCAL_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
41def parse_function_body(source, name, anon_name):
42    # XXX
43    raise NotImplementedError
44
45
46def 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)
69            resolve(None, None, None, text)
70            yield None, text
71        elif inline_kind:
72            log_match('', m)
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)
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)
105            yield resolve('statement', compound_bare, None, text, parent), text
106        elif compound_labeled:
107            log_match('', m)
108            yield resolve('statement', compound_labeled, None, text, parent), text
109        elif compound_paren:
110            log_match('', m)
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)
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)
148            yield resolve('statement', simple_stmt, None, text, parent), text
149        elif var_ending:
150            log_match('', m)
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
174LOCAL_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))
187LOCAL_STATICS_RE = re.compile(rf'^ \s* {LOCAL_STATICS}', re.VERBOSE)
188
189
190def 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
212def _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)
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)
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            depth += 1
270        elif block_close:
271            depth -= 1
272        elif stmt_end:
273            pass
274        else:
275            # This should be unreachable.
276            raise NotImplementedError
277        srcinfo.advance(remainder)
278        yield None, depth
279