import pytest from core.builtin_concepts import BuiltinConcepts from core.concept import Concept, PROPERTIES_TO_SERIALIZE from core.global_symbols import NotInit from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator from evaluators.OneSuccessEvaluator import OneSuccessEvaluator from evaluators.PythonEvaluator import PythonEvalError from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.parsers.parsers_utils import CMV, CC, compare_with_test_object, CB class TestSheerkaNonRegMemory(TestUsingMemoryBasedSheerka): @pytest.mark.parametrize("text, expected", [ ("1 + 1", 2), ("sheerka.test()", 'I have access to Sheerka !') ]) def test_i_can_evaluate_python_expressions_with_no_variable(self, text, expected): sheerka = self.get_sheerka() res = sheerka.evaluate_user_input(text) assert len(res) == 1 assert res[0].status assert res[0].value == expected def test_i_can_recognize_concept_with_python_body(self): sheerka = self.get_sheerka() concept = Concept(name="one", body="1") sheerka.test_only_add_in_cache(concept) text = "one" res = sheerka.evaluate_user_input(text) assert len(res) == 1 assert res[0].status assert res[0].value == concept # sanity check evaluated = sheerka.evaluate_concept(self.get_context(eval_body=True), res[0].value) compare_with_test_object(evaluated, CB("one", 1)) def test_i_can_recognize_concept_with_concept_body(self): sheerka, context, concept_one, concept_un = self.init_concepts("one", Concept(name="un", body="one")) res = sheerka.evaluate_user_input("un") return_value = res[0].value assert len(res) == 1 assert res[0].status assert return_value == concept_un # sanity check evaluated = sheerka.evaluate_concept(self.get_context(eval_body=True), return_value) compare_with_test_object(evaluated, CB("un", CB("one", NotInit))) def test_i_can_recognize_concept_with_no_body(self): sheerka = self.get_sheerka() concept = Concept(name="one") sheerka.test_only_add_in_cache(concept) text = "one" res = sheerka.evaluate_user_input(text) assert len(res) == 1 assert res[0].status assert res[0].value == concept assert id(res[0].value) != id(concept) def test_is_unique_property_is_used_when_evaluating(self): sheerka = self.get_sheerka() concept = Concept(name="one", is_unique=True) sheerka.test_only_add_in_cache(concept) text = "one" res = sheerka.evaluate_user_input(text) assert len(res) == 1 assert res[0].status assert res[0].value == concept assert id(res[0].value) == id(concept) def test_i_can_evaluate_def_concept_request(self): text = """ def concept a + b where isinstance(a, int) and isinstance(b, int) pre isinstance(a, int) and isinstance(b, int) post isinstance(res, int) as: def func(x,y): return x+y func(a,b) """ expected = self.get_default_concept() expected.get_metadata().id = "1001" expected.get_metadata().desc = None expected.get_metadata().variables = [("a", None), ("b", None)] expected.get_metadata().parameters = ["a", "b"] expected.init_key() sheerka = self.get_sheerka(cache_only=False) res = sheerka.evaluate_user_input(text) assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT) concept_saved = res[0].value.body for prop in PROPERTIES_TO_SERIALIZE: assert getattr(concept_saved.get_metadata(), prop) == getattr(expected.get_metadata(), prop) # cache is up to date service = sheerka.services[SheerkaConceptManager.NAME] assert service.has_key(concept_saved.key) assert service.has_id(concept_saved.id) assert service.has_name(concept_saved.name) assert service.has_hash(concept_saved.get_definition_hash()) assert sheerka.om.copy(SheerkaConceptManager.CONCEPTS_BY_FIRST_KEYWORD_ENTRY) == {'+': ['1001']} # sdp is up to date assert sheerka.om.current_sdp().exists(SheerkaConceptManager.CONCEPTS_BY_KEY_ENTRY, expected.key) def test_i_can_evaluate_def_concept_part_when_one_part_is_a_ref_of_another_concept(self): """ In this test, we test that the properties of 'concept a xx b' (which are 'a' and 'b') are correctly detected, thanks to the source code 'a plus b' in its body :return: """ sheerka = self.get_sheerka() # concept 'a plus b' is known concept_a_plus_b = Concept(name="a plus b").def_var("a").def_var("b").init_key() sheerka.test_only_add_in_cache(concept_a_plus_b) res = sheerka.evaluate_user_input("def concept a xx b as a plus b") expected = Concept(name="a xx b", body="a plus b").def_var("a").def_var("b").init_key() expected.get_metadata().id = "1001" expected.get_metadata().parameters = ["a", "b"] assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT) concept_saved = res[0].value.body for prop in PROPERTIES_TO_SERIALIZE: assert getattr(concept_saved.get_metadata(), prop) == getattr(expected.get_metadata(), prop) assert sheerka.services[SheerkaConceptManager.NAME].has_key(concept_saved.key) def test_i_cannot_evaluate_the_same_def_concept_twice(self): text = """ def concept a + b where isinstance(a, int) and isinstance(b, int) pre isinstance(a, int) and isinstance(b, int) post isinstance(res, int) as: def func(x,y): return x+y func(a,b) """ sheerka = self.get_sheerka() sheerka.evaluate_user_input(text) res = sheerka.evaluate_user_input(text) assert len(res) == 1 assert not res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.CONCEPT_ALREADY_DEFINED) @pytest.mark.parametrize("text", [ "", " ", "\n", ]) def test_i_can_recognize_a_empty_input(self, text): sheerka = self.get_sheerka() res = sheerka.evaluate_user_input(text) assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.NOP) def test_i_can_recognize_concept_with_variable(self): sheerka, context, concept_foo, concept_hello = self.init_test().with_concepts( "foo", Concept(name="hello a").def_var("a"), create_new=True).unpack() res = sheerka.evaluate_user_input("hello foo") return_value = res[0].value assert len(res) == 1 assert res[0].status assert sheerka.isinstance(return_value, concept_hello) # sanity check evaluated = sheerka.evaluate_concept(self.get_context(eval_body=True), return_value) assert evaluated.get_value("a") == concept_foo def test_i_can_recognize_concept_with_variable_and_python_as_body(self): sheerka, context, hello_a, foo = self.init_concepts( Concept(name="hello a", body="'hello ' + a").def_var("a"), Concept(name="foo", body="'foo'") ) res = sheerka.evaluate_user_input("hello foo") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, hello_a) # sanity check evaluated = sheerka.evaluate_concept(self.get_context(eval_body=True), res[0].value) assert evaluated.body == "hello foo" assert evaluated.get_hints().is_evaluated compare_with_test_object(evaluated.get_value("a"), CB("foo", "foo")) assert evaluated.get_value("a").get_hints().is_evaluated def test_i_can_recognize_duplicate_concepts_with_same_value(self): # when multiple result, choose the one that is the more specific (that has the less variables) sheerka = self.get_sheerka() self.create_and_add_in_cache_concept(sheerka, Concept(name="hello a", body="'hello ' + a"), variables=["a"]) self.create_and_add_in_cache_concept(sheerka, Concept(name="hello foo", body="'hello foo'")) self.create_and_add_in_cache_concept(sheerka, Concept(name="foo", body="'foo'")) res = sheerka.evaluate_user_input("hello foo") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, "hello foo") assert res[0].value.body == NotInit assert res[0].who == sheerka.get_evaluator_name(OneSuccessEvaluator.NAME) def test_i_cannot_manage_duplicate_concepts_when_the_values_are_different(self): sheerka = self.get_sheerka() self.create_and_add_in_cache_concept(sheerka, Concept(name="hello a", body="'hello ' + a").def_var("a")) self.create_and_add_in_cache_concept(sheerka, Concept(name="hello b", body="'hello you ' + b").def_var("b")) self.create_and_add_in_cache_concept(sheerka, Concept(name="foo", body="'another value'")) res = sheerka.evaluate_user_input("eval hello foo") assert len(res) == 1 assert not res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.TOO_MANY_SUCCESS) concepts = res[0].value.body assert len(concepts) == 2 sorted_values = sorted(concepts, key=lambda x: x.value) assert sorted_values[0].value == "hello another value" assert sorted_values[1].value == "hello you another value" def test_i_can_manage_concepts_with_the_same_key_when_values_are_the_same(self): sheerka = self.get_sheerka() context = self.get_context(sheerka) sheerka.create_new_concept(context, Concept(name="hello a", body="'hello ' + a").def_var("a")) sheerka.create_new_concept(context, Concept(name="hello b", body="'hello ' + b").def_var("b")) res = sheerka.evaluate_user_input("eval hello 'foo'") assert len(res) == 1 assert res[0].status assert res[0].value == "hello foo" # I don't know yet the one to choose assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME) def test_i_can_create_concepts_with_python_code_as_body(self): sheerka = self.get_sheerka() context = self.get_context(sheerka) sheerka.create_new_concept(context, Concept(name="concepts", body="sheerka.test()")) res = sheerka.evaluate_user_input("eval concepts") assert len(res) == 1 assert res[0].status assert res[0].value == sheerka.test() def test_i_can_recognize_bnf_definitions(self): sheerka = self.get_sheerka() concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' | 'two'")[0].body.body res = sheerka.evaluate_user_input("one") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_a) def test_i_can_recognize_bnf_definitions_with_variables(self): sheerka = self.get_sheerka() concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' | 'two'")[0].body.body concept_b = sheerka.evaluate_user_input("def concept b from bnf a 'three'")[0].body.body res = sheerka.evaluate_user_input("one three") assert len(res) == 1 assert res[0].status return_value = res[0].value assert sheerka.isinstance(return_value, concept_b) # sanity check evaluated = sheerka.evaluate_concept(self.get_context(sheerka=sheerka, eval_body=True), return_value) assert evaluated.body == "one three" assert evaluated.get_hints().is_evaluated assert evaluated.get_value("a") == sheerka.new(concept_a.key, body="one").init_key() assert evaluated.get_value("a").get_hints().is_evaluated @pytest.mark.parametrize("user_input", [ "def concept greetings from def hello a where a", "def concept greetings from hello a where a"]) def test_i_can_recognize_a_concept_defined_using_from_def(self, user_input): sheerka = self.get_sheerka() greetings = sheerka.evaluate_user_input(user_input)[0].body.body res = sheerka.evaluate_user_input("hello 'foo'") assert len(res) == 1 assert res[0].status concept_found = res[0].value assert sheerka.isinstance(concept_found, greetings) assert concept_found.get_value("a") == "foo" assert concept_found.get_hints().need_validation res = sheerka.evaluate_user_input("greetings") assert len(res) == 1 assert res[0].status concept_found = res[0].value assert sheerka.isinstance(concept_found, greetings) assert concept_found.get_value("a") == NotInit assert not concept_found.get_hints().need_validation @pytest.mark.parametrize("desc, definitions", [ ("Simple form", [ "def concept one as 1", "def concept two as 2", "def concept twenties from bnf 'twenty' (one|two)=unit as 20 + unit" ]), ("When twenty is a concept", [ "def concept one as 1", "def concept two as 2", "def concept twenty as 20", "def concept twenties from bnf twenty (one|two)=unit as twenty + unit" ]), ("When digit is a concept", [ "def concept one as 1", "def concept two as 2", "def concept twenty as 20", "def concept digit from bnf one|two", "def concept twenties from bnf twenty digit as twenty + digit" ]), ("When using isa and concept twenty", [ "def concept one as 1", "def concept two as 2", "def concept number", "set_isa(one, number)", "set_isa(two, number)", "def concept twenties from bnf 'twenty' number as 20 + number" ]), ("When using isa and concept twenty", [ "def concept one as 1", "def concept two as 2", "def concept twenty as 20", "def concept number", "set_isa(one, number)", "set_isa(two, number)", "def concept twenties from bnf twenty number as 20 + number" ]), ]) def test_i_can_mix_concept_with_python_to_define_numbers(self, desc, definitions): sheerka = self.get_sheerka() sheerka.add_to_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED) for definition in definitions: sheerka.evaluate_user_input(definition) res = sheerka.evaluate_user_input("twenty one") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, "twenties") assert sheerka.evaluate_concept(self.get_context(sheerka=sheerka, eval_body=True), res[0].body).body == 21 res = sheerka.evaluate_user_input("twenty one + 1") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("twenty one + one") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("twenty one + twenty two") assert len(res) == 1 assert res[0].status assert res[0].body == 43 res = sheerka.evaluate_user_input("1 + twenty one") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("1 + 1 + twenty one") assert len(res) == 1 assert res[0].status assert res[0].body == 23 sheerka.remove_fom_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED) def test_i_can_mix_bnf_and_isa(self): """ if 'one' isa 'number, twenty number should be recognized :return: """ sheerka = self.get_sheerka() sheerka.evaluate_user_input("def concept one as 1") sheerka.evaluate_user_input("def concept two as 2") sheerka.evaluate_user_input("def concept number") sheerka.evaluate_user_input("global_truth(set_isa(one, number))") sheerka.evaluate_user_input("global_truth(set_isa(two, number))") sheerka.evaluate_user_input("def concept twenties from bnf 'twenty' number as 20 + number") res = sheerka.evaluate_user_input("twenty one") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, "twenties") assert sheerka.evaluate_concept(self.get_context(sheerka=sheerka, eval_body=True), res[0].value).body == 21 res = sheerka.evaluate_user_input("twenty one + 1") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("twenty one + one") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("twenty one + twenty two") assert len(res) == 1 assert res[0].status assert res[0].body == 43 res = sheerka.evaluate_user_input("1 + twenty one") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("1 + 1 + twenty one") assert len(res) == 1 assert res[0].status assert res[0].body == 23 def test_i_can_mix_bnf_and_isa_2(self): sheerka = self.get_sheerka() sheerka.add_to_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED) init = [ "def concept one as 1", "def concept twenty as 20", "def concept number", "set_isa(one, number)", "set_isa(twenty, number)", "def concept twenties from bnf twenty number as twenty + number" ] for exp in init: sheerka.evaluate_user_input(exp) res = sheerka.evaluate_user_input("eval twenty one") assert len(res) == 1 assert res[0].status assert res[0].body == 21 sheerka.remove_fom_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED) def test_i_can_use_concepts_defined_with_from(self): sheerka = self.get_sheerka() init = [ "def concept plus from a plus b as a + b", "def concept one as 1", ] for exp in init: sheerka.evaluate_user_input(exp) res = sheerka.evaluate_user_input("eval one plus one") assert len(res) == 1 assert res[0].status assert res[0].body == 2 res = sheerka.evaluate_user_input("eval 1 plus one") assert len(res) == 1 assert res[0].status assert res[0].body == 2 res = sheerka.evaluate_user_input("eval one plus 1") assert len(res) == 1 assert res[0].status assert res[0].body == 2 res = sheerka.evaluate_user_input("eval 1 plus 2") assert len(res) == 1 assert res[0].status assert res[0].body == 3 res = sheerka.evaluate_user_input("eval 1 plus 1") assert len(res) == 1 assert res[0].status assert res[0].body == 2 def test_i_can_mix_bnf_concept_and_concept(self): definitions = [ "def concept one as 1", "def concept two as 2", "def concept twenties from bnf 'twenty' (one|two)=unit as 20 + unit", "def concept a plus b as a + b" ] sheerka = self.init_scenario(definitions) res = sheerka.evaluate_user_input("eval 1 plus 2") assert len(res) == 1 assert res[0].status assert res[0].body == 3 res = sheerka.evaluate_user_input("eval 1 plus one") assert len(res) == 1 assert res[0].status assert res[0].body == 2 res = sheerka.evaluate_user_input("eval 1 + 1 plus 1") assert len(res) == 1 assert res[0].status assert res[0].body == 3 res = sheerka.evaluate_user_input("eval 1 plus twenty one") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("eval one plus 1") assert len(res) == 1 assert res[0].status assert res[0].body == 2 res = sheerka.evaluate_user_input("eval one plus two") assert len(res) == 1 assert res[0].status assert res[0].body == 3 res = sheerka.evaluate_user_input("eval one plus twenty one") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("eval twenty one plus 1") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("eval twenty one plus one") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("eval twenty one plus twenty two") assert len(res) == 1 assert res[0].status assert res[0].body == 43 def test_i_can_use_bnf_alias(self): definitions = [ "def concept two as 2", "def concept number", "set_isa(two, number)", "def concept plus_one from bnf number=n1 'plus_one' as n1 + 1", ] sheerka = self.init_scenario(definitions, global_truth=True) res = sheerka.evaluate_user_input("eval two plus_one") assert len(res) == 1 assert res[0].status assert res[0].body == 3 def test_eval_does_not_break_valid_result(self): sheerka = self.get_sheerka() sheerka.evaluate_user_input("def concept one as 1") sheerka.evaluate_user_input("def concept two as 2") res = sheerka.evaluate_user_input("one + two") assert len(res) == 1 assert res[0].status assert res[0].body == 3 res = sheerka.evaluate_user_input("eval one + two") assert len(res) == 1 assert res[0].status assert res[0].body == 3 @pytest.mark.parametrize("text", [ "'hello", '"foo" + "string', "c::", "c:foo\nbar:", "c:foo", "def concept 'name", "def concept name from bnf 'name" ]) def test_i_can_manage_tokenizer_error(self, text): sheerka = self.get_sheerka() sheerka.test_only_add_in_cache(Concept("foo")) res = sheerka.evaluate_user_input(text) assert len(res) > 1 for r in [r for r in res if r.who.startswith("parsers.")]: assert not r.status def test_i_can_recognize_concept_from_string(self): sheerka = self.get_sheerka() sheerka.test_only_add_in_cache(Concept("one", body="1")) res = sheerka.evaluate_user_input("'one'") assert len(res) == 1 assert res[0].status assert res[0].body == "one" res = sheerka.evaluate_user_input("eval 'one'") assert len(res) == 1 assert res[0].status assert res[0].body == "one" @pytest.mark.parametrize("expression", [ "def concept twenties from bnf 'twenty' (one | two)=unit as 20 + unit", "def concept twenties from bnf 'twenty' (one | two)=unit as twenty + unit", "def concept twenties from bnf twenty (one | two)=unit as 20 + unit", "def concept twenties from bnf twenty (one | two)=unit as twenty + unit", ]) def test_i_can_evaluate_bnf_concepts(self, expression): sheerka = self.get_sheerka() sheerka.evaluate_user_input("def concept one as 1") sheerka.evaluate_user_input("def concept two as 2") sheerka.evaluate_user_input("def concept twenty as 20") sheerka.evaluate_user_input(expression) res = sheerka.evaluate_user_input("eval twenty one") assert len(res) == 1 assert res[0].status assert res[0].body == 21 def test_i_can_use_where_in_bnf(self): init = [ "def concept one as 1", "def concept two as 2", "def concept three as 3", "def concept twenty as 20", "def concept number", "set_isa(one, number)", "set_isa(two, number)", "set_isa(three, number)", "def concept twenties from bnf twenty number where number <= 2 as twenty + number" ] sheerka = self.init_scenario(init, global_truth=True) context = self.get_context(sheerka) res = sheerka.evaluate_user_input("twenty one") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, "twenties") res = sheerka.evaluate_user_input("eval twenty one") assert len(res) == 1 assert res[0].status assert res[0].body == 21 res = sheerka.evaluate_user_input("twenty two") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, "twenties") res = sheerka.evaluate_user_input("eval twenty two") assert len(res) == 1 assert res[0].status assert res[0].body == 22 res = sheerka.evaluate_user_input("twenty three") assert sheerka.has_error(context, res, __type=BuiltinConcepts.CONDITION_FAILED) res = sheerka.evaluate_user_input("eval twenty three") assert sheerka.has_error(context, res, __type=BuiltinConcepts.CONDITION_FAILED) def test_i_can_manage_some_type_of_infinite_recursion(self): sheerka = self.get_sheerka() sheerka.evaluate_user_input("def concept one as 1") sheerka.evaluate_user_input("def concept 1 as one") res = sheerka.evaluate_user_input("one + 1") assert len(res) == 1 assert res[0].status assert res[0].body == 2 res = sheerka.evaluate_user_input("1 + 1") assert len(res) == 1 assert res[0].status assert res[0].body == 2 def test_i_can_evaluate_bnf_concept_with_where_clause(self): sheerka = self.get_sheerka() sheerka.evaluate_user_input("def concept a from bnf 'bar' | 'baz'") sheerka.evaluate_user_input("def concept b as 'hello world'") sheerka.evaluate_user_input("def concept foobar from bnf 'foo' a where a=='bar' as b") res = sheerka.evaluate_user_input("foo bar") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, "foobar") assert res[0].body.body == NotInit res = sheerka.evaluate_user_input("eval foo bar") assert len(res) == 1 assert res[0].status assert res[0].body == "hello world" res = sheerka.evaluate_user_input("foo baz") assert sheerka.has_error(self.get_context(sheerka), res, __type=BuiltinConcepts.CONDITION_FAILED) res = sheerka.evaluate_user_input("eval foo baz") assert sheerka.has_error(self.get_context(sheerka), res, __type=BuiltinConcepts.CONDITION_FAILED) res = sheerka.evaluate_user_input("c:foobar:") # where clause must not be evaluated assert len(res) == 1 assert res[0].status res = sheerka.evaluate_user_input("foobar") # where clause must not be evaluated assert len(res) == 1 assert res[0].status res = sheerka.evaluate_user_input("eval foobar") # where clause is forced assert len(res) == 1 # error assert not res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.CONDITION_FAILED) @pytest.mark.skip("Not ready for that") def test_i_can_manage_missing_variables_from_bnf_parsing(self): definitions = [ "def concept one as 1", "def concept number", "set_isa(one, number)", "def concept hundreds from bnf number=n1 'hundred' ('and' number=n2)? where n1<10 and n2<100 as n1 * 100 + n2", ] sheerka = self.init_scenario(definitions) res = sheerka.evaluate_user_input("one hundred") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, "hundreds") res = sheerka.evaluate_user_input("eval one hundred") assert len(res) == 1 assert res[0].status assert res[0].body == "100" def test_i_can_say_than_bnf_concept_isa_another_concept(self): sheerka = self.get_sheerka() sheerka.evaluate_user_input("def concept number") sheerka.evaluate_user_input("def concept one as 1") sheerka.evaluate_user_input("def concept two as 2") sheerka.evaluate_user_input("def concept twenties from bnf 'twenty' (one|two)=unit as 20 + unit") res = sheerka.evaluate_user_input("global_truth(set_isa(twenties, number))") assert len(res) == 1 assert res[0].status twenties = sheerka.get_by_key("twenties") number = sheerka.get_by_key("number") assert sheerka.isa(twenties, number) def test_i_can_mix_sya_concepts_and_bnf_concept(self): definitions = [ "def concept one as 1", "def concept two as 2", "def concept three as 3", "def concept plus from a plus b as a + b", "def concept mult from a mult b as a * b", "def concept twenties from bnf 'twenty' (one|two)=unit as 20 + unit", "set_is_greater_than(BuiltinConcepts.PRECEDENCE, mult, plus, 'Sya')" ] sheerka = self.init_scenario(definitions) res = sheerka.evaluate_user_input("eval one plus two mult three") assert len(res) == 1 assert res[0].status assert res[0].body == 7 res = sheerka.evaluate_user_input("eval two mult three plus one") assert len(res) == 1 assert res[0].status assert res[0].body == 7 res = sheerka.evaluate_user_input("eval 1 plus two mult 3") assert len(res) == 1 assert res[0].status assert res[0].body == 7 res = sheerka.evaluate_user_input("eval 2 mult 3 plus one") assert len(res) == 1 assert res[0].status assert res[0].body == 7 res = sheerka.evaluate_user_input("eval twenty two plus 1") assert len(res) == 1 assert res[0].status assert res[0].body == 23 res = sheerka.evaluate_user_input("eval 1 plus twenty two") assert len(res) == 1 assert res[0].status assert res[0].body == 23 res = sheerka.evaluate_user_input("eval twenty one plus twenty two") assert len(res) == 1 assert res[0].status assert res[0].body == 43 res = sheerka.evaluate_user_input("eval twenty two plus twenty one mult two") assert len(res) == 1 assert res[0].status assert res[0].body == 64 def test_concepts_parsed_by_atom_parser_must_not_be_evaluated(self): definitions = [ "def concept mult from a mult b as a * b", ] sheerka = self.init_scenario(definitions) res = sheerka.evaluate_user_input("eval mult") assert res[0].status assert isinstance(res[0].body, Concept) @pytest.mark.skip("Need to be fixed") def test_concepts_parsed_by_atom_parser_must_not_be_evaluated_2(self): definitions = [ "def concept a mult b as a * b", ] sheerka = self.init_scenario(definitions) res = sheerka.evaluate_user_input("eval a mult b") assert res[0].status assert isinstance(res[0].body, Concept) def test_i_can_express_comparison(self): definitions = [ "def concept one", "def concept two", "def concept three", "def concept four", ] sheerka = self.init_scenario(definitions) res = sheerka.evaluate_user_input("set_is_greater_than('some_prop', two, one)") assert res[0].status res = sheerka.evaluate_user_input("set_is_less_than('some_prop', two, three)") assert res[0].status res = sheerka.evaluate_user_input("get_weights('some_prop')") assert res[0].status assert res[0].body == {'c:one|1001:': 1, 'c:two|1002:': 2, 'c:three|1003:': 3} # test i use a concept to define relation sheerka.evaluate_user_input("def concept a > b as set_is_greater_than('some_prop', a, b)") res = sheerka.evaluate_user_input("eval four > three") assert res[0].status assert sheerka.get_weights("some_prop") == {'c:one|1001:': 1, 'c:two|1002:': 2, 'c:three|1003:': 3, 'c:four|1004:': 4} def test_i_can_evaluate_expression_when_using_token_concept(self): sheerka, context, one, two, three, is_less_than = self.init_concepts( Concept("one", body="1"), Concept("two", body="2"), Concept("three", body="3"), self.from_def_concept("<", "a < b", ["a", "b"], body="set_is_less_than('some_prop', a, b)") ) expression = "c:one: < c:two:" res = sheerka.evaluate_user_input(expression) assert res[0].status compare_with_test_object(res[0].body, CMV(is_less_than, a="c:one:", b="c:two:")) assert res[0].body.a == NotInit # concept is not evaluated assert res[0].body.b == NotInit # concept is not evaluated expression = "eval c:one: < c:two:" res = sheerka.evaluate_user_input(expression) assert res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.SUCCESS) assert sheerka.get_weights("some_prop") == {'c:one|1001:': 1, 'c:two|1002:': 2} # it now also works using the concepts names expression = "eval two < three" res = sheerka.evaluate_user_input(expression) assert res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.SUCCESS) assert sheerka.get_weights("some_prop") == {'c:one|1001:': 1, 'c:two|1002:': 2, 'c:three|1003:': 3} def test_i_can_detect_multiple_errors_when_evaluating_a_concept(self): sheerka, context, foo, plus_one = self.init_concepts( Concept("foo", body="'string'"), Concept("a plus one", body="a + 1").def_var("a") ) res = sheerka.evaluate_user_input("eval foo plus one") assert not res[0].status assert context.sheerka.isinstance(res[0].body, BuiltinConcepts.CONCEPT_EVAL_ERROR) assert context.sheerka.isinstance(res[0].body.body, BuiltinConcepts.ERROR) error0 = res[0].body.body.body[0] assert isinstance(error0, PythonEvalError) assert isinstance(error0.error, TypeError) assert error0.error.args[0] == "unsupported operand type(s) for +: 'Concept' and 'int'" error1 = res[0].body.body.body[1] assert isinstance(error1, PythonEvalError) assert isinstance(error1.error, TypeError) assert error1.error.args[0] == 'can only concatenate str (not "int") to str' def test_i_can_evaluate_bnf_concept_defined_with_group_after_restart(self): """ BNF Concepts defined with group and being themselves part a s group get messed up after restart :return: """ init = [ "def concept one as 1", "def concept two as 2", "def concept twenty as 20", "def concept number", "set_isa(one, number)", "set_isa(two, number)", "set_isa(twenty, number)", "def concept twenties from bnf twenty number where number < 10 as twenty + number", "set_isa(twenties, number)", ] sheerka = self.init_scenario(init, global_truth=True) # simulate that sheerka was stopped and restarted sheerka.clear_bnf_definition() sheerka.om.get(SheerkaConceptManager.CONCEPTS_BY_KEY_ENTRY, "twenties").set_compiled({}) res = sheerka.evaluate_user_input("eval twenty one") assert res[0].status assert res[0].body == 21 def test_i_can_use_last_created_concept_to_define_set(self): init = [ "def concept number", "def concept one", ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("set_isa(last_created_concept(), number)") assert res[0].status assert sheerka.isinstance(res[0].body, "one") assert sheerka.isa(res[0].body, sheerka.new("number")) def test_i_can_evaluate_sya_and_ret_concepts(self): init = [ "def concept one as 1", "def concept plus from a plus b as a + b", "def concept the a ret a" ] sheerka = self.init_scenario(init) the = sheerka.get_by_name("the a") res = sheerka.evaluate_user_input("one plus the one") assert res[0].status plus = res[0].body assert isinstance(plus, Concept) assert plus.name == "plus" assert plus.get_compiled()["a"] == sheerka.new("one") compare_with_test_object(plus.get_compiled()["b"], CC(the, a=sheerka.new("one"))) res = sheerka.evaluate_user_input("eval one plus the one") assert res[0].status assert res[0].body == 2 def test_i_can_evaluate_command(self): init = [ "def concept command as 'Executed !'", "set_isa(c:command:, __AUTO_EVAL)", ] # Since command is a __COMMAND, the body is auto evaluated # and we return the body, not the concept sheerka = self.init_scenario(init, global_truth=True) res = sheerka.evaluate_user_input("command") assert res[0].status assert res[0].body == "Executed !" def test_i_can_manage_question(self): init = [ "def concept one", "def concept foo", "def concept number", "set_isa(one, number)", "def concept x is a y as isa(x,y) pre in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", "def concept x is a y as set_isa(x,y)", ] sheerka = self.init_scenario(init, global_truth=True) res = sheerka.evaluate_user_input("question(one is a number)") # automatically evaluated assert len(res) == 1 assert res[0].status assert res[0].body res = sheerka.evaluate_user_input("question(foo is a number)") # automatically evaluated assert len(res) == 1 assert res[0].status assert not res[0].body def test_where_clause_implicitly_infer_to_question(self): init = [ "def concept one as 1", "def concept number", "set_isa(one, number)", "def concept one as 10", # to make sure that it won't be rejected because of the cast "def concept x is a y as isa(x,y) pre in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", "def concept x is a y as set_isa(x,y)", ] # Explanations # There are two concepts 'one'. The first one is tagged as a number while the second one is not # So the first one should be picked. # the second concept 'one' 's value is an integer, to make sure that it won't be rejected because of its type sheerka = self.init_scenario(init, global_truth=True) res = sheerka.evaluate_user_input("def concept x plus y where x is a number as x + y") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.NEW_CONCEPT) # Let's check that the concept is correctly understood res = sheerka.evaluate_user_input("eval one plus 2") assert len(res) == 1 assert res[0].status assert res[0].body == 3 # it means that the first 'one' was chosen def test_i_can_manage_concept_that_refers_to_question(self): init = [ "def concept one", "def concept foo", "def concept number", "set_isa(one, number)", "def concept q from q ? as question(q) auto_eval True", "def concept is_a from x is a y as isa(x,y) pre in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", "set_is_greater_than(BuiltinConcepts.PRECEDENCE, c:is_a:, c:q:, 'Sya')", ] sheerka = self.init_scenario(init, global_truth=True) res = sheerka.evaluate_user_input("one is a number ?") # automatically evaluated assert len(res) == 1 assert res[0].status assert isinstance(res[0].body, bool) and res[0].body # the body MUST be a boolean res = sheerka.evaluate_user_input("foo is a number ?") # automatically evaluated assert len(res) == 1 assert res[0].status assert isinstance(res[0].body, bool) and not res[0].body # the body MUST be a boolean # x is a y is supposed to be a question. It cannot be used if not in a context of a question res = sheerka.evaluate_user_input("one is a number") assert sheerka.has_error(self.get_context(sheerka), res, __type=BuiltinConcepts.CONDITION_FAILED) def test_i_can_evaluate_source_code_with_concept(self): init = [ "def concept the a ret a", ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("desc(c:the a:)") assert len(res) == 1 assert res[0].status def test_i_can_parse_concept_with_variables_using_short_name(self): init = [ "def concept foo from a foo b where a,b", "def concept bar from bar a where a", "def concept baz from a baz where a", ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("desc(foo)") assert len(res) == 1 assert res[0].status res = sheerka.evaluate_user_input("desc(bar)") assert len(res) == 1 assert res[0].status res = sheerka.evaluate_user_input("desc(baz)") assert len(res) == 1 assert res[0].status def test_i_can_define_a_concept_when_where_clause_contains_the_name_of_the_variable(self): init = [ "def concept x is a y as isa(x,y) pre is_question()", ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("def concept a x b where a is a number as a + b") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.NEW_CONCEPT) def test_bnf_node_parsers_are_updated_when_concepts_are_modified(self): init = [ "def concept number", "def concept one", "def concept twenties from bnf 'twenty' number as 20 + number", ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("twenty one") assert len(res) > 1 # not recognized sheerka.evaluate_user_input("global_truth(set_isa(one, number))") res = sheerka.evaluate_user_input("twenty one") assert len(res) == 1 assert res[0].status def test_i_can_set_attribute(self): init = [ "def concept size", "def concept adjective", "def concept beautiful", "def concept little", "def concept house", "def concept little x as set_attr(x, size, little) ret x", "def concept beautiful x as set_attr(x, adjective, beautiful) ret x", ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("eval beautiful little house") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, "house") assert sheerka.get_attr(res[0].body, sheerka.new("size")) == sheerka.new("little") assert sheerka.get_attr(res[0].body, sheerka.new("adjective")) == sheerka.new("beautiful") def test_i_can_display_multiple_concepts_using_desc_command(self): init = [ "def concept foo as 1", "def concept foo as 2", ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("desc(foo)") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.TO_MULTI) def test_i_can_evaluate_pseudo_recursive_definition(self): init = [ "def concept a + b as a + b", ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("eval 1 + 1") assert len(res) == 1 assert res[0].status assert res[0].body == 2 def test_i_can_parse_when_multiple_ontology_layers(self): init = [ "def concept one as 1", "def concept two as 2", "def concept a plus b as a + b", # sya node "def concept twenties from bnf 'twenty' (one | two)=unit as 20 + unit", # bnf node ] sheerka = self.init_scenario(init) sheerka.push_ontology(self.get_context(sheerka), "new ontology") res = sheerka.evaluate_user_input("eval one") assert len(res) == 1 assert res[0].status assert res[0].body == 1 res = sheerka.evaluate_user_input("eval one plus two") assert len(res) == 1 assert res[0].status assert res[0].body == 3 res = sheerka.evaluate_user_input("eval twenty one") assert len(res) == 1 assert res[0].status assert res[0].body == 21 def test_i_can_define_rules_priorities(self): sheerka, context, r1, r2 = self.init_test().with_format_rules(("True", "True"), ("False", "False")).unpack() sheerka.evaluate_user_input( "def concept rule x > rule y where isinstance(x, int) and isinstance(y, int) as set_is_greater_than(__PRECEDENCE, r:|x:, r:|y:, 'Rule')") res = sheerka.evaluate_user_input(f"eval rule {r1.id} > rule {r2.id}") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.SUCCESS) weights = sheerka.get_weights(BuiltinConcepts.PRECEDENCE, 'Rule') assert weights[r1.str_id] == weights[r2.str_id] + 1 def test_i_can_get_history_after_def_rule_parser(self): sheerka = self.get_sheerka() sheerka.save_execution_context = True sheerka.evaluate_user_input("when True then answer('that is true')") res = sheerka.evaluate_user_input("history()") assert len(res) == 1 assert res[0].status l = list(res[0].body.body) assert len(l) > 0 sheerka.save_execution_context = False def test_i_can_define_a_regex_concept_and_parse_it(self): sheerka, context = self.init_test().unpack() res = sheerka.evaluate_user_input("def concept binary from bnf r'[01]+'") assert len(res) == 1 assert res[0].status res = sheerka.evaluate_user_input("01001") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, "binary") def test_parsing_expression_are_dynamic(self): init = [ "def concept two", "def concept number", "def concept nb times from bnf number 'times'", "set_isa(two, number)", # defined after 'nb times' ] sheerka = self.init_scenario(init, global_truth=True) res = sheerka.evaluate_user_input("two times") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].body, "nb times") def test_i_can_evaluate_when_body_is_a_function_that_references_inner_variables(self): init = [ "def concept two as 2", "def concept number", "set_isa(two, number)", "def concept cars", "def concept quantify x from bnf number x as set_attr(x, 'qty', number) ret x" ] sheerka = self.init_scenario(init, global_truth=True) res = sheerka.evaluate_user_input("eval two cars") assert len(res) == 1 assert res[0].status assert sheerka.objvalue(res[0].body.get_value("qty")) == 2 def test_i_can_evaluate_when_body_is_a_function_that_references_inner_variables_using_alias(self): init = [ "def concept two as 2", "def concept number", "set_isa(two, number)", "def concept cars", "def concept quantify x from bnf number=n1 x as set_attr(x, 'qty', n1) ret x" ] sheerka = self.init_scenario(init, global_truth=True) res = sheerka.evaluate_user_input("eval two cars") assert len(res) == 1 assert res[0].status assert sheerka.objvalue(res[0].body.get_value("qty")) == 2 def test_i_can_implement_the_concept_and(self): # Normally, redefining 'and' leads to a circular ref between the concept and the python init = [ "def concept x and y as x and y", "set_is_lesser(__PRECEDENCE, c:x and y:, 'Sya')", ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("def concept foo x y where x > 1 and y > 2") assert len(res) == 1 assert res[0].status def test_i_can_use_result_from_memory_filtering(self): init = [ "def concept female", "def concept girl", "set_isa(girl, female)", "def concept she ret memory('isa(self, female)')", "girl" ] sheerka = self.init_scenario(init, global_truth=True) context = self.get_context(sheerka) res = sheerka.evaluate_user_input("set_attr(she, 'my_attr', 'my value')") assert len(res) == 1 assert res[0].status girl_from_memory = sheerka.get_last_from_memory(context, "girl") assert girl_from_memory.obj.get_value("my_attr") == "my value" def test_i_can_use_result_from_memory_filtering_within_other_concept(self): init = [ "def concept female", "def concept girl", "set_isa(girl, female)", "def concept she ret memory('isa(self, female)')", "def concept x attribute y equals z as set_attr(x, y, z)", "girl" ] sheerka = self.init_scenario(init, global_truth=True) context = self.get_context(sheerka) res = sheerka.evaluate_user_input("eval she attribute 'my_attr' equals 'my value'") assert len(res) == 1 assert res[0].status girl_from_memory = sheerka.get_last_from_memory(context, "girl") assert girl_from_memory.obj.get_value("my_attr") == "my value" def test_i_cannot_use_method_that_alter_the_global_state_within_question(self): init = [ "def concept foo as question(set_debug(True))", ] sheerka = self.init_scenario(init) context = self.get_context(sheerka) res = sheerka.evaluate_user_input("question(set_debug(True))") assert sheerka.has_error(context, res, __type="MethodAccessError") res = sheerka.evaluate_user_input("eval foo") assert sheerka.has_error(context, res, __type="MethodAccessError") def test_i_can_use_function_parser_and_complex_concept(self): init = [ "def concept the x ret x", "def concept foo", "def concept bar", ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("smart_get_attr(the foo, bar)") assert len(res) == 1 assert res[0].status def test_i_can_get_smart_get_attr_for_complex_concepts(self): init = [ "def concept q from q ? as question(q) auto_eval True", "set_is_lesser(__PRECEDENCE, q, 'Sya')", "def concept a x ret x where isinstance(x, Concept)", "def concept the x ret memory(x)", "def concept short", "def concept color", "def concept red", "def concept blue", "def concept adjective", "set_isa(red, color)", "set_isa(blue, color)", "set_isa(color, adjective)", "def concept what is the x of y pre is_question() as smart_get_attr(y, x)", "def concept qualify x from bnf adjective x as set_attr(x, c:adjective:, adjective) ret x", ] sheerka = self.init_scenario(init, global_truth=True) res = sheerka.evaluate_user_input("eval a red short") res = sheerka.evaluate_user_input("what is the color of the short ?") assert len(res) == 1 assert res[0].status assert res[0].value == "red" sheerka.evaluate_user_input("eval a blue short") res = sheerka.evaluate_user_input("what is the color of the short ?") assert len(res) == 1 assert res[0].status assert res[0].value == "blue" def test_i_can_access_a_concept_after_a_global_set_attr(self): init = [ "def concept foo", "def concept color", "def concept red", "global_truth(set_attr(foo, color, red))" ] sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("inspect(foo)") assert len(res) == 1 assert res[0].status