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 ComparisonNode, SequenceNode, VariableNode from parsers.BaseParser import ErrorSink from parsers.ExpressionParser import ExpressionParser from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.parsers.parsers_utils import ADD, AND, EQ, EXPR, FN, GT, GTE, IN, LT, LTE, L_EXPR, MULT, NEQ, NIN, NOT, OR, \ VAR, get_expr_node_from_test_node 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", ADD(VAR("var1"), VAR("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"), L_EXPR("(", ")", "var2.attr2", "var3.attr3"))), ("var1 not in (var2.attr2, var3.attr3)", NIN(VAR("var1"), L_EXPR("(", ")", "var2.attr2", "var3.attr3"))), ("var1 < var2 and var3 > var4", AND(LT(VAR("var1"), VAR("var2")), GT(VAR("var3"), VAR("var4")))), ("var1 < var2 + var3", LT(VAR("var1"), ADD(VAR("var2"), VAR("var3")))), ("func1(one, 1 + 2, func2(3))", FN("func1", "one", "1 + 2", "func2(3)")), ("func(var.attr)", FN("func", "var.attr")), ("func(var1.attr1 and var2.attr2)", FN("func", "var1.attr1 and var2.attr2")), ("func(var1.attr1 > var2.attr2)", FN("func", "var1.attr1 > var2.attr2")), ("__ret", VAR("__ret")), ("f(x) is another b(y)", EXPR("f(x) is another b(y)")), ("var1 + var2 * var3", ADD(VAR("var1"), MULT(VAR("var2"), VAR("var3")))), ("(a) + (b)", ADD(VAR("a"), VAR("b"), source="(a) + (b)")), ("(a + b) < (c + d)", LT(ADD(VAR("a"), VAR("b")), ADD(VAR("c"), VAR("d")), source="(a + b) < (c + d)")), ("(a, b, c)", L_EXPR("(", ")", VAR("a"), VAR("b"), VAR("c"))), ("[a, b, c]", L_EXPR("[", "]", VAR("a"), VAR("b"), VAR("c"))), ("{a, b, c}", L_EXPR("{", "}", VAR("a"), VAR("b"), VAR("c"))), ]) 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, ADD("var1", "var2"), default_expr_obj=VAR) @pytest.mark.parametrize("expression, expected", [ ("ret.status in ('a', 1 , func())", "new_var in ('a', 1 , func())"), ("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_clone(self): sheerka, context, parser, parser_input, error_sink = self.init_parser_with_source("foo x + 1") expr = parser.parse_input(context, parser_input, error_sink) assert isinstance(expr, SequenceNode) clone = expr.clone() assert expr == clone