import ast import pytest from core.builtin_concepts import ReturnValueConcept from core.builtin_concepts_ids import BuiltinConcepts from core.builtin_helpers import ensure_evaluated from core.concept import Concept, DEFINITION_TYPE_DEF from core.rule import Rule from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules from core.sheerka.services.SheerkaExecute import ParserInput from core.sheerka.services.SheerkaRuleManager import ReteConditionExprVisitor, PythonConditionExprVisitor, \ CompiledCondition from core.sheerka.services.sheerka_service import FailedToCompileError from evaluators.PythonEvaluator import PythonEvaluator from parsers.BaseParser import ErrorSink from parsers.ExpressionParser import ExpressionParser from parsers.PythonParser import PythonNode from sheerkapython.python_wrapper import Expando from sheerkarete.network import ReteNetwork from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.core.test_SheerkaRuleManager import PYTHON_EVALUATOR_NAME from tests.parsers.parsers_utils import get_rete_conditions, NEGCOND class BaseTestSheerkaRuleManagerRulesCompilation(TestUsingMemoryBasedSheerka): @staticmethod def get_conditions(context, expression): parser = ExpressionParser() error_sink = ErrorSink() parser_input = ParserInput(expression) parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) visitor = PythonConditionExprVisitor(context) conditions = visitor.get_conditions(parsed) return conditions @staticmethod def check_against_rete(rule_expression, rule_conditions, objects): """ :param rule_expression: :param rule_conditions: :param objects: list of tuple(name, obj) :return: """ # check against a Rete network network = ReteNetwork() rule = Rule("test", rule_expression, None) rule.metadata.id = 9999 rule.metadata.is_compiled = True rule.metadata.is_enabled = True rule.rete_disjunctions = rule_conditions network.add_rule(rule) for name, value in objects.items(): network.add_obj(name, value) matches = list(network.matches) assert len(matches) == 1 @staticmethod def check_against_python(context, rule_expression, rule_conditions, objects, expected_result=True): bag = {} for name, value in objects.items(): bag[name] = value evaluate_rules_service = context.sheerka.services[SheerkaEvaluateRules.NAME] rule = Rule(name="test", predicate=rule_expression) rule.compiled_conditions = rule_conditions rule.metadata.is_enabled = True rule.metadata.is_compiled = True evaluation = evaluate_rules_service.evaluate_rules(context, [rule], bag, set()) assert expected_result in evaluation and len(evaluation[expected_result]) == 1 @staticmethod def evaluate_condition(context, expression, condition, objects): with context.push("Testing conditions SheerkaRuleManagerRulesCompilation", expression) as sub_context: sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED) sub_context.protected_hints.add(BuiltinConcepts.EVAL_WHERE_REQUESTED) sub_context.protected_hints.add(BuiltinConcepts.EVAL_UNTIL_SUCCESS_REQUESTED) sub_context.protected_hints.add(BuiltinConcepts.EVAL_QUESTION_REQUESTED) sub_context.sheerka.add_many_to_short_term_memory(sub_context, objects) evaluator = PythonEvaluator() for c in condition.concepts_to_reset: c.get_hints().is_evaluated = False return evaluator.eval(sub_context, condition.return_value) @classmethod def get_testing_objects(cls, context, objects_names, objects_mappings=None): if objects_names is None: return {} result = {} for name in objects_names: if isinstance(name, tuple): name, value = name if value == "sheerka": value = context.sheerka else: value = None if objects_mappings and value in objects_mappings: value = objects_mappings[value] if name == "__ret": return_value = ReturnValueConcept("Test", True, value) result["__ret"] = return_value elif name == "__ret.status": return_value = ReturnValueConcept("Test", True, value) result["__ret.status"] = return_value.status elif name == "body": return_value = ReturnValueConcept("Test", True, value) result["body"] = return_value.body elif name == "a_string": result["a_string"] = value or "hello world!" elif name == "an_int": result["an_int"] = value or 10 else: result[name] = value return result @staticmethod def get_variables_names_from_expected_variables(expected_variables): return {v[0] if isinstance(v, tuple) else v for v in expected_variables} @staticmethod def func_true(*args, **kwargs): return True @staticmethod def func_identity(x): return x def validate_python_test(self, context, expression, expected_compiled, expected_text, expected_variables, expected_not_variables, expected_objects): sheerka = context.sheerka conditions = BaseTestSheerkaRuleManagerRulesCompilation.get_conditions(context, expression) assert len(conditions) == 1 assert isinstance(conditions[0], CompiledCondition) if expected_compiled is None: # manage cases where we only check for variable existence assert conditions[0].evaluator_type is None assert conditions[0].return_value is None else: # check what was compiled ast_ = ast.parse(expected_compiled, "", 'exec' if "\n" in expected_compiled else 'eval') expected_python_node = PythonNode(expected_compiled, ast_, expected_text) assert conditions[0].evaluator_type == PYTHON_EVALUATOR_NAME assert sheerka.isinstance(conditions[0].return_value, BuiltinConcepts.RETURN_VALUE) assert sheerka.objvalue(conditions[0].return_value) == expected_python_node # check that variables detected assert conditions[0].variables == self.get_variables_names_from_expected_variables(expected_variables) # check that variables that MUST not be present assert conditions[0].not_variables == expected_not_variables # check the objects returned assert len(expected_objects) == len(conditions[0].objects) for obj in expected_objects: if isinstance(obj, tuple): assert conditions[0].objects[obj[0]] == obj[1] else: assert obj in conditions[0].objects return conditions class TestSheerkaRuleManagerRulesCompilationExists(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing variable existence: __ret (exists) __ret.status (exists) body (exists) __ret and __ret.status (both exist) """ @pytest.mark.parametrize("expression, expected_as_list_of_str, expected_variables", [ ( "__ret", ["#__x_00__|__name__|'__ret'"], {"__ret"} ), ( "__ret.status", ["#__x_00__|__name__|'__ret.status'"], {"__ret.status"} ), ( "body", ["#__x_00__|__name__|'body'"], {"body"} ), ( "__ret and __ret.status", ["#__x_00__|__name__|'__ret'", "#__x_01__|__name__|'__ret.status'"], {"__ret", "__ret.status"} ), ]) def test_rete(self, expression, expected_as_list_of_str, expected_variables): sheerka, context = self.init_test().unpack() parser = ExpressionParser() expected = get_rete_conditions(*expected_as_list_of_str) error_sink = ErrorSink() parser_input = ParserInput(expression) parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) visitor = ReteConditionExprVisitor(context) conditions = visitor.get_conditions(parsed) assert conditions == [expected] # check against a Rete network objects = self.get_testing_objects(context, expected_variables) self.check_against_rete(expression, conditions, objects) @pytest.mark.parametrize("expression, expected_variables", [ ("__ret", {"__ret"}), ("__ret.status", {"__ret.status"}), ("body", {"body"}), ("__ret and __ret.status", {"__ret", "__ret.status"}) ]) def test_python(self, expression, expected_variables): sheerka, context = self.init_test().unpack() conditions = self.validate_python_test(context, expression, None, None, expected_variables, set(), set()) # check against SheerkaEvaluateRules objects = self.get_testing_objects(context, expected_variables) self.check_against_python(context, expression, conditions, objects) class TestSheerkaRuleManagerRulesCompilationNotExists(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing NOT existence not __ret not not __ret not __ret.status not body not __ret and not __ret.status __ret and not ret.status __ret and not __error """ @pytest.mark.parametrize("expression, expected_as_list_of_str", [ ("not __ret", [NEGCOND("#__x_00__|__name__|'__ret'")]), ("not not __ret", ["#__x_00__|__name__|'__ret'"]), ("not __ret.status", [NEGCOND("#__x_00__|__name__|'__ret.status'")]), ("not body", [NEGCOND("#__x_00__|__name__|'body'")]), ( "not __ret and not __ret.status", [NEGCOND("#__x_00__|__name__|'__ret'"), NEGCOND("#__x_01__|__name__|'__ret.status'")] ), ( "__ret and not __ret.status", ["#__x_00__|__name__|'__ret'", NEGCOND("#__x_01__|__name__|'__ret.status'")] ), ( "__ret and not __error", ["#__x_00__|__name__|'__ret'", NEGCOND("#__x_01__|__name__|'__error'")] ), ]) def test_rete(self, expression, expected_as_list_of_str): sheerka, context = self.init_test().unpack() parser = ExpressionParser() expected = get_rete_conditions(*expected_as_list_of_str) error_sink = ErrorSink() parser_input = ParserInput(expression) parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) visitor = ReteConditionExprVisitor(context) conditions = visitor.get_conditions(parsed) assert conditions == [expected] # # check against a Rete network # objects = self.get_testing_objects(context, expected_variables) # self.check_against_rete(expression, conditions, objects) @pytest.mark.parametrize("expression, expected_variables, expected_not_variables", [ ("not __ret", set(), {"__ret"}), ("not not __ret", {"__ret"}, set()), ("not __ret.status", set(), {"__ret.status"}), ("not body", set(), {"body"}), ("not __ret and not __ret.status", set(), {"__ret", "__ret.status"}), ("__ret and not __ret.status", {"__ret"}, {"__ret.status"}), ("__ret and not __error", {"__ret"}, {"__error"}), ]) def test_python(self, expression, expected_variables, expected_not_variables): sheerka, context = self.init_test().unpack() conditions = self.validate_python_test(context, expression, None, None, expected_variables, expected_not_variables, set()) # check against SheerkaEvaluateRules objects = self.get_testing_objects(context, expected_variables) self.check_against_python(context, expression, conditions, objects) class TestSheerkaRuleManagerRulesCompilationSimplePython(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing Python True False 10 + 5 'hello world' a + self a + 10 a + " world !" a + foo a + twenty one a + my friend """ @pytest.mark.parametrize("expression, e_compiled, e_text, e_variables, e_objects, e_result", [ ( "True", "True", "True", set(), set(), True ), ( "False", "False", "False", set(), set(), False ), ( "10 + 5", "__o_00__", "10 + 5", set(), {("__o_00__", 15)}, 15 ), ( "'hello world'", "__o_00__", "'hello world'", set(), {("__o_00__", 'hello world')}, 'hello world' ), ( "a + self", "a + self", "a + self", {("a", 10), ("self", "foo")}, set(), 15 ), ( "a + 10", "a + 10", "a + 10", {("a", 10)}, set(), 20 ), ( "a + 'world !'", "a + 'world !'", "a + 'world !'", {("a", "hello ")}, set(), "hello world !" ), ( "a + foo", "a + foo", "a + foo", {("a", 10), ("foo", "foo")}, set(), 15 ), ( "a + twenty one", "a + __C__twenties__1004__C__", "a + twenty one", {("a", 10)}, {"__C__twenties__1004__C__"}, 31 ), ( "a + my friend", "a + __C__my0friend__1005__C__", "a + my friend", {("a", "hello ")}, {'__C__my0friend__1005__C__'}, "hello my friend" ), ]) def test_python(self, expression, e_compiled, e_text, e_variables, e_objects, e_result): sheerka, context, foo, one, two, twenties, my_friend = self.init_concepts( Concept("foo", body="5"), Concept("one", body="1"), Concept("two", body="2"), Concept("twenties", definition="'twenty' (one|two)=n", body='20 + n').def_var("n"), Concept("my friend", body="'my friend'"), create_new=True ) ensure_evaluated(context, foo, eval_body=True) ensure_evaluated(context, my_friend, eval_body=True) conditions = self.validate_python_test(context, expression, e_compiled, e_text, e_variables, set(), e_objects) # check against SheerkaEvaluateRules variables_mapping = { "foo": foo, } namespace = self.get_testing_objects(context, e_variables, variables_mapping) res = self.evaluate_condition(context, expression, conditions[0], namespace) assert res.status assert sheerka.objvalue(res) == e_result class TestSheerkaRuleManagerRulesCompilationEquality(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing simple equality: a == 10 __ret.status == True self == 'a' self == sheerka self == BuiltinConcepts.TO_DICT self == hello 'my friend' a == b """ @pytest.mark.parametrize("expression, expected_as_list_of_str, expected_variables", [ ("a == 10", ["#__x_00__|__name__|'a'", "#__x_00__|__self__|10"], {("a", 10)}), ("__ret.status == True", ["#__x_00__|__name__|'__ret'", "#__x_00__|status|True"], {"__ret"}), ("self == 'a'", ["#__x_00__|__name__|'self'", "#__x_00__|__self__|'a'"], {("self", 'a')}), ("self == sheerka", ["#__x_00__|__name__|'self'", "#__x_00__|__self__|'__sheerka__'"], {("self", "sheerka")}), ( "self == BuiltinConcepts.TO_DICT", ["#__x_00__|__name__|'self'", "#__x_00__|__self__|BuiltinConcepts.TO_DICT"], {("self", BuiltinConcepts.TO_DICT)} ), ("self == hello 'my friend'", ["#__x_00__|__name__|'self'", "#__x_00__|__is_concept__|True", "#__x_00__|key|'hello __var__0'", "#__x_00__|a|'my friend'"], {("self", "hello_my_friend")} ), # ("a == b", # ["#__x_00__|__name__|'a'", # "#__x_01__|__name__|'b'", # "#__x_00__|__self__|#__x_01__"], # {("a", 10), ("b", 10)}), ]) def test_rete(self, expression, expected_as_list_of_str, expected_variables): sheerka, context, greetings = self.init_test().with_concepts( Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), create_new=True ).unpack() parser = ExpressionParser() expected = get_rete_conditions(*expected_as_list_of_str) error_sink = ErrorSink() parser_input = ParserInput(expression) parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) visitor = ReteConditionExprVisitor(context) conditions = visitor.get_conditions(parsed) assert conditions == [expected] # check against a Rete network objects_mappings = {"hello_my_friend": sheerka.new(greetings, a='my friend')} objects = self.get_testing_objects(context, expected_variables, objects_mappings) self.check_against_rete(expression, conditions, objects) # KSI: 2021-05-06 The last test done not produce any match because the WME (b, __self__, 10) # is not added to memory. @pytest.mark.parametrize("expression, expected_compiled, expected_variables, expected_objects", [ ("a == 10", "a == __o_00__", {("a", 10)}, {("__o_00__", 10)}), ("__ret.status == True", "__ret.status == True", {"__ret"}, set()), ("self == 'a'", "self == __o_00__", {("self", 'a')}, {("__o_00__", 'a')}), ("self == sheerka", "is_sheerka(self)", {("self", "sheerka")}, {}), ( "self == BuiltinConcepts.TO_DICT", "self == BuiltinConcepts.TO_DICT", {("self", BuiltinConcepts.TO_DICT)}, set() ), ( "self == hello 'my friend'", """isinstance(self, Concept) and self.key == 'hello __var__0' and self.a == __o_01__""", {("self", "hello_my_friend")}, {("__o_01__", "my friend")} ), ("a == b", "a == b", {("a", 10), ("b", 10)}, {}), ]) def test_python(self, expression, expected_compiled, expected_variables, expected_objects): sheerka, context, greetings = self.init_test().with_concepts( Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), create_new=True ).unpack() conditions = self.validate_python_test(context, expression, expected_compiled, expression, expected_variables, set(), expected_objects) # check against SheerkaEvaluateRules objects_mappings = {"hello_my_friend": sheerka.new(greetings, a='my friend')} objects = self.get_testing_objects(context, expected_variables, objects_mappings) self.check_against_python(context, expression, conditions, objects) class TestSheerkaRuleManagerRulesCompilationOtherConditions(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing other conditions than equality a > 10 a >= 10 a < 10 a <= 10 a != 10 a > 10 and b <= 5 __ret.value > 10 10 > __ret.value a + self > 10 a + 10 > 10 a + " world !" == "hello world !" a + foo > 10 a + twenty one > 21 a + my friend == 'hello my friend' 10 < a + self 10 < a + 10 'hello world !' == a + ' world !' 10 < a + foo 10 > a + twenty one 'hello my friend' == a + my friend """ @pytest.mark.parametrize("expression, e_compiled, e_variables, e_objects, e_result", [ ("a > 10", "a > __o_00__", {("a", 10)}, {("__o_00__", 10)}, False), ("a >= 10", "a >= __o_00__", {("a", 10)}, {("__o_00__", 10)}, True), ("a < 10", "a < __o_00__", {("a", 10)}, {("__o_00__", 10)}, False), ("a <= 10", "a <= __o_00__", {("a", 10)}, {("__o_00__", 10)}, True), ("a != 10", "a != __o_00__", {("a", 10)}, {("__o_00__", 10)}, False), ( "a > 10 and b <= 5", "a > __o_00__ and b <= __o_01__", {("a", 11), ("b", 4)}, {("__o_00__", 10), ("__o_01__", 5)}, True ), ( "__ret.value > 10", "__ret.value > __o_00__", {("__ret", 15)}, {("__o_00__", 10)}, True ), ( "10 > __ret.value", "__o_00__ > __ret.value", {("__ret", 15)}, {("__o_00__", 10)}, False ), ( "a + self > 10", "a + self > __o_00__", {("a", 6), ("self", "foo")}, {("__o_00__", 10)}, True ), ( "a + 10 > 10", "a + 10 > __o_00__", {("a", 5)}, {("__o_00__", 10)}, True ), ( "a + 'world !' == 'hello world !'", "a + 'world !' == __o_00__", {("a", "hello ")}, {("__o_00__", 'hello world !')}, True ), ( "a + foo > 10", "a + foo > __o_00__", {("a", 6), ("foo", "foo")}, {("__o_00__", 10)}, True ), ( "a + twenty one > 21", "a + __C__twenties__1004__C__ > __o_00__", {("a", 5)}, {"__C__twenties__1004__C__", ("__o_00__", 21)}, True ), ( "a + my friend == 'hello my friend'", "a + __C__my0friend__1005__C__ == __o_00__", {("a", "hello ")}, {"__C__my0friend__1005__C__", ("__o_00__", 'hello my friend')}, True ), ( "10 < a + self", "__o_00__ < a + self", {("a", 6), ("self", "foo")}, {("__o_00__", 10)}, True ), ( "10 > a + 10", "__o_00__ > a + 10", {("a", 5)}, {("__o_00__", 10)}, False ), ( "'hello world !' != a + 'world !'", "__o_00__ != a + 'world !'", {("a", "hello ")}, {("__o_00__", 'hello world !')}, False ), ( "10 < a + foo", "__o_00__ < a + foo", {("a", 6), ("foo", "foo")}, {("__o_00__", 10)}, True ), ( "21 > a + twenty one", "__o_00__ > a + __C__twenties__1004__C__", {("a", 5)}, {"__C__twenties__1004__C__", ("__o_00__", 21)}, False ), ( "'hello my friend' == a + my friend", "__o_00__ == a + __C__my0friend__1005__C__", {("a", "hello ")}, {"__C__my0friend__1005__C__", ("__o_00__", 'hello my friend')}, True ), ]) def test_python(self, expression, e_compiled, e_variables, e_objects, e_result): sheerka, context, foo, one, two, twenties, my_friend = self.init_concepts( Concept("foo", body="5"), Concept("one", body="1"), Concept("two", body="2"), Concept("twenties", definition="'twenty' (one|two)=n", body='20 + n').def_var("n"), Concept("my friend", body="'my friend'"), create_new=True ) ensure_evaluated(context, foo, eval_body=True) ensure_evaluated(context, my_friend, eval_body=True) conditions = self.validate_python_test(context, expression, e_compiled, expression, e_variables, set(), e_objects) # check against SheerkaEvaluateRules variables_mapping = { "foo": foo, } objects = self.get_testing_objects(context, e_variables, variables_mapping) self.check_against_python(context, expression, conditions, objects, expected_result=e_result) class TestSheerkaRuleManagerRulesCompilationFunctionsCall(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing functions no var : isinstance('hello', str) with var: isinstance(a_string, str) with and : isinstance(a_string, str) and isinstance(an_int, int) function with concept: isinstance(self, girl) function with concept: isinstance(self, a little boy) function with enum: func_true(self, BuiltinConcepts.TO_DICT) """ def test_rete(self): pass @pytest.mark.parametrize("expression, e_compiled, e_text, e_variables, e_objects", [ ( "isinstance('hello', str)", "isinstance(__o_00__, str)", "isinstance('hello', str)", set(), {"__o_00__"} ), ( "isinstance(a_string, str)", "isinstance(a_string, str)", "isinstance(a_string, str)", {"a_string"}, {} ), ( "not isinstance(an_int, str)", "not (isinstance(an_int, str))", "not (isinstance(an_int, str))", {"an_int"}, {} ), ( "isinstance(a_string, str) and isinstance(an_int, int)", "isinstance(a_string, str) and isinstance(an_int, int)", "isinstance(a_string, str) and isinstance(an_int, int)", {"an_int", "a_string"}, {} ), ( "isinstance(self, girl)", "isinstance(self, __o_00__)", "isinstance(self, girl)", {("self", "girl")}, {"__o_00__"} ), ( "isinstance(self, a little boy)", "isinstance(self, __o_00__)", "isinstance(self, a little boy)", {("self", "a little boy")}, {"__o_00__"} ), ( "func_true(self, BuiltinConcepts.TO_DICT)", "func_true(self, BuiltinConcepts.TO_DICT)", "func_true(self, BuiltinConcepts.TO_DICT)", {("self", BuiltinConcepts.TO_DICT)}, {} ), ]) def test_python(self, expression, e_compiled, e_text, e_variables, e_objects): sheerka, context, girl, little_boy = self.init_test().with_concepts( Concept("girl"), Concept("a little boy"), create_new=True ).unpack() conditions = self.validate_python_test(context, expression, e_compiled, e_text, e_variables, set(), e_objects) # check against SheerkaEvaluateRules objects_mappings = {"girl": girl, "a little boy": little_boy} objects = self.get_testing_objects(context, e_variables, objects_mappings) objects["func_true"] = self.func_true self.check_against_python(context, expression, conditions, objects) class TestSheerkaRuleManagerRulesCompilationRecognizeConcept(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing recognize(path, concept) recognize by name : recognize(__ret.body, greetings) recognize by id : recognize(__ret.body, c:|1001:) recognize by name using c_str : recognize(__ret.body, c:greetings:) recognize by name + str condition : recognize(__ret.body, greetings) and __ret.body.a == 'my friend' recognize by name + sheerka condition : recognize(__ret.body, greetings) and __ret.body.a == sheerka recognize by name + concept condition : recognize(__ret.body, greetings) and __ret.body.a == foo recognize by instance using sheerka : recognize(__ret.body, hello sheerka) recognize by instance using a string : recognize(__ret.body, hello 'my friend') recognize by instance using a concept : recognize(__ret.body, hello foo) recognize by instance using long concept : recognize(__ret.body, hello my best friend) recognize self : recognize(self, greetings) """ @pytest.mark.parametrize("expression, expected_as_list_of_str, expected_variable, greeting_var", [ ( "recognize(__ret.body, greetings)", ["#__x_00__|__name__|'__ret'", "#__x_00__|body|#__x_01__", "#__x_01__|__is_concept__|True", "#__x_01__|name|'greetings'"], "__ret", None ), ( "recognize(__ret.body, c:|1001:)", ["#__x_00__|__name__|'__ret'", "#__x_00__|body|#__x_01__", "#__x_01__|__is_concept__|True", "#__x_01__|id|'1001'"], "__ret", None ), ( "recognize(__ret.body, c:greetings:)", ["#__x_00__|__name__|'__ret'", "#__x_00__|body|#__x_01__", "#__x_01__|__is_concept__|True", "#__x_01__|name|'greetings'"], "__ret", None ), ( "recognize(__ret.body, greetings) and __ret.body.a == 'my friend'", ["#__x_00__|__name__|'__ret'", "#__x_00__|body|#__x_01__", "#__x_01__|__is_concept__|True", "#__x_01__|name|'greetings'", "#__x_01__|a|'my friend'"], "__ret", "my friend", ), ( "recognize(__ret.body, greetings) and __ret.body.a == sheerka", ["#__x_00__|__name__|'__ret'", "#__x_00__|body|#__x_01__", "#__x_01__|__is_concept__|True", "#__x_01__|name|'greetings'", "#__x_01__|a|'__sheerka__'"], "__ret", "sheerka", ), ( "recognize(__ret.body, greetings) and __ret.body.a == foo", ["#__x_00__|__name__|'__ret'", "#__x_00__|body|#__x_01__", "#__x_01__|__is_concept__|True", "#__x_01__|name|'greetings'", "#__x_01__|a|#__x_02__", "#__x_02__|__is_concept__|True", "#__x_02__|key|'foo'"], "__ret", "foo", ), ( "recognize(__ret.body, hello sheerka)", ["#__x_00__|__name__|'__ret'", "#__x_00__|body|#__x_01__", "#__x_01__|__is_concept__|True", "#__x_01__|key|'hello __var__0'", "#__x_01__|a|'__sheerka__'"], "__ret", "sheerka", ), ( "recognize(__ret.body, hello 'my friend')", ["#__x_00__|__name__|'__ret'", "#__x_00__|body|#__x_01__", "#__x_01__|__is_concept__|True", "#__x_01__|key|'hello __var__0'", "#__x_01__|a|'my friend'"], "__ret", "my friend", ), ( "recognize(__ret.body, hello foo)", ["#__x_00__|__name__|'__ret'", "#__x_00__|body|#__x_01__", "#__x_01__|__is_concept__|True", "#__x_01__|key|'hello __var__0'", "#__x_01__|a|#__x_02__", "#__x_02__|__is_concept__|True", "#__x_02__|key|'foo'"], "__ret", "foo", ), ( "recognize(__ret.body, hello my best friend)", ["#__x_00__|__name__|'__ret'", "#__x_00__|body|#__x_01__", "#__x_01__|__is_concept__|True", "#__x_01__|key|'hello __var__0'", "#__x_01__|a|#__x_02__", "#__x_02__|__is_concept__|True", "#__x_02__|key|'my best friend'"], "__ret", "my best friend", ), ( "recognize(self, greetings)", ["#__x_00__|__name__|'self'", "#__x_00__|__is_concept__|True", "#__x_00__|name|'greetings'"], "self", None, ) ]) def test_rete(self, expression, expected_as_list_of_str, expected_variable, greeting_var): sheerka, context, greetings, foo, my_best_friend = self.init_test().with_concepts( Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), Concept("foo"), Concept("my best friend"), create_new=True ).unpack() parser = ExpressionParser() expected = get_rete_conditions(*expected_as_list_of_str) error_sink = ErrorSink() parser_input = ParserInput(expression) parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) visitor = ReteConditionExprVisitor(context) conditions = visitor.get_conditions(parsed) assert conditions == [expected] # check against a Rete network variable_map = { "foo": foo, "my best friend": my_best_friend, "sheerka": sheerka } variable = variable_map.get(greeting_var, greeting_var) to_recognize = sheerka.new_from_template(greetings, greetings.key, a=variable) objects = self.get_testing_objects(context, [(expected_variable, to_recognize)]) self.check_against_rete(expression, conditions, objects) @pytest.mark.parametrize("expression, e_compiled, e_text, e_variables, greeting_var, e_objects", [ ( "recognize(__ret.body, greetings)", "__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.name == 'greetings'", "__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.name == 'greetings'", {"__ret"}, None, set() ), ( "recognize(__ret.body, c:|1001:)", "__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.id == '1001'", "__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.id == '1001'", {"__ret"}, None, set() ), ( "recognize(__ret.body, c:greetings:)", "__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.name == 'greetings'", "__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.name == 'greetings'", {"__ret"}, None, set() ), ( "recognize(__ret.body, greetings) and __ret.body.a == 'my friend'", "__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.name == 'greetings' and __x_00__.a == __o_00__", "__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.name == 'greetings' and __ret.body.a == 'my friend'", {"__ret"}, "my friend", {("__o_00__", "my friend")} ), ( "recognize(__ret.body, greetings) and __ret.body.a == sheerka", """__x_00__ = __ret.body isinstance(__x_00__, Concept) and __x_00__.name == 'greetings' and is_sheerka(__x_00__.a)""", """__x_00__ = __ret.body isinstance(__x_00__, Concept) and __x_00__.name == 'greetings' and __ret.body.a == sheerka""", {"__ret"}, "sheerka", set() ), ( "recognize(__ret.body, greetings) and __ret.body.a == foo", """__x_00__ = __ret.body __x_01__ = __x_00__.a isinstance(__x_00__, Concept) and __x_00__.name == 'greetings' and isinstance(__x_01__, Concept) and __x_01__.key == 'foo'""", """__x_00__ = __ret.body __x_01__ = __x_00__.a isinstance(__x_00__, Concept) and __x_00__.name == 'greetings' and __ret.body.a == foo""", {"__ret"}, "foo", set() ), ( "recognize(__ret.body, hello sheerka)", """__x_00__ = __ret.body isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and is_sheerka(__x_00__.a)""", """__x_00__ = __ret.body isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and __x_00__.a == sheerka""", {"__ret"}, "sheerka", set() ), ( "recognize(__ret.body, hello 'my friend')", """__x_00__ = __ret.body isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and __x_00__.a == __o_00__""", """__x_00__ = __ret.body isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and __x_00__.a == 'my friend'""", {"__ret"}, "my friend", {('__o_00__', 'my friend')} ), ( "recognize(__ret.body, hello foo)", """__x_00__ = __ret.body __x_01__ = __x_00__.a isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and isinstance(__x_01__, Concept) and __x_01__.key == 'foo'""", """__x_00__ = __ret.body __x_01__ = __x_00__.a isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and __x_00__.a == c:foo|1002:""", {"__ret"}, "foo", {'__o_00__'} ), ( "recognize(__ret.body, hello my best friend)", """__x_00__ = __ret.body __x_01__ = __x_00__.a isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and isinstance(__x_01__, Concept) and __x_01__.key == 'my best friend'""", """__x_00__ = __ret.body __x_01__ = __x_00__.a isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and __x_00__.a == c:my best friend|1003:""", {"__ret"}, "my best friend", {'__o_00__'} ), ( "recognize(self, greetings)", "isinstance(self, Concept) and self.name == 'greetings'", "isinstance(self, Concept) and self.name == 'greetings'", {"self"}, None, set() ) ]) def test_python(self, expression, e_compiled, e_text, e_variables, greeting_var, e_objects): sheerka, context, greetings, foo, my_best_friend = self.init_test().with_concepts( Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"), Concept("foo"), Concept("my best friend"), create_new=True ).unpack() conditions = self.validate_python_test(context, expression, e_compiled, e_text, e_variables, set(), e_objects) # check against SheerkaEvaluateRules variable_map = { "foo": foo, "my best friend": my_best_friend, "sheerka": Expando("sheerka", {}) } variable = variable_map.get(greeting_var, greeting_var) to_recognize = sheerka.new_from_template(greetings, greetings.key, a=variable) expected_variable = next(iter(e_variables)) objects = self.get_testing_objects(context, [(expected_variable, to_recognize)]) self.check_against_python(context, expression, conditions, objects) class TestSheerkaRuleManagerRulesCompilationEvalQuestionConcept(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing question using concepts with no variable : girl is a human with variable : self is a human with long concept : the little boy is a human being with long concept + variable : the little boy is a self with long concept + variable : self is a human being with a special symbol : self is a 'human' with a special symbol : the little boy is a 'human' """ def test_rete(self): pass # I don't know yet what to do @pytest.mark.parametrize("expression, expected_compiled, expected_variables", [ ( "girl is a human", "evaluate_question(__o_00__)", set(), ), ( "self is a human", "evaluate_question(__o_00__)", {"self"}, ), ( "the little boy is a human being", "evaluate_question(__o_00__)", set(), ), ( "the little boy is a self", "evaluate_question(__o_00__)", {"self"}, ), ( "self is a human being", "evaluate_question(__o_00__)", {"self"}, ), ( "self is a 'human'", "evaluate_question(__o_00__)", {"self"}, ), ( "the little boy is a 'human'", "evaluate_question(__o_00__)", set(), ), ]) def test_python(self, expression, expected_compiled, expected_variables): sheerka, context, girl, human, little_boy, human_being, isa = self.init_test().with_concepts( Concept("girl"), Concept("human"), Concept("the little boy"), Concept("human being"), Concept("x is a y", pre="is_question()").def_var("x").def_var("y"), create_new=True ).unpack() parser = ExpressionParser() error_sink = ErrorSink() parser_input = ParserInput(expression) parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) visitor = PythonConditionExprVisitor(context) conditions = visitor.get_conditions(parsed) assert len(conditions) == 1 assert isinstance(conditions[0], CompiledCondition) ast_ = ast.parse(expected_compiled, "", 'exec' if "\n" in expected_compiled else 'eval') expected_python_node = PythonNode(expected_compiled, ast_, expression) assert conditions[0].evaluator_type == PYTHON_EVALUATOR_NAME assert sheerka.isinstance(conditions[0].return_value, BuiltinConcepts.RETURN_VALUE) assert sheerka.objvalue(conditions[0].return_value) == expected_python_node assert "__o_00__" in conditions[0].objects assert conditions[0].variables == expected_variables assert len(conditions[0].concepts_to_reset) == 1 assert sheerka.isinstance(next(iter(conditions[0].concepts_to_reset)), isa) # check against SheerkaEvaluateRules self.check_against_python(context, expression, conditions, self.get_testing_objects(context, expected_variables)) class TestSheerkaRuleManagerRulesCompilationEvalQuestionConceptWithNot(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing question using concepts mixed with others conditions using not : not girl is a human using not : not self is a human """ def test_rete(self): pass @pytest.mark.parametrize( "expression, expected_compiled, expected_text, expected_variables", [ ( "not girl is a human", "not (evaluate_question(__o_00__))", "not (girl is a human)", set(), ), ( "not self is a human", "not (evaluate_question(__o_00__))", "not (self is a human)", {"self"}, ), ]) def test_python(self, expression, expected_compiled, expected_text, expected_variables): sheerka, context, girl, human, little_boy, human_being, isa = self.init_test().with_concepts( Concept("girl"), Concept("human"), Concept("the little boy"), Concept("human being"), Concept("x is a y", pre="is_question()").def_var("x").def_var("y"), create_new=True ).unpack() conditions = self.validate_python_test(context, expression, expected_compiled, expected_text, expected_variables, set(), {"__o_00__"}) # check against SheerkaEvaluateRules self.check_against_python(context, expression, conditions, self.get_testing_objects(context, expected_variables), False) class TestSheerkaRuleManagerRulesCompilationEvalConceptMixedWithOther(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing question using concepts mixed with others conditions using and with python : girl is a human being and isinstance(a_string, str) using and with another concept : self is a human being and isinstance(self, girl) starting with python : isinstance(x, str) and girl is a human being starting with python : isinstance(self, girl) and self is a human being multiple python + same variable : self is a human being and isinstance(self, girl) and isinstance(self, girl) """ def test_rete(self): pass # I don't know yet what to do @pytest.mark.parametrize( "expression, expected_compiled, expected_variables, expected_objects", [ ( "girl is a human being and isinstance(a_string, str)", "evaluate_question(__o_00__) and isinstance(a_string, str)", {"a_string"}, {"__o_00__"} ), ( "self is a human being and isinstance(self, girl)", "evaluate_question(__o_00__) and isinstance(self, __o_01__)", {("self", "girl")}, {"__o_00__", "__o_01__"} ), ( "isinstance(a_string, str) and girl is a human being", "isinstance(a_string, str) and evaluate_question(__o_00__)", {"a_string"}, {"__o_00__"} ), ( "isinstance(self, girl) and self is a human being", "isinstance(self, __o_00__) and evaluate_question(__o_01__)", {("self", "girl")}, {"__o_00__", "__o_01__"} ), ( "self is a human being and isinstance(self, girl) and isinstance(self, girl)", "evaluate_question(__o_00__) and isinstance(self, __o_01__) and isinstance(self, __o_02__)", {("self", "girl")}, {"__o_00__", "__o_01__", "__o_02__"} ), ]) def test_python(self, expression, expected_compiled, expected_variables, expected_objects): sheerka, context, girl, human_being, isa = self.init_test().with_concepts( Concept("girl"), Concept("human being"), Concept("x is a y", pre="is_question()").def_var("x").def_var("y"), create_new=True ).unpack() conditions = self.validate_python_test(context, expression, expected_compiled, expression, expected_variables, set(), expected_objects) # check against SheerkaEvaluateRules variables_mapping = { "girl": girl, "human being": human_being } testing_objects = self.get_testing_objects(context, expected_variables, variables_mapping) self.check_against_python(context, expression, conditions, testing_objects, True) class TestSheerkaRuleManagerRulesCompilationEvalNonQuestionConcept(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing complex concepts composition with BNF : twenty two additions : twenty two + one additions : twenty two + twenty one additions : twenty two plus one additions : twenty two plus twenty one with function: func_identity(twenty two) with function: func_identity(twenty two + one) with function: func_identity(twenty two + twenty one) with function: func_identity(twenty two plus one) with function: func_identity(twenty two plus twenty one) with special char : 'one' plus 'two' with special char : twenty two plus 2 """ def test_rete(self): pass # I don't know yet what to do @pytest.mark.parametrize("expression, e_compiled, e_text, e_objects, e_result", [ ( "twenty two", "__o_00__", "twenty two", {"__o_00__"}, 22 ), ( "twenty two + one", "__o_00__", "twenty two + one", {("__o_00__", 23)}, 23 ), ( "twenty two + twenty one", "__o_00__", "twenty two + twenty one", {("__o_00__", 43)}, 43 ), ( "twenty two plus one", "__o_00__", "twenty two plus one", {"__o_00__"}, 23 ), ( "twenty two plus twenty one", "__o_00__", "twenty two plus twenty one", {"__o_00__"}, 43 ), ( "func_identity(twenty two)", "func_identity(__o_00__)", "func_identity(twenty two)", {"__o_00__"}, 22 ), ( "func_identity(twenty two + one)", "func_identity(__o_00__)", "func_identity(twenty two + one)", {("__o_00__", 23)}, 23 ), ( "func_identity(twenty two + twenty one)", "func_identity(__o_00__)", "func_identity(twenty two + twenty one)", {("__o_00__", 43)}, 43 ), ( "func_identity(twenty two plus one)", "func_identity(__o_00__)", "func_identity(twenty two plus one)", {"__o_00__"}, 23 ), ( "func_identity(twenty two plus twenty one)", "func_identity(__o_00__)", "func_identity(twenty two plus twenty one)", {"__o_00__"}, 43 ), ( "'one' plus 'two'", "__o_00__", "'one' plus 'two'", {"__o_00__"}, 'onetwo' ), ( "twenty two plus 2", "__o_00__", "twenty two plus 2", {"__o_00__"}, 24 ), ]) def test_python(self, expression, e_compiled, e_text, e_objects, e_result): sheerka, context, one, two, twenties, plus = self.init_test().with_concepts( Concept("one", body="1"), Concept("two", body="2"), Concept("twenties", definition="'twenty' (one|two)=n", body='20 + n').def_var("n"), Concept("a plus b", body="a + b").def_var("a").def_var("b"), create_new=True ).unpack() conditions = self.validate_python_test(context, expression, e_compiled, e_text, set(), set(), e_objects) # check against SheerkaEvaluateRules namespace = {"func_identity": self.func_identity} res = self.evaluate_condition(context, expression, conditions[0], namespace) assert res.status assert sheerka.objvalue(res) == e_result class TestSheerkaRuleManagerRulesCompilationMultipleSameConcept(BaseTestSheerkaRuleManagerRulesCompilation): """ Test when a concept returns multiple results The compilation should fail : No need to execute a condition if we are not sure of the meaning self is a bar """ def test_rete(self): sheerka, context, bar, isa_1, isa_2 = self.init_test().with_concepts( Concept("bar"), Concept("x is a y").def_var("x").def_var("y"), Concept("u is a v").def_var("u").def_var("v"), create_new=True ).unpack() with pytest.raises(FailedToCompileError): parser = ExpressionParser() error_sink = ErrorSink() parser_input = ParserInput("self is a bar") parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) visitor = ReteConditionExprVisitor(context) visitor.get_conditions(parsed) def test_python(self): sheerka, context, bar, isa_1, isa_2 = self.init_test().with_concepts( Concept("bar"), Concept("x is a y").def_var("x").def_var("y"), Concept("u is a v").def_var("u").def_var("v"), create_new=True ).unpack() with pytest.raises(FailedToCompileError): parser = ExpressionParser() error_sink = ErrorSink() parser_input = ParserInput("self is a bar") parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) visitor = PythonConditionExprVisitor(context) visitor.get_conditions(parsed) class TestSheerkaRuleManagerRulesCompilationNot(BaseTestSheerkaRuleManagerRulesCompilation): """ Testing not not __ret.status == True not recognize(__ret.body, hello sheerka) not a cat is a pet and not bird is an animal # where x is a y is a concept not a cat is a pet and not x > 5 # concept mixed with python """ pass class TestCompiledCondition(BaseTestSheerkaRuleManagerRulesCompilation): @pytest.mark.parametrize("expression, expected", [ ("self is a 'foo'", {"x is a y"}), ("set self is a 'foo'", set()), ]) def test_i_can_get_concept_to_reset(self, expression, expected): """ When compiled conditions, sometimes there are concepts to reset between two usages :param expression: :param expected: :return: """ sheerka, context, question, not_a_question = self.init_concepts( Concept("x is a y", pre="is_question()").def_var("x").def_var("y"), Concept("set x is a y").def_var("x").def_var("y"), ) conditions = self.get_conditions(context, expression) assert len(conditions) == 1 assert set(c.name for c in conditions[0].concepts_to_reset) == expected def test_i_can_reset_concepts_when_multiple_levels(self): """ When compiled conditions, sometimes there are concepts to reset between two usages :return: """ sheerka, context, is_instance, is_int, is_integer = self.init_concepts( Concept("x is an instance of y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"), Concept("x is a int", pre="is_question()", body="x is an instance of int").def_var("x"), Concept("x is an integer", pre="is_question()", body="x is a int").def_var("x"), ) expression = "self is an integer" conditions = conditions = self.get_conditions(context, expression) assert len(conditions) == 1 assert set(c.name for c in conditions[0].concepts_to_reset) == {"x is an instance of y", "x is a int", "x is an integer"} # So I can evaluate multiple times res = self.evaluate_condition(context, expression, conditions[0], {'self': 10}) assert res.status assert sheerka.objvalue(res.body) res = self.evaluate_condition(context, expression, conditions[0], {'self': "string"}) assert res.status assert not sheerka.objvalue(res.body) def test_i_can_reset_concepts_when_multiple_levels_and_concept_node(self): """ When compiled conditions, sometimes there are concepts to reset between two usages :return: """ # in this example, x + 2 is an int won't be parsed as an ExactNodeConcept, but as a ConceptNode sheerka, context, is_int, is_integer = self.init_concepts( Concept("x is a int", pre="is_question()", body="isinstance(x, int)").def_var("x"), Concept("x is an integer", pre="is_question()", body="x + 2 is a int").def_var("x"), create_new=True ) expression = "self is an integer" conditions = self.get_conditions(context, expression) assert len(conditions) == 1 assert set(c.name for c in conditions[0].concepts_to_reset) == {"x is a int", "x is an integer"} # So I can evaluate multiple times res = self.evaluate_condition(context, expression, conditions[0], {'self': 10}) assert res.status assert sheerka.objvalue(res.body) res = self.evaluate_condition(context, expression, conditions[0], {'self': "string"}) assert not res.status def test_long_name_concept_set_are_not_considered_as_variables(self): sheerka, context, one, number = self.init_concepts( "one", "all numbers", ) sheerka.set_isa(context, one, number) expression = "all numbers < 5" conditions = self.get_conditions(context, expression) assert len(conditions) == 1 assert conditions[0].return_value.body.body.source == '__o_00__ < __o_01__'