Files
Sheerka-Old/tests/core/test_SheerkaEvaluateConcept.py
T
kodjo 89e1f20975 Fixed #131 : Implement ExprToConditions
Fixed #130 : ArithmeticOperatorParser
Fixed #129 : python_wrapper : create_namespace
Fixed #128 : ExpressionParser: Cannot parse func(x) infixed concept 'xxx'
2021-10-13 16:06:57 +02:00

1128 lines
50 KiB
Python

import pytest
from core.builtin_concepts import BuiltinConcepts, ParserResultConcept, ReturnValueConcept
from core.concept import Concept, ConceptParts, DEFINITION_TYPE_DEF, DoNotResolve, InfiniteRecursionResolved
from core.global_symbols import NotFound, NotInit
from core.sheerka.services.SheerkaEvaluateConcept import EvaluationHints, SheerkaEvaluateConcept
from core.sheerka.services.SheerkaExecute import ParserInput
from core.sheerka.services.SheerkaMemory import SheerkaMemory
from parsers.BaseParser import BaseParser
from parsers.ExactConceptParser import ExactConceptParser
from parsers.ExpressionParser import ExpressionParser
from parsers.PythonParser import PythonNode, PythonParser
from parsers.SyaNodeParser import SyaNodeParser
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.evaluators.EvaluatorTestsUtils import exact, pr_ret_val, python_ret_val
from tests.parsers.parsers_utils import CB, compare_with_test_object
class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("body, expected", [
(None, NotInit),
("", ""),
("1", 1),
("1+1", 2),
("'one'", "one"),
("'one' + 'two'", "onetwo"),
("True", True),
("1 > 2", False),
])
def test_i_can_evaluate_a_concept_with_simple_body(self, body, expected):
sheerka, context, concept = self.init_test(eval_body=True).with_concepts(Concept("foo", body=body)).unpack()
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.body == expected
assert evaluated.get_metadata().body == body
assert evaluated.get_metadata().pre is None
assert evaluated.get_metadata().post is None
assert evaluated.get_metadata().where is None
assert evaluated.variables() == {}
assert evaluated.get_hints().is_evaluated
assert len(evaluated.values()) == 0 if body is None else 1
assert "foo" in sheerka.services[SheerkaMemory.NAME].registration
@pytest.mark.parametrize("expr, expected", [
("", ""),
("1", 1),
("1+1", 2),
("'one'", "one"),
("'one' + 'two'", "onetwo"),
("True", True),
("1 > 2", False),
])
def test_i_can_evaluate_the_other_metadata(self, expr, expected):
"""
I only test PRE, it's the same for the others
:param expr:
:param expected:
:return:
"""
sheerka, context, concept = self.init_concepts(Concept("foo", post=expr))
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.get_metadata().body is None
assert evaluated.get_metadata().post == expr
assert evaluated.get_metadata().pre is None
assert evaluated.get_metadata().where is None
assert evaluated.get_value(ConceptParts.POST) == expected
assert evaluated.variables() == {}
assert not evaluated.get_hints().is_evaluated
assert len(evaluated.values) == 0 if expr is None else 1
@pytest.mark.parametrize("expr, expected", [
(None, NotInit),
("", ""),
("1", 1),
("1+1", 2),
("'one'", "one"),
("'one' + 'two'", "onetwo"),
("True", True),
("1 > 2", False),
])
def test_i_can_evaluate_a_concept_with_variable(self, expr, expected):
sheerka, context, concept = self.init_concepts(Concept("foo").def_var("a", expr), eval_body=True)
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.get_metadata().pre is None
assert evaluated.get_metadata().pre is None
assert evaluated.get_metadata().post is None
assert evaluated.get_metadata().where is None
assert evaluated.variables() == {"a": expected}
assert evaluated.get_hints().is_evaluated
def test_i_can_evaluate_when_the_body_is_the_name_of_the_concept(self):
# to prove that I can distinguish from a string
sheerka, context, concept = self.init_test(eval_body=True).with_concepts(
Concept("foo", body="'foo'"),
create_new=True).unpack()
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.body == "foo"
def test_i_can_evaluate_metadata_using_do_not_resolve(self):
sheerka, context, concept = self.init_concepts(Concept("foo"), eval_body=True)
concept.get_compiled()[ConceptParts.BODY] = DoNotResolve("do not resolve")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.body == "do not resolve"
assert evaluated.get_hints().is_evaluated
def test_i_can_evaluate_variable_using_do_not_resolve(self):
sheerka, context, concept = self.init_concepts(Concept("foo").def_var("a"), eval_body=True)
concept.get_compiled()["a"] = DoNotResolve("do not resolve")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.get_value("a") == "do not resolve"
assert evaluated.get_hints().is_evaluated
def test_original_value_is_overridden_when_using_do_no_resolve(self):
concept = Concept("foo", body="original value").def_var("a", "original value")
sheerka, context, concept = self.init_concepts(concept, eval_body=True)
concept.get_compiled()["a"] = DoNotResolve("do not resolve")
concept.get_compiled()[ConceptParts.BODY] = DoNotResolve("do not resolve")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.body == "do not resolve"
assert evaluated.get_value("a") == "do not resolve"
assert evaluated.get_hints().is_evaluated
def test_variables_are_evaluated_before_body(self):
sheerka, context, concept = self.init_concepts(Concept("foo", body="a+1").def_var("a", "10"), eval_body=True)
evaluated = sheerka.evaluate_concept(context, concept)
compare_with_test_object(evaluated, CB(concept, 11))
def test_i_can_evaluate_when_another_concept_is_referenced(self):
sheerka, context, concept_a, concept = self.init_concepts(
Concept("a"),
Concept("foo", body="a"),
eval_body=True)
evaluated = sheerka.evaluate_concept(context, concept)
compare_with_test_object(evaluated, CB("foo", CB("a", NotInit)))
assert evaluated.get_hints().is_evaluated
assert evaluated.body.get_hints().is_evaluated
def test_i_can_evaluate_when_the_referenced_concept_has_a_body(self):
sheerka, context, concept_a, concept = self.init_concepts(
Concept("a", body="1"),
Concept("foo", body="a"),
eval_body=True)
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
compare_with_test_object(evaluated.body, CB("a", 1))
assert not concept_a.get_hints().is_evaluated
assert evaluated.get_hints().is_evaluated
def test_i_can_evaluate_concept_of_concept_when_the_leaf_has_a_body(self):
sheerka, context, concept_a, concept_b, concept_c, concept_d = self.init_concepts(
Concept("a", body="'a'"),
Concept("b", body="a"),
Concept("c", body="b"),
Concept("d", body="c"),
eval_body=True)
evaluated = sheerka.evaluate_concept(context, concept_d)
assert evaluated.key == concept_d.key
expected = CB("c", CB("b", CB("a", "a")))
compare_with_test_object(evaluated.body, expected)
assert sheerka.objvalue(evaluated) == 'a'
assert evaluated.get_hints().is_evaluated
def test_i_can_evaluate_concept_of_concept_does_not_have_a_body(self):
sheerka, context, concept_a, concept_b, concept_c, concept_d = self.init_concepts(
Concept("a"),
Concept("b", body="a"),
Concept("c", body="b"),
Concept("d", body="c"),
eval_body=True)
evaluated = sheerka.evaluate_concept(context, concept_d)
assert evaluated.key == concept_d.key
expected = CB("c", CB("b", CB("a", NotInit)))
compare_with_test_object(evaluated.body, expected)
compare_with_test_object(sheerka.objvalue(evaluated), CB("a", NotInit))
assert evaluated.get_hints().is_evaluated
def test_i_can_evaluate_concept_when_variables_reference_others_concepts_1(self):
"""
The body references a variable.
The variable reference a concept
The variable name is also the name of a concept
:return:
"""
sheerka, context, concept_a, concept = self.init_concepts(
Concept("a"),
Concept("foo", body="a").def_var("a", "a"),
eval_body=True)
evaluated = sheerka.evaluate_concept(context, concept)
# first, prop a is evaluated to concept_a
# then body is evaluated to prop a -> concept_a
assert evaluated.key == concept.key
assert evaluated.body == concept_a
def test_i_can_evaluate_concept_when_variables_reference_others_concepts_2(self):
"""
Same test,
but the name of the property and the name of the concept are different
:return:
"""
sheerka, context, concept_a = self.init_concepts(Concept(name="a"), eval_body=True)
concept = Concept("foo", body="concept_a").def_var("concept_a", "a")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.body == concept_a
def test_i_can_evaluate_concept_when_variables_reference_others_concepts_3(self):
"""
The body references a variable.
The variable reference a concept
The name of the variable is also the name of a concept, but the variable points to something else
:return:
"""
sheerka, context, concept_a, concept_b = self.init_concepts("a", "b", eval_body=True)
concept = Concept("foo", body="a").def_var("a", "b")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.body == concept_b
def test_i_can_evaluate_concept_when_variables_reference_others_concepts_4(self):
"""
The body references a variable.
The variable reference a concept
:return:
"""
sheerka, context, concept_b = self.init_concepts("b", eval_body=True)
concept = Concept("foo", body="a").def_var("a", "b")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.body == concept_b
def test_i_can_evaluate_concept_when_variables_reference_others_concepts_with_body(self):
sheerka, context, *concepts = self.init_concepts(
Concept(name="a", body="1"),
Concept(name="b", body="2"),
eval_body=True
)
concept = Concept("foo", body="propA + propB").def_var("propA", "a").def_var("propB", "b")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.body == 3
def test_i_can_evaluate_concept_when_variables_is_a_concept(self):
sheerka, context, concept_a = self.init_concepts(Concept(name="a", body="'a'"), eval_body=True)
concept = Concept("foo").def_var("a")
concept.get_compiled()["a"] = concept_a
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
compare_with_test_object(evaluated.get_value("a"), CB("a", "a"))
def test_i_can_evaluate_concept_when_variable_is_a_concept_token(self):
sheerka, context, concept_a, concept_b = self.init_concepts(
"a",
Concept("b").def_var("var_name", "c:a:"), # c:a: means concept 'a'
eval_body=True)
evaluated = sheerka.evaluate_concept(context, concept_b)
assert evaluated.key == concept_b.key
assert evaluated.get_value("var_name") == concept_a
def test_i_can_evaluate_concept_when_body_isa_concept_token(self):
sheerka, context, concept_a, concept_b = self.init_concepts(
"a",
Concept("b", body="c:a:"), # c:a: means concept 'a'
eval_body=True)
evaluated = sheerka.evaluate_concept(context, concept_b)
assert evaluated.key == concept_b.key
assert evaluated.body == concept_a
def test_i_can_evaluate_when_variable_asts_is_a_list(self):
sheerka = self.get_sheerka()
foo = Concept("foo", body="1").init_key()
concept = Concept("to_eval").def_var("prop")
concept.get_compiled()["prop"] = [foo, DoNotResolve("1")]
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, True), concept)
variables = evaluated.get_value("prop")
assert len(variables) == 2
compare_with_test_object(variables[0], CB("foo", 1))
assert variables[1] == "1"
def test_i_can_evaluate_when_compiled_is_set_up_with_return_value(self):
sheerka = self.get_sheerka()
python_node = PythonNode("1 +1 ").init_ast()
parser_result = ParserResultConcept(parser="who", value=python_node)
concept = Concept("to_eval").def_var("prop")
concept.get_compiled()["prop"] = [ReturnValueConcept("who", True, parser_result)]
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, True), concept)
assert evaluated.get_value("prop") == 2
# also works when only one return value
concept = Concept("to_eval").def_var("prop")
concept.get_compiled()["prop"] = ReturnValueConcept("who", True, parser_result)
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, True), concept)
assert evaluated.get_value("prop") == 2
def test_i_can_evaluate_when_body_is_a_concept_with_its_own_variables(self):
sheerka, context, plus, add = self.init_concepts(
Concept("a plus b", body="a + b").def_var("a").def_var("b"),
Concept("add a b", body="a plus b").def_var("a").def_var("b"),
eval_body=True)
add_instance = self.get_concept_instance(sheerka, add, a="1", b="2")
evaluated = sheerka.evaluate_concept(context, add_instance)
assert evaluated.key == add_instance.key
assert evaluated.get_hints().is_evaluated
assert sheerka.objvalue(evaluated) == 3
def test_i_can_evaluate_when_body_is_a_concept_with_its_own_variables_and_different_names(self):
sheerka, context, plus, add = self.init_concepts(
Concept("a plus b", body="a + b").def_var("a").def_var("b"),
Concept("add x y", body="x plus y").def_var("x").def_var("y"),
eval_body=True)
add_instance = self.get_concept_instance(sheerka, add, x="1", y="2")
evaluated = sheerka.evaluate_concept(context, add_instance)
assert evaluated.key == add_instance.key
assert evaluated.get_hints().is_evaluated
assert sheerka.objvalue(evaluated) == 3
def test_i_can_evaluate_when_body_is_a_concept_with_its_own_variables_multiple_levels(self):
sheerka, context, plus, add, inc = self.init_concepts(
Concept("a plus b", body="a + b").def_var("a").def_var("b"),
Concept("add x y", body="x plus y").def_var("x").def_var("y"),
Concept("inc number", body="add number 1").def_var("number"),
eval_body=True)
inc_instance = self.get_concept_instance(sheerka, inc, number="1")
evaluated = sheerka.evaluate_concept(context, inc_instance)
assert evaluated.key == inc_instance.key
assert evaluated.get_hints().is_evaluated
assert sheerka.objvalue(evaluated) == 2
def test_i_can_evaluate_a_concept_that_references_another_concept_twice(self):
"""
Test that a new instance of concept is return when the metadata refers to a concept
:return:
"""
sheerka, context, predicate, foo = self.init_concepts(
Concept("Sometimes True", body="in_context('a')", pre="is_question()"),
Concept("foo", pre="Sometimes True"))
foo1 = sheerka.new("foo")
foo1 = sheerka.evaluate_concept(context, foo1) # 'a' is not in context, so it fails
context2 = self.get_context(sheerka)
context2.add_to_protected_hints('a')
foo2 = sheerka.new("foo")
foo2 = sheerka.evaluate_concept(context2, foo2) # 'a' is now in context
assert sheerka.isinstance(foo1, BuiltinConcepts.CONDITION_FAILED)
assert sheerka.isinstance(foo2, "foo")
def test_i_can_reference_sheerka(self):
sheerka = self.get_sheerka()
concept = Concept("foo", body="sheerka.test()").init_key()
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, True), concept)
assert evaluated.key == concept.key
assert evaluated.body == sheerka.test()
def test_variables_values_takes_precedence_over_the_outside_world(self):
sheerka, context, concept_a, concept_b = self.init_concepts(
Concept(name="a", body="'concept_a'"),
Concept(name="b", body="'concept_b'"),
eval_body=True
)
concept = Concept("foo", body="a")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
compare_with_test_object(evaluated.body, CB("a", "concept_a")) # this test was already done
# so check this one.
concept = Concept("foo", body="a").def_var("a", "'property_a'")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.body == 'property_a'
# or this one.
concept = Concept("foo", body="a").def_var("a", "b")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
compare_with_test_object(evaluated.body, CB("b", "concept_b"))
def test_variables_values_takes_precedence(self):
sheerka, context, concept_a, concept_b = self.init_concepts(
Concept(name="a", body="'concept_a'"),
Concept(name="b", body="'concept_b'"),
eval_body=True
)
concept = Concept("foo", body="a + b").def_var("a", "'prop_a'").init_key()
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.body == 'prop_aconcept_b'
def test_i_can_reference_sub_property_of_a_variable(self):
sheerka, context, concept_a = self.init_concepts(
Concept(name="concept_a").def_var("subProp", "'sub_a'"),
eval_body=True
)
concept = Concept("foo", body="a.subProp").def_var("a", "concept_a")
evaluated = sheerka.evaluate_concept(context, concept)
compare_with_test_object(evaluated, CB(concept.key, "sub_a"))
def test_i_cannot_evaluate_concept_if_variable_is_in_error(self):
sheerka = self.get_sheerka()
concept = Concept(name="concept_a").def_var("subProp", "undef_concept")
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, True), concept)
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONCEPT_EVAL_ERROR)
@pytest.mark.parametrize("where_clause, expected, expected_prop, expected_body", [
("True", True, None, NotInit),
("False", False, ConceptParts.WHERE, NotInit),
("self < 10", False, ConceptParts.WHERE, 10),
("self < 11", True, None, 10),
("a < 20", False, "a", NotInit),
("a > 19", True, None, NotInit),
("a + self > 20", True, None, 10),
("a + self < 20", False, ConceptParts.WHERE, 10),
])
def test_i_can_evaluate_simple_where(self, where_clause, expected, expected_prop, expected_body):
# We check that the WHERE condition is correctly evaluated
# We also check that the body is evaluated only when it's mandatory
sheerka, context, concept = self.init_concepts(
Concept("foo", body="10", where=where_clause).def_var("a", "20"),
eval_body=False, # to check when the evaluation is forced
eval_where=True,
)
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, True),
concept,
hints=EvaluationHints(eval_body=False))
if expected:
assert evaluated.key == concept.key
assert concept.body == expected_body
else:
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONDITION_FAILED)
assert evaluated.body == where_clause
assert evaluated.concept == concept
assert evaluated.prop == expected_prop
assert concept.body == expected_body
def test_i_can_evaluate_where_when_using_other_concept(self):
sheerka, context, foo_true, foo_false = self.init_concepts(
Concept("foo_true", body="True"),
Concept("foo_false", body="False"),
)
concept = Concept("foo", where="foo_true")
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, True), concept)
assert evaluated.key == concept.key
concept = Concept("foo", where="foo_false")
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, True), concept)
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONDITION_FAILED)
assert evaluated.body == "foo_false"
concept = Concept("foo", where="foo_false")
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, False), concept)
assert evaluated.key == concept.key
def test_i_can_evaluate_context_hint_multiple_times(self):
"""
Previous behaviour (that we want to change)
When def concept foo as global_truth(xxx) pre yyy
the context hint for the global truth is set during the initialisation of the ast
if the body is computed straight away it's ok,
But if the concept is evaluated a second time, the asts is not computed again, so the context hint is not set
:return:
"""
sheerka, context, foo = self.init_concepts(
Concept("foo", body="global_truth(in_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED))")
)
foo_instance = sheerka.new("foo")
evaluated = sheerka.evaluate_concept(context, foo_instance, hints=EvaluationHints(eval_body=False))
assert ConceptParts.BODY in evaluated.get_compiled()
assert evaluated.body == NotInit
assert not evaluated.get_hints().is_evaluated
evaluated = sheerka.evaluate_concept(context, foo_instance,
hints=EvaluationHints(eval_body=True)) # evaluate the body this time
assert isinstance(evaluated.body, bool) and evaluated.body
def test_i_can_apply_intermediate_where_condition_using_python(self):
sheerka, context, one_1, one_str, plus = self.init_concepts(
Concept("one", body="1"),
Concept("one", body="'one'"),
Concept("a plus b", body="a + b", where="isinstance(a, int)").def_var("a", "one").def_var("b", "2"),
eval_body=True,
eval_where=True,
)
evaluated = sheerka.evaluate_concept(context, plus)
assert evaluated.key == plus.key
assert evaluated.body == 3
def test_i_can_apply_intermediate_where_condition_when_multiple_variables_using_python(self):
sheerka, context, one_1, one_str, two_2, two_str, plus = self.init_concepts(
Concept("one", body="1"),
Concept("one", body="'one'"),
Concept("two", body="2"),
Concept("two", body="'two'"),
Concept("a plus b",
body="a + b",
where="isinstance(a, int) and isinstance(b, int)").def_var("a", "one").def_var("b", "two"),
eval_body=True,
eval_where=True,
)
evaluated = sheerka.evaluate_concept(context, plus)
assert evaluated.key == plus.key
assert evaluated.body == 3
def test_i_can_apply_intermediate_where_condition_using_other_concepts(self):
sheerka, context, one_1, one_str, is_an_int, plus = self.init_concepts(
Concept("one", body="1"),
Concept("one", body="'one'"),
Concept("x is an int", body="isinstance(x, int)", pre="is_question()").def_var("x"),
Concept("a plus b", body="a + b", where="a is an int").def_var("a", "one").def_var("b", "2"),
eval_body=True,
eval_where=True,
)
evaluated = sheerka.evaluate_concept(context, plus)
assert evaluated.key == plus.key
assert evaluated.body == 3
def test_i_can_apply_intermediate_where_condition_when_multiple_levels(self):
sheerka, context, one_1, one_str, is_an_int, is_an_integer, plus = self.init_concepts(
Concept("one", body="1"),
Concept("one", body="'one'"),
Concept("x is an int", body="isinstance(x, int)", pre="is_question()").def_var("x"),
Concept("y is an integer", body="y is an int", pre="is_question()").def_var("y"),
Concept("a plus b", body="a + b", where="a is an integer").def_var("a", "one").def_var("b", "2"),
eval_body=True,
eval_where=True,
)
evaluated = sheerka.evaluate_concept(context, plus)
assert evaluated.key == plus.key
assert evaluated.body == 3
def test_i_can_apply_intermediate_where_condition_choosing_the_correct_where_concept(self):
# in this test, there are two where conditions with the same name
# We need to use the 'PRE' condition to select the correct one
sheerka, context, one_1, one_str, is_an_int, is_an_integer, plus = self.init_concepts(
Concept("one", body="1"),
Concept("one", body="'one'"),
Concept("x is an int",
body="isinstance(x, int)",
pre="in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)").def_var("x"),
Concept("x is an int", body="False").def_var("x"),
Concept("a plus b", body="a + b", where="a is an int").def_var("a", "one").def_var("b", "2"),
eval_body=True,
eval_where=True,
)
evaluated = sheerka.evaluate_concept(context, plus)
assert evaluated.key == plus.key
assert evaluated.body == 3
def test_i_can_apply_intermediate_where_condition_when_multiple_variables(self):
# The test does not work because the and condition is not correctly supported
# We need the ExpressionParser
sheerka, context, one_1, one_str, two_2, two_str, is_an_int, plus = self.init_concepts(
Concept("one", body="1"),
Concept("one", body="'one'"),
Concept("two", body="2"),
Concept("two", body="'two'"),
Concept("x is an int", body="isinstance(x, int)", pre="is_question()").def_var("x"),
Concept("a plus b",
body="a + b",
where="a is an int and isinstance(b, int)").def_var("a", "one").def_var("b", "two"),
eval_body=True,
eval_where=True,
)
evaluated = sheerka.evaluate_concept(context, plus)
assert evaluated.key == plus.key
assert evaluated.body == 3
def test_i_cannot_evaluate_when_intermediate_where_condition_fails(self):
sheerka, context, one_1, is_an_int, plus = self.init_concepts(
Concept("one", body="'one'"),
Concept("x is an int", body="isinstance(x, int)", pre="is_question()").def_var("x"),
Concept("a plus b", body="a + b", where="a is an int").def_var("a", "one").def_var("b", "2"),
eval_body=True,
eval_where=True,
)
evaluated = sheerka.evaluate_concept(context, plus)
assert evaluated.key == str(BuiltinConcepts.CONDITION_FAILED)
assert evaluated.body == "a is an int"
def test_i_can_enable_disable_where_clause_evaluation(self):
sheerka, context, concept = self.init_concepts(
Concept("foo", body="10", where="a > 10").def_var("a", None),
)
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
context.protected_hints.add(BuiltinConcepts.EVAL_WHERE_REQUESTED)
evaluated = sheerka.evaluate_concept(context, concept)
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONCEPT_EVAL_ERROR)
def test_i_can_detect_and_resolve_infinite_recursion_with_numeric_constant(self):
sheerka, context, one_str, one_digit = self.init_concepts(
Concept("one", body="1"),
Concept("1", body="one"),
eval_body=True
)
sheerka.test_only_add_in_cache(one_str)
sheerka.test_only_add_in_cache(one_digit)
evaluated = sheerka.evaluate_concept(context, one_digit)
assert evaluated.key == one_digit.key
assert evaluated.body == InfiniteRecursionResolved(1)
evaluated = sheerka.evaluate_concept(context, one_str)
assert evaluated.key == one_str.key
assert evaluated.body == InfiniteRecursionResolved(1)
def test_i_can_detect_and_resolve_infinite_recursion_with_boolean_constant(self):
sheerka, context, true_str, true_bool = self.init_concepts(
Concept("true", body="True"),
Concept("True", body="true"),
eval_body=True
)
evaluated = sheerka.evaluate_concept(context, true_str)
assert evaluated.key == true_str.key
assert evaluated.body == InfiniteRecursionResolved(True)
evaluated = sheerka.evaluate_concept(context, true_bool)
assert evaluated.key == true_bool.key
assert evaluated.body == InfiniteRecursionResolved(True)
def test_i_can_detect_infinite_recursion_with_constant_with_more_concepts(self):
sheerka, context, c1, c2, c3, c4 = self.init_concepts(
Concept("one", body="1"),
Concept("1", body="2"),
Concept("2", body="3"),
Concept("3", body="one"),
eval_body=True
)
for concept, expected in ((c1, 3), (c2, 1), (c3, 2), (c4, 3)):
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
assert evaluated.body == InfiniteRecursionResolved(expected)
def test_i_can_detect_infinite_recursion_when_no_constant(self):
sheerka, context, foo, bar, baz, qux = self.init_concepts(
Concept("foo", body="bar"),
Concept("bar", body="baz"),
Concept("baz", body="qux"),
Concept("qux", body="foo"),
eval_body=True
)
evaluated = sheerka.evaluate_concept(context, foo)
assert sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
assert evaluated.body == {foo, bar, baz, qux}
evaluated = sheerka.evaluate_concept(context, bar)
assert sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
assert evaluated.body == {foo, bar, baz, qux}
evaluated = sheerka.evaluate_concept(context, baz)
assert sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
assert evaluated.body == {foo, bar, baz, qux}
evaluated = sheerka.evaluate_concept(context, qux)
assert sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
assert evaluated.body == {foo, bar, baz, qux}
def test_i_can_detect_infinite_recursion(self):
sheerka, context, foo = self.init_concepts(
Concept("foo", body="foo"),
eval_body=True
)
evaluated = sheerka.evaluate_concept(context, foo)
assert sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
assert evaluated.body == {foo}
# def test_i_can_detect_auto_recursion_when_evaluating_another_concept(self):
# sheerka, context, one_1, one_str, plus = self.init_concepts(
# Concept("one", body="1"),
# Concept("one", body="one"),
# Concept("a plus b", body="a + b", where="isinstance(a, int)").def_var("a", "one").def_var("b", "2")
# )
#
# evaluated = sheerka.evaluate_concept(context, plus, eval_body=True)
# sheerka.isinstance(evaluated, BuiltinConcepts.CHICKEN_AND_EGG)
def test_i_can_manage_auto_recursion_when_constant(self):
sheerka = self.get_sheerka()
one = Concept("1", body="1")
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, True), one)
assert evaluated.key == one.key
assert evaluated.body == 1
def test_i_can_evaluate_thousand_concept(self):
sheerka, context, thousand, number, forties, forty, one = self.init_concepts(
Concept("thousand", body="number * 1000").def_var("number"),
Concept("number"),
Concept("forties", body="forty + number").def_var("forty").def_var("number"),
Concept("forty", body="40"),
Concept("one", body="1"),
eval_body=True
)
one = sheerka.new("one")
number2 = sheerka.new("number")
number2.get_compiled()["one"] = one
number2.get_compiled()[ConceptParts.BODY] = one
forties = sheerka.new("forties")
forties.get_compiled()["forty"] = sheerka.new("forty")
forties.get_compiled()["number"] = number2
number1 = sheerka.new("number")
number1.get_compiled()["forties"] = forties
number1.get_compiled()[ConceptParts.BODY] = forties
forty_one_thousand = sheerka.new("thousand")
forty_one_thousand.get_compiled()["number"] = number1
evaluated = sheerka.evaluate_concept(context, forty_one_thousand)
assert evaluated.body == 41000
def test_i_can_evaluate_command(self):
sheerka, context, command = self.init_concepts(Concept("command", body="a = 10"))
evaluated = sheerka.evaluate_concept(context, command)
assert evaluated.key == command.key
assert sheerka.get_from_memory(context, "a") == NotFound
sheerka.set_isa(context, command, sheerka.new(BuiltinConcepts.AUTO_EVAL))
evaluated = sheerka.evaluate_concept(context, sheerka.new("command"))
assert evaluated.key == command.key
assert sheerka.get_from_memory(context, "a").obj == 10
def test_python_builtin_function_are_forbidden_in_where_pre_post_ret(self, capsys):
# I do the test only for PRE, as it will be the same for the other ConceptPart
sheerka, context, foo, bar = self.init_concepts(
Concept("foo", body="print('10')"), # print will be executed
Concept("bar", pre="print('10')"), # print won't be executed
)
evaluated = sheerka.evaluate_concept(context, foo, hints=EvaluationHints(eval_body=True))
captured = capsys.readouterr()
assert evaluated.key == foo.key
assert captured.out == "10\n"
evaluated = sheerka.evaluate_concept(context, bar)
captured = capsys.readouterr()
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONCEPT_EVAL_ERROR)
error = evaluated.body
assert sheerka.isinstance(error, BuiltinConcepts.ERROR)
assert captured.out == ""
def test_i_can_failed_a_concept_when_pre_clause_is_not_validated(self, capsys):
sheerka, context, concept = self.init_concepts(
Concept("foo", pre="in_context('foo')", body="print('10')"),
)
evaluated = sheerka.evaluate_concept(context, concept, hints=EvaluationHints(eval_body=True))
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONDITION_FAILED)
assert evaluated.body == "in_context('foo')"
assert evaluated.concept == concept
assert evaluated.prop == ConceptParts.PRE
captured = capsys.readouterr()
assert captured.out == ""
@pytest.mark.parametrize("concept, eval_body, expected", [
(Concept("foo"), False, []),
(Concept("foo", pre="pre", post="post", ret="ret", where="where"), False, ["#pre#", "#post#"]),
(Concept("foo", pre="pr", post="p", ret="r", where="w"), True,
["#pre#", "variables", "#body#", "#ret#", "#post#"]),
(Concept("foo", pre="pre", body="body"), False, ["#pre#"]),
(Concept("foo", pre="pre", body="body"), True, ["#pre#", "variables", "#body#"]),
(Concept("foo", pre="a").def_var("a"), False, ["variables", "#pre#"]),
(Concept("foo", pre="self"), False, ["#body#", "#pre#"]),
(Concept("foo", pre="self + a").def_var("a"), False, ["variables", "#body#", "#pre#"]),
(Concept("foo", pre="self + a", ret="ret").def_var("a"), False, ["variables", "#body#", "#pre#"]),
(Concept("foo", pre="self + a", ret="ret").def_var("a"), True, ["variables", "#body#", "#pre#", "#ret#"]),
(Concept("foo", body="body"), False, [])
])
def test_i_can_compute_metadata_to_eval(self, concept, eval_body, expected):
sheerka, context, concept = self.init_concepts(concept)
service = sheerka.services[SheerkaEvaluateConcept.NAME]
if eval_body:
context.add_to_protected_hints(BuiltinConcepts.EVAL_BODY_REQUESTED)
service.initialize_concept_asts(context, concept)
assert service.compute_metadata_to_eval(context, concept) == expected
def test_i_can_compute_metadata_to_eval_for_where_and_body(self):
sheerka = self.get_sheerka()
service = sheerka.services[SheerkaEvaluateConcept.NAME]
context = self.get_context(sheerka, eval_where=True)
concept = Concept("foo", where="where")
service.initialize_concept_asts(context, concept)
assert service.compute_metadata_to_eval(context, concept) == ["#where#"]
concept = Concept("foo", where="a").def_var("a")
service.initialize_concept_asts(context, concept)
assert service.compute_metadata_to_eval(context, concept) == ["variables", "#where#"]
concept = Concept("foo", where="self")
service.initialize_concept_asts(context, concept)
assert service.compute_metadata_to_eval(context, concept) == ["#body#", "#where#"]
context = self.get_context(sheerka, eval_body=True)
concept = Concept("foo")
assert service.compute_metadata_to_eval(context, concept) == ["variables", "#body#"]
context = self.get_context(sheerka, eval_body=True)
concept = Concept("foo").def_var("a")
assert service.compute_metadata_to_eval(context, concept) == ["variables", "#body#"]
context = self.get_context(sheerka, eval_body=True)
concept = Concept("foo", body="body").def_var("a")
assert service.compute_metadata_to_eval(context, concept) == ["variables", "#body#"]
@pytest.mark.parametrize("concept, expected", [
(Concept("foo"), True),
])
def test_is_evaluated_is_correctly_set(self, concept, expected):
sheerka, context, concept = self.init_concepts(concept)
evaluated = sheerka.evaluate_concept(context, concept, hints=EvaluationHints(eval_body=True))
assert evaluated.key == concept.key
assert concept.get_hints().is_evaluated == expected
def test_i_only_compute_the_requested_metadata(self):
sheerka, context, concept = self.init_concepts(
Concept("foo", pre="True", post="'post'", ret="'ret'", where="True", body="'body'").def_var("a", "'a'")
)
context.protected_hints.add(BuiltinConcepts.EVAL_WHERE_REQUESTED) # to prove that we do not care
context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED) # to prove that we do not care
evaluated = sheerka.evaluate_concept(context, concept, metadata=[ConceptParts.PRE])
assert evaluated.values() == {"a": NotInit, ConceptParts.PRE: True}
def test_i_can_manage_ret(self):
sheerka, context, foo, bar = self.init_concepts("foo", Concept("bar", ret="foo"))
res = sheerka.evaluate_concept(context, bar)
assert sheerka.isinstance(res, "bar")
res = sheerka.evaluate_concept(context, bar, hints=EvaluationHints(eval_body=True))
assert sheerka.isinstance(res, "foo")
# And the result is still the same after a second call
assert bar.get_hints().is_evaluated
res = sheerka.evaluate_concept(context, bar, hints=EvaluationHints(eval_body=True))
assert sheerka.isinstance(res, "foo")
def test_ret_is_evaluated_only_is_body_is_requested(self):
sheerka, context, foo, bar = self.init_concepts("foo", Concept("bar", ret="__NOT_FOUND"))
res = sheerka.evaluate_concept(context, bar, hints=EvaluationHints(eval_body=False))
assert res.id == bar.id
def test_i_can_eval_concept_with_rules(self):
sheerka, context, foo = self.init_concepts(Concept("foo a", body="a.name").def_var("a", "r:|1:"))
res = sheerka.evaluate_concept(context, foo, hints=EvaluationHints(eval_body=True))
assert res.body == "Print return values"
def test_i_can_manage_python_concept_infinite_recursion_when_initializing_ast(self):
sheerka, context, foo = self.init_concepts(Concept("a + b", body="a + b").def_var("a").def_var("b"))
evaluator = SheerkaEvaluateConcept(sheerka)
evaluator.initialize_concept_asts(context, foo)
res = foo.get_compiled()["#body#"]
assert len(res) == 1
assert sheerka.isinstance(res[0], BuiltinConcepts.RETURN_VALUE)
assert res[0].who == BaseParser.get_name(PythonParser.NAME)
# TODO validate that a rule is created
def test_can_detect_recursive_definition_with_exact_concept(self):
sheerka, context, foo = self.init_concepts("foo")
evaluator = SheerkaEvaluateConcept(sheerka)
# 'def concept foo as foo'
return_values = [pr_ret_val(foo, parser=exact), python_ret_val("foo")]
res = evaluator.get_recursive_definitions(context, foo, return_values)
assert list(r.name for r in res) == [BaseParser.get_name("ExactConcept")]
def test_i_can_detect_when_no_recursive_definition(self):
sheerka, context, foo, bar = self.init_concepts("foo", "bar")
evaluator = SheerkaEvaluateConcept(sheerka)
# 'def concept foo as bar'
return_values = [pr_ret_val(bar, parser=exact), python_ret_val("foo")]
res = evaluator.get_recursive_definitions(context, foo, return_values)
assert list(res) == []
def test_i_can_detect_when_no_recursive_definition2(self):
sheerka, context, q = self.init_concepts(
Concept("q", definition="q ?", definition_type=DEFINITION_TYPE_DEF).def_var("q"))
evaluator = SheerkaEvaluateConcept(sheerka)
# i dunno how to construct the return value
return_values = [pr_ret_val(q, parser=exact)]
res = evaluator.get_recursive_definitions(context, q, return_values)
assert list(res) == []
def test_i_do_not_mess_up_use_copy_when_exact_concept(self):
sheerka, context, one, number, isa = self.init_concepts(
"one",
"number",
Concept("x is a y", body="isa(x,y)", pre="is_question()").def_var("x").def_var("y"))
evaluator = SheerkaEvaluateConcept(sheerka)
parsed_return_value = ExactConceptParser().parse(context, ParserInput("one is a number"))
concept = parsed_return_value[0].body.body
# just get the compiled
context.add_to_protected_hints(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
evaluated = evaluator.evaluate_concept(context, concept)
assert evaluated.get_compiled()["x"][0].body.body.get_hints().use_copy
assert evaluated.get_compiled()["y"][0].body.body.get_hints().use_copy
# get the body
evaluated = evaluator.evaluate_concept(context, concept, hints=EvaluationHints(eval_body=True))
assert evaluated.get_compiled()["x"][0].body.body.get_hints().use_copy
assert evaluated.get_compiled()["y"][0].body.body.get_hints().use_copy
assert not evaluated.get_value("x").get_hints().use_copy
assert not evaluated.get_value("y").get_hints().use_copy
def test_i_do_not_mess_up_use_copy_when_expression_parser(self):
sheerka, context, one, number, isa = self.init_concepts(
"one",
"number",
Concept("x is a y", body="isa(x,y)", pre="is_question()").def_var("x").def_var("y"))
context.add_to_protected_hints(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
evaluator = SheerkaEvaluateConcept(sheerka)
parsed_return_value = ExpressionParser().parse(context, ParserInput("one is a number"))
concept = next(iter(parsed_return_value.body.body.compiled[0].return_value.body.body.objects.values()))
assert concept.get_compiled()["x"][0].body.body.get_hints().use_copy
assert concept.get_compiled()["y"][0].body.body.get_hints().use_copy
# get the body
evaluated = evaluator.evaluate_concept(context, concept, hints=EvaluationHints(eval_body=True))
assert evaluated.get_compiled()["x"][0].body.body.get_hints().use_copy
assert evaluated.get_compiled()["y"][0].body.body.get_hints().use_copy
assert not evaluated.get_value("x").get_hints().use_copy
assert not evaluated.get_value("y").get_hints().use_copy
def test_i_do_not_evaluate_the_body_when_validation_only_is_set(self):
sheerka, context, red, shirt, a_x, red_x = self.init_concepts(
"red",
Concept("shirt", body="set_attr(self, 'body_shirt_is_evaluated', True)"),
Concept("a x", body="set_attr(x, 'body_ax_is_evaluated', True)", ret="x").def_var("x"),
Concept("red x", body="set_attr(x, 'color', 'red')", ret="x").def_var("x"),
create_new=True)
parsed_ret_val = SyaNodeParser().parse(context, ParserInput("a red shirt"))
# Sanity check for normal behaviour
to_evaluate1 = parsed_ret_val.body.body[0].concept.copy()
evaluated1 = sheerka.evaluate_concept(context,
to_evaluate1,
hints=EvaluationHints(eval_body=True, expression_only=False))
assert sheerka.isinstance(evaluated1, shirt)
assert evaluated1.get_value("body_ax_is_evaluated") == True
assert evaluated1.get_value("body_shirt_is_evaluated") == True
assert evaluated1.get_value("color") == "red"
assert evaluated1.body == sheerka.new(BuiltinConcepts.SUCCESS)
# check validation_only behaviour
to_evaluate2 = parsed_ret_val.body.body[0].concept.copy()
evaluated2 = sheerka.evaluate_concept(context,
to_evaluate2,
hints=EvaluationHints(eval_body=True, expression_only=True))
assert sheerka.isinstance(evaluated2, shirt)
assert evaluated2.get_value("body_ax_is_evaluated") == NotInit
assert evaluated2.get_value("body_shirt_is_evaluated") == NotInit
assert evaluated2.get_value("color") == NotInit
assert evaluated2.body == NotInit
def test_methods_with_side_effect_are_not_called_when_eval_body_is_false(self):
sheerka, context, red, shirt, a_x, red_x = self.init_concepts(
"red",
Concept("shirt", body="set_attr(self, 'body_shirt_is_evaluated', True)"),
Concept("a x", body="set_attr(x, 'body_ax_is_evaluated', True)", ret="x").def_var("x"),
Concept("red x", body="set_attr(x, 'color', 'red')", ret="x").def_var("x"),
create_new=True,
)
parsed_ret_val = SyaNodeParser().parse(context, ParserInput("a red shirt"))
to_evaluate = parsed_ret_val.body.body[0].concept
evaluated = sheerka.evaluate_concept(context, to_evaluate, hints=EvaluationHints(eval_body=False))
assert sheerka.isinstance(evaluated, a_x)
assert "x" in evaluated.get_compiled()
assert ConceptParts.BODY in evaluated.get_compiled()
assert ConceptParts.RET in evaluated.get_compiled()
assert sheerka.isinstance(evaluated.get_compiled()["x"], red_x)
assert evaluated.get_compiled()["x"].get_compiled()["x"] == shirt # so, it's not evaluated
# sanity check
parsed_ret_val = SyaNodeParser().parse(context, ParserInput("a red shirt"))
to_evaluate = parsed_ret_val.body.body[0].concept
evaluated = sheerka.evaluate_concept(context, to_evaluate, hints=EvaluationHints(eval_body=True))
assert sheerka.isinstance(evaluated, shirt)
assert evaluated.get_value("body_ax_is_evaluated") == True
assert evaluated.get_value("body_shirt_is_evaluated") == True
assert evaluated.get_value("color") == "red"
def test_concept_is_not_evaluated_when_method_access_error(self):
sheerka, context, foo = self.init_concepts(Concept("foo", body="set_attr(self, 'prop_name', 'prop_value')"))
evaluated = sheerka.evaluate_concept(context, foo, hints=EvaluationHints(eval_body=True, expression_only=True))
assert sheerka.isinstance(evaluated, foo)
assert not foo.get_hints().is_evaluated
def test_dynamic_concepts_are_never_considered_as_evaluated(self):
sheerka, context, foo = self.init_concepts("foo")
i = 0
def get_new_id():
nonlocal i
i += 1
return i
dynamic_foo = sheerka.new_dynamic(foo, "test", body="get_new_id()")
context.add_to_short_term_memory("get_new_id", get_new_id)
evaluated = sheerka.evaluate_concept(context, dynamic_foo, hints=EvaluationHints(eval_body=True))
assert evaluated.key == dynamic_foo.key
assert evaluated.body == 1
assert not evaluated.get_hints().is_evaluated
evaluated = sheerka.evaluate_concept(context, dynamic_foo, hints=EvaluationHints(eval_body=True))
assert evaluated.body == 2
assert not evaluated.get_hints().is_evaluated