mirror of https://github.com/python/cpython
178 lines
4.4 KiB
Python
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
|