import pytest from core.builtin_concepts import BuiltinConcepts from core.concept import Concept from core.sheerka.services.SheerkaExecute import ParserInput from parsers.BaseParser import ErrorSink, UnexpectedTokenParsingError from parsers.FunctionParser import FunctionParser from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.parsers.parsers_utils import FN, SCN, SEQ, compute_expected_array, \ get_expr_node_from_test_node, 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="#TestFunctionParserOld#") 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, strict=True, **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] expr_parser = kwargs.get("expr_parser", None) parser = FunctionParser(strict, expr_parser=expr_parser) return sheerka, context, parser def init_parser_with_source(self, source, strict=True): sheerka, context, parser = self.init_parser(None, strict) error_sink = ErrorSink() parser_input = ParserInput(source) parser.reset_parser_input(parser_input, error_sink) return sheerka, context, parser, parser_input, error_sink 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", "func1(one)", "two", "func2(func3(), func4(three))")), ("func(r:|1:)", FN("func", "r:|1:")), ("func(bar fn(x))", FN("func", "bar fn(x)")), ("func([one, two])", FN("func", "[one, two]")), ("func((one, two))", FN("func", "(one, two)")), ("func({one, two})", FN("func", "{one, two}")), ]) def test_i_can_parse_input_function_when_expr_parser_is_none(self, expression, expected): sheerka, context, parser, parser_input, error_sink = self.init_parser_with_source(expression) expected = get_expr_node_from_test_node(expression, expected) parsed = parser.parse_input(context, parser_input, error_sink) assert not error_sink.has_error assert parsed == expected parser.strict = False parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) assert not error_sink.has_error assert parsed == expected def test_i_can_parse_input_when_prefixed_depending_on_strict_mode(self): expression = "bar fn(x)" expected = SEQ("bar ", FN("fn", "x")) sheerka, context, parser, parser_input, error_sink = self.init_parser_with_source(expression) expected = get_expr_node_from_test_node(expression, expected) parsed = parser.parse_input(context, parser_input, error_sink) assert error_sink.has_error assert parsed is None assert isinstance(error_sink.sink[0], UnexpectedTokenParsingError) parser.strict = False parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) assert not error_sink.has_error assert parsed == expected def test_i_can_parse_input_when_suffixed_depending_on_strict_mode(self): expression = "fn(x) bar" expected = SEQ(FN("fn", "x"), " bar") sheerka, context, parser, parser_input, error_sink = self.init_parser_with_source(expression) expected = get_expr_node_from_test_node(expression, expected) parsed = parser.parse_input(context, parser_input, error_sink) assert error_sink.has_error assert parsed is None assert isinstance(error_sink.sink[0], UnexpectedTokenParsingError) parser.strict = False parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) assert not error_sink.has_error assert parsed == expected def test_i_can_parse_input_when_infixed_and_suffixed(self): expression = "foo fn(x) bar" expected = SEQ("foo ", FN("fn", "x"), " bar") sheerka, context, parser, parser_input, error_sink = self.init_parser_with_source(expression) expected = get_expr_node_from_test_node(expression, expected) parser.strict = False parser.reset_parser_input(parser_input, error_sink) parsed = parser.parse_input(context, parser_input, error_sink) assert not error_sink.has_error assert parsed == expected @pytest.mark.parametrize("text, expected, objects", [ ("func()", SCN("func()"), {}), (" func()", SCN("func()"), {}), ("func(one)", SCN("func(one)"), {"__o_00__": "one"}), ("func(one, unknown, two)", SCN("func(one, unknown, two)"), {"__o_00__": "one", "__o_01__": "two"}), ("func(one, twenty two)", SCN("func(one, twenty two)"), {"__o_00__": "one", "__o_01__": "twenties"}), ("func(one plus two, three)", SCN("func(one plus two, three)"), {"__o_00__": "plus"}), ("func(func1(one), two)", SCN("func(func1(one), two)"), {"__o_00__": "one", "__o_01__": "two"}), ]) def test_i_can_parse(self, text, expected, objects): 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 node = res.body.body assert res.status assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT) transformed_expression = get_test_obj(node, resolved_expected) assert transformed_expression == resolved_expected assert node.python_node is not None assert node.return_value is not None # compare objects actual_objects = {k: v.id for k, v in node.python_node.objects.items()} expected_objects = {k: cmap[v].id for k, v in objects.items()} assert actual_objects == expected_objects # @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 FunctionParserOld.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)