import pytest from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, ParserResultConcept from core.concept import Concept, DoNotResolve, ConceptParts, InfiniteRecursionResolved, \ DEFINITION_TYPE_DEF from core.global_symbols import NotInit, NotFound from core.sheerka.services.SheerkaEvaluateConcept import 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 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") 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')"), Concept("foo", pre="c: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' in context + new instance of 'Sometimes True' 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) def test_key_is_initialized_by_evaluation(self): sheerka = self.get_sheerka() concept = Concept("foo") evaluated = sheerka.evaluate_concept(self.get_context(sheerka, True), concept) assert evaluated.key == concept.init_key().key @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, 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_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, 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, 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#", "#ret#", "#post#", "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, 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, 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, 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, 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, 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="ExactConcept"), python_ret_val("foo")] res = evaluator.get_recursive_definitions(context, foo, return_values) assert list(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="ExactConcept"), 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="ExactConcept")] 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, 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")) 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 context.add_to_protected_hints(BuiltinConcepts.EVAL_QUESTION_REQUESTED) evaluated = evaluator.evaluate_concept(context, concept, 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, eval_body=True, validation_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, eval_body=True, validation_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, 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, 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, eval_body=True, validation_only=True) assert sheerka.isinstance(evaluated, foo) assert not foo.get_hints().is_evaluated