Working on #48 : Working

This commit is contained in:
2021-03-09 09:33:50 +01:00
parent a799ab2bbd
commit 07f0d3670d
6 changed files with 94 additions and 26 deletions
+7 -3
View File
@@ -18,9 +18,9 @@ from sheerkarete.conditions import Condition, AndConditions
class ReteConditionsEmitter:
def __init__(self, context):
from parsers.ComparisonParser import ComparisonParser
from parsers.RelationalOperatorParser import RelationalOperatorParser
self.context = context
self.comparison_parser = ComparisonParser()
self.comparison_parser = RelationalOperatorParser()
self.var_counter = 0
self.variables = {}
@@ -69,8 +69,10 @@ class LogicalOperatorParser(BaseParser):
Or to help to understand why a python expression returns True or False
"""
NAME = "LogicalOperator"
def __init__(self, **kwargs):
super().__init__("Expression", 50, False, yield_eof=True)
super().__init__(self.NAME, 50, False, yield_eof=True)
self.and_tokens = list(Tokenizer(" and ", yield_eof=False))
self.and_not_tokens = list(Tokenizer(" and not ", yield_eof=False))
self.not_tokens = list(Tokenizer("not ", yield_eof=False))
@@ -111,6 +113,8 @@ class LogicalOperatorParser(BaseParser):
if token and token.type != TokenKind.EOF:
self.add_error(UnexpectedTokenParsingError(f"Unexpected token '{token}'", token, []))
if isinstance(tree, ParenthesisNode):
tree = tree.node
value = self.get_return_value_body(context.sheerka, self.parser_input.as_text(), tree, tree)
ret = self.sheerka.ret(
@@ -4,11 +4,12 @@ from core.builtin_concepts_ids import BuiltinConcepts
from core.sheerka.services.SheerkaExecute import ParserInput, SheerkaExecute
from core.tokenizer import TokenKind, Token
from core.utils import get_text_from_tokens
from parsers.BaseParser import BaseParser
from parsers.expressions import ComparisonNode, ParenthesisMismatchError, NameExprNode, ComparisonType, VariableNode
from parsers.BaseParser import BaseParser, UnexpectedTokenParsingError
from parsers.expressions import ComparisonNode, ParenthesisMismatchError, NameExprNode, ComparisonType, VariableNode, \
ParenthesisNode, LeftPartNotFoundError
class ComparisonParser(BaseParser):
class RelationalOperatorParser(BaseParser):
"""
Parses xxx (== | > | < | >= | <= | != | in | not in) yyy
Nothing else
@@ -47,6 +48,8 @@ class ComparisonParser(BaseParser):
self.parser_input.next_token()
node = self.parse_compare()
if isinstance(node, ParenthesisNode):
node = node.node
value = self.get_return_value_body(context.sheerka, self.parser_input.as_text(), node, node)
@@ -67,21 +70,42 @@ class ComparisonParser(BaseParser):
return left
right = self.parse_names()
if isinstance(right, ParenthesisNode):
right = right.node
end = right.end if right else self.parser_input.pos
return ComparisonNode(start, end, self.parser_input.tokens[start: end + 1], comp, left, right)
def parse_names(self):
def stop():
return token.type == TokenKind.EOF or \
paren_count == 0 and token.type == TokenKind.RPAR or \
self.eat_comparison(False)
token = self.parser_input.token
if token.type == TokenKind.EOF:
return None
if token.type == TokenKind.LPAR:
start = self.parser_input.pos
self.parser_input.next_token()
expr = self.parse_compare()
token = self.parser_input.token
if token.type != TokenKind.RPAR:
self.error_sink.append(
UnexpectedTokenParsingError(f"Unexpected token '{token}'", token, [TokenKind.RPAR]))
return expr
end = self.parser_input.pos
self.parser_input.next_token()
return ParenthesisNode(start, end, None, expr)
buffer = []
paren_count = 0
last_lparen = None
last_rparen = None
start = self.parser_input.pos
while (paren_count > 0 or not self.eat_comparison(False)) and token.type != TokenKind.EOF:
while not stop():
buffer.append(token)
if token.type == TokenKind.LPAR:
last_lparen = token
@@ -92,8 +116,10 @@ class ComparisonParser(BaseParser):
self.parser_input.next_token(False)
token = self.parser_input.token
if paren_count != 0:
pass
if len(buffer) == 0:
if token.type != TokenKind.RPAR:
self.error_sink.append(LeftPartNotFoundError())
return None
if paren_count > 0:
self.error_sink.append(ParenthesisMismatchError(last_lparen))
+8 -2
View File
@@ -224,14 +224,20 @@ class ParenthesisNode(ExprNode):
if self.start != other.start or self.end != other.end:
return False
if other.tokens is not None and other.tokens != self.tokens:
return False
# if other.tokens is not None and other.tokens != self.tokens:
# return False
return self.node == other.node
def __hash__(self):
return hash((self.start, self.end, self.node))
def __repr__(self):
return f"ParenthesisNode(start={self.start}, end={self.end}, node={self.node!r})"
def __str__(self):
return f"({self.node})"
class VariableNode(ExprNode):
def __init__(self, start, end, tokens, name, *attributes):
+22 -9
View File
@@ -13,7 +13,8 @@ from parsers.BaseNodeParser import UnrecognizedTokensNode, SourceCodeNode, RuleN
from parsers.FunctionParser import FunctionNode
from parsers.PythonParser import PythonNode
from parsers.SyaNodeParser import SyaConceptParserHelper
from parsers.expressions import NameExprNode, AndNode, OrNode, NotNode, VariableNode, ComparisonNode, ComparisonType
from parsers.expressions import NameExprNode, AndNode, OrNode, NotNode, VariableNode, ComparisonNode, ComparisonType, \
ParenthesisNode
from sheerkarete.common import V
from sheerkarete.conditions import Condition, AndConditions
@@ -66,56 +67,62 @@ class VAR:
class EQ:
left: object
right: object
source = None
source: str = None
@dataclass
class NEQ:
left: object
right: object
source = None
source: str = None
@dataclass
class GT:
left: object
right: object
source = None
source: str = None
@dataclass
class GTE:
left: object
right: object
source = None
source: str = None
@dataclass
class LT:
left: object
right: object
source = None
source: str = None
@dataclass
class LTE:
left: object
right: object
source = None
source: str = None
@dataclass
class IN:
left: object
right: object
source = None
source: str = None
@dataclass
class NIN: # for NOT INT
left: object
right: object
source = None
source: str = None
@dataclass
class PAREN: # for parenthesis node
node: object
source: str = None
class CC:
@@ -1010,6 +1017,12 @@ def get_expr_node_from_test_node(full_text, test_node):
return ComparisonNode(start, end, full_text_as_tokens[start: end + 1],
node_type, left_node, right_node)
if isinstance(node, PAREN):
value_as_tokens = list(Tokenizer(node.source, yield_eof=False))
start = tokens_index(full_text_as_tokens, value_as_tokens, 0)
end = start + len(value_as_tokens) - 1
return ParenthesisNode(start, end, value_as_tokens, get_expr_node(node.node))
return get_expr_node(test_node)
@@ -49,6 +49,9 @@ class TestLogicalOperatorParser(TestUsingMemoryBasedSheerka):
("not ((a and b) or (c and d))", NOT(OR(AND(EXPR("a"), EXPR("b")), AND(EXPR("c"), EXPR("d")),
source="(a and b) or (c and d)"),
source="not ((a and b) or (c and d))")),
("(one and two)", AND(EXPR("one"), EXPR("two"))),
("(one or two)", OR(EXPR("one"), EXPR("two"))),
("(not one)", NOT(EXPR("one"))),
])
def test_i_can_parse_expression(self, expression, expected):
sheerka, context, parser = self.init_parser()
@@ -3,16 +3,17 @@ import pytest
from core.builtin_concepts_ids import BuiltinConcepts
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import TokenKind, Tokenizer
from parsers.ComparisonParser import ComparisonParser
from parsers.RelationalOperatorParser import RelationalOperatorParser
from parsers.expressions import ParenthesisMismatchError
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.parsers.parsers_utils import get_expr_node_from_test_node, VAR, EXPR, EQ, NEQ, GT, GTE, LT, LTE, IN, NIN
from tests.parsers.parsers_utils import get_expr_node_from_test_node, VAR, EXPR, EQ, NEQ, GT, GTE, LT, LTE, IN, NIN, \
PAREN
class TestComparisonParser(TestUsingMemoryBasedSheerka):
class TestRelationalOperatorParser(TestUsingMemoryBasedSheerka):
def init_parser(self):
sheerka, context = self.init_concepts()
parser = ComparisonParser()
parser = RelationalOperatorParser()
return sheerka, context, parser
def test_i_can_detect_empty_expression(self):
@@ -35,13 +36,16 @@ class TestComparisonParser(TestUsingMemoryBasedSheerka):
("var_name.attr >= 10", GTE(VAR("var_name.attr"), EXPR("10"))),
("var_name.attr < 10", LT(VAR("var_name.attr"), EXPR("10"))),
("var_name.attr <= 10", LTE(VAR("var_name.attr"), EXPR("10"))),
("var_name.attr in (a, b)", IN(VAR("var_name.attr"), EXPR("(a, b)"))),
("var_name.attr not in (a, b)", NIN(VAR("var_name.attr"), EXPR("(a, b)"))),
("var_name.attr in (a, b)", IN(VAR("var_name.attr"), EXPR("a, b"))),
("var_name.attr not in (a, b)", NIN(VAR("var_name.attr"), EXPR("a, b"))),
("var1.attr1 == var2.attr2", EQ(VAR("var1.attr1"), VAR("var2.attr2"))),
("var1.attr1 == (var2.attr2)", EQ(VAR("var1.attr1"), VAR("var2.attr2"))),
("var_name.attr in (a.b, b.c)", IN(VAR("var_name.attr"), PAREN(EXPR("a.b, b.c"), source="(a.b, b.c)"))),
("not a var identifier", EXPR("not a var identifier")),
("func()", EXPR("func()")),
("func(a, not an identifier, x >5)", EXPR("func(a, not an identifier, x >5)")),
("(var_name.attr != var_name2.attr2)", NEQ(VAR("var_name.attr"), VAR("var_name2.attr2")))
])
def test_i_can_parse_simple_expressions(self, expression, expected):
sheerka, context, parser = self.init_parser()
@@ -82,6 +86,18 @@ class TestComparisonParser(TestUsingMemoryBasedSheerka):
assert res.body.body[0].token.type == parenthesis_type
assert res.body.body[0].token.index == index
def test_i_can_detect_syntax_error(self):
sheerka, context, parser = self.init_parser()
expression = "var in a"
res = parser.parse(context, ParserInput(expression))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert isinstance(res.body.body[0], UnexpectedTokenError)
def test_i_can_parse_tokens_rather_than_parser_input(self):
sheerka, context, parser = self.init_parser()
expression = "var1.attr1 == var2.attr2"