From 02dbb2a7f4d0804c56122cc7ca681224fe659abc Mon Sep 17 00:00:00 2001 From: Oleg Broytman Date: Sun, 22 Jun 2014 03:49:24 +0400 Subject: [PATCH] Allow ' AND ' and ' and ' --- ChangeLog | 2 +- TODO | 2 -- parser/grammar | 20 ++++++++++++++++++-- parser/parser.py | 41 +++++++++++++++++++++++++++++++++++++++-- parser/test_lexer.py | 30 ++++++++++++++++++++++++++++++ parser/test_parser.py | 3 +++ 6 files changed, 91 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 30d3438..b1310a4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ Version 0.3 (2014-06-??) - Allow '&&' and '||'. + Allow '&&', '||', ' AND ' and ' and '. Version 0.2 (2014-06-06) diff --git a/TODO b/TODO index 12fe231..2a96d7b 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,3 @@ -Allow ' AND ' and ' and '. - Allow ' OR ' and ' or '. Allow 'NOT ' and 'not '. diff --git a/parser/grammar b/parser/grammar index 7c34096..3332611 100644 --- a/parser/grammar +++ b/parser/grammar @@ -8,7 +8,7 @@ # Parentheses are allowed to group expressions; for example: # TAG & (TAG | TAG) # !(TAG | TAG) -# Allowed operators: conjunction - & && +# Allowed operators: conjunction - & && AND and # disjunction - | || # negation - ! # This is a simple version of the grammar and it allows @@ -28,10 +28,26 @@ SP1 : '[ \t]+' expression : NAME | expression SP0 AND_OP AND_OP SP0 expression | expression SP0 AND_OP SP0 expression + | l_expression and_word r_expression | NOT_OP SP0 expression | expression SP0 OR_OP OR_OP SP0 expression | expression SP0 OR_OP SP0 expression - | '(' SP0 expression SP0 ')' + | expression_parens + +l_expression : expression_parens + | expression_sp + +r_expression : expression_parens + | sp_expression + +expression_parens : '(' SP0 expression SP0 ')' + +sp_expression : SP1 expression + +expression_sp : expression SP1 + +and_word : 'A' 'N' 'D' + | 'a' 'n' 'd' SP0 : SP1 | empty diff --git a/parser/parser.py b/parser/parser.py index 0af77f6..d278e4b 100644 --- a/parser/parser.py +++ b/parser/parser.py @@ -33,6 +33,10 @@ def p_expression_and(p): """expression : expression SP0 AND_OP SP0 expression""" p[0] = ('AND', p[1], p[5]) +def p_expression_and_word(p): + """expression : l_expression and_word r_expression""" + p[0] = ('AND', p[1], p[3]) + def p_expression_not(p): """expression : NOT_OP SP0 expression""" p[0] = ('NOT', p[3]) @@ -45,17 +49,50 @@ def p_expression_or(p): """expression : expression SP0 OR_OP SP0 expression""" p[0] = ('OR', p[1], p[5]) +def p_expression_in_parens(p): + """expression : expression_parens""" + p[0] = p[1] + +def p_l_expression(p): + """l_expression : expression_parens + | expression SP1 + """ + if len(p) == 2: + p[0] = p[1] + elif len(p) == 3: + p[0] = p[1] + else: + raise ValueError(p) + +def p_r_expression(p): + """r_expression : expression_parens + | SP1 expression + """ + if len(p) == 2: + p[0] = p[1] + elif len(p) == 3: + p[0] = p[2] + else: + raise ValueError(p) + def p_expression_parens(p): - """expression : '(' SP0 expression SP0 ')'""" + """expression_parens : '(' SP0 expression SP0 ')'""" p[0] = ('PARENS', p[3]) +def p_and_word(p): + """and_word : NAME""" + if p[1] in ('AND', 'and'): + p[0] = p[1] + else: + raise SyntaxError + def p_SP0(p): """SP0 : SP1 | empty """ def p_empty(p): - """empty : """ + """empty :""" def p_error(p): """Avoid warnings on stderr""" diff --git a/parser/test_lexer.py b/parser/test_lexer.py index ac3be81..c31593e 100755 --- a/parser/test_lexer.py +++ b/parser/test_lexer.py @@ -59,5 +59,35 @@ class TestLexer(unittest.TestCase): self.assertEqual(lextoken.lineno, 1) self.assertEqual(lextoken.lexpos, 9) + def test_05_expression_2(self): + lexer.input('xxx and yyy') + tokens = list(lexer) + self.assertEqual(len(tokens), 5) + lextoken = tokens[0] + self.assertEqual(lextoken.type, 'NAME') + self.assertEqual(lextoken.value, 'xxx') + self.assertEqual(lextoken.lineno, 1) + self.assertEqual(lextoken.lexpos, 0) + lextoken = tokens[1] + self.assertEqual(lextoken.type, 'SP1') + self.assertEqual(lextoken.value, ' ') + self.assertEqual(lextoken.lineno, 1) + self.assertEqual(lextoken.lexpos, 3) + lextoken = tokens[2] + self.assertEqual(lextoken.type, 'NAME') + self.assertEqual(lextoken.value, 'and') + self.assertEqual(lextoken.lineno, 1) + self.assertEqual(lextoken.lexpos, 4) + lextoken = tokens[3] + self.assertEqual(lextoken.type, 'SP1') + self.assertEqual(lextoken.value, ' ') + self.assertEqual(lextoken.lineno, 1) + self.assertEqual(lextoken.lexpos, 7) + lextoken = tokens[4] + self.assertEqual(lextoken.type, 'NAME') + self.assertEqual(lextoken.value, 'yyy') + self.assertEqual(lextoken.lineno, 1) + self.assertEqual(lextoken.lexpos, 9) + if __name__ == "__main__": unittest.main() diff --git a/parser/test_parser.py b/parser/test_parser.py index d7363c0..50870e9 100755 --- a/parser/test_parser.py +++ b/parser/test_parser.py @@ -30,6 +30,9 @@ class TestParser(unittest.TestCase): self.assertEqual(parser.parse('!(xxx || yyy)'), ('NOT', ('PARENS', ('OR', ('NAME', 'xxx'), ('NAME', 'yyy')))) ) + self.assertEqual(parser.parse('xxx and yyy'), + ('AND', ('NAME', 'xxx'), ('NAME', 'yyy')) + ) def test_05_bad_expression(self): self.assertIs(parser.parse('!(xxx&yyy'), None) -- 2.39.2