aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/c-analyzer/c_parser/match.py')
-rw-r--r--Tools/c-analyzer/c_parser/match.py177
1 files changed, 177 insertions, 0 deletions
diff --git a/Tools/c-analyzer/c_parser/match.py b/Tools/c-analyzer/c_parser/match.py
new file mode 100644
index 00000000000..3b5068fd11b
--- /dev/null
+++ b/Tools/c-analyzer/c_parser/match.py
@@ -0,0 +1,177 @@
+import re
+
+from . import info as _info
+from .parser._regexes import SIMPLE_TYPE
+
+
+_KIND = _info.KIND
+
+
+def match_storage(decl, expected):
+ default = _info.get_default_storage(decl)
+ #assert default
+ if expected is None:
+ expected = {default}
+ elif isinstance(expected, str):
+ expected = {expected or default}
+ elif not expected:
+ expected = _info.STORAGE
+ else:
+ expected = {v or default for v in expected}
+ storage = _info.get_effective_storage(decl, default=default)
+ return storage in expected
+
+
+##################################
+# decl matchers
+
+def is_type_decl(item):
+ return _KIND.is_type_decl(item.kind)
+
+
+def is_decl(item):
+ return _KIND.is_decl(item.kind)
+
+
+def is_pots(typespec, *,
+ _regex=re.compile(rf'^{SIMPLE_TYPE}$', re.VERBOSE),
+ ):
+
+ if not typespec:
+ return None
+ if type(typespec) is not str:
+ _, _, _, typespec, _ = _info.get_parsed_vartype(typespec)
+ return _regex.match(typespec) is not None
+
+
+def is_funcptr(vartype):
+ if not vartype:
+ return None
+ _, _, _, _, abstract = _info.get_parsed_vartype(vartype)
+ return _is_funcptr(abstract)
+
+
+def _is_funcptr(declstr):
+ if not declstr:
+ return None
+ # XXX Support "(<name>*)(".
+ return '(*)(' in declstr.replace(' ', '')
+
+
+def is_forward_decl(decl):
+ if decl.kind is _KIND.TYPEDEF:
+ return False
+ elif is_type_decl(decl):
+ return not decl.data
+ elif decl.kind is _KIND.FUNCTION:
+ # XXX This doesn't work with ParsedItem.
+ return decl.signature.isforward
+ elif decl.kind is _KIND.VARIABLE:
+ # No var decls are considered forward (or all are...).
+ return False
+ else:
+ raise NotImplementedError(decl)
+
+
+def can_have_symbol(decl):
+ return decl.kind in (_KIND.VARIABLE, _KIND.FUNCTION)
+
+
+def has_external_symbol(decl):
+ if not can_have_symbol(decl):
+ return False
+ if _info.get_effective_storage(decl) != 'extern':
+ return False
+ if decl.kind is _KIND.FUNCTION:
+ return not decl.signature.isforward
+ else:
+ # It must be a variable, which can only be implicitly extern here.
+ return decl.storage != 'extern'
+
+
+def has_internal_symbol(decl):
+ if not can_have_symbol(decl):
+ return False
+ return _info.get_actual_storage(decl) == 'static'
+
+
+def is_external_reference(decl):
+ if not can_have_symbol(decl):
+ return False
+ # We have to check the declared storage rather tnan the effective.
+ if decl.storage != 'extern':
+ return False
+ if decl.kind is _KIND.FUNCTION:
+ return decl.signature.isforward
+ # Otherwise it's a variable.
+ return True
+
+
+def is_local_var(decl):
+ if not decl.kind is _KIND.VARIABLE:
+ return False
+ return True if decl.parent else False
+
+
+def is_global_var(decl):
+ if not decl.kind is _KIND.VARIABLE:
+ return False
+ return False if decl.parent else True
+
+
+##################################
+# filtering with matchers
+
+def filter_by_kind(items, kind):
+ if kind == 'type':
+ kinds = _KIND._TYPE_DECLS
+ elif kind == 'decl':
+ kinds = _KIND._TYPE_DECLS
+ try:
+ okay = kind in _KIND
+ except TypeError:
+ kinds = set(kind)
+ else:
+ kinds = {kind} if okay else set(kind)
+ for item in items:
+ if item.kind in kinds:
+ yield item
+
+
+##################################
+# grouping with matchers
+
+def group_by_category(decls, categories, *, ignore_non_match=True):
+ collated = {}
+ for decl in decls:
+ # Matchers should be mutually exclusive. (First match wins.)
+ for category, match in categories.items():
+ if match(decl):
+ if category not in collated:
+ collated[category] = [decl]
+ else:
+ collated[category].append(decl)
+ break
+ else:
+ if not ignore_non_match:
+ raise Exception(f'no match for {decl!r}')
+ return collated
+
+
+def group_by_kind(items):
+ collated = {kind: [] for kind in _KIND}
+ for item in items:
+ try:
+ collated[item.kind].append(item)
+ except KeyError:
+ raise ValueError(f'unsupported kind in {item!r}')
+ return collated
+
+
+def group_by_kinds(items):
+ # Collate into kind groups (decl, type, etc.).
+ collated = {_KIND.get_group(k): [] for k in _KIND}
+ for item in items:
+ group = _KIND.get_group(item.kind)
+ collated[group].append(item)
+ return collated