Files
Sheerka-Old/tests/core/test_SheerkaEvaluateConcept.py
T
kodjo 7dcaa9c111 Fixed #29: Parsers: Implement parsing memoization
Fixed #77 : Parser: ShortTermMemoryParser should be called separately
Fixed #78 : Remove VariableNode usage
Fixed #79 : ConceptManager: Implement compile caching
Fixed #80 : SheerkaExecute : parsers_key is not correctly computed
Fixed #81 : ValidateConceptEvaluator : Validate concept's where and pre clauses right after the parsing
Fixed #82 : SheerkaIsAManager: isa() failed when the set as a body
Fixed #83 : ValidateConceptEvaluator : Support BNF and SYA Concepts
Fixed #84 : ExpressionParser: Implement the parser as a standard parser
Fixed #85 : Services: Give order to services
Fixed #86 : cannot manage smart_get_attr(the short, color)
2021-06-07 21:14:03 +02:00

1083 lines
47 KiB
Python

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