diff --git a/parser.g b/parser.g index 0314d8a14d0d3f75381ff455e86a86cc2740168f_cGFyc2VyLmc=..c225f5dc1a7efe912be190a374b9b18d89a585bf_cGFyc2VyLmc= 100644 --- a/parser.g +++ b/parser.g @@ -268,8 +268,16 @@ rule exprs_not<<S>>: NOT balanced_expr<<S>> {{ return Not(balanced_expr) }} | balanced_expr<<S>> {{ return balanced_expr }} -rule balanced_expr<<S>>: expr_add<<S>> expr_op<<S>> {{ expr_op.insert(0, expr_add); return expr_op }} - | r"\(" logical_expr<<S>> r"\)" {{ return logical_expr }} +#// XXX ambiguity, expr_add may also have '(' as first token. Hence +#// put "(" logical_expr<<S>> ")" rule first. We can then parse: +#// +#// Any T2 WHERE T1 relation T2 HAVING (1 < COUNT(T1)); +#// +#// but not +#// +#// Any T2 WHERE T1 relation T2 HAVING (1+2) < COUNT(T1); +rule balanced_expr<<S>>: r"\(" logical_expr<<S>> r"\)" {{ return logical_expr }} + | expr_add<<S>> expr_op<<S>> {{ expr_op.insert(0, expr_add); return expr_op }} # // cant use expr<<S>> without introducing some ambiguities rule expr_op<<S>>: CMP_OP expr_add<<S>> {{ return Comparison(CMP_OP.upper(), expr_add) }} diff --git a/parser.py b/parser.py index 0314d8a14d0d3f75381ff455e86a86cc2740168f_cGFyc2VyLnB5..c225f5dc1a7efe912be190a374b9b18d89a585bf_cGFyc2VyLnB5 100644 --- a/parser.py +++ b/parser.py @@ -519,9 +519,17 @@ def balanced_expr(self, S, _parent=None): _context = self.Context(_parent, self._scanner, 'balanced_expr', [S]) _token = self._peek('r"\\("', 'NULL', 'DATE', 'DATETIME', 'TRUE', 'FALSE', 'FLOAT', 'INT', 'STRING', 'SUBSTITUTE', 'VARIABLE', 'E_TYPE', 'FUNCTION', context=_context) - expr_add = self.expr_add(S, _context) - expr_op = self.expr_op(S, _context) - expr_op.insert(0, expr_add); return expr_op + if _token == 'r"\\("': + self._scan('r"\\("', context=_context) + logical_expr = self.logical_expr(S, _context) + self._scan('r"\\)"', context=_context) + return logical_expr + elif 1: + expr_add = self.expr_add(S, _context) + expr_op = self.expr_op(S, _context) + expr_op.insert(0, expr_add); return expr_op + else: + raise runtime.SyntaxError(_token[0], 'Could not match balanced_expr') def expr_op(self, S, _parent=None): _context = self.Context(_parent, self._scanner, 'expr_op', [S]) diff --git a/test/unittest_parser.py b/test/unittest_parser.py index 0314d8a14d0d3f75381ff455e86a86cc2740168f_dGVzdC91bml0dGVzdF9wYXJzZXIucHk=..c225f5dc1a7efe912be190a374b9b18d89a585bf_dGVzdC91bml0dGVzdF9wYXJzZXIucHk= 100644 --- a/test/unittest_parser.py +++ b/test/unittest_parser.py @@ -133,6 +133,21 @@ ' GROUPBY T2' ' WHERE T1 relation T2' ' HAVING 1 < COUNT(T1) OR COUNT(T1) IN (3,4);', + + 'Any T2' + ' GROUPBY T2' + ' WHERE T1 relation T2' + ' HAVING (COUNT(T1) IN (1,2)) OR (COUNT(T1) IN (3,4));', + + 'Any T2' + ' GROUPBY T2' + ' WHERE T1 relation T2' + ' HAVING (1 < COUNT(T1) OR COUNT(T1) IN (3,4));', + + 'Any T2' + ' GROUPBY T2' + ' WHERE T1 relation T2' + ' HAVING 1+2 < COUNT(T1);', ) class ParserHercule(TestCase):