summaryrefslogtreecommitdiff
path: root/Tools/c-analyzer/c_parser/parser/_global.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/c-analyzer/c_parser/parser/_global.py')
-rw-r--r--Tools/c-analyzer/c_parser/parser/_global.py179
1 files changed, 179 insertions, 0 deletions
diff --git a/Tools/c-analyzer/c_parser/parser/_global.py b/Tools/c-analyzer/c_parser/parser/_global.py
new file mode 100644
index 0000000000..35947c1299
--- /dev/null
+++ b/Tools/c-analyzer/c_parser/parser/_global.py
@@ -0,0 +1,179 @@
+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_body
+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