import pytest from core.builtin_concepts_ids import BuiltinConcepts from core.sheerka.services.SheerkaExecute import ParserInput from core.tokenizer import Tokenizer from parsers.BaseExpressionParser import VariableNode, ComparisonNode, ExprNode from parsers.BaseParser import ErrorSink, BaseParser from parsers.ExpressionParser import ExpressionParser from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.parsers.parsers_utils import get_expr_node_from_test_node, VAR, EXPR, FN, AND, NOT, OR, GT, GTE, LT, LTE, EQ, \ NEQ, IN, NIN class TestExpressionParser(TestUsingMemoryBasedSheerka): def init_parser(self): sheerka, context = self.init_concepts() parser = ExpressionParser() return sheerka, context, parser def init_parser_with_source(self, source): sheerka, context, parser = self.init_parser() 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) @pytest.mark.parametrize("expression, expected", [ ("var1 + var2", EXPR("var1 + var2")), ("variable", VAR("variable")), ("var.attr", VAR("var.attr")), ("var1 and var2", AND(VAR("var1"), VAR("var2"))), ("var1 or var2", OR(VAR("var1"), VAR("var2"))), ("not var", NOT(VAR("var"))), ("var1 > var2", GT(VAR("var1"), VAR("var2"))), ("var1 >= var2", GTE(VAR("var1"), VAR("var2"))), ("var1 < var2", LT(VAR("var1"), VAR("var2"))), ("var1 <= var2", LTE(VAR("var1"), VAR("var2"))), ("var1 = var2", EQ(VAR("var1"), VAR("var2"))), ("var1 == var2", EQ(VAR("var1"), VAR("var2"))), ("var1 != var2", NEQ(VAR("var1"), VAR("var2"))), ("var1 in (var2.attr2, var3.attr3)", IN(VAR("var1"), EXPR("var2.attr2, var3.attr3"))), ("var1 not in (var2.attr2, var3.attr3)", NIN(VAR("var1"), EXPR("var2.attr2, var3.attr3"))), ("var1 < var2 and var3 > var4", AND(LT(VAR("var1"), VAR("var2")), GT(VAR("var3"), VAR("var4")))), ("func1(one, 1 + 2, func2(3))", FN("func1(", (")", 1), [(VAR("one"), ", "), (EXPR("1 + 2"), ", "), FN("func2(", ")", [EXPR("3")])])), ("func(var.attr)", FN("func(", ")", [VAR("var.attr")])), ("func(var1.attr1 and var2.attr2)", FN("func(", ")", [AND(VAR("var1.attr1"), VAR("var2.attr2"))])), ("func(var1.attr1 > var2.attr2)", FN("func(", ")", [GT(VAR("var1.attr1"), VAR("var2.attr2"))])), ("func1(var1) and func2(var2)", AND(FN("func1(", ")", [VAR("var1")]), FN("func2(", (")", 1), [VAR("var2")]))), ("__ret", VAR("__ret")), # ("func1().func2()", []) ]) def test_i_can_parse_input(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 @pytest.mark.parametrize("expression", [ "var.attr1.attr2", "var . attr1 . attr2", ]) def test_i_can_parse_input_variable(self, expression): sheerka, context, parser, parser_input, error_sink = self.init_parser_with_source(expression) parsed = parser.parse_input(context, parser_input, error_sink) assert not error_sink.has_error assert isinstance(parsed, VariableNode) assert parsed.name == "var" assert parsed.attributes == ["attr1", "attr2"] def test_i_can_parse_input_sub_tokens(self): sheerka, context, parser = self.init_parser() expression = "do not care var1 + var2 do not care either" parser_input = ParserInput("text", list(Tokenizer(expression, yield_eof=False)), start=6, end=10) error_sink = ErrorSink() 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 == get_expr_node_from_test_node(expression, EXPR("var1 + var2")) @pytest.mark.parametrize("expression, expected", [ ("ret.status in ('a', 1 , func())", "new_var in ('a', 1 , func())"), ("ret.status not in ('a', 1 , func())", "new_var not in ('a', 1 , func())"), ("ret.status == 10", "new_var == 10"), ("ret.status == 'a'", "new_var == 'a'"), ]) def test_i_can_rebuild_source(self, expression, expected): sheerka, context, parser, parser_input, error_sink = self.init_parser_with_source(expression) parsed = parser.parse_input(context, parser_input, error_sink) new_source = ComparisonNode.rebuild_source("new_var", parsed.comp, parsed.right.get_source()) assert new_source == expected def test_i_cannot_parse_empty_string(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_i_can_compile(self): sheerka, context, parser = self.init_parser() text = ParserInput("a > b and c < d") res = parser.parse(context, text) assert res.who == BaseParser.PREFIX + ExpressionParser.NAME assert res.status assert sheerka.isinstance(res.body, BuiltinConcepts.PARSER_RESULT) assert isinstance(res.body.body, ExprNode) assert res.body.body.compiled is not None