From b4b7ad7bdf38bc6cb84d0f29cbae0df6e46a0eda Mon Sep 17 00:00:00 2001 From: Oleg Broytman Date: Sun, 22 Jun 2014 19:32:58 +0400 Subject: [PATCH] Allow 'NOT ' and 'not ' --- ChangeLog | 2 +- TODO | 2 -- parser/grammar | 6 +++++- parser/parser.py | 19 +++++++++++++++---- parser/test_lexer.py | 2 +- parser/test_parser.py | 6 ++++++ 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5b2e9eb..649b099 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ Version 0.3 (2014-06-??) - Allow '&&', '||', ' AND ', ' and ', ' OR ' and ' or '. + Allow '&&', '||', ' AND ', ' and ', ' OR ', ' or ', 'NOT ' and 'not '. Version 0.2 (2014-06-06) diff --git a/TODO b/TODO index 1985735..0126e73 100644 --- a/TODO +++ b/TODO @@ -1,3 +1 @@ -Allow 'NOT ' and 'not '. - Forbid stupid expressions: double NOT, extra parens and so on. diff --git a/parser/grammar b/parser/grammar index b88b9ed..14753c8 100644 --- a/parser/grammar +++ b/parser/grammar @@ -10,7 +10,7 @@ # !(TAG | TAG) # Allowed operators: conjunction - & && AND and # disjunction - | || OR or -# negation - ! +# negation - ! NOT not # This is a simple version of the grammar and it allows # rather stupid expressions, like !!TAG or ((TAG)); in the future # it will be fixed by making the grammar more complex and stricter. @@ -33,6 +33,7 @@ expression : NAME | expression SP0 OR_OP SP0 expression | l_expression or_word r_expression | NOT_OP SP0 expression + | not_word r_expression | expression_parens l_expression : expression_parens @@ -53,6 +54,9 @@ and_word : 'A' 'N' 'D' or_word : 'O' 'R' | 'o' 'r' +not_word : 'N' 'O' 'T' + | 'n' 'o' 't' + SP0 : SP1 | empty empty : diff --git a/parser/parser.py b/parser/parser.py index cf463da..e214698 100644 --- a/parser/parser.py +++ b/parser/parser.py @@ -4,9 +4,11 @@ from ply import lex, yacc literals = '()' -tokens = ('NAME', 'AND_OP', 'OR_OP', 'NOT_OP', 'SP1') +tokens = ('OP_WORD', 'NAME', 'AND_OP', 'OR_OP', 'NOT_OP', 'SP1') -t_NAME = '([a-z][a-z0-9_]*)|AND|OR|NOT' +t_OP_WORD = '(AND|and|OR|or|NOT|not)' + +t_NAME = '([a-z][a-z0-9_]*)' t_AND_OP = '&' @@ -39,6 +41,8 @@ def p_expression_op_word(p): p[0] = ('AND', p[1], p[3]) elif p[2] in ('OR', 'or'): p[0] = ('OR', p[1], p[3]) + else: + raise ValueError(p) def p_expression_or_or(p): """expression : expression SP0 OR_OP OR_OP SP0 expression""" @@ -52,6 +56,13 @@ def p_expression_not(p): """expression : NOT_OP SP0 expression""" p[0] = ('NOT', p[3]) +def p_expression_not_word(p): + """expression : op_word r_expression""" + if p[1] in ('NOT', 'not'): + p[0] = ('NOT', p[2]) + else: + raise ValueError(p) + def p_expression_in_parens(p): """expression : expression_parens""" p[0] = p[1] @@ -83,8 +94,8 @@ def p_expression_parens(p): p[0] = ('PARENS', p[3]) def p_op_word(p): - """op_word : NAME""" - if p[1] in ('AND', 'and', 'OR', 'or'): + """op_word : OP_WORD""" + if p[1] in ('AND', 'and', 'OR', 'or', 'NOT', 'not'): p[0] = p[1] else: raise SyntaxError diff --git a/parser/test_lexer.py b/parser/test_lexer.py index c31593e..ba46ee6 100755 --- a/parser/test_lexer.py +++ b/parser/test_lexer.py @@ -74,7 +74,7 @@ class TestLexer(unittest.TestCase): self.assertEqual(lextoken.lineno, 1) self.assertEqual(lextoken.lexpos, 3) lextoken = tokens[2] - self.assertEqual(lextoken.type, 'NAME') + self.assertEqual(lextoken.type, 'OP_WORD') self.assertEqual(lextoken.value, 'and') self.assertEqual(lextoken.lineno, 1) self.assertEqual(lextoken.lexpos, 4) diff --git a/parser/test_parser.py b/parser/test_parser.py index c6d353e..4696047 100755 --- a/parser/test_parser.py +++ b/parser/test_parser.py @@ -39,6 +39,12 @@ class TestParser(unittest.TestCase): self.assertEqual(parser.parse('xxx OR yyy'), ('OR', ('NAME', 'xxx'), ('NAME', 'yyy')) ) + self.assertEqual(parser.parse('not xxx'), + ('NOT', ('NAME', 'xxx')) + ) + self.assertEqual(parser.parse('NOT xxx'), + ('NOT', ('NAME', 'xxx')) + ) def test_05_bad_expression(self): self.assertIs(parser.parse('!(xxx&yyy'), None) -- 2.39.2