Working on #48 : Fixed first version of ExpressionParser.py

This commit is contained in:
2021-03-12 09:42:38 +01:00
parent 19908b97a6
commit a49f8bed71
5 changed files with 29 additions and 109 deletions
+3 -1
View File
@@ -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
+9 -91
View File
@@ -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
+11 -1
View File
@@ -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):
-13
View File
@@ -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):
+6 -3
View File
@@ -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)