diff --git a/src/parsers/BaseParser.py b/src/parsers/BaseParser.py index dba95a6..b045a3e 100644 --- a/src/parsers/BaseParser.py +++ b/src/parsers/BaseParser.py @@ -13,6 +13,9 @@ class ErrorSink: def __init__(self): self.sink = [] + def __repr__(self): + return f"Errors({self.sink})" + def add_error(self, error): self.sink.append(error) @@ -258,4 +261,3 @@ class BaseParserInputParser(BaseParser): by_ids.add(c.id) return list_a - diff --git a/src/parsers/ExpressionParser.py b/src/parsers/ExpressionParser.py index 0865084..aeea408 100644 --- a/src/parsers/ExpressionParser.py +++ b/src/parsers/ExpressionParser.py @@ -1,12 +1,9 @@ -from core.builtin_concepts_ids import BuiltinConcepts -from core.sheerka.services.SheerkaExecute import ParserInput from core.tokenizer import TokenKind from core.utils import get_text_from_tokens -from parsers.BaseParser import ErrorSink +from parsers.BaseExpressionParser import NameExprNode, VariableNode, BaseExpressionParser from parsers.FunctionParser import FunctionParser from parsers.LogicalOperatorParser import LogicalOperatorParser from parsers.RelationalOperatorParser import RelationalOperatorParser -from parsers.BaseExpressionParser import ParenthesisNode, NameExprNode, VariableNode, BaseExpressionParser class ExpressionParser(BaseExpressionParser): @@ -19,59 +16,15 @@ class ExpressionParser(BaseExpressionParser): def __init__(self, **kwargs): super().__init__(ExpressionParser.NAME, 60, False, yield_eof=False) self.variable_parser = VariableOrNamesParser() - self.function_parser = FunctionParser() - self.relational_parser = RelationalOperatorParser(expr_parser=self.variable_parser) + self.function_parser = FunctionParser(expr_parser=self, tokens_parser=self.variable_parser) + self.relational_parser = RelationalOperatorParser(expr_parser=self.function_parser) self.logical_parser = LogicalOperatorParser(expr_parser=self.relational_parser) - def parse(self, context, parser_input: ParserInput): - """ - :param context: - :param parser_input: - :return: - """ - - if not isinstance(parser_input, ParserInput): - return None - - context.log(f"Parsing '{parser_input}' with {self.NAME}Parser", self.name) - sheerka = context.sheerka - - if parser_input.is_empty(): - return context.sheerka.ret(self.name, - False, - sheerka.new(BuiltinConcepts.IS_EMPTY)) - - error_sink = ErrorSink() - if not self.reset_parser_input(parser_input, error_sink): - return context.sheerka.ret( - self.name, - False, - context.sheerka.new(BuiltinConcepts.ERROR, body=error_sink.sink)) - - node = self.parse_input(context, parser_input, error_sink) - if isinstance(node, ParenthesisNode): - node = node.node - - value = self.get_return_value_body(context.sheerka, parser_input.as_text(), node, node, error_sink.sink) - - ret = context.sheerka.ret( - self.name, - not error_sink.has_error, - value) - - return ret - def parse_input(self, context, parser_input, error_sink): - pos = parser_input.pos - for parser in [self.logical_parser, - self.relational_parser, - self.variable_parser]: # [self.logical_parser, self.relational_parser, self.function_parser]: - parser_input.seek(pos) # reset position - res = parser.parse_input(context, parser_input, error_sink) - if res and not error_sink.has_error: - return res + return self.logical_parser.parse_input(context, parser_input, error_sink) - return None + def parse_tokens_stop_condition(self, token, parser_input): + pass class VariableOrNamesParser(BaseExpressionParser): @@ -80,44 +33,6 @@ class VariableOrNamesParser(BaseExpressionParser): def __init__(self, **kwargs): super().__init__(VariableOrNamesParser.NAME, 60, False, yield_eof=False) - def parse(self, context, parser_input: ParserInput): - """ - :param context: - :param parser_input: - :return: - """ - - if not isinstance(parser_input, ParserInput): - return None - - context.log(f"Parsing '{parser_input}' with {self.NAME}Parser", self.name) - sheerka = context.sheerka - - if parser_input.is_empty(): - return context.sheerka.ret(self.name, - False, - sheerka.new(BuiltinConcepts.IS_EMPTY)) - - error_sink = ErrorSink() - if not self.reset_parser_input(parser_input, error_sink): - return context.sheerka.ret( - self.name, - False, - context.sheerka.new(BuiltinConcepts.ERROR, body=error_sink.sink)) - - node = self.parse_input(context, parser_input, error_sink) - if isinstance(node, ParenthesisNode): - node = node.node - - value = self.get_return_value_body(context.sheerka, parser_input.as_text(), node, node, error_sink.sink) - - ret = context.sheerka.ret( - self.name, - not error_sink.has_error, - value) - - return ret - def parse_input(self, context, parser_input, error_sink): # try to recognize a VariableNode dots_found = [] @@ -148,3 +63,6 @@ class VariableOrNamesParser(BaseExpressionParser): parser_input.as_tokens(), parts[0], *parts[1:]) + + def parse_tokens_stop_condition(self, token, parser_input): + pass diff --git a/src/parsers/FunctionParser.py b/src/parsers/FunctionParser.py index b1be237..5d66019 100644 --- a/src/parsers/FunctionParser.py +++ b/src/parsers/FunctionParser.py @@ -40,6 +40,7 @@ class FunctionParser(BaseExpressionParser): self.sep = sep self.longest_concepts_only = longest_concepts_only self.expr_parser = kwargs.get("expr_parser", None) + self.tokens_parser = kwargs.get("tokens_parser", None) def function_parser_get_return_value_body(self, context, source, source_code_node): if source_code_node.error_when_parsing: @@ -71,7 +72,16 @@ class FunctionParser(BaseExpressionParser): return res[0] if len(res) == 1 else res def parse_input(self, context, parser_input, error_sink): - return self.parse_function(context, parser_input, error_sink) + # when FunctionParser is used by LexerNode or SheerkaExecute, it must fail if no function is found + # when it is used by ExpressionParser, it must default to VariableOrNamesParser + pos = parser_input.pos + res = self.parse_function(context, parser_input, error_sink) + if (not res or error_sink.has_error) and self.tokens_parser: + parser_input.seek(pos) + error_sink.clear() + return self.tokens_parser.parse_input(context, parser_input, error_sink) + + return res def parse_function(self, context, parser_input, error_sink): diff --git a/tests/parsers/parsers_utils.py b/tests/parsers/parsers_utils.py index ac083a1..b513141 100644 --- a/tests/parsers/parsers_utils.py +++ b/tests/parsers/parsers_utils.py @@ -907,19 +907,6 @@ class FN: if isinstance(other, FN): return self.first == other.first and self.last == other.last and self.parameters == other.parameters - # if isinstance(other, FunctionNode): - # if self.first != other.first.value or self.last != other.last.value: - # return False - # if len(self.parameters) != len(other.parameters): - # return False - # for self_parameter, other_parameter in zip(self.parameters, other.parameters): - # value = other_parameter.value.value if isinstance(self_parameter[0], str) else other_parameter.value - # sep = other_parameter.separator.value if other_parameter.separator else None - # if self_parameter[0] != value or self_parameter[1] != sep: - # return False - # - # return True - return False def __hash__(self): diff --git a/tests/parsers/test_ExpressionParser.py b/tests/parsers/test_ExpressionParser.py index b7e2e69..ccc4b0d 100644 --- a/tests/parsers/test_ExpressionParser.py +++ b/tests/parsers/test_ExpressionParser.py @@ -3,11 +3,11 @@ 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 from parsers.BaseParser import ErrorSink from parsers.ExpressionParser import ExpressionParser -from parsers.BaseExpressionParser import VariableNode from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka -from tests.parsers.parsers_utils import get_expr_node_from_test_node, VAR, EXPR, AND, OR, NOT, GT, GTE, LT, LTE, EQ, \ +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 @@ -47,7 +47,10 @@ class TestExpressionParser(TestUsingMemoryBasedSheerka): ("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")])])), ]) def test_i_can_parse_input(self, expression, expected): sheerka, context, parser, parser_input, error_sink = self.init_parser_with_source(expression)