131 lines
3.7 KiB
Python
131 lines
3.7 KiB
Python
from core.ast.nodes import GenericNodeConcept, NodeConcept
|
|
from core.builtin_concepts import ListConcept
|
|
|
|
|
|
class ConceptNodeVisitor:
|
|
"""
|
|
Base class to visit NodeConcept
|
|
It is insolently inspired by python AST.Visitor class
|
|
"""
|
|
|
|
def visit(self, node):
|
|
|
|
"""Visit a node."""
|
|
name = node.node_type if isinstance(node, GenericNodeConcept) else node.name
|
|
name = str(name).capitalize()
|
|
|
|
method = 'visit_' + name
|
|
visitor = getattr(self, method, self.generic_visit)
|
|
return visitor(node)
|
|
|
|
def generic_visit(self, node):
|
|
"""Called if no explicit visitor function exists for a node."""
|
|
for field, value in iter_props(node):
|
|
if isinstance(value, ListConcept):
|
|
for item in value:
|
|
if isinstance(item, NodeConcept):
|
|
self.visit(item)
|
|
elif isinstance(value, NodeConcept):
|
|
self.visit(value)
|
|
|
|
def visit_Constant(self, node):
|
|
value = node.get_prop("value")
|
|
type_name = _const_node_type_names.get(type(value))
|
|
if type_name is None:
|
|
for cls, name in _const_node_type_names.items():
|
|
if isinstance(value, cls):
|
|
type_name = name
|
|
break
|
|
if type_name is not None:
|
|
method = 'visit_' + type_name
|
|
try:
|
|
visitor = getattr(self, method)
|
|
except AttributeError:
|
|
pass
|
|
else:
|
|
import warnings
|
|
warnings.warn(f"{method} is deprecated; add visit_Constant",
|
|
PendingDeprecationWarning, 2)
|
|
return visitor(node)
|
|
return self.generic_visit(node)
|
|
|
|
|
|
class UnreferencedNamesVisitor(ConceptNodeVisitor):
|
|
def __init__(self, sheerka):
|
|
self.names = set()
|
|
self.sheerka = sheerka
|
|
|
|
def visit_Name(self, node):
|
|
parents = get_parents(node)
|
|
if ("For", "target") in parents: # variable used by the 'for' iteration
|
|
return
|
|
|
|
if ("Call", "func") in parents: # name of the function
|
|
return
|
|
|
|
if ("Assign", "targets") in parents: # variable which is assigned
|
|
return
|
|
|
|
if self.can_be_discarded(self.sheerka.value(node), parents):
|
|
return
|
|
|
|
self.names.add(self.sheerka.value(node))
|
|
|
|
def can_be_discarded(self, variable_name, parents):
|
|
|
|
for node in (parent.node for parent in parents):
|
|
if node is None:
|
|
return False
|
|
|
|
if node.get_node_type() == "For" and self.sheerka.value(node.get_prop("target")) == variable_name:
|
|
# variable used by the loop
|
|
return True
|
|
|
|
if node.get_node_type() == "FunctionDef":
|
|
# variable defined as a function parameter
|
|
args = node.get_prop("args")
|
|
args_values = list(self.sheerka.get_values(args.get_prop("args")))
|
|
if variable_name in args_values:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
class ExtractPredicateVisitor(ConceptNodeVisitor):
|
|
def __init__(self, variable_name):
|
|
self.predicates = []
|
|
self.variable_name = variable_name
|
|
|
|
|
|
|
|
|
|
def get_parents(node):
|
|
if node.parent is None:
|
|
return []
|
|
|
|
res = []
|
|
while True:
|
|
if node.parent is None:
|
|
break
|
|
res.append(node.parent)
|
|
node = node.parent.node
|
|
|
|
return res
|
|
|
|
|
|
def iter_props(node):
|
|
for p in node.props:
|
|
yield p, node.props[p].value
|
|
|
|
|
|
_const_node_type_names = {
|
|
bool: 'NameConstant', # should be before int
|
|
type(None): 'NameConstant',
|
|
int: 'Num',
|
|
float: 'Num',
|
|
complex: 'Num',
|
|
str: 'Str',
|
|
bytes: 'Bytes',
|
|
type(...): 'Ellipsis',
|
|
}
|