Files
Sheerka-Old/tests/parsers/test_LogicalOperatorParser.py
T
kodjo 89e1f20975 Fixed #131 : Implement ExprToConditions
Fixed #130 : ArithmeticOperatorParser
Fixed #129 : python_wrapper : create_namespace
Fixed #128 : ExpressionParser: Cannot parse func(x) infixed concept 'xxx'
2021-10-13 16:06:57 +02:00

265 lines
12 KiB
Python

import pytest
from core.builtin_concepts import BuiltinConcepts
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import TokenKind
from parsers.BaseExpressionParser import AndNode, IsAQuestionVisitor, LeftPartNotFoundError, NotNode, OrNode, \
ParenthesisMismatchError, TrueifyVisitor, compile_disjunctions
from parsers.BaseParser import UnexpectedEofParsingError, UnexpectedTokenParsingError
from parsers.LogicalOperatorParser import LogicalOperatorParser
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.parsers.parsers_utils import AND, EXPR, NOT, OR, get_expr_node_from_test_node
class DoNotCompareStartStopContextManager:
def __init__(self):
self.original_not_eq = None
self.original_and_eq = None
self.original_or_eq = None
@staticmethod
def not_eq(self, other):
if not isinstance(other, NotNode):
return False
return self.node == other.node
@staticmethod
def and_eq(self, other):
if not isinstance(other, AndNode):
return False
return self.parts == other.parts
@staticmethod
def or_eq(self, other):
if not isinstance(other, OrNode):
return False
return self.parts == other.parts
def __enter__(self):
self.original_not_eq = NotNode.__eq__
self.original_and_eq = AndNode.__eq__
self.original_or_eq = OrNode.__eq__
NotNode.__eq__ = self.not_eq
AndNode.__eq__ = self.and_eq
OrNode.__eq__ = self.or_eq
def __exit__(self, *args):
NotNode.__eq__ = self.original_not_eq
AndNode.__eq__ = self.original_and_eq
OrNode.__eq__ = self.original_or_eq
class TestLogicalOperatorParser(TestUsingMemoryBasedSheerka):
def init_parser(self):
sheerka, context = self.init_concepts()
parser = LogicalOperatorParser()
return sheerka, context, parser
@pytest.mark.parametrize("expression, expected", [
("one complicated expression", EXPR("one complicated expression")),
("function_call(a,b,c)", EXPR("function_call(a,b,c)")),
("one expression or another expression", OR(EXPR("one expression"), EXPR("another expression"))),
("one expression and another expression", AND(EXPR("one expression"), EXPR("another expression"))),
("not one", NOT(EXPR("one"))),
("one and not two", AND(EXPR("one"), NOT(EXPR("two")))),
("not one and two", AND(NOT(EXPR("one")), EXPR("two"))),
("one or not two", OR(EXPR("one"), NOT(EXPR("two")))),
("not one or two", OR(NOT(EXPR("one")), EXPR("two"))),
("one or two or three", OR(EXPR("one"), EXPR("two"), EXPR("three"))),
("one and two and three", AND(EXPR("one"), EXPR("two"), EXPR("three"))),
("one or two and three", OR(EXPR("one"), AND(EXPR("two"), EXPR("three")))),
("one and two or three", OR(AND(EXPR("one"), EXPR("two")), EXPR("three"))),
("one and (two or three)", AND(EXPR("one"), OR(EXPR("two"), EXPR("three")), source="one and (two or three)")),
("not not one", NOT(NOT(EXPR("one")))),
("not (one and two)", NOT(AND(EXPR("one"), EXPR("two")), source="not (one and two)")),
("one 'and' two or three", OR(EXPR("one 'and' two"), EXPR("three"))),
("not ((a or b) and (c or d))", NOT(AND(OR(EXPR("a"), EXPR("b")), OR(EXPR("c"), EXPR("d")),
source="(a or b) and (c or d)"),
source="not ((a or b) and (c or d))")),
("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()
expected = get_expr_node_from_test_node(expression, expected)
res = parser.parse(context, ParserInput(expression))
wrapper = res.body
expressions = res.body.body
assert res.status
assert sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert expressions == expected
@pytest.mark.parametrize("expression, expected_errors", [
("one or", [UnexpectedEofParsingError("while parsing 'or'")]),
("one and", [UnexpectedEofParsingError("while parsing 'and'")]),
("and one", [LeftPartNotFoundError("and", 0)]),
("or one", [LeftPartNotFoundError("or", 0)]),
("or", [LeftPartNotFoundError("or", 0), UnexpectedEofParsingError("while parsing 'or'")]),
("and", [LeftPartNotFoundError("and", 0), UnexpectedEofParsingError("while parsing 'and'")]),
])
def test_i_can_detect_error(self, expression, expected_errors):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(expression))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert res.body.body == expected_errors
def test_i_can_detect_unexpected_not_error(self):
sheerka, context, parser = self.init_parser()
expression = "a cat is not a human"
res = parser.parse(context, ParserInput(expression))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert isinstance(res.body.body[0], UnexpectedTokenParsingError)
@pytest.mark.parametrize("expression, expected_error, parenthesis_type, index", [
("(", BuiltinConcepts.NOT_FOR_ME, TokenKind.LPAR, 0),
(")", BuiltinConcepts.NOT_FOR_ME, TokenKind.RPAR, 0),
("one and two(", BuiltinConcepts.ERROR, TokenKind.LPAR, 11),
("one (", BuiltinConcepts.NOT_FOR_ME, TokenKind.LPAR, 4),
("one (and", BuiltinConcepts.NOT_FOR_ME, TokenKind.LPAR, 4),
("one and two)", BuiltinConcepts.ERROR, TokenKind.RPAR, 11),
("one )", BuiltinConcepts.ERROR, TokenKind.RPAR, 4),
("one ) and", BuiltinConcepts.ERROR, TokenKind.RPAR, 4),
])
def test_i_can_detect_unbalanced_parenthesis(self, expression, expected_error, parenthesis_type, index):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(expression))
assert not res.status
if expected_error == BuiltinConcepts.NOT_FOR_ME:
assert sheerka.isinstance(res.body, BuiltinConcepts.NOT_FOR_ME)
assert isinstance(res.body.reason[0], ParenthesisMismatchError)
assert res.body.reason[0].token.type == parenthesis_type
assert res.body.reason[0].token.index == index
else:
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert isinstance(res.body.body[0], ParenthesisMismatchError)
assert res.body.body[0].token.type == parenthesis_type
assert res.body.body[0].token.index == index
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, to_trueify, to_skip, expected", [
("a", ["b"], ["a"], "a"),
("b", ["b"], ["a"], "True"),
("a and b", ["b"], ["a"], "a and True"),
("b or a", ["b"], ["a"], "True or a"),
("isinstance(b, str)", ["b"], ["a"], "True"),
("isinstance(b, str) or instance(a, str)", ["b"], ["a"], "True or instance(a, str)"),
("a and b or c", ["b", "c"], ["a"], "a and True or True"),
("a + b or a + c", ["b", "c"], ["a"], "a + b or a + c"),
])
def test_i_can_trueify(self, expression, to_trueify, to_skip, expected):
sheerka, context, parser = self.init_parser()
expr_node = parser.parse(context, ParserInput(expression)).body.body
translated_node = TrueifyVisitor(to_trueify, to_skip).visit(expr_node)
assert str(translated_node) == expected
@pytest.mark.parametrize("expression, expected", [
("foo", None),
("", None),
("is_question()", True),
(" is_question() ", True),
("is_question ( ) ", True),
("is _question()", None),
("is_ question()", None),
("is _ question()", None),
("context.in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", True),
("not is_question()", False),
("not context.in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", False),
("not foo", None),
("not is_question() and not is_question()", False),
("not is_question() and is_question()", False),
("not is_question() and foo", False),
("is_question() and not is_question()", False),
("is_question() and is_question()", True),
("is_question() and foo", True),
("foo and not is_question()", False),
("foo and is_question()", True),
("foo and bar", None),
("not is_question() or not is_question()", False),
("not is_question() or is_question()", True),
("not is_question() or foo", False),
("is_question() or not is_question()", True),
("is_question() or is_question()", True),
("is_question() or foo", True),
("foo or not is_question()", False),
("foo or is_question()", True),
("foo or bar", None),
])
def test_is_a_question(self, expression, expected):
sheerka, context, parser = self.init_parser()
expr_node = parser.parse(context, ParserInput(expression)).body.body
assert IsAQuestionVisitor().visit(expr_node) == expected
@pytest.mark.parametrize("expression, expected", [
("a", [EXPR("a")]),
("a and b and c", [AND(EXPR("a"), EXPR("b"), EXPR("c"))]),
("a or b or c", [EXPR("a"),
EXPR("b"),
EXPR("c")]),
("a and b or c", [AND(EXPR("a"), EXPR("b")), EXPR("c")]),
("a or b and c", [EXPR("a"),
AND(EXPR("b"), EXPR("c"))]),
("(a or b) and c", [AND(EXPR("a"), EXPR("c")),
AND(EXPR("b"), EXPR("c"))]),
("a or (b or c) and d", [EXPR("a"),
AND(EXPR("b"), EXPR("d")),
AND(EXPR("c"), EXPR("d"))]),
("not a", [NOT(EXPR("a"))]),
("not (a and b)", [NOT(AND(EXPR("a"), EXPR("b")))]),
("not (a or b)", [AND(NOT(EXPR("a")), NOT(EXPR("b")))]),
("(a or b) and not (c or d)", [AND(EXPR("a"), NOT(EXPR("c")), NOT(EXPR("d"))),
AND(EXPR("b"), NOT(EXPR("c")), NOT(EXPR("d")))]),
("(a or b) or not (c or d)", [EXPR("a"),
EXPR("b"),
AND(NOT(EXPR("c")), NOT(EXPR("d")))]),
("(a and b) and not (c or d)", [AND(EXPR("a"), EXPR("b"), NOT(EXPR("c")), NOT(EXPR("d")))]),
("(a and b) or not (c or d)", [AND(EXPR("a"), EXPR("b")),
AND(NOT(EXPR("c")), NOT(EXPR("d")))]),
("a and (b and c)", [AND(EXPR("a"), EXPR("b"), EXPR("c"))]),
("a or (b or c)", [EXPR("a"),
EXPR("b"),
EXPR("c")]),
("a and b and not c", [AND(EXPR("a"), EXPR("b"), NOT(EXPR("c")))]),
("not a and b", [AND(NOT(EXPR("a")), EXPR("b"))]),
("not a and not b", [AND(NOT(EXPR("a")), NOT(EXPR("b")))]),
("not a or not b", [NOT(EXPR("a")),
NOT(EXPR("b"))]),
])
def test_i_can_compile_disjunction(self, expression, expected):
sheerka, context, parser = self.init_parser()
resolved_expected = [get_expr_node_from_test_node(expression, e) for e in expected]
expr_node = parser.parse(context, ParserInput(expression)).body.body
with DoNotCompareStartStopContextManager():
res = compile_disjunctions(expr_node)
assert res == resolved_expected