Added first version of DebugManager. Implemented draft of the rule engine
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.builtin_helpers import expect_one, only_successful, parse_unrecognized, evaluate
|
||||
from core.concept import Concept, DoNotResolve, ConceptParts, InfiniteRecursionResolved, NotInit, ensure_concept
|
||||
from core.builtin_helpers import expect_one, only_successful, parse_unrecognized, evaluate, ensure_concept
|
||||
from core.concept import Concept, DoNotResolve, ConceptParts, InfiniteRecursionResolved, NotInit, AllConceptParts, \
|
||||
concept_part_value
|
||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||
from core.sheerka.services.sheerka_service import BaseService
|
||||
from core.tokenizer import Tokenizer
|
||||
@@ -47,8 +48,10 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
|
||||
parent = context.get_parent()
|
||||
while parent is not None:
|
||||
if parent.who == context.who and parent.action == BuiltinConcepts.EVALUATING_CONCEPT and \
|
||||
parent.obj == concept and parent.obj.compiled == concept.compiled:
|
||||
if (parent.who == context.who and
|
||||
parent.action == BuiltinConcepts.EVALUATING_CONCEPT and
|
||||
parent.obj == concept and
|
||||
parent.obj.get_compiled() == concept.get_compiled()):
|
||||
return True
|
||||
|
||||
parent = parent.get_parent()
|
||||
@@ -90,15 +93,15 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
vars_needed = False
|
||||
body_needed = False
|
||||
|
||||
if concept_part in concept.compiled and concept.compiled[concept_part] is not None:
|
||||
concept_part_source = getattr(concept.metadata, concept_part.value)
|
||||
if concept_part in concept.get_compiled() and concept.get_compiled()[concept_part] is not None:
|
||||
concept_part_source = getattr(concept.get_metadata(), concept_part_value(concept_part))
|
||||
|
||||
assert concept_part_source is not None
|
||||
|
||||
tokens = [t.str_value for t in Tokenizer(concept_part_source)]
|
||||
|
||||
if check_vars:
|
||||
for var_name in (v[0] for v in concept.metadata.variables):
|
||||
for var_name in (v[0] for v in concept.get_metadata().variables):
|
||||
if var_name in tokens:
|
||||
vars_needed = True
|
||||
ret.append("variables")
|
||||
@@ -106,9 +109,9 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
|
||||
if check_body and "self" in tokens:
|
||||
body_needed = True
|
||||
ret.append("body")
|
||||
ret.append(ConceptParts.BODY)
|
||||
|
||||
ret.append(concept_part.value)
|
||||
ret.append(concept_part)
|
||||
|
||||
return ret, vars_needed, body_needed
|
||||
|
||||
@@ -121,16 +124,16 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
:param var_name:
|
||||
:return:
|
||||
"""
|
||||
if concept.metadata.where is None or concept.metadata.where.strip() == "":
|
||||
if concept.get_metadata().where is None or concept.get_metadata().where.strip() == "":
|
||||
return None
|
||||
|
||||
ret = ExpressionParser().parse(context, ParserInput(concept.metadata.where))
|
||||
ret = ExpressionParser().parse(context, ParserInput(concept.get_metadata().where))
|
||||
if not ret.status:
|
||||
# TODO: manage invalid where clause
|
||||
return None
|
||||
expr = ret.body.body
|
||||
|
||||
to_trueify = [v[0] for v in concept.metadata.variables if v[0] != var_name]
|
||||
to_trueify = [v[0] for v in concept.get_metadata().variables if v[0] != var_name]
|
||||
trueified_where = str(TrueifyVisitor(to_trueify, [var_name]).visit(expr))
|
||||
|
||||
tokens = [t.str_value for t in Tokenizer(trueified_where)]
|
||||
@@ -140,7 +143,7 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
compiled = compile(trueified_where, "<where clause>", "eval")
|
||||
except Exception:
|
||||
pass
|
||||
return WhereClauseDef(concept, concept.metadata.where, trueified_where, var_name, compiled)
|
||||
return WhereClauseDef(concept, concept.get_metadata().where, trueified_where, var_name, compiled)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -153,7 +156,8 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
:return:
|
||||
"""
|
||||
ret = []
|
||||
for r in [r for r in return_values if r.status]:
|
||||
valid_return_values = [r for r in return_values if r.status]
|
||||
for r in valid_return_values:
|
||||
if where_clause_def.compiled:
|
||||
try:
|
||||
if eval(where_clause_def.compiled, {where_clause_def.prop: self.sheerka.objvalue(r)}):
|
||||
@@ -177,10 +181,13 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
if len(ret) > 0:
|
||||
return ret
|
||||
|
||||
reason = [r.body for r in return_values] if len(valid_return_values) == 0 else None
|
||||
|
||||
return self.sheerka.new(BuiltinConcepts.CONDITION_FAILED,
|
||||
body=where_clause_def.clause,
|
||||
concept=where_clause_def.concept,
|
||||
prop=where_clause_def.prop)
|
||||
prop=where_clause_def.prop,
|
||||
reason=reason)
|
||||
|
||||
def manage_infinite_recursion(self, context):
|
||||
"""
|
||||
@@ -194,7 +201,7 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
concepts_found = set()
|
||||
while parent and parent.obj:
|
||||
if parent.who == context.who and parent.action == BuiltinConcepts.EVALUATING_CONCEPT:
|
||||
body = parent.obj.metadata.body
|
||||
body = parent.obj.get_metadata().body
|
||||
try:
|
||||
return self.sheerka.ret(self.NAME, True, InfiniteRecursionResolved(eval(body)))
|
||||
except Exception:
|
||||
@@ -226,11 +233,11 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
return self.sheerka.resolve(identifier)
|
||||
return None
|
||||
|
||||
for part_key in ConceptParts:
|
||||
if part_key in concept.compiled:
|
||||
for part_key in AllConceptParts:
|
||||
if part_key in concept.get_compiled():
|
||||
continue
|
||||
|
||||
source = getattr(concept.metadata, part_key.value)
|
||||
source = getattr(concept.get_metadata(), concept_part_value(part_key))
|
||||
if source is None: # or not isinstance(source, str):
|
||||
continue
|
||||
|
||||
@@ -238,22 +245,22 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
raise Exception("Invalid concept init. metadata must be a string")
|
||||
|
||||
if source.strip() == "":
|
||||
concept.compiled[part_key] = DoNotResolve(source)
|
||||
concept.get_compiled()[part_key] = DoNotResolve(source)
|
||||
else:
|
||||
# first case, when the metadata references another concept via c:xxx: keyword
|
||||
if concept_found := parse_token_concept(source):
|
||||
context.log(f"Recognized concept '{concept_found}'", self.NAME)
|
||||
concept.compiled[part_key] = concept_found
|
||||
concept.get_compiled()[part_key] = concept_found
|
||||
else:
|
||||
res = parse_unrecognized(context,
|
||||
source,
|
||||
parsers="all",
|
||||
prop=part_key,
|
||||
filter_func=only_successful)
|
||||
concept.compiled[part_key] = res.body.body if is_only_successful(res) else res
|
||||
concept.get_compiled()[part_key] = res.body.body if is_only_successful(res) else res
|
||||
|
||||
for var_name, default_value in concept.metadata.variables:
|
||||
if var_name in concept.compiled:
|
||||
for var_name, default_value in concept.get_metadata().variables:
|
||||
if var_name in concept.get_compiled():
|
||||
continue
|
||||
|
||||
if default_value is None:
|
||||
@@ -263,23 +270,23 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
raise Exception("Invalid concept init. variable metadata must be a string")
|
||||
|
||||
if default_value.strip() == "":
|
||||
concept.compiled[var_name] = DoNotResolve(default_value)
|
||||
concept.get_compiled()[var_name] = DoNotResolve(default_value)
|
||||
else:
|
||||
# first case, when the metadata references another concept via c:xxx: keyword
|
||||
if concept_found := parse_token_concept(default_value):
|
||||
context.log(f"Recognized concept '{concept_found}'", self.NAME)
|
||||
concept.compiled[var_name] = concept_found
|
||||
concept.get_compiled()[var_name] = concept_found
|
||||
else:
|
||||
res = parse_unrecognized(context,
|
||||
default_value,
|
||||
parsers="all",
|
||||
prop=var_name,
|
||||
filter_func=only_successful)
|
||||
concept.compiled[var_name] = res.body.body if is_only_successful(res) else res
|
||||
concept.get_compiled()[var_name] = res.body.body if is_only_successful(res) else res
|
||||
|
||||
# Updates the cache of concepts when possible
|
||||
if self.sheerka.has_id(concept.id):
|
||||
self.sheerka.get_by_id(concept.id).compiled = concept.compiled
|
||||
self.sheerka.get_by_id(concept.id).set_compiled(concept.get_compiled())
|
||||
|
||||
def resolve(self,
|
||||
context,
|
||||
@@ -325,8 +332,8 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
with context.push(BuiltinConcepts.EVALUATING_ATTRIBUTE,
|
||||
current_prop,
|
||||
desc=desc,
|
||||
obj=current_concept,
|
||||
path=path) as sub_context:
|
||||
obj=current_concept) as sub_context:
|
||||
sub_context.add_inputs(path=path)
|
||||
|
||||
if force_evaluation:
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||
@@ -352,7 +359,7 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
else:
|
||||
# update short term memory with current concept variables
|
||||
if current_concept:
|
||||
for var in current_concept.metadata.variables:
|
||||
for var in current_concept.get_metadata().variables:
|
||||
value = current_concept.get_value(var[0])
|
||||
if value != NotInit:
|
||||
sub_context.add_to_short_term_memory(var[0], current_concept.get_value(var[0]))
|
||||
@@ -433,23 +440,23 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
:return: value of the evaluation or error
|
||||
"""
|
||||
|
||||
if concept.metadata.is_evaluated:
|
||||
if concept.get_metadata().is_evaluated:
|
||||
return concept
|
||||
|
||||
# I cannot use cache because of concept like 'number'.
|
||||
# They don't have variables, but their values change every time they are instantiated
|
||||
# TODO: Need to find a way to cache despite of them
|
||||
# need_body = eval_body or context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||
# if need_body and len(concept.metadata.variables) == 0 and context.sheerka.has_id(concept.id):
|
||||
# if need_body and len(concept.get_metadata().variables) == 0 and context.sheerka.has_id(concept.id):
|
||||
# from_cache = context.sheerka.get_by_id(concept.id)
|
||||
# if from_cache.metadata.is_evaluated:
|
||||
# if from_cache.get_metadata().is_evaluated:
|
||||
# concept.set_value(ConceptParts.BODY, from_cache.body)
|
||||
# concept.metadata.is_evaluated = True
|
||||
# concept.get_metadata().is_evaluated = True
|
||||
# return concept
|
||||
|
||||
desc = f"Evaluating concept {concept}"
|
||||
with context.push(BuiltinConcepts.EVALUATING_CONCEPT, concept, desc=desc, eval_body=eval_body) as sub_context:
|
||||
|
||||
with context.push(BuiltinConcepts.EVALUATING_CONCEPT, concept, desc=desc) as sub_context:
|
||||
sub_context.add_inputs(eval_body=eval_body)
|
||||
if eval_body:
|
||||
# ask for body evaluation
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||
@@ -466,8 +473,8 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
|
||||
for metadata_to_eval in all_metadata_to_eval:
|
||||
if metadata_to_eval == "variables":
|
||||
for var_name in (v for v in concept.variables() if v in concept.compiled):
|
||||
prop_ast = concept.compiled[var_name]
|
||||
for var_name in (v for v in concept.variables() if v in concept.get_compiled()):
|
||||
prop_ast = concept.get_compiled()[var_name]
|
||||
|
||||
w_clause = self.get_where_clause_def(context, concept, var_name)
|
||||
# TODO, manage when the where clause cannot be parsed
|
||||
@@ -485,17 +492,17 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
else:
|
||||
concept.set_value(var_name, resolved)
|
||||
else:
|
||||
part_key = ConceptParts(metadata_to_eval)
|
||||
part_key = metadata_to_eval
|
||||
|
||||
# do not evaluate where when the body is a set
|
||||
# Indeed, the way that the where clause is expressed is not a valid python or concept code
|
||||
if part_key == ConceptParts.WHERE and self.sheerka.isaset(sub_context, concept.body):
|
||||
continue
|
||||
|
||||
if part_key not in concept.compiled or concept.compiled[part_key] is None:
|
||||
if part_key not in concept.get_compiled() or concept.get_compiled()[part_key] is None:
|
||||
continue
|
||||
|
||||
metadata_ast = concept.compiled[part_key]
|
||||
metadata_ast = concept.get_compiled()[part_key]
|
||||
|
||||
# if part_key is PRE, POST or WHERE, the concept need to be evaluated
|
||||
# if we want the predicates to be resolved => so force_eval = True
|
||||
@@ -514,7 +521,7 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
# validate PRE and WHERE condition
|
||||
if part_key in (ConceptParts.PRE, ConceptParts.WHERE) and not self.sheerka.objvalue(resolved):
|
||||
return self.sheerka.new(BuiltinConcepts.CONDITION_FAILED,
|
||||
body=getattr(concept.metadata, metadata_to_eval),
|
||||
body=getattr(concept.get_metadata(), concept_part_value(metadata_to_eval)),
|
||||
concept=concept,
|
||||
prop=part_key)
|
||||
|
||||
@@ -524,19 +531,19 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
|
||||
concept.init_key() # Necessary for old unit tests. To remove someday
|
||||
|
||||
if "body" in all_metadata_to_eval:
|
||||
concept.metadata.is_evaluated = True
|
||||
if ConceptParts.BODY in all_metadata_to_eval:
|
||||
concept.get_metadata().is_evaluated = True
|
||||
|
||||
# # update the cache for concepts with no variables
|
||||
# Cannot use cache. See the comment at the beginning of this method
|
||||
# if len(concept.metadata.variables) == 0:
|
||||
# if len(concept.get_metadata().variables) == 0:
|
||||
# self.sheerka.cache_manager.put(self.sheerka.CONCEPTS_BY_ID_ENTRY, concept.id, concept)
|
||||
|
||||
if not concept.metadata.is_builtin:
|
||||
if not concept.get_metadata().is_builtin:
|
||||
self.sheerka.register_object(sub_context, concept.name, concept)
|
||||
|
||||
# manage RET metadata
|
||||
if sub_context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED) and ConceptParts.RET in concept.values:
|
||||
if sub_context.in_context(BuiltinConcepts.EVAL_BODY_REQUESTED) and ConceptParts.RET in concept.values():
|
||||
return concept.get_value(ConceptParts.RET)
|
||||
else:
|
||||
return concept
|
||||
@@ -547,7 +554,7 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
needed, variables, body = self.get_needed_metadata(concept, ConceptParts.PRE, True, True)
|
||||
to_eval.extend(needed)
|
||||
|
||||
if context.in_context(BuiltinConcepts.EVAL_WHERE_REQUESTED) or concept.metadata.need_validation:
|
||||
if context.in_context(BuiltinConcepts.EVAL_WHERE_REQUESTED) or concept.get_metadata().need_validation:
|
||||
# What are the cases where we do not need a validation ?
|
||||
# see test_sheerka_non_reg::test_i_can_evaluate_bnf_concept_with_where_clause()
|
||||
# res = sheerka.evaluate_user_input("foobar")
|
||||
@@ -572,7 +579,7 @@ class SheerkaEvaluateConcept(BaseService):
|
||||
to_eval.append('variables')
|
||||
|
||||
if not body:
|
||||
to_eval.append("body")
|
||||
to_eval.append(ConceptParts.BODY)
|
||||
|
||||
return to_eval
|
||||
|
||||
|
||||
Reference in New Issue
Block a user