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