Fixed #68: Implement SheerkaQL
Fixed #70: SheerkaFilterManager : Pipe functions Fixed #71: SheerkaFilterManager : filter_objects Fixed #75: SheerkaMemory: Enhance memory() to use the filtering capabilities Fixed #76: SheerkaEvaluateConcept: Concepts that modify the state of the system must not be evaluated during question
This commit is contained in:
@@ -3,7 +3,7 @@ import time
|
||||
from os import path
|
||||
|
||||
from core.builtin_concepts_ids import BuiltinConcepts, BuiltinContainers
|
||||
from core.builtin_helpers import ensure_concept_or_rule
|
||||
from core.builtin_helpers import ensure_concept_or_rule, ensure_concept
|
||||
from core.concept import Concept
|
||||
from core.global_symbols import SHEERKA_BACKUP_FOLDER
|
||||
from core.sheerka.services.SheerkaHistoryManager import SheerkaHistoryManager
|
||||
@@ -37,6 +37,7 @@ class SheerkaAdmin(BaseService):
|
||||
self.sheerka.bind_service_method(self.admin_history, False, as_name="history")
|
||||
self.sheerka.bind_service_method(self.admin_history, False, as_name="history")
|
||||
self.sheerka.bind_service_method(self.sdp, False)
|
||||
self.sheerka.bind_service_method(self.atomic_def, False)
|
||||
|
||||
def caches_names(self):
|
||||
"""
|
||||
@@ -218,6 +219,20 @@ class SheerkaAdmin(BaseService):
|
||||
|
||||
return self.sheerka.isinstance(a, b)
|
||||
|
||||
@staticmethod
|
||||
def atomic_def(a):
|
||||
"""
|
||||
Return the 'atomic definition' of a concept
|
||||
a concept key stripped from its 'var' tokens
|
||||
>>> assert atomic_def(Concept('a plus b').def_var("a").def_var("b")) == "plus"
|
||||
>>> assert atomic_def(Concept('x is a y').def_var("x").def_var("y")) == "is a"
|
||||
:param a:
|
||||
:return:
|
||||
"""
|
||||
ensure_concept(a)
|
||||
|
||||
return a.get_atomic_def()
|
||||
|
||||
@staticmethod
|
||||
def is_container(obj):
|
||||
"""
|
||||
|
||||
@@ -126,7 +126,8 @@ class SheerkaConceptManager(BaseService):
|
||||
self.sheerka.bind_service_method(self.get_by_name, False, visible=False)
|
||||
self.sheerka.bind_service_method(self.get_by_hash, False, visible=False)
|
||||
self.sheerka.bind_service_method(self.get_by_id, False, visible=False)
|
||||
self.sheerka.bind_service_method(self.is_not_a_variable, False, visible=False)
|
||||
self.sheerka.bind_service_method(self.is_not_a_concept_name, False, visible=False)
|
||||
self.sheerka.bind_service_method(self.is_a_concept_name, False, visible=False)
|
||||
self.sheerka.bind_service_method(self.get_concepts_by_first_token, False, visible=False)
|
||||
self.sheerka.bind_service_method(self.get_concepts_by_first_regex, False, visible=False)
|
||||
self.sheerka.bind_service_method(self.get_concepts_bnf_definitions, False, visible=False)
|
||||
@@ -705,7 +706,7 @@ class SheerkaConceptManager(BaseService):
|
||||
|
||||
return refs
|
||||
|
||||
def is_not_a_variable(self, name):
|
||||
def is_not_a_concept_name(self, name):
|
||||
"""
|
||||
Given a name tells if it refers to a variable name
|
||||
:param name:
|
||||
@@ -713,6 +714,14 @@ class SheerkaConceptManager(BaseService):
|
||||
"""
|
||||
return self.sheerka.om.get(self.sheerka.CONCEPTS_BY_NAME_ENTRY, name) is NotFound
|
||||
|
||||
def is_a_concept_name(self, name):
|
||||
"""
|
||||
Given a name tells if it refers to a variable name
|
||||
:param name:
|
||||
:return:
|
||||
"""
|
||||
return self.sheerka.om.get(self.sheerka.CONCEPTS_BY_NAME_ENTRY, name) is not NotFound
|
||||
|
||||
def clear_bnf_definition(self, concept_id=None):
|
||||
if concept_id:
|
||||
self.sheerka.om.delete(self.CONCEPTS_BNF_DEFINITIONS_ENTRY, concept_id)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.builtin_helpers import expect_one, only_successful, evaluate, ensure_concept
|
||||
from core.builtin_helpers import expect_one, only_successful, evaluate_from_source, ensure_concept
|
||||
from core.concept import Concept, DoNotResolve, ConceptParts, InfiniteRecursionResolved, AllConceptParts, \
|
||||
concept_part_value
|
||||
from core.global_symbols import NotInit, CURRENT_OBJ
|
||||
@@ -11,9 +11,9 @@ from core.sheerka.services.SheerkaExecute import ParserInput
|
||||
from core.sheerka.services.sheerka_service import BaseService
|
||||
from core.tokenizer import Tokenizer
|
||||
from core.utils import unstr_concept
|
||||
from parsers.BaseExpressionParser import TrueifyVisitor
|
||||
from parsers.BaseNodeParser import ConceptNode
|
||||
from parsers.LogicalOperatorParser import LogicalOperatorParser
|
||||
from parsers.BaseExpressionParser import TrueifyVisitor
|
||||
|
||||
CONCEPT_EVALUATION_STEPS = [
|
||||
BuiltinConcepts.BEFORE_EVALUATION,
|
||||
@@ -26,6 +26,11 @@ class ChickenAndEggException(Exception):
|
||||
error: Concept
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConceptEvalException(Exception):
|
||||
error: Concept
|
||||
|
||||
|
||||
@dataclass
|
||||
class WhereClauseDef:
|
||||
concept: Concept # concept on which the where clause is applied
|
||||
@@ -43,6 +48,8 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
|
||||
def initialize(self):
|
||||
self.sheerka.bind_service_method(self.evaluate_concept, True)
|
||||
self.sheerka.bind_service_method(self.call_concept, True)
|
||||
self.sheerka.bind_service_method(self.call_concept, False, as_name="evaluate_question")
|
||||
self.sheerka.bind_service_method(self.set_auto_eval, True)
|
||||
|
||||
@staticmethod
|
||||
@@ -197,12 +204,12 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
ret.append(r) # it cannot be solved unitary, let's give a chance to the global where condition
|
||||
else:
|
||||
# it means that the where condition is an expression that needs to be executed
|
||||
evaluation_res = evaluate(context,
|
||||
where_clause_def.trueified,
|
||||
desc=f"Apply where clause on '{where_clause_def.prop}'",
|
||||
expect_success=True,
|
||||
is_question=True,
|
||||
stm={where_clause_def.prop: r.body})
|
||||
evaluation_res = evaluate_from_source(context,
|
||||
where_clause_def.trueified,
|
||||
desc=f"Apply where clause on '{where_clause_def.prop}'",
|
||||
expect_success=True,
|
||||
is_question=True,
|
||||
stm={where_clause_def.prop: r.body})
|
||||
one_res = expect_one(context, evaluation_res)
|
||||
if one_res.status:
|
||||
value = context.sheerka.objvalue(one_res)
|
||||
@@ -624,6 +631,25 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
else:
|
||||
return concept
|
||||
|
||||
def call_concept(self, context, concept, *args, **kwargs):
|
||||
"""
|
||||
call the concept using either args or kwargs (not both)
|
||||
:param context:
|
||||
:param concept:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
|
||||
evaluated = self.evaluate_concept(context, concept)
|
||||
if self.sheerka.has_error(context, evaluated):
|
||||
raise ConceptEvalException(evaluated)
|
||||
|
||||
if ConceptParts.BODY in evaluated.get_compiled():
|
||||
return evaluated.body
|
||||
else:
|
||||
return evaluated
|
||||
|
||||
def compute_metadata_to_eval(self, context, concept):
|
||||
to_eval = []
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from core.builtin_concepts import BuiltinConcepts
|
||||
from core.builtin_helpers import expect_one
|
||||
from core.global_symbols import EVENT_RULE_CREATED, EVENT_RULE_DELETED, EVENT_RULE_ID_DELETED
|
||||
from core.sheerka.services.sheerka_service import BaseService
|
||||
from evaluators.ConceptEvaluator import ConceptEvaluator
|
||||
from sheerkarete.network import ReteNetwork
|
||||
|
||||
DISABLED_RULES = "#disabled#"
|
||||
@@ -105,10 +104,18 @@ class SheerkaEvaluateRules(BaseService):
|
||||
:return:
|
||||
"""
|
||||
|
||||
results = self.evaluate_conditions(context, rule.compiled_conditions, bag)
|
||||
|
||||
debugger = context.get_debugger(SheerkaEvaluateRules.NAME, "evaluate_rule", new_debug_id=False)
|
||||
debugger.debug_rule(rule, results)
|
||||
|
||||
return expect_one(context, results)
|
||||
|
||||
def evaluate_conditions(self, context, conditions, bag):
|
||||
bag_variables = set(bag.keys())
|
||||
|
||||
results = []
|
||||
for compiled_condition in rule.compiled_conditions:
|
||||
for compiled_condition in conditions:
|
||||
|
||||
if compiled_condition.variables.intersection(bag_variables) != compiled_condition.variables:
|
||||
continue
|
||||
@@ -123,22 +130,19 @@ class SheerkaEvaluateRules(BaseService):
|
||||
else:
|
||||
|
||||
# do not forget to reset the 'is_evaluated' in the case of a concept
|
||||
if compiled_condition.evaluator_type == ConceptEvaluator.NAME:
|
||||
compiled_condition.concept.get_metadata().is_evaluated = False
|
||||
for concept in compiled_condition.concepts_to_reset:
|
||||
concept.get_metadata().is_evaluated = False
|
||||
|
||||
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
|
||||
results = [res]
|
||||
results = [res] # don't we care about the other failing results ?
|
||||
break
|
||||
else:
|
||||
results.append(res)
|
||||
|
||||
debugger = context.get_debugger(SheerkaEvaluateRules.NAME, "evaluate_rule", new_debug_id=False)
|
||||
debugger.debug_rule(rule, results)
|
||||
|
||||
return expect_one(context, results)
|
||||
return results
|
||||
|
||||
def remove_from_rete_memory(self, lst):
|
||||
if lst is None:
|
||||
|
||||
@@ -43,7 +43,6 @@ class SheerkaHasAManager(BaseService):
|
||||
def hasa(self, concept_a, concept_b):
|
||||
"""
|
||||
Check that concept 'a' has/owns concept 'b'
|
||||
:param context:
|
||||
:param concept_a:
|
||||
:param concept_b:
|
||||
:return:
|
||||
|
||||
@@ -10,6 +10,7 @@ from core.sheerka.services.sheerka_service import BaseService, ServiceObj
|
||||
|
||||
@dataclass
|
||||
class MemoryObject(ServiceObj):
|
||||
timestamp: float
|
||||
obj: object
|
||||
|
||||
def __eq__(self, other):
|
||||
@@ -21,6 +22,9 @@ class MemoryObject(ServiceObj):
|
||||
def __hash__(self):
|
||||
return hash((self.event_id, self.obj))
|
||||
|
||||
def __repr__(self):
|
||||
return f"MemoryObject({self.obj}, timestamp={self.timestamp})"
|
||||
|
||||
|
||||
class SheerkaMemory(BaseService):
|
||||
NAME = "Memory"
|
||||
@@ -38,7 +42,7 @@ class SheerkaMemory(BaseService):
|
||||
self.sheerka.bind_service_method(self.get_all_short_term_memory, False, visible=False)
|
||||
self.sheerka.bind_service_method(self.add_to_short_term_memory, True, visible=False)
|
||||
self.sheerka.bind_service_method(self.remove_context, True, as_name="clear_short_term_memory", visible=False)
|
||||
self.sheerka.bind_service_method(self.add_to_memory, True, visible=False)
|
||||
self.sheerka.bind_service_method(self.add_to_memory, True)
|
||||
self.sheerka.bind_service_method(self.add_many_to_short_term_memory, True, visible=False)
|
||||
self.sheerka.bind_service_method(self.get_from_memory, False)
|
||||
self.sheerka.bind_service_method(self.get_last_from_memory, False)
|
||||
@@ -125,20 +129,31 @@ class SheerkaMemory(BaseService):
|
||||
"""
|
||||
last = self.sheerka.om.get(SheerkaMemory.OBJECTS_ENTRY, key)
|
||||
if last is NotFound:
|
||||
self.sheerka.om.put(SheerkaMemory.OBJECTS_ENTRY, key, MemoryObject(context.event.get_digest(), concept))
|
||||
self.sheerka.om.put(SheerkaMemory.OBJECTS_ENTRY, key, MemoryObject(context.event.get_digest(),
|
||||
context.event.date.timestamp(),
|
||||
concept))
|
||||
return
|
||||
|
||||
if not isinstance(last, list) and last.obj == concept:
|
||||
# replace with the new one
|
||||
self.sheerka.om.delete(SheerkaMemory.OBJECTS_ENTRY, key, last)
|
||||
self.sheerka.om.put(SheerkaMemory.OBJECTS_ENTRY, key, MemoryObject(context.event.get_digest(), concept))
|
||||
self.sheerka.om.put(SheerkaMemory.OBJECTS_ENTRY, key, MemoryObject(context.event.get_digest(),
|
||||
context.event.date.timestamp(),
|
||||
concept))
|
||||
return
|
||||
|
||||
if isinstance(last, list) and last[-1].obj == concept:
|
||||
# replace with the new one
|
||||
self.sheerka.om.delete(SheerkaMemory.OBJECTS_ENTRY, key, last[-1])
|
||||
self.sheerka.om.put(SheerkaMemory.OBJECTS_ENTRY, key, MemoryObject(context.event.get_digest(), concept))
|
||||
self.sheerka.om.put(SheerkaMemory.OBJECTS_ENTRY, key, MemoryObject(context.event.get_digest(),
|
||||
context.event.date.timestamp(),
|
||||
concept))
|
||||
return
|
||||
|
||||
self.sheerka.om.put(SheerkaMemory.OBJECTS_ENTRY, key, MemoryObject(context.event.get_digest(), concept))
|
||||
# append the new one
|
||||
self.sheerka.om.put(SheerkaMemory.OBJECTS_ENTRY, key, MemoryObject(context.event.get_digest(),
|
||||
context.event.date.timestamp(),
|
||||
concept))
|
||||
|
||||
def get_from_memory(self, context, key):
|
||||
""""
|
||||
@@ -207,14 +222,35 @@ class SheerkaMemory(BaseService):
|
||||
"""
|
||||
name_to_use = name.name if isinstance(name, Concept) else name
|
||||
self.unregister_object(context, name_to_use)
|
||||
obj = self.get_from_memory(context, name_to_use)
|
||||
if obj is NotFound:
|
||||
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"#name": name})
|
||||
obj = self.get_last_from_memory(context, name_to_use)
|
||||
if obj is not NotFound:
|
||||
return obj.obj
|
||||
|
||||
if isinstance(obj, list):
|
||||
obj = obj[-1]
|
||||
all_objects = self.sheerka.om.list(SheerkaMemory.OBJECTS_ENTRY)
|
||||
all_objects_copy = []
|
||||
for obj in all_objects:
|
||||
if isinstance(obj, list):
|
||||
all_objects_copy.append(obj.copy())
|
||||
else:
|
||||
all_objects_copy.append([obj])
|
||||
|
||||
return obj.obj
|
||||
while len(all_objects_copy) > 0:
|
||||
current_list = []
|
||||
temp = []
|
||||
for obj in all_objects_copy:
|
||||
current_list.append(obj.pop(-1))
|
||||
if len(obj) > 0:
|
||||
temp.append(obj)
|
||||
|
||||
all_objects_copy = temp
|
||||
current_list = sorted(current_list, key=lambda o: o.timestamp, reverse=True)
|
||||
current_objects = [o.obj for o in current_list]
|
||||
|
||||
res = self.sheerka.filter_objects(context, current_objects, name)
|
||||
if len(res) > 0:
|
||||
return res[0] # only the first, as it should have a better timestamp
|
||||
|
||||
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"#name": name})
|
||||
|
||||
def mem(self):
|
||||
keys = sorted([k for k in self.sheerka.om.list(SheerkaMemory.OBJECTS_ENTRY)])
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
from cache.FastCache import FastCache
|
||||
from core.builtin_concepts_ids import BuiltinContainers, BuiltinConcepts
|
||||
from core.concept import Concept, ConceptParts
|
||||
from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules
|
||||
from core.sheerka.services.sheerka_service import BaseService
|
||||
from core.tokenizer import Tokenizer, TokenKind
|
||||
from core.utils import as_bag
|
||||
from sheerkapython.python_wrapper import create_namespace, ObjectContainer, get_type
|
||||
from sheerkaql.lexer import Lexer
|
||||
from sheerkaql.parser import Parser
|
||||
|
||||
|
||||
class SheerkaQueryManager(BaseService):
|
||||
"""
|
||||
This class manage the queries on objects across the system
|
||||
"""
|
||||
NAME = "QueryManager"
|
||||
OBJECTS_ROOT_ALIAS = "__xxx__objects__xx__"
|
||||
QUERY_PARAMETER_PREFIX = "__xxx__query_parameter__xx__"
|
||||
|
||||
def __init__(self, sheerka):
|
||||
super().__init__(sheerka)
|
||||
self.queries = FastCache()
|
||||
self.conditions = FastCache()
|
||||
self.lexer = Lexer()
|
||||
self.rule_evaluator = None
|
||||
|
||||
def initialize(self):
|
||||
self.sheerka.bind_service_method(self.filter_objects, False)
|
||||
self.sheerka.bind_service_method(self.select_objects, False)
|
||||
self.sheerka.bind_service_method(self.collect_attributes, False)
|
||||
|
||||
self.sheerka.bind_service_method(self.filter_objects, False, as_name="pipe_where")
|
||||
self.sheerka.bind_service_method(self.select_objects, False, as_name="pipe_select")
|
||||
self.sheerka.bind_service_method(self.collect_attributes, False, as_name="pipe_props")
|
||||
|
||||
self.sheerka.register_debug_vars(SheerkaQueryManager.NAME, "filter_objects", "query")
|
||||
|
||||
def initialize_deferred(self, context, is_first_time):
|
||||
self.rule_evaluator = self.sheerka.services[SheerkaEvaluateRules.NAME]
|
||||
|
||||
def get_query_by_kwargs(self, local_namespace, **kwargs):
|
||||
"""
|
||||
Create a predicate using kwargs and filter the result
|
||||
:param local_namespace:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
if not kwargs:
|
||||
return None
|
||||
|
||||
objects_in_context_index = 0
|
||||
conditions = []
|
||||
for k, v in kwargs.items():
|
||||
current_variable_name = f"{self.QUERY_PARAMETER_PREFIX}_{objects_in_context_index:02}"
|
||||
objects_in_context_index += 1
|
||||
local_namespace[current_variable_name] = v
|
||||
|
||||
if k == "__type":
|
||||
conditions.append(f"get_type(self) == {current_variable_name}")
|
||||
|
||||
elif k == "atomic_def":
|
||||
conditions.append(f"atomic_def(self) == {current_variable_name}")
|
||||
|
||||
elif k in ("__self", "_"):
|
||||
conditions.append(f"self == {current_variable_name}")
|
||||
|
||||
else:
|
||||
conditions.append(f"self.{k} == {current_variable_name}")
|
||||
|
||||
return ' and '.join(conditions)
|
||||
|
||||
def filter_objects(self, context, objects, predicate=None, **kwargs):
|
||||
"""
|
||||
filter the given objects using the conditions from kwargs
|
||||
for each k,v in kwargs, the equality k == v is added
|
||||
for k starting with a double underscore '__', a special treatment may be done
|
||||
__type : get the type of object (in Sheerka world)
|
||||
:param context:
|
||||
:param objects:
|
||||
:param predicate:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
|
||||
debugger = context.get_debugger(SheerkaQueryManager.NAME, "filter_objects")
|
||||
|
||||
original_container = None
|
||||
if isinstance(objects, Concept) and objects.key in BuiltinContainers:
|
||||
original_container = objects
|
||||
objects = objects.body
|
||||
|
||||
debugger.debug_entering(nb_objects=len(objects), predicate=predicate, **kwargs)
|
||||
local_namespace = {}
|
||||
query_by_kwargs = self.get_query_by_kwargs(local_namespace, **kwargs)
|
||||
|
||||
if predicate is not None and query_by_kwargs is not None:
|
||||
query = f"({predicate}) and ({query_by_kwargs})"
|
||||
elif predicate is not None:
|
||||
query = predicate
|
||||
elif query_by_kwargs is not None:
|
||||
query = query_by_kwargs
|
||||
else:
|
||||
query = None
|
||||
|
||||
if debugger.is_enabled():
|
||||
debugger.debug_var("query", query)
|
||||
for k, v in local_namespace.items():
|
||||
debugger.debug_var("query_parameter", f"{k} = {v}")
|
||||
|
||||
if query and query in self.conditions:
|
||||
# Then try using RuleManager
|
||||
objects = self.execute_conditions(context, query, objects, local_namespace)
|
||||
elif query:
|
||||
try:
|
||||
# Fist try with the FLWR parser
|
||||
full_query = f"{self.OBJECTS_ROOT_ALIAS}.items[{query}]"
|
||||
objects = self.execute_flwr_query(context, full_query, objects, local_namespace)
|
||||
except SyntaxError:
|
||||
# Then try using RuleManager
|
||||
objects = self.execute_conditions(context, query, objects, local_namespace)
|
||||
|
||||
if original_container:
|
||||
original_container.set_value(ConceptParts.BODY, objects)
|
||||
return original_container
|
||||
else:
|
||||
return objects
|
||||
|
||||
def select_objects(self, context, objects, *props, **kwargs):
|
||||
"""
|
||||
From the input objects, create output objects
|
||||
The definition of these new objects can come from a flwr query
|
||||
:param context:
|
||||
:param objects:
|
||||
:param query:
|
||||
:return:
|
||||
"""
|
||||
|
||||
def sanitize_property(p):
|
||||
if not isinstance(p, str):
|
||||
raise SyntaxError(f"{p} is not the name of an attribute")
|
||||
|
||||
tokens = list(Tokenizer(p, yield_eof=False))
|
||||
if len(tokens) == 1 and tokens[0].type == TokenKind.IDENTIFIER:
|
||||
return f"self.{p}"
|
||||
else:
|
||||
return p
|
||||
|
||||
original_container = None
|
||||
if isinstance(objects, Concept) and objects.key in BuiltinContainers:
|
||||
original_container = objects
|
||||
objects = objects.body
|
||||
|
||||
requested_properties = [sanitize_property(prop) for prop in props]
|
||||
|
||||
if kwargs:
|
||||
items = [f"'{k}': {sanitize_property(v)}" for k, v in kwargs.items()]
|
||||
requested_properties.append("{" + ", ".join(items) + "}")
|
||||
|
||||
query = f"for self in {self.OBJECTS_ROOT_ALIAS}.items return " + ", ".join(requested_properties)
|
||||
objects = self.execute_flwr_query(context, query, objects, {})
|
||||
|
||||
if original_container:
|
||||
original_container.set_value(ConceptParts.BODY, objects)
|
||||
return original_container
|
||||
else:
|
||||
return objects
|
||||
|
||||
def collect_attributes(self, objects, take=10):
|
||||
"""
|
||||
Given a list of object, returns the attributes that can be requested
|
||||
:param objects:
|
||||
:param take: no need to browse the whole list, you can just use a sample
|
||||
:return:
|
||||
"""
|
||||
if isinstance(objects, Concept) and objects.key in BuiltinContainers:
|
||||
objects = objects.body
|
||||
|
||||
result = {}
|
||||
for obj in (objects if take <= 0 else objects[:take]):
|
||||
object_type = get_type(obj)
|
||||
attrs = set(p for p in as_bag(obj).keys() if p != "self")
|
||||
if object_type is result:
|
||||
result[object_type].update(attrs)
|
||||
else:
|
||||
result[object_type] = attrs
|
||||
|
||||
result = {k: sorted(list(v)) for k, v in result.items()}
|
||||
return self.sheerka.new(BuiltinConcepts.TO_DICT, body=result)
|
||||
|
||||
def execute_flwr_query(self, context, query, objects, local_namespace):
|
||||
"""
|
||||
Execute a FLWOR query on objects
|
||||
:param context:
|
||||
:param query: query to execute (as a string to compile)
|
||||
:param objects: list of objects to filter
|
||||
:param local_namespace: objects of the namespace that are already known
|
||||
:return:
|
||||
"""
|
||||
if query not in self.queries:
|
||||
parser = Parser()
|
||||
compiled_query = parser.parse(bytes(query, 'utf-8').decode('unicode_escape'), lexer=self.lexer)
|
||||
self.queries.put(query, (compiled_query, parser.names, parser.sheerka_names))
|
||||
|
||||
compiled_query, names, sheerka_names = self.queries.get(query)
|
||||
|
||||
namespace = create_namespace(context,
|
||||
self.NAME,
|
||||
names,
|
||||
sheerka_names,
|
||||
{self.OBJECTS_ROOT_ALIAS: ObjectContainer(objects)},
|
||||
expression_only=True,
|
||||
allow_builtins=True)
|
||||
namespace.update(local_namespace) # override if needed
|
||||
|
||||
return compiled_query(namespace)
|
||||
|
||||
def execute_conditions(self, context, query, objects, local_namespace):
|
||||
"""
|
||||
|
||||
:param context:
|
||||
:param query:
|
||||
:param objects:
|
||||
:param local_namespace:
|
||||
:return:
|
||||
"""
|
||||
|
||||
if query not in self.conditions:
|
||||
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager
|
||||
rule_manager = self.sheerka.services[SheerkaRuleManager.NAME]
|
||||
compilation_results = rule_manager.compile_when(context, self.NAME, query)
|
||||
self.conditions.put(query, compilation_results.python_conditions)
|
||||
|
||||
conditions = self.conditions.get(query)
|
||||
results = []
|
||||
with context.push(BuiltinConcepts.EXEC_CODE, query) as sub_context:
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_WHERE_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_UNTIL_SUCCESS_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
|
||||
sub_context.sheerka.add_many_to_short_term_memory(sub_context, local_namespace)
|
||||
sub_context.deactivate_push()
|
||||
|
||||
for obj in objects:
|
||||
local_namespace["self"] = obj
|
||||
res = self.rule_evaluator.evaluate_conditions(sub_context, conditions, local_namespace)
|
||||
successful = list(filter(lambda r: r.status and type(r.body) == bool and r.body, res))
|
||||
if successful:
|
||||
results.append(obj)
|
||||
|
||||
return results
|
||||
@@ -1,28 +1,27 @@
|
||||
import operator
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from itertools import product
|
||||
from typing import Union, Set, List, Tuple
|
||||
|
||||
from cache.Cache import Cache
|
||||
from cache.ListIfNeededCache import ListIfNeededCache
|
||||
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
|
||||
from core.builtin_helpers import ensure_evaluated, expect_one, evaluate
|
||||
from core.builtin_helpers import ensure_evaluated, expect_one, evaluate_from_source, \
|
||||
get_possible_variables_from_concept, is_a_question
|
||||
from core.concept import Concept
|
||||
from core.global_symbols import EVENT_RULE_PRECEDENCE_MODIFIED, RULE_COMPARISON_CONTEXT, NotFound, ErrorObj, \
|
||||
EVENT_RULE_CREATED, EVENT_RULE_DELETED, NotInit
|
||||
from core.rule import Rule, ACTION_TYPE_PRINT
|
||||
from core.sheerka.Sheerka import RECOGNIZED_BY_NAME, RECOGNIZED_BY_ID
|
||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||
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, merge_dictionaries, merge_sets
|
||||
from evaluators.PythonEvaluator import PythonEvaluator, Expando
|
||||
from core.utils import index_tokens, COLORS, get_text_from_tokens, merge_dictionaries, merge_sets, get_safe_str_value
|
||||
from evaluators.PythonEvaluator import PythonEvaluator
|
||||
from parsers.BaseExpressionParser import AndNode, ExpressionVisitor, VariableNode, ComparisonNode, FunctionNode, \
|
||||
ComparisonType, NotNode, NameExprNode
|
||||
from parsers.BaseNodeParser import ConceptNode
|
||||
from parsers.LogicalOperatorParser import LogicalOperatorParser
|
||||
from parsers.PythonParser import PythonParser
|
||||
from sheerkapython.python_wrapper import Expando, sheerka_globals
|
||||
from sheerkarete.common import V
|
||||
from sheerkarete.conditions import AndConditions, Condition, NegatedCondition, NegatedConjunctiveConditions
|
||||
from sheerkarete.network import FACT_NAME, FACT_SELF
|
||||
@@ -424,7 +423,7 @@ class FormatRuleActionParser(IterParser):
|
||||
|
||||
def return_list(self, args, kwargs):
|
||||
"""
|
||||
Looking for variable_name, [recurse_on], [recursion_depth], [items_prop]
|
||||
Looking for greeting_var, [recurse_on], [recursion_depth], [items_prop]
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:return:
|
||||
@@ -624,7 +623,8 @@ class CompiledCondition:
|
||||
return_value: Union[ReturnValueConcept, None] # compiled source as ReturnValue
|
||||
variables: Set[str] # variables that must be present in bag
|
||||
not_variables: Set[str] # variables that must not be present in bag
|
||||
concept: Union[Concept, None] = None # compiled source as concept
|
||||
objects: dict
|
||||
concepts_to_reset: set # concepts to reset before every evaluation
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -763,12 +763,14 @@ class SheerkaRuleManager(BaseService):
|
||||
parsed = parsed_expr_ret.body.body
|
||||
rete_conditions, python_conditions = None, None
|
||||
if parsed_expr_ret.status:
|
||||
# get the conditions as rete conditions
|
||||
try:
|
||||
rete_visitor = ReteConditionExprVisitor(context)
|
||||
rete_conditions = rete_visitor.get_conditions(parsed)
|
||||
except FailedToCompileError as err:
|
||||
pass
|
||||
|
||||
# get the conditions as sheerka conditions
|
||||
try:
|
||||
python_visitor = PythonConditionExprVisitor(context)
|
||||
python_conditions = python_visitor.get_conditions(parsed)
|
||||
@@ -1059,10 +1061,40 @@ class SheerkaRuleManager(BaseService):
|
||||
|
||||
|
||||
class GetConditionExprVisitor(ExpressionVisitor):
|
||||
"""
|
||||
Base class for ReteConditionExprVisitor and PythonConditionExprVisitor
|
||||
"""
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
self.var_counter = 0
|
||||
self.variables = {}
|
||||
self.obj_counter = 0
|
||||
self.objects_mapping = {}
|
||||
|
||||
def get_object_name(self, obj, objects=None):
|
||||
"""
|
||||
object found during the parsing are not serialized
|
||||
They are kept in a dictionary and this function returns a new name for every new object
|
||||
:return:
|
||||
"""
|
||||
if objects is None:
|
||||
objects = {}
|
||||
|
||||
if self.context.sheerka.is_sheerka(obj):
|
||||
return "sheerka", objects
|
||||
|
||||
try:
|
||||
return self.objects_mapping[id(obj)], objects
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
object_name = f"__o_{self.obj_counter:02}__"
|
||||
self.obj_counter += 1
|
||||
|
||||
self.objects_mapping[id(obj)] = object_name
|
||||
objects[object_name] = obj
|
||||
return object_name, objects
|
||||
|
||||
def add_variable(self, target):
|
||||
"""
|
||||
@@ -1078,7 +1110,7 @@ class GetConditionExprVisitor(ExpressionVisitor):
|
||||
def inner_unpack_variable(self, variable_path: List[str]) -> Tuple[str, str]:
|
||||
"""
|
||||
When variable_path = a.b.c.d
|
||||
returns (x0, d) if x0 = a.b.c else (a, b.c.d)
|
||||
returns (x0, d) if (exist x0 = a.b.c) else (a, b.c.d)
|
||||
:param variable_path:
|
||||
:return:
|
||||
"""
|
||||
@@ -1108,24 +1140,47 @@ class GetConditionExprVisitor(ExpressionVisitor):
|
||||
return self.variables[path]
|
||||
|
||||
root, attr = self.inner_unpack_variable(variable_path)
|
||||
var_name = self.add_variable(root + "." + attr)
|
||||
return var_name, (root, attr)
|
||||
if attr:
|
||||
var_name = self.add_variable(root + "." + attr)
|
||||
return var_name, (root, attr)
|
||||
else:
|
||||
return root, (root, None)
|
||||
|
||||
def evaluate(self, source, eval_body):
|
||||
res = evaluate(self.context,
|
||||
source,
|
||||
evaluators=CONDITIONS_VISITOR_EVALUATORS,
|
||||
desc=None,
|
||||
eval_body=eval_body,
|
||||
eval_where=False,
|
||||
is_question=False,
|
||||
expect_success=False,
|
||||
stm=None)
|
||||
def evaluate_from_source(self, source, is_question=False, return_body=False):
|
||||
res = evaluate_from_source(self.context,
|
||||
source,
|
||||
evaluators=CONDITIONS_VISITOR_EVALUATORS,
|
||||
desc=None,
|
||||
eval_body=not is_question,
|
||||
eval_where=False,
|
||||
is_question=is_question,
|
||||
expect_success=False,
|
||||
stm=None)
|
||||
res = expect_one(self.context, res)
|
||||
if not res.status:
|
||||
raise FailedToCompileError([f"Failed to evaluate '{source}'"])
|
||||
|
||||
return res.value
|
||||
if return_body:
|
||||
if not res.status:
|
||||
raise FailedToCompileError(res.body)
|
||||
|
||||
return res.body
|
||||
else:
|
||||
return res
|
||||
|
||||
def is_a_possible_variable(self, name):
|
||||
"""
|
||||
tells whether or not the name can be a variable
|
||||
:param name:
|
||||
:return:
|
||||
"""
|
||||
if self.context.sheerka.is_a_concept_name(name):
|
||||
return False
|
||||
|
||||
try:
|
||||
eval(name, sheerka_globals)
|
||||
except:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class ReteConditionExprVisitor(GetConditionExprVisitor):
|
||||
@@ -1162,29 +1217,38 @@ class ReteConditionExprVisitor(GetConditionExprVisitor):
|
||||
def init_or_get_variable_from_attr(self, variable_path: List[str], conditions):
|
||||
path = ".".join(variable_path)
|
||||
if path in self.variables:
|
||||
return self.variables[path]
|
||||
return V(self.variables[path])
|
||||
|
||||
root, attr = self.init_or_get_variable_from_name(variable_path, conditions)
|
||||
var_name = self.add_variable(path)
|
||||
variable = V(var_name)
|
||||
conditions.append(Condition(root, attr, variable))
|
||||
return variable
|
||||
if attr:
|
||||
var_name = self.add_variable(path)
|
||||
variable = V(var_name)
|
||||
conditions.append(Condition(root, attr, variable))
|
||||
return variable
|
||||
else:
|
||||
return root
|
||||
|
||||
def visit_VariableNode(self, expr_node: VariableNode):
|
||||
if expr_node.attributes_str is None:
|
||||
# try to recognize a concept
|
||||
res = evaluate(self.context,
|
||||
expr_node.name,
|
||||
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 res.status and isinstance(res.value, Concept):
|
||||
return self.recognize_concept(["__ret", "body"], res.value, {})
|
||||
res = self.evaluate_from_source(expr_node.name, is_question=True)
|
||||
if self.context.sheerka.has_error(self.context, res, __type=BuiltinConcepts.TOO_MANY_SUCCESS):
|
||||
raise FailedToCompileError([res])
|
||||
|
||||
# if expr_node.attributes_str is None:
|
||||
# # try to recognize a concept
|
||||
# res = evaluate_from_source(self.context,
|
||||
# expr_node.name,
|
||||
# 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 res.status and isinstance(res.value, Concept):
|
||||
# return self.recognize_concept(["__ret", "body"], res.value, {})
|
||||
|
||||
conditions = []
|
||||
variable_name = expr_node.get_source()
|
||||
@@ -1204,7 +1268,7 @@ class ReteConditionExprVisitor(GetConditionExprVisitor):
|
||||
def visit_ComparisonNode(self, expr_node: ComparisonNode):
|
||||
if isinstance(expr_node.left, VariableNode):
|
||||
conditions = []
|
||||
value = self.evaluate(expr_node.right.get_source(), True)
|
||||
value = self.evaluate_from_source(expr_node.right.get_source(), return_body=True)
|
||||
self.add_to_condition(expr_node.left.unpack(), value, conditions)
|
||||
return conditions
|
||||
else:
|
||||
@@ -1268,15 +1332,15 @@ class ReteConditionExprVisitor(GetConditionExprVisitor):
|
||||
return negate_conditions(exists_condition, sub_conditions)
|
||||
|
||||
def visit_NameExprNode(self, expr_node: NameExprNode):
|
||||
res = evaluate(self.context,
|
||||
expr_node.get_source(),
|
||||
evaluators=CONDITIONS_VISITOR_EVALUATORS,
|
||||
desc=None,
|
||||
eval_body=True,
|
||||
eval_where=False,
|
||||
is_question=False,
|
||||
expect_success=False,
|
||||
stm=None)
|
||||
res = evaluate_from_source(self.context,
|
||||
expr_node.get_source(),
|
||||
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 res.status and isinstance(res.value, Concept):
|
||||
return self.recognize_concept(["__ret", "body"], res.value, {})
|
||||
@@ -1295,7 +1359,7 @@ class ReteConditionExprVisitor(GetConditionExprVisitor):
|
||||
if not concept_as_str:
|
||||
return FailedToCompileError([f"Missing concept in for {variable_path}"])
|
||||
|
||||
concept = self.evaluate(concept_as_str, True)
|
||||
concept = self.evaluate_from_source(concept_as_str, return_body=True)
|
||||
else:
|
||||
concept = concept_to_recognize
|
||||
|
||||
@@ -1341,9 +1405,35 @@ class PythonConditionExprVisitorObj:
|
||||
not_variables: set
|
||||
|
||||
@staticmethod
|
||||
def combine_with_and(left, right):
|
||||
def create_function(first, last, parameters):
|
||||
|
||||
def create_and(a, b):
|
||||
def get_function_as_text(parameter):
|
||||
if parameter is None:
|
||||
return f"{first}{last}"
|
||||
|
||||
else:
|
||||
return f"{first}{parameter}{last}"
|
||||
|
||||
if parameters is None:
|
||||
source = get_function_as_text(None)
|
||||
return PythonConditionExprVisitorObj(source, source, {}, set(), set())
|
||||
|
||||
parameters_as_list = parameters if isinstance(parameters, list) else [parameters]
|
||||
|
||||
res = []
|
||||
for obj in parameters_as_list:
|
||||
res.append(PythonConditionExprVisitorObj(get_function_as_text(obj.text),
|
||||
get_function_as_text(obj.source),
|
||||
obj.objects,
|
||||
obj.variables,
|
||||
obj.not_variables))
|
||||
|
||||
return res[0] if len(res) == 1 else res
|
||||
|
||||
@staticmethod
|
||||
def create_and(left, right):
|
||||
|
||||
def get_source(a, b):
|
||||
if a is None and b is None:
|
||||
return None
|
||||
|
||||
@@ -1351,38 +1441,47 @@ class PythonConditionExprVisitorObj:
|
||||
return b
|
||||
if b is None or b == "":
|
||||
return a
|
||||
return a + " and " + b
|
||||
return a + " and " + b # no need to protect with parenthesis
|
||||
|
||||
left_as_list = left if isinstance(left, list) else [left]
|
||||
right_as_list = right if isinstance(right, list) else [right]
|
||||
|
||||
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),
|
||||
merge_sets(left_obj.not_variables, right_obj.not_variables)))
|
||||
|
||||
return res[0] if len(res) == 1 else res
|
||||
return PythonConditionExprVisitorObj(get_source(left.text, right.text),
|
||||
get_source(left.source, right.source),
|
||||
merge_dictionaries(left.objects, right.objects),
|
||||
merge_sets(left.variables, right.variables),
|
||||
merge_sets(left.not_variables, right.not_variables))
|
||||
|
||||
@staticmethod
|
||||
def combine_with_not(node):
|
||||
def combine_with_comma(left, right):
|
||||
|
||||
def create_not(a):
|
||||
def get_source(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 + ", " + b # no need to protect with parenthesis
|
||||
|
||||
if left is None:
|
||||
return right
|
||||
|
||||
return PythonConditionExprVisitorObj(get_source(left.text, right.text),
|
||||
get_source(left.source, right.source),
|
||||
merge_dictionaries(left.objects, right.objects),
|
||||
merge_sets(left.variables, right.variables),
|
||||
merge_sets(left.not_variables, right.not_variables))
|
||||
|
||||
@staticmethod
|
||||
def create_not(node):
|
||||
|
||||
def get_source(a):
|
||||
return f"not ({a})"
|
||||
|
||||
node_as_list = node if isinstance(node, list) else [node]
|
||||
res = []
|
||||
for obj in node_as_list:
|
||||
res.append(PythonConditionExprVisitorObj(create_not(obj.text),
|
||||
create_not(obj.source),
|
||||
obj.objects,
|
||||
obj.variables,
|
||||
obj.not_variables))
|
||||
|
||||
return res[0] if len(res) == 1 else res
|
||||
return PythonConditionExprVisitorObj(get_source(node.text),
|
||||
get_source(node.source),
|
||||
node.objects,
|
||||
node.variables,
|
||||
node.not_variables)
|
||||
|
||||
|
||||
class PythonConditionExprVisitor(GetConditionExprVisitor):
|
||||
@@ -1390,13 +1489,24 @@ class PythonConditionExprVisitor(GetConditionExprVisitor):
|
||||
def __init__(self, context):
|
||||
super().__init__(context)
|
||||
self.know_object_variables = {}
|
||||
self.check_variable_existence_only = True
|
||||
self.concepts_to_reset = set()
|
||||
|
||||
def get_conditions(self, expr_node):
|
||||
self.check_variable_existence_only = True
|
||||
self.var_counter = 0
|
||||
self.variables.clear()
|
||||
self.concepts_to_reset.clear()
|
||||
|
||||
visitor_obj = self.visit(expr_node)
|
||||
if visitor_obj.source:
|
||||
if self.check_variable_existence_only:
|
||||
return [CompiledCondition(None,
|
||||
None,
|
||||
visitor_obj.variables,
|
||||
visitor_obj.not_variables,
|
||||
visitor_obj.objects,
|
||||
self.concepts_to_reset)]
|
||||
else:
|
||||
if self.variables:
|
||||
variables_definitions = "\n".join([f"{v} = {k}" for k, v in self.variables.items()])
|
||||
source = variables_definitions + "\n" + visitor_obj.source
|
||||
@@ -1409,48 +1519,18 @@ class PythonConditionExprVisitor(GetConditionExprVisitor):
|
||||
if ret.status:
|
||||
ret.body.body.original_source = text
|
||||
ret.body.body.objects = visitor_obj.objects
|
||||
return [CompiledCondition(PythonEvaluator.NAME, ret, visitor_obj.variables, visitor_obj.not_variables)]
|
||||
return [CompiledCondition(PythonEvaluator.NAME,
|
||||
ret,
|
||||
visitor_obj.variables,
|
||||
visitor_obj.not_variables,
|
||||
visitor_obj.objects,
|
||||
self.concepts_to_reset)]
|
||||
|
||||
else:
|
||||
errors = ret.body.reason if self.context.sheerka.isinstance(ret.body, BuiltinConcepts.NOT_FOR_ME) \
|
||||
else ret.body.body
|
||||
raise FailedToCompileError(errors)
|
||||
|
||||
else:
|
||||
return [CompiledCondition(None, None, visitor_obj.variables, visitor_obj.not_variables)]
|
||||
|
||||
def get_variable(self, expr_node):
|
||||
"""
|
||||
From a ExprNode, try to know if it refers to a bag entry or it it's a python valid name
|
||||
:param expr_node:
|
||||
:return:
|
||||
"""
|
||||
if not isinstance(expr_node, VariableNode):
|
||||
return None
|
||||
|
||||
var_root = expr_node.name
|
||||
if var_root in self.know_object_variables:
|
||||
return self.know_object_variables[var_root]
|
||||
|
||||
if self.context.sheerka.fast_resolve(var_root):
|
||||
self.know_object_variables[var_root] = None
|
||||
return None
|
||||
|
||||
python_parser = PythonParser()
|
||||
ret = python_parser.parse(self.context, ParserInput(var_root))
|
||||
if not ret.status:
|
||||
self.know_object_variables[var_root] = var_root
|
||||
return var_root
|
||||
|
||||
python_evaluator = PythonEvaluator()
|
||||
ret = python_evaluator.eval(self.context, ret)
|
||||
if not ret.status:
|
||||
self.know_object_variables[var_root] = var_root
|
||||
return var_root
|
||||
|
||||
self.know_object_variables[var_root] = None
|
||||
return None
|
||||
|
||||
def get_new_variable(self, variable_path: List[str], obj_variables):
|
||||
obj_variables.add(variable_path[0])
|
||||
var_name, var_def = self.inner_get_new_variable(variable_path)
|
||||
@@ -1467,43 +1547,65 @@ class PythonConditionExprVisitor(GetConditionExprVisitor):
|
||||
return root + "." + attribute
|
||||
|
||||
def visit_VariableNode(self, expr_node: VariableNode):
|
||||
# try to reconize a concept
|
||||
if expr_node.attributes_str is None and not expr_node.name.startswith("__"):
|
||||
# try to recognize a concept
|
||||
res = evaluate(self.context,
|
||||
expr_node.name,
|
||||
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)
|
||||
res = self.evaluate_from_source(expr_node.name, is_question=True)
|
||||
if res.status and isinstance(res.value, Concept):
|
||||
return self.recognize_concept(["__ret", "body"], res.value, {})
|
||||
self.check_variable_existence_only = False
|
||||
if is_a_question(self.context, res.value):
|
||||
return self.evaluate_concept_as_question(expr_node.name, res.value)
|
||||
else:
|
||||
return self.evaluate_concept(expr_node.name, res.value)
|
||||
else:
|
||||
if self.context.sheerka.has_error(self.context, res, __type=BuiltinConcepts.TOO_MANY_SUCCESS):
|
||||
raise FailedToCompileError([res])
|
||||
|
||||
variable_name = expr_node.get_source()
|
||||
variables = {variable_name} if not res.status else set()
|
||||
return PythonConditionExprVisitorObj(variable_name, variable_name, {}, variables, set())
|
||||
|
||||
variable_name = expr_node.get_source()
|
||||
return PythonConditionExprVisitorObj(None, None, {}, {variable_name}, set())
|
||||
variables_detected = {variable_name} if self.is_a_possible_variable(variable_name) else set()
|
||||
return PythonConditionExprVisitorObj(variable_name, variable_name, {}, variables_detected, set())
|
||||
|
||||
def visit_ComparisonNode(self, expr_node: ComparisonNode):
|
||||
self.check_variable_existence_only = False
|
||||
|
||||
if not isinstance(expr_node.left, VariableNode):
|
||||
# KSI 2021-04-22. Not quite sure of the reason why I have this piece of code
|
||||
left = self.visit(expr_node.left)
|
||||
source = expr_node.get_source()
|
||||
return PythonConditionExprVisitorObj(source, source, {}, left.variables, left.not_variables)
|
||||
|
||||
value = self.evaluate(expr_node.right.get_source(), True)
|
||||
return self.create_comparison_condition(expr_node.left.unpack(), expr_node.comp, value)
|
||||
right = self.visit(expr_node.right)
|
||||
if right.source in right.objects:
|
||||
return self.create_comparison_condition(expr_node.left.unpack(),
|
||||
expr_node.comp,
|
||||
right.source,
|
||||
right.objects[right.source],
|
||||
right.objects,
|
||||
expr_node.get_source())
|
||||
else:
|
||||
value = self.evaluate_from_source(expr_node.right.get_source(), return_body=True)
|
||||
object_name, objects = self.get_object_name(value)
|
||||
return self.create_comparison_condition(expr_node.left.unpack(),
|
||||
expr_node.comp,
|
||||
object_name,
|
||||
value,
|
||||
objects,
|
||||
expr_node.get_source())
|
||||
|
||||
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)
|
||||
current_visitor_obj = PythonConditionExprVisitorObj.create_and(current_visitor_obj, visitor_obj)
|
||||
|
||||
return current_visitor_obj
|
||||
|
||||
def visit_FunctionNode(self, expr_node: FunctionNode):
|
||||
self.check_variable_existence_only = False
|
||||
|
||||
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}'"])
|
||||
@@ -1512,46 +1614,48 @@ class PythonConditionExprVisitor(GetConditionExprVisitor):
|
||||
expr_node.parameters[1].value,
|
||||
{})
|
||||
else:
|
||||
source = expr_node.get_source()
|
||||
obj_variables = set()
|
||||
params_as_visitor_obj = None
|
||||
for param in expr_node.parameters:
|
||||
if (variable := self.get_variable(param.value)) is not None:
|
||||
obj_variables.add(variable)
|
||||
return PythonConditionExprVisitorObj(source, source, {}, obj_variables, set())
|
||||
current_visitor_obj = self.visit(param.value)
|
||||
params_as_visitor_obj = PythonConditionExprVisitorObj.combine_with_comma(params_as_visitor_obj,
|
||||
current_visitor_obj)
|
||||
|
||||
return PythonConditionExprVisitorObj.create_function(expr_node.first, expr_node.last, params_as_visitor_obj)
|
||||
|
||||
def visit_NotNode(self, expr_node: NotNode):
|
||||
visitor_obj = self.visit(expr_node.node)
|
||||
if visitor_obj.source is None:
|
||||
return PythonConditionExprVisitorObj(None, None, {}, visitor_obj.not_variables, visitor_obj.variables)
|
||||
if self.check_variable_existence_only:
|
||||
return PythonConditionExprVisitorObj(visitor_obj.text,
|
||||
visitor_obj.source,
|
||||
visitor_obj.objects,
|
||||
visitor_obj.not_variables,
|
||||
visitor_obj.variables)
|
||||
|
||||
return PythonConditionExprVisitorObj.combine_with_not(visitor_obj)
|
||||
return PythonConditionExprVisitorObj.create_not(visitor_obj)
|
||||
|
||||
def visit_NameExprNode(self, expr_node: NameExprNode):
|
||||
res = evaluate(self.context,
|
||||
expr_node.get_source(),
|
||||
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 res.status and isinstance(res.value, Concept):
|
||||
return self.recognize_concept(["__ret", "body"], res.value, {})
|
||||
self.check_variable_existence_only = False
|
||||
source = expr_node.get_source()
|
||||
res = self.evaluate_from_source(source, is_question=False)
|
||||
if res.status:
|
||||
obj_name, objects = self.get_object_name(res.value)
|
||||
return PythonConditionExprVisitorObj(source, obj_name, objects, set(), set())
|
||||
else:
|
||||
raise FailedToCompileError([expr_node])
|
||||
|
||||
def recognize_concept(self, variable_path, concept_to_recognize, concept_variables: dict):
|
||||
def recognize_concept(self, variable_path, concept_to_recognize, concept_variables: dict, original_source=None):
|
||||
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}"])
|
||||
|
||||
concept = self.evaluate(concept_as_str, True)
|
||||
concept = self.evaluate_from_source(concept_as_str, return_body=True)
|
||||
else:
|
||||
concept = concept_to_recognize
|
||||
|
||||
obj_variables = set()
|
||||
var_name = self.get_new_variable(variable_path, obj_variables)
|
||||
objects = {}
|
||||
|
||||
source = f"isinstance({var_name}, Concept)"
|
||||
|
||||
@@ -1563,33 +1667,51 @@ class PythonConditionExprVisitor(GetConditionExprVisitor):
|
||||
source += f" and {var_name}.key == '{concept.key}'"
|
||||
concept_variables.update({k: v for k, v in concept.variables().items() if v is not NotInit})
|
||||
|
||||
text = source
|
||||
for var_name, var_value in concept_variables.items():
|
||||
new_var_path = variable_path.copy()
|
||||
new_var_path.append(var_name)
|
||||
variable_condition = self.create_comparison_condition(new_var_path, ComparisonType.EQUALS, var_value)
|
||||
obj_name, objects = self.get_object_name(var_value, objects)
|
||||
variable_condition = self.create_comparison_condition(new_var_path,
|
||||
ComparisonType.EQUALS,
|
||||
obj_name,
|
||||
var_value,
|
||||
objects)
|
||||
source += " and " + variable_condition.source
|
||||
obj_variables.update(variable_condition.objects)
|
||||
text += " and " + variable_condition.text
|
||||
|
||||
return PythonConditionExprVisitorObj(source, source, {}, obj_variables, set())
|
||||
return PythonConditionExprVisitorObj(original_source or text, source, objects, obj_variables, set())
|
||||
|
||||
def evaluate_concept_as_question(self, original_text, concept):
|
||||
concept_var_name, objects = self.get_object_name(concept)
|
||||
source = f"evaluate_question({concept_var_name})"
|
||||
variables = get_possible_variables_from_concept(self.context, concept)
|
||||
self.concepts_to_reset.add(concept)
|
||||
return PythonConditionExprVisitorObj(original_text, source, objects, variables, set())
|
||||
|
||||
def evaluate_concept(self, original_text, concept):
|
||||
ensure_evaluated(self.context, concept, eval_body=True)
|
||||
source, objects = self.get_object_name(concept)
|
||||
return PythonConditionExprVisitorObj(original_text, source, objects, set(), set())
|
||||
|
||||
def create_comparison_condition(self, left_path, op, right_name, right_value, objects, original_source=None):
|
||||
possible_variables = set()
|
||||
var_root, var_attr = self.unpack_variable(left_path, possible_variables)
|
||||
left = self.construct_variable(var_root, var_attr)
|
||||
|
||||
if original_source is None:
|
||||
right = get_safe_str_value(right_value)
|
||||
original_source = ComparisonNode.rebuild_source(left, op, right)
|
||||
|
||||
def create_comparison_condition(self, var_path, op, value):
|
||||
obj_variables = set()
|
||||
if op == ComparisonType.EQUALS:
|
||||
if self.context.sheerka.is_sheerka(value):
|
||||
var_root, var_attr = self.unpack_variable(var_path, obj_variables)
|
||||
source = f"is_sheerka({self.construct_variable(var_root, var_attr)})"
|
||||
return PythonConditionExprVisitorObj(source, source, {}, obj_variables, set())
|
||||
if isinstance(value, Concept):
|
||||
return self.recognize_concept(var_path, value, {})
|
||||
if self.context.sheerka.is_sheerka(right_value):
|
||||
source = f"is_sheerka({left})"
|
||||
return PythonConditionExprVisitorObj(original_source, source, objects, possible_variables, set())
|
||||
if isinstance(right_value, Concept):
|
||||
return self.recognize_concept(left_path, right_value, {}, original_source)
|
||||
else:
|
||||
if isinstance(value, str):
|
||||
value = "'" + value + "'"
|
||||
var_root, var_attr = self.unpack_variable(var_path, obj_variables)
|
||||
source = ComparisonNode.rebuild_source(self.construct_variable(var_root, var_attr), op, value)
|
||||
return PythonConditionExprVisitorObj(source, source, {}, obj_variables, set())
|
||||
source = ComparisonNode.rebuild_source(left, op, right_name)
|
||||
return PythonConditionExprVisitorObj(original_source, source, objects, possible_variables, set())
|
||||
else:
|
||||
if isinstance(value, str):
|
||||
value = "'" + value + "'"
|
||||
var_root, var_attr = self.unpack_variable(var_path, obj_variables)
|
||||
source = ComparisonNode.rebuild_source(self.construct_variable(var_root, var_attr), op, value)
|
||||
return PythonConditionExprVisitorObj(source, source, {}, obj_variables, set())
|
||||
source = ComparisonNode.rebuild_source(left, op, right_name)
|
||||
return PythonConditionExprVisitorObj(original_source, source, objects, possible_variables, set())
|
||||
|
||||
@@ -94,7 +94,6 @@ class SheerkaVariableManager(BaseService):
|
||||
if who in self.bound_variables and key in self.bound_variables[who]:
|
||||
service = self.sheerka if who == self.sheerka.name else self.sheerka.services[who]
|
||||
setattr(service, key, value)
|
||||
print(f"{service=} is set")
|
||||
|
||||
def load_var(self, who, key):
|
||||
variable = self.sheerka.om.get(self.VARIABLES_ENTRY, who + "|" + key)
|
||||
|
||||
Reference in New Issue
Block a user