• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import re
2
3from ._regexes import (
4    GLOBAL as _GLOBAL,
5)
6from ._common import (
7    log_match,
8    parse_var_decl,
9    set_capture_groups,
10)
11from ._compound_decl_body import DECL_BODY_PARSERS
12#from ._func_body import parse_function_body
13from ._func_body import parse_function_statics as parse_function_body
14
15
16GLOBAL = set_capture_groups(_GLOBAL, (
17    'EMPTY',
18    'COMPOUND_LEADING',
19    'COMPOUND_KIND',
20    'COMPOUND_NAME',
21    'FORWARD_KIND',
22    'FORWARD_NAME',
23    'MAYBE_INLINE_ACTUAL',
24    'TYPEDEF_DECL',
25    'TYPEDEF_FUNC_PARAMS',
26    'VAR_STORAGE',
27    'FUNC_INLINE',
28    'VAR_DECL',
29    'FUNC_PARAMS',
30    'FUNC_DELIM',
31    'FUNC_LEGACY_PARAMS',
32    'VAR_INIT',
33    'VAR_ENDING',
34))
35GLOBAL_RE = re.compile(rf'^ \s* {GLOBAL}', re.VERBOSE)
36
37
38def parse_globals(source, anon_name):
39    for srcinfo in source:
40        m = GLOBAL_RE.match(srcinfo.text)
41        if not m:
42            # We need more text.
43            continue
44        for item in _parse_next(m, srcinfo, anon_name):
45            if callable(item):
46                parse_body = item
47                yield from parse_body(source)
48            else:
49                yield item
50    else:
51        # We ran out of lines.
52        if srcinfo is not None:
53            srcinfo.done()
54        return
55
56
57def _parse_next(m, srcinfo, anon_name):
58    (
59     empty,
60     # compound type decl (maybe inline)
61     compound_leading, compound_kind, compound_name,
62     forward_kind, forward_name, maybe_inline_actual,
63     # typedef
64     typedef_decl, typedef_func_params,
65     # vars and funcs
66     storage, func_inline, decl,
67     func_params, func_delim, func_legacy_params,
68     var_init, var_ending,
69     ) = m.groups()
70    remainder = srcinfo.text[m.end():]
71
72    if empty:
73        log_match('global empty', m)
74        srcinfo.advance(remainder)
75
76    elif maybe_inline_actual:
77        log_match('maybe_inline_actual', m)
78        # Ignore forward declarations.
79        # XXX Maybe return them too (with an "isforward" flag)?
80        if not maybe_inline_actual.strip().endswith(';'):
81            remainder = maybe_inline_actual + remainder
82        yield srcinfo.resolve(forward_kind, None, forward_name)
83        if maybe_inline_actual.strip().endswith('='):
84            # We use a dummy prefix for a fake typedef.
85            # XXX Ideally this case would not be caught by MAYBE_INLINE_ACTUAL.
86            _, name, data = parse_var_decl(f'{forward_kind} {forward_name} fake_typedef_{forward_name}')
87            yield srcinfo.resolve('typedef', data, name, parent=None)
88            remainder = f'{name} {remainder}'
89        srcinfo.advance(remainder)
90
91    elif compound_kind:
92        kind = compound_kind
93        name = compound_name or anon_name('inline-')
94        # Immediately emit a forward declaration.
95        yield srcinfo.resolve(kind, name=name, data=None)
96
97        # un-inline the decl.  Note that it might not actually be inline.
98        # We handle the case in the "maybe_inline_actual" branch.
99        srcinfo.nest(
100            remainder,
101            f'{compound_leading or ""} {compound_kind} {name}',
102        )
103        def parse_body(source):
104            _parse_body = DECL_BODY_PARSERS[compound_kind]
105
106            data = []  # members
107            ident = f'{kind} {name}'
108            for item in _parse_body(source, anon_name, ident):
109                if item.kind == 'field':
110                    data.append(item)
111                else:
112                    yield item
113            # XXX Should "parent" really be None for inline type decls?
114            yield srcinfo.resolve(kind, data, name, parent=None)
115
116            srcinfo.resume()
117        yield parse_body
118
119    elif typedef_decl:
120        log_match('typedef', m)
121        kind = 'typedef'
122        _, name, data = parse_var_decl(typedef_decl)
123        if typedef_func_params:
124            return_type = data
125            # This matches the data for func declarations.
126            data = {
127                'storage': None,
128                'inline': None,
129                'params': f'({typedef_func_params})',
130                'returntype': return_type,
131                'isforward': True,
132            }
133        yield srcinfo.resolve(kind, data, name, parent=None)
134        srcinfo.advance(remainder)
135
136    elif func_delim or func_legacy_params:
137        log_match('function', m)
138        kind = 'function'
139        _, name, return_type = parse_var_decl(decl)
140        func_params = func_params or func_legacy_params
141        data = {
142            'storage': storage,
143            'inline': func_inline,
144            'params': f'({func_params})',
145            'returntype': return_type,
146            'isforward': func_delim == ';',
147        }
148
149        yield srcinfo.resolve(kind, data, name, parent=None)
150        srcinfo.advance(remainder)
151
152        if func_delim == '{' or func_legacy_params:
153            def parse_body(source):
154                yield from parse_function_body(source, name, anon_name)
155            yield parse_body
156
157    elif var_ending:
158        log_match('global variable', m)
159        kind = 'variable'
160        _, name, vartype = parse_var_decl(decl)
161        data = {
162            'storage': storage,
163            'vartype': vartype,
164        }
165        yield srcinfo.resolve(kind, data, name, parent=None)
166
167        if var_ending == ',':
168            # It was a multi-declaration, so queue up the next one.
169            _, qual, typespec, _ = vartype.values()
170            remainder = f'{storage or ""} {qual or ""} {typespec} {remainder}'
171        srcinfo.advance(remainder)
172
173        if var_init:
174            _data = f'{name} = {var_init.strip()}'
175            yield srcinfo.resolve('statement', _data, name=None)
176
177    else:
178        # This should be unreachable.
179        raise NotImplementedError
180