import pytest from core.builtin_concepts import BuiltinConcepts from core.concept import Concept, PROPERTIES_TO_SERIALIZE, simplec, CMV, NotInit, CC from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator from evaluators.OneSuccessEvaluator import OneSuccessEvaluator from evaluators.PythonEvaluator import PythonEvalError from parsers.BaseNodeParser import SyaAssociativity from parsers.BnfNodeParser import Sequence, StrMatch, OrderedChoice, Optional, ConceptExpression from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka 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.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) assert evaluated == simplec("one", 1) def test_i_can_recognize_concept_with_concept_body(self): sheerka = self.get_sheerka() concept_one = Concept(name="one") concept_un = Concept(name="un", body="one") sheerka.add_in_cache(concept_one) sheerka.add_in_cache(concept_un) 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) assert evaluated == simplec("un", simplec("one", NotInit)) def test_i_can_recognize_concept_with_no_body(self): sheerka = self.get_sheerka() concept = Concept(name="one") sheerka.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.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.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 assert sheerka.has_key(concept_saved.key) assert sheerka.has_id(concept_saved.id) assert sheerka.has_name(concept_saved.name) assert sheerka.has_hash(concept_saved.get_definition_hash()) assert sheerka.cache_manager.copy(sheerka.CONCEPTS_BY_FIRST_KEYWORD_ENTRY) == {'+': ['1001']} # sdp is up to date assert sheerka.sdp.exists(sheerka.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.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" 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.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_concepts( "foo", Concept(name="hello a").def_var("a"), create_new=True) 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 = self.get_sheerka() hello_a = sheerka.add_in_cache(Concept(name="hello a", body="'hello ' + a").def_var("a")) sheerka.add_in_cache(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_metadata().is_evaluated assert evaluated.get_value("a") == simplec("foo", "foo") assert evaluated.get_value("a").get_metadata().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_metadata().is_evaluated assert evaluated.get_value("a") == sheerka.new(concept_a.key, body="one").init_key() assert evaluated.get_value("a").get_metadata().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_metadata().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_metadata().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() 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 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("set_isa(one, number)") sheerka.evaluate_user_input("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() 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 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) 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.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.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): sheerka = self.get_sheerka() 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" ] for exp in init: sheerka.evaluate_user_input(exp) 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 len(res) == 1 assert not res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.MULTIPLE_ERRORS) assert str(BuiltinConcepts.CONDITION_FAILED) in [error.key for error in sheerka.get_error(res[0].body.body)] res = sheerka.evaluate_user_input("eval twenty three") assert len(res) == 1 assert not res[0].status assert str(BuiltinConcepts.CONDITION_FAILED) in [error.key for error in sheerka.get_error(res[0].body.body)] 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 len(res) == 1 assert not res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.CONDITION_FAILED) res = sheerka.evaluate_user_input("eval foo baz") assert len(res) == 1 assert not res[0].status assert sheerka.isinstance(res[0].body, BuiltinConcepts.CONDITION_FAILED) res = sheerka.evaluate_user_input("foobar") assert len(res) == 1 assert res[0].status res = sheerka.evaluate_user_input("eval foobar") 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("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", ] sheerka = self.init_scenario(definitions) context = self.get_context(sheerka) sheerka.force_sya_def(context, [ (sheerka.get_by_name("mult").id, 20, SyaAssociativity.Right), (sheerka.get_by_name("plus").id, 10, SyaAssociativity.Right), ]) 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", "def concept 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) # 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_concepts_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_concepts_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 assert 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_concepts_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_concepts_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) # simulate that sheerka was stopped and restarted sheerka.cache_manager.clear(sheerka.CONCEPTS_GRAMMARS_ENTRY) sheerka.cache_manager.get(sheerka.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.isa(sheerka.new("one"), 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") assert 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) 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) 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) 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)", "set_auto_eval(q)", "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) res = sheerka.evaluate_user_input("one is a number ?") # automatically evaluated assert len(res) == 1 assert res[0].status assert res[0].body == True # 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 res[0].body == False # 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 len(res) == 1 assert not res[0].status assert sheerka.isinstance(res[0].body, 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(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 sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("desc(bar)") assert len(res) == 1 assert res[0].status sheerka = self.init_scenario(init) res = sheerka.evaluate_user_input("desc(baz)") assert len(res) == 1 assert res[0].status def test_i_can_eval_concepts_fed_with_functions(self): init = [ "def concept inc a as a + 1", "def concept one as 1" ] def times_five(i): return i * 5 sheerka = self.init_scenario(init) sheerka.locals["times_five"] = times_five res = sheerka.evaluate_user_input("eval inc times_five(one)") assert len(res) == 1 assert res[0].status assert res[0].body == 6 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 sheerka.evaluate_user_input("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") class TestSheerkaNonRegFile(TestUsingFileBasedSheerka): def test_i_can_def_several_concepts(self): sheerka = self.get_sheerka() sheerka.evaluate_user_input("def concept foo") sheerka = self.get_sheerka() res = sheerka.evaluate_user_input("def concept bar") assert len(res) == 1 assert res[0].status assert res[0].body.body.id == "1002" def test_i_can_create_concept_with_bnf_definition(self): sheerka = self.get_sheerka() concept_a = self.bnf_concept("a", expression=OrderedChoice(StrMatch("one"), StrMatch("two"))) sheerka.create_new_concept(self.get_context(sheerka), concept_a) res = sheerka.evaluate_user_input("def concept plus from bnf a ('plus' plus)?") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT) saved_concept = sheerka.sdp.get(sheerka.CONCEPTS_BY_KEY_ENTRY, "plus") assert saved_concept.key == "plus" assert saved_concept.get_metadata().definition == "a ('plus' plus)?" assert "a" in saved_concept.values() assert "plus" in saved_concept.values() expected_bnf = Sequence( ConceptExpression(concept_a, rule_name="a"), Optional(Sequence(StrMatch("plus"), ConceptExpression("plus")))) new_concept = res[0].value.body assert new_concept.get_metadata().name == "plus" assert new_concept.get_metadata().definition == "a ('plus' plus)?" assert new_concept.get_bnf() == expected_bnf assert "a" in new_concept.values() assert "plus" in new_concept.values() def test_i_can_recognize_bnf_definitions_from_separate_instances(self): sheerka = self.get_sheerka() concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' 'two'")[0].body.body sheerka = self.get_sheerka() res = sheerka.evaluate_user_input("one two") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_a) # add another bnf definition concept_b = sheerka.evaluate_user_input("def concept b from bnf a 'three'")[0].body.body sheerka = self.get_sheerka() res = sheerka.evaluate_user_input("one two") # previous one still works assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_a) res = self.get_sheerka().evaluate_user_input("one two three") # new one works assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_b) evaluated = sheerka.evaluate_concept(self.get_context(eval_body=True), res[0].value) assert evaluated.body == "one two three" assert evaluated.get_value("a") == sheerka.new(concept_a.key, body="one two").init_key() def test_i_can_eval_sophisticated_bnf_concepts_after_restart(self): self.init_scenario([ "def concept one as 1", "def concept number", "set_isa(one, number)", "def concept twenty as 20", "set_isa(twenty, number)", "def concept twenties from bnf twenty number where number < 10 as twenty + number", "set_isa(twenties, number)", "def concept thirty as 30", "set_isa(thirty, number)", "def concept thirties from bnf thirty number where number < 10 as thirty + number", "set_isa(thirties, number)", ]) sheerka = self.get_sheerka() # another instance assert sheerka.evaluate_user_input("eval twenty one")[0].body == 21 assert sheerka.evaluate_user_input("eval thirty one")[0].body == 31