diff --git a/py_rql/grammar.py b/py_rql/grammar.py index 57c790e..a7757dc 100644 --- a/py_rql/grammar.py +++ b/py_rql/grammar.py @@ -13,26 +13,28 @@ RQL_GRAMMAR = r""" start: _top_term? - + _top_term: term | term_and_comma term: expr_term | logical | tuple - + | _L_BRACE logical _R_BRACE + | _L_BRACE tuple _R_BRACE + expr_term: comp | listing | searching | ordering | select | _L_BRACE expr_term _R_BRACE - + logical: and_op | or_op | not_op - -and_op: _and + +and_op: _and | _L_BRACE _and _R_BRACE _and: _AND? _logical_exp @@ -57,7 +59,7 @@ comp: comp_term _L_BRACE prop _COMMA val _R_BRACE | prop _EQUALITY comp_term _EQUALITY val | prop _EQUALITY val - + listing: list_term _L_BRACE prop _COMMA _L_BRACE val (_COMMA val)* _R_BRACE _R_BRACE searching: search_term _L_BRACE prop _COMMA val _R_BRACE @@ -65,7 +67,7 @@ select: select_term _signed_props _signed_props: _L_BRACE _R_BRACE | _L_BRACE sign_prop (_COMMA sign_prop)* _R_BRACE - + val: prop | tuple | QUOTED_VAL @@ -78,11 +80,11 @@ | ordering_term | select_term | PROP - + tuple: _TUPLE _L_BRACE (comp|searching) (_COMMA (comp|searching))* _R_BRACE !sign_prop: ["+"|"-"] prop - + !comp_term: "eq" | "ne" | "gt" | "ge" | "lt" | "le" !logical_term: _AND | _OR | _NOT !list_term: "in" | "out" @@ -90,7 +92,7 @@ !ordering_term: "ordering" !select_term: "select" - + PROP: /[a-zA-Z]/ /[\w\-\.]/* QUOTED_VAL: /"[^"]*"/ | /'[^']*'/ diff --git a/tests/test_parser/test_logical.py b/tests/test_parser/test_logical.py index 89ef646..580bdec 100644 --- a/tests/test_parser/test_logical.py +++ b/tests/test_parser/test_logical.py @@ -7,7 +7,11 @@ import pytest from lark.exceptions import LarkError -from py_rql.constants import ComparisonOperators, LogicalOperators +from py_rql.constants import ( + ComparisonOperators, + LogicalOperators, + SearchOperators, +) from py_rql.parser import RQLParser from tests.test_parser.utils import LogicalTransformer @@ -199,3 +203,48 @@ def test_or_chain(query): (ComparisonOperators.EQ, 'p4', 'v4'), ], } + + +def test_logical_tuple_and_not(): + result = logical_transform('(not(id=fg))') + + not_grammar_key = LogicalOperators.get_grammar_key(LogicalOperators.NOT) + + assert result == { + not_grammar_key: [(ComparisonOperators.EQ, 'id', 'fg')], + } + + +def test_logical_tuple_and_not_w_and_nesting(): + result = logical_transform('((not(eq(id,fg)))&(not(ge(external_id,fg))))') + + and_grammar_key = LogicalOperators.get_grammar_key(LogicalOperators.AND) + not_grammar_key = LogicalOperators.get_grammar_key(LogicalOperators.NOT) + + assert result == { + and_grammar_key: [ + {not_grammar_key: [(ComparisonOperators.EQ, 'id', 'fg')]}, + {not_grammar_key: [(ComparisonOperators.GE, 'external_id', 'fg')]} + ] + } + + +def test_logical_tuple_nesting_ands_and_ors(): + result = logical_transform( + '((((eq(id,*baba*))&(eq(external_id,*baba*))))|(((eq(id,*dziad*)))))', + ) + + and_grammar_key = LogicalOperators.get_grammar_key(LogicalOperators.AND) + or_grammar_key = LogicalOperators.get_grammar_key(LogicalOperators.OR) + + assert result == { + or_grammar_key: [ + { + and_grammar_key: [ + (ComparisonOperators.EQ, 'id', '*baba*'), + (ComparisonOperators.EQ, 'external_id', '*baba*') + ] + }, + (ComparisonOperators.EQ, 'id', '*dziad*') + ] + }