| import re | |
| from ._regexes import ( | |
| GLOBAL as _GLOBAL, | |
| ) | |
| from ._common import ( | |
| log_match, | |
| parse_var_decl, | |
| set_capture_groups, | |
| ) | |
| from ._compound_decl_body import DECL_BODY_PARSERS | |
| from ._func_body import parse_function_statics as parse_function_body | |
| GLOBAL = set_capture_groups(_GLOBAL, ( | |
| 'EMPTY', | |
| 'COMPOUND_LEADING', | |
| 'COMPOUND_KIND', | |
| 'COMPOUND_NAME', | |
| 'FORWARD_KIND', | |
| 'FORWARD_NAME', | |
| 'MAYBE_INLINE_ACTUAL', | |
| 'TYPEDEF_DECL', | |
| 'TYPEDEF_FUNC_PARAMS', | |
| 'VAR_STORAGE', | |
| 'FUNC_INLINE', | |
| 'VAR_DECL', | |
| 'FUNC_PARAMS', | |
| 'FUNC_DELIM', | |
| 'FUNC_LEGACY_PARAMS', | |
| 'VAR_INIT', | |
| 'VAR_ENDING', | |
| )) | |
| GLOBAL_RE = re.compile(rf'^ \s* {GLOBAL}', re.VERBOSE) | |
| def parse_globals(source, anon_name): | |
| for srcinfo in source: | |
| m = GLOBAL_RE.match(srcinfo.text) | |
| if not m: | |
| # We need more text. | |
| continue | |
| for item in _parse_next(m, srcinfo, anon_name): | |
| if callable(item): | |
| parse_body = item | |
| yield from parse_body(source) | |
| else: | |
| yield item | |
| else: | |
| # We ran out of lines. | |
| if srcinfo is not None: | |
| srcinfo.done() | |
| return | |
| def _parse_next(m, srcinfo, anon_name): | |
| ( | |
| empty, | |
| # compound type decl (maybe inline) | |
| compound_leading, compound_kind, compound_name, | |
| forward_kind, forward_name, maybe_inline_actual, | |
| # typedef | |
| typedef_decl, typedef_func_params, | |
| # vars and funcs | |
| storage, func_inline, decl, | |
| func_params, func_delim, func_legacy_params, | |
| var_init, var_ending, | |
| ) = m.groups() | |
| remainder = srcinfo.text[m.end():] | |
| if empty: | |
| log_match('global empty', m) | |
| srcinfo.advance(remainder) | |
| elif maybe_inline_actual: | |
| log_match('maybe_inline_actual', m) | |
| # Ignore forward declarations. | |
| # XXX Maybe return them too (with an "isforward" flag)? | |
| if not maybe_inline_actual.strip().endswith(';'): | |
| remainder = maybe_inline_actual + remainder | |
| yield srcinfo.resolve(forward_kind, None, forward_name) | |
| if maybe_inline_actual.strip().endswith('='): | |
| # We use a dummy prefix for a fake typedef. | |
| # XXX Ideally this case would not be caught by MAYBE_INLINE_ACTUAL. | |
| _, name, data = parse_var_decl(f'{forward_kind} {forward_name} fake_typedef_{forward_name}') | |
| yield srcinfo.resolve('typedef', data, name, parent=None) | |
| remainder = f'{name} {remainder}' | |
| srcinfo.advance(remainder) | |
| elif compound_kind: | |
| kind = compound_kind | |
| name = compound_name or anon_name('inline-') | |
| # Immediately emit a forward declaration. | |
| yield srcinfo.resolve(kind, name=name, data=None) | |
| # un-inline the decl. Note that it might not actually be inline. | |
| # We handle the case in the "maybe_inline_actual" branch. | |
| srcinfo.nest( | |
| remainder, | |
| f'{compound_leading or ""} {compound_kind} {name}', | |
| ) | |
| def parse_body(source): | |
| _parse_body = DECL_BODY_PARSERS[compound_kind] | |
| data = [] # members | |
| ident = f'{kind} {name}' | |
| for item in _parse_body(source, anon_name, ident): | |
| if item.kind == 'field': | |
| data.append(item) | |
| else: | |
| yield item | |
| # XXX Should "parent" really be None for inline type decls? | |
| yield srcinfo.resolve(kind, data, name, parent=None) | |
| srcinfo.resume() | |
| yield parse_body | |
| elif typedef_decl: | |
| log_match('typedef', m) | |
| kind = 'typedef' | |
| _, name, data = parse_var_decl(typedef_decl) | |
| if typedef_func_params: | |
| return_type = data | |
| # This matches the data for func declarations. | |
| data = { | |
| 'storage': None, | |
| 'inline': None, | |
| 'params': f'({typedef_func_params})', | |
| 'returntype': return_type, | |
| 'isforward': True, | |
| } | |
| yield srcinfo.resolve(kind, data, name, parent=None) | |
| srcinfo.advance(remainder) | |
| elif func_delim or func_legacy_params: | |
| log_match('function', m) | |
| kind = 'function' | |
| _, name, return_type = parse_var_decl(decl) | |
| func_params = func_params or func_legacy_params | |
| data = { | |
| 'storage': storage, | |
| 'inline': func_inline, | |
| 'params': f'({func_params})', | |
| 'returntype': return_type, | |
| 'isforward': func_delim == ';', | |
| } | |
| yield srcinfo.resolve(kind, data, name, parent=None) | |
| srcinfo.advance(remainder) | |
| if func_delim == '{' or func_legacy_params: | |
| def parse_body(source): | |
| yield from parse_function_body(source, name, anon_name) | |
| yield parse_body | |
| elif var_ending: | |
| log_match('global variable', m) | |
| kind = 'variable' | |
| _, name, vartype = parse_var_decl(decl) | |
| data = { | |
| 'storage': storage, | |
| 'vartype': vartype, | |
| } | |
| yield srcinfo.resolve(kind, data, name, parent=None) | |
| if var_ending == ',': | |
| # It was a multi-declaration, so queue up the next one. | |
| _, qual, typespec, _ = vartype.values() | |
| remainder = f'{storage or ""} {qual or ""} {typespec} {remainder}' | |
| srcinfo.advance(remainder) | |
| if var_init: | |
| _data = f'{name} = {var_init.strip()}' | |
| yield srcinfo.resolve('statement', _data, name=None) | |
| else: | |
| # This should be unreachable. | |
| raise NotImplementedError |