import pytest from core.builtin_concepts import BuiltinConcepts from core.concept import Concept from core.sheerka.services.SheerkaExecute import ParserInput from parsers.FunctionParser import FunctionParser from parsers.PythonParser import PythonErrorNode from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.parsers.parsers_utils import compute_expected_array, SCN, SCWC, CN, UTN, CNC, RN, FN, get_test_obj cmap = { "one": Concept("one"), "two": Concept("two"), "twenties": Concept("twenties", definition="'twenty' (one|two)=unit").def_var("unit"), "plus": Concept("a plus b").def_var("a").def_var("b"), } class TestFunctionParser(TestUsingMemoryBasedSheerka): shared_ontology = None @classmethod def setup_class(cls): init_test_helper = cls().init_test(cache_only=False, ontology="#TestFunctionParser#") sheerka, context, *updated = init_test_helper.with_concepts(*cmap.values(), create_new=True).unpack() for i, concept_name in enumerate(cmap): cmap[concept_name] = updated[i] cls.shared_ontology = sheerka.get_ontology(context) sheerka.pop_ontology(context) def init_parser(self, my_concepts_map=None, **kwargs): if my_concepts_map is None: sheerka, context = self.init_test().unpack() sheerka.add_ontology(context, self.shared_ontology) else: sheerka, context, *updated = self.init_test().with_concepts(*my_concepts_map.values(), **kwargs).unpack() for i, pair in enumerate(my_concepts_map): my_concepts_map[pair] = updated[i] parser = FunctionParser() return sheerka, context, parser def test_i_can_detect_empty_expression(self): sheerka, context, parser = self.init_parser() res = parser.parse(context, ParserInput("")) assert not res.status assert sheerka.isinstance(res.body, BuiltinConcepts.IS_EMPTY) def test_input_must_be_a_parser_input(self): sheerka, context, parser = self.init_parser() parser.parse(context, "not a parser input") is None def test_i_cannot_parse_when_not_a_function(self): sheerka, context, parser = self.init_parser() res = parser.parse(context, ParserInput("not a function")) assert not res.status assert sheerka.isinstance(res.body, BuiltinConcepts.NOT_FOR_ME) @pytest.mark.parametrize("expression, expected", [ ("func()", FN("func(", ")", [])), ("concept(one)", FN("concept(", ")", ["one"])), ("func(one)", FN("func(", ")", ["one"])), ("func(a long two, 'three', ;:$*)", FN("func(", ")", ["a long two, ", "'three', ", ";:$*"])), ("func(func1(one), two, func2(func3(), func4(three)))", FN("func(", ")", [ (FN("func1(", ")", ["one"]), ", "), "two, ", (FN("func2(", ")", [ (FN("func3(", ")", []), ", "), (FN("func4(", ")", ["three"]), None), ]), None) ])), ]) def test_i_can_parse_function(self, expression, expected): sheerka, context, parser = self.init_parser() parser.reset_parser(context, ParserInput(expression)) parser.parser_input.next_token() res = parser.parse_function() transformed_res = get_test_obj(res, expected) assert transformed_res == expected def test_i_can_parse_function_when_rule(self): sheerka, context, parser = self.init_parser() expected = FN("func(", ")", ["r:|1:"]) parser.reset_parser(context, ParserInput("func(r:|1:)")) parser.parser_input.next_token() res = parser.parse_function() transformed_res = get_test_obj(res, expected) assert transformed_res == expected @pytest.mark.parametrize("text, expected", [ ("func()", SCN("func()")), (" func()", SCN("func()")), ("func(one)", SCWC("func(", ")", CN("one"))), ("func(one, unknown, two)", SCWC("func(", ")", CN("one"), ", ", UTN("unknown"), (", ", 1), CN("two"))), ("func(one, twenty two)", SCWC("func(", ")", "one", ", ", CN("twenties", "twenty two"))), ("func(one plus two, three)", SCWC("func(", ")", CNC("plus", a="one", b="two"), ", ", UTN("three"))), ("func(func1(one), two)", SCWC("func(", (")", 1), SCWC("func1(", ")", "one"), ", ", "two")) ]) def test_i_can_parse(self, text, expected): sheerka, context, parser = self.init_parser() resolved_expected = compute_expected_array(cmap, text, [expected])[0] res = parser.parse(context, ParserInput(text)) parser_result = res.body expression = res.body.body assert res.status assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT) transformed_expression = get_test_obj(expression, resolved_expected) assert transformed_expression == resolved_expected assert expression.python_node is not None assert expression.return_value is not None def test_i_can_parse_when_multiple_results_when_requested(self): # the previous output was # [ # SCWC("func(", ")", "one", ", ", "twenty ", "two"), # SCWC("func(", ")", "one", ", ", CN("twenties", "twenty two")) # ] # But the first one is now filtered out, as it's not a valid python function call sheerka, context, parser = self.init_parser() parser.longest_concepts_only = False text = "func(one, twenty two)" expected = [SCWC("func(", ")", "one", ", ", CN("twenties", "twenty two"))] resolved_expected = compute_expected_array(cmap, text, expected) results = parser.parse(context, ParserInput(text)) assert len(results) == 2 res = results[0] assert not res.status assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR) assert len(res.body.body) == 1 assert (res.body.body[0], PythonErrorNode) res = results[1] parser_result = res.body expressions = res.body.body assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT) transformed_expressions = get_test_obj(expressions, resolved_expected[0]) assert transformed_expressions == resolved_expected[0] def test_i_can_parse_when_the_parameter_is_not_a_concept(self): """ It's not a concept, but it can be a valid short term memory object :return: """ sheerka, context, parser = self.init_parser() text = "func(unknown_concept)" res = parser.parse(context, ParserInput(text)) expected = [SCWC("func(", ")", "unknown_concept")] resolved_expected = compute_expected_array(cmap, text, expected) assert res.status parsed = res.body.body transformed_parsed = get_test_obj([parsed], resolved_expected) assert transformed_parsed == resolved_expected def test_i_can_parse_when_the_concept_is_not_found(self): """ We do not check yet if it's a valid concept If you find a cheap way to do so, simply remove this test :return: """ sheerka, context, parser = self.init_parser() text = "func(c:|xxx:)" res = parser.parse(context, ParserInput(text)) assert res.status def test_i_can_parse_when_rules(self): sheerka, context, parser = self.init_parser() text = "func(r:|1:)" expected = SCWC("func(", ")", RN("1")) resolved_expected = compute_expected_array(cmap, text, [expected])[0] res = parser.parse(context, ParserInput(text)) parser_result = res.body expression = res.body.body transformed_expression = get_test_obj(expression, resolved_expected) assert res.status assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT) assert transformed_expression == resolved_expected assert expression.python_node is not None assert expression.return_value is not None @pytest.mark.parametrize("text, expected_error_type", [ ("one", BuiltinConcepts.NOT_FOR_ME), # no function found ("$*!", BuiltinConcepts.NOT_FOR_ME), # no function found ("func(", BuiltinConcepts.ERROR), # function found, but incomplete ("func(one", BuiltinConcepts.ERROR), # function found, but incomplete ("func(one, two, ", BuiltinConcepts.ERROR), # function found, but incomplete ("func(one) and func(two)", BuiltinConcepts.ERROR), # to many function ("one func(one)", BuiltinConcepts.NOT_FOR_ME), # function not found ! (as it is not the first) ("func(a=b, c)", BuiltinConcepts.ERROR), # function found, but cannot be parsed ("func(one two)", BuiltinConcepts.ERROR), # function found, but cannot be parsed ]) def test_i_cannot_parse(self, text, expected_error_type): sheerka, context, parser = self.init_parser() res = parser.parse(context, ParserInput(text)) assert not res.status assert sheerka.isinstance(res.body, expected_error_type) @pytest.mark.parametrize("sequence, expected", [ (None, None), ([["a"]], [["a"]]), ([["a"], ["b", "c"]], [["a"]]), ([["b", "c"], ["a"]], [["a"]]), ([["b", "c"], ["a"], ["d", "e"], ["f"]], [["a"], ["f"]]), ]) def test_i_can_get_the_longest_concept_sequence(self, sequence, expected): assert FunctionParser.get_longest_concepts(sequence) == expected def test_concepts_found_are_fully_initialized(self): sheerka, context, parser = self.init_parser() res = parser.parse(context, ParserInput("func(one plus three)")) concept = res.body.body.nodes[0].concept assert res.status assert isinstance(concept.get_compiled()["a"], Concept) # three is not recognized, # so it will be transformed into list of ReturnValueConcept that indicate how to recognized it assert isinstance(concept.get_compiled()["b"], list) for item in concept.get_compiled()["b"]: assert sheerka.isinstance(item, BuiltinConcepts.RETURN_VALUE)