cpython/Tools/c-analyzer/c_parser/match.py

178 lines
4.4 KiB
Python

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