Fixed #51 : I can compile simple recognize
This commit is contained in:
@@ -159,7 +159,7 @@ class SheerkaEvaluateRules(BaseService):
|
||||
if compiled_condition.evaluator_type == ConceptEvaluator.NAME:
|
||||
compiled_condition.concept.get_metadata().is_evaluated = False
|
||||
|
||||
evaluator = self.evaluators_by_name[compiled_condition.evaluator]
|
||||
evaluator = self.evaluators_by_name[compiled_condition.evaluator_type]
|
||||
res = evaluator.eval(context, compiled_condition.return_value)
|
||||
if res.status and isinstance(res.body, bool) and res.body:
|
||||
# one successful value found. No need to look any further
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import operator
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from itertools import product
|
||||
from typing import Union, Set, List
|
||||
|
||||
from cache.Cache import Cache
|
||||
@@ -14,7 +15,7 @@ from core.rule import Rule, ACTION_TYPE_PRINT
|
||||
from core.sheerka.Sheerka import RECOGNIZED_BY_NAME, RECOGNIZED_BY_ID
|
||||
from core.sheerka.services.sheerka_service import BaseService, FailedToCompileError
|
||||
from core.tokenizer import Keywords, TokenKind, Token, IterParser
|
||||
from core.utils import index_tokens, COLORS, get_text_from_tokens
|
||||
from core.utils import index_tokens, COLORS, get_text_from_tokens, merge_dictionaries, merge_sets
|
||||
from evaluators.ConceptEvaluator import ConceptEvaluator
|
||||
from evaluators.PythonEvaluator import PythonEvaluator, Expando
|
||||
from parsers.BaseExpressionParser import AndNode, ExpressionVisitor, VariableNode, ComparisonNode, FunctionNode
|
||||
@@ -1309,19 +1310,166 @@ class ReteConditionExprVisitor(ExpressionVisitor):
|
||||
conditions.append(Condition(left, attr, value))
|
||||
|
||||
|
||||
@dataclass()
|
||||
class PythonConditionExprVisitorObj:
|
||||
text: Union[str, None] # human readable
|
||||
source: Union[str, None] # python expression to compile
|
||||
objects: dict
|
||||
variables: set
|
||||
|
||||
@staticmethod
|
||||
def combine_with_and(left, right):
|
||||
left_as_list = left if isinstance(left, list) else [left]
|
||||
right_as_list = right if isinstance(right, list) else [right]
|
||||
|
||||
def create_and(a, b):
|
||||
if a is None and b is None:
|
||||
return None
|
||||
|
||||
if a is None or a == "":
|
||||
return b
|
||||
if b is None or b == "":
|
||||
return a
|
||||
return a + " and " + b
|
||||
|
||||
left_right_product = list(product(left_as_list, right_as_list))
|
||||
res = []
|
||||
for left_obj, right_obj in left_right_product:
|
||||
res.append(PythonConditionExprVisitorObj(create_and(left_obj.text, right_obj.text),
|
||||
create_and(left_obj.source, right_obj.source),
|
||||
merge_dictionaries(left_obj.objects, right_obj.objects),
|
||||
merge_sets(left_obj.variables, right_obj.variables)))
|
||||
|
||||
return res[0] if len(res) == 1 else res
|
||||
|
||||
|
||||
class PythonConditionExprVisitor(ExpressionVisitor):
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
self.var_counter = 0
|
||||
self.variables = set()
|
||||
self.variables = {}
|
||||
|
||||
def get_conditions(self, expr_node):
|
||||
self.var_counter = 0
|
||||
self.variables.clear()
|
||||
|
||||
condition = self.visit(expr_node)
|
||||
return [condition]
|
||||
visitor_obj = self.visit(expr_node)
|
||||
if visitor_obj.source:
|
||||
if self.variables:
|
||||
variables_definitions = "\n".join([f"{v} = {k}" for k, v in self.variables.items()])
|
||||
source = variables_definitions + "\n" + visitor_obj.source
|
||||
text = variables_definitions + "\n" + visitor_obj.text
|
||||
else:
|
||||
source = visitor_obj.source
|
||||
text = visitor_obj.text
|
||||
|
||||
ret = self.context.sheerka.parse_python(self.context, source)
|
||||
if ret.status:
|
||||
ret.body.body.original_source = text
|
||||
ret.body.body.objects = visitor_obj.objects
|
||||
return [CompiledCondition(PythonEvaluator.NAME, ret, visitor_obj.variables)]
|
||||
else:
|
||||
return [CompiledCondition(None, None, visitor_obj.variables)]
|
||||
|
||||
def add_variable(self, target):
|
||||
var_name = f"__x_{self.var_counter:02}__"
|
||||
self.var_counter += 1
|
||||
self.variables[target] = var_name
|
||||
return var_name
|
||||
|
||||
def init_or_get_variable_from_name(self, variable_path: List[str], obj_variables):
|
||||
|
||||
if len(variable_path) > 1:
|
||||
left = variable_path[:-1]
|
||||
right = [variable_path[-1]]
|
||||
|
||||
while left:
|
||||
var_name = ".".join(left)
|
||||
if var_name in self.variables:
|
||||
return self.variables[var_name], ".".join(right)
|
||||
|
||||
right.insert(0, left.pop())
|
||||
|
||||
if variable_path[0] not in self.variables:
|
||||
self.add_variable(variable_path[0])
|
||||
obj_variables.add(variable_path[0])
|
||||
|
||||
return self.variables[variable_path[0]], ".".join(variable_path[1:])
|
||||
|
||||
def init_or_get_variable_from_path(self, variable_path: List[str], obj_variables):
|
||||
path = ".".join(variable_path)
|
||||
if path in self.variables:
|
||||
return self.variables[path]
|
||||
|
||||
obj_variables.add(variable_path[0])
|
||||
return self.add_variable(path)
|
||||
|
||||
def visit_VariableNode(self, expr_node: VariableNode):
|
||||
# no evaluator to call, simply check that the variable is in the bag
|
||||
return CompiledCondition(None, None, {expr_node.name})
|
||||
if not expr_node.attributes and expr_node.name.startswith("__"):
|
||||
return PythonConditionExprVisitorObj(None, None, {}, {expr_node.name})
|
||||
|
||||
source = expr_node.get_source() + " == True"
|
||||
return PythonConditionExprVisitorObj(source, source, {}, {expr_node.name})
|
||||
|
||||
def visit_ComparisonNode(self, expr_node: ComparisonNode):
|
||||
if isinstance(expr_node.left, VariableNode):
|
||||
source = expr_node.get_source()
|
||||
return PythonConditionExprVisitorObj(source, source, {}, {expr_node.left.name})
|
||||
else:
|
||||
raise FailedToCompileError([expr_node])
|
||||
|
||||
def visit_AndNode(self, expr_node: AndNode):
|
||||
current_visitor_obj = self.visit(expr_node.parts[0])
|
||||
for node in expr_node.parts[1:]:
|
||||
visitor_obj = self.visit(node)
|
||||
current_visitor_obj = PythonConditionExprVisitorObj.combine_with_and(current_visitor_obj, visitor_obj)
|
||||
|
||||
return current_visitor_obj
|
||||
|
||||
def visit_FunctionNode(self, expr_node: FunctionNode):
|
||||
if expr_node.first.value == "recognize(":
|
||||
if not isinstance(expr_node.parameters[0].value, VariableNode):
|
||||
return FailedToCompileError([f"Cannot recognize '{expr_node.parameters[0].value}'"])
|
||||
|
||||
return self.recognize_concept(expr_node.parameters[0].value.unpack(),
|
||||
expr_node.parameters[1].value,
|
||||
{})
|
||||
|
||||
def recognize_concept(self, variable_path, concept_to_recognize, concept_variables: dict):
|
||||
if not isinstance(concept_to_recognize, Concept):
|
||||
concept_as_str = concept_to_recognize.get_source()
|
||||
if not concept_as_str:
|
||||
return FailedToCompileError([f"Missing concept in for {variable_path}"])
|
||||
|
||||
res = evaluate(self.context,
|
||||
concept_as_str,
|
||||
evaluators=CONDITIONS_VISITOR_EVALUATORS,
|
||||
desc=None,
|
||||
eval_body=True,
|
||||
eval_where=False,
|
||||
is_question=False,
|
||||
expect_success=False,
|
||||
stm=None)
|
||||
res = expect_one(self.context, res)
|
||||
|
||||
if not res.status:
|
||||
return FailedToCompileError([f"Unknown concept {concept_as_str}"])
|
||||
concept = res.body
|
||||
else:
|
||||
concept = concept_to_recognize
|
||||
|
||||
obj_variables = set()
|
||||
variable = self.init_or_get_variable_from_path(variable_path, obj_variables)
|
||||
|
||||
source = f"isinstance({variable}, Concept)"
|
||||
|
||||
if concept.get_hint(BuiltinConcepts.RECOGNIZED_BY) == RECOGNIZED_BY_NAME:
|
||||
source += f" and {variable}.name == '{concept.name}'"
|
||||
elif concept.get_hint(BuiltinConcepts.RECOGNIZED_BY) == RECOGNIZED_BY_ID:
|
||||
source += f" and {variable}.id == '{concept.id}'"
|
||||
else:
|
||||
source += f" and {variable}.key == '{concept.key}'"
|
||||
concept_variables.update({k: v for k, v in concept.variables().items() if v is not NotInit})
|
||||
|
||||
return PythonConditionExprVisitorObj(source, source, {}, obj_variables)
|
||||
|
||||
@@ -305,6 +305,40 @@ def dict_product(a, b):
|
||||
return res
|
||||
|
||||
|
||||
def merge_dictionaries(a, b):
|
||||
"""
|
||||
Returns a new dictionary which is the merge
|
||||
:param a:
|
||||
:param b:
|
||||
:return:
|
||||
"""
|
||||
if a is None and b is None:
|
||||
return None
|
||||
|
||||
res = {}
|
||||
if a:
|
||||
res.update(a)
|
||||
|
||||
if b:
|
||||
res.update(b)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def merge_sets(a, b):
|
||||
if a is None and b is None:
|
||||
return None
|
||||
|
||||
res = set()
|
||||
if a:
|
||||
res.update(a)
|
||||
|
||||
if b:
|
||||
res.update(b)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def get_n_clones(obj, n):
|
||||
objs = [obj]
|
||||
for i in range(n - 1):
|
||||
|
||||
Reference in New Issue
Block a user