First version of explain. Creating a new parser was a wrong approach. Need to reimplement
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Tuple, Callable
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from parsers.BaseParser import Node
|
||||
|
||||
|
||||
class ExprNode(Node):
|
||||
"""
|
||||
Base ExprNode
|
||||
eval() must be overridden
|
||||
"""
|
||||
|
||||
def eval(self, obj):
|
||||
return True
|
||||
|
||||
|
||||
@dataclass
|
||||
class PropertyEqualsNode(ExprNode):
|
||||
prop: str
|
||||
value: object
|
||||
|
||||
def eval(self, obj):
|
||||
if hasattr(obj, self.prop):
|
||||
return str(getattr(obj, self.prop)) == self.value
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@dataclass()
|
||||
class PropertyContainsNode(ExprNode):
|
||||
prop: str
|
||||
value: object
|
||||
|
||||
def eval(self, obj):
|
||||
if hasattr(obj, self.prop):
|
||||
return self.value in str(getattr(obj, self.prop))
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@dataclass
|
||||
class PropertyEqualsSequenceNode(ExprNode):
|
||||
"""
|
||||
To use when the test must be done across parent and child
|
||||
"""
|
||||
props: List[str]
|
||||
values: List[object]
|
||||
|
||||
def eval(self, obj):
|
||||
index = len(self.props) - 1
|
||||
|
||||
while True:
|
||||
if not hasattr(obj, self.props[index]) or getattr(obj, self.props[index]) != self.values[index]:
|
||||
return False
|
||||
|
||||
if index == 0:
|
||||
break
|
||||
|
||||
index -= 1
|
||||
obj = obj.get_parent() if hasattr(obj, "get_parent") else obj.parent
|
||||
if obj is None:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@dataclass()
|
||||
class IsaNode(ExprNode):
|
||||
"""
|
||||
To use to replicate instanceof, sheerka.instanceof,
|
||||
"""
|
||||
obj_class: object
|
||||
|
||||
def eval(self, obj):
|
||||
if isinstance(self.obj_class, type):
|
||||
return isinstance(obj, self.obj_class)
|
||||
|
||||
if isinstance(self.obj_class, (BuiltinConcepts, str)):
|
||||
return isinstance(obj, Concept) and str(self.obj_class) == obj.key
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@dataclass()
|
||||
class LambdaNode(ExprNode):
|
||||
"""
|
||||
Generic expression to ease the tests
|
||||
"""
|
||||
lambda_exp: Callable[[object], bool]
|
||||
|
||||
def eval(self, obj):
|
||||
try:
|
||||
return self.lambda_exp(obj)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class AndNode(ExprNode):
|
||||
parts: Tuple[ExprNode]
|
||||
|
||||
def __init__(self, *parts: ExprNode):
|
||||
self.parts = parts
|
||||
|
||||
def eval(self, obj):
|
||||
res = self.parts[0].eval(obj) and self.parts[1].eval(obj)
|
||||
for part in self.parts[2:]:
|
||||
res &= part.eval(obj)
|
||||
return res
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class OrNode(ExprNode):
|
||||
parts: Tuple[ExprNode]
|
||||
|
||||
def __init__(self, *parts: ExprNode):
|
||||
self.parts = parts
|
||||
|
||||
def eval(self, obj):
|
||||
res = self.parts[0].eval(obj) or self.parts[1].eval(obj)
|
||||
for part in self.parts[2:]:
|
||||
res |= part.eval(obj)
|
||||
return res
|
||||
|
||||
|
||||
@dataclass()
|
||||
class NotNode(ExprNode):
|
||||
node: ExprNode
|
||||
|
||||
def eval(self, obj):
|
||||
return not self.node.eval(obj)
|
||||
|
||||
|
||||
class FalseNode(ExprNode):
|
||||
def eval(self, obj):
|
||||
return False
|
||||
|
||||
|
||||
class TrueNode(ExprNode):
|
||||
def eval(self, obj):
|
||||
return True
|
||||
|
||||
|
||||
class ExpressionParser:
|
||||
"""
|
||||
will parser logic expression
|
||||
like not (a and b or c)
|
||||
|
||||
The nodes can be used for custom filtering (ex with ExplanationConcept)
|
||||
Or to help to understand why a python expression returns True or False
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ExpressionVisitor:
|
||||
"""
|
||||
Pyhtonic implementation of visitors for ExprNode
|
||||
"""
|
||||
|
||||
def visit(self, expr_node):
|
||||
name = expr_node.__class__.__name__
|
||||
|
||||
method = 'visit_' + name
|
||||
visitor = getattr(self, method, self.generic_visit)
|
||||
return visitor(expr_node)
|
||||
|
||||
def generic_visit(self, expr_node):
|
||||
"""Called if no explicit visitor function exists for a node."""
|
||||
for field, value in expr_node.__dict__.items():
|
||||
if isinstance(value, (list, tuple)):
|
||||
for item in value:
|
||||
if isinstance(item, ExprNode):
|
||||
self.visit(item)
|
||||
elif isinstance(value, ExprNode):
|
||||
self.visit(value)
|
||||
Reference in New Issue
Block a user