]> git.phdru.name Git - phdru.name/cgi-bin/blog-ru/search-tags.git/commitdiff
Allow ' AND ' and ' and '
authorOleg Broytman <phd@phdru.name>
Sat, 21 Jun 2014 23:49:24 +0000 (03:49 +0400)
committerOleg Broytman <phd@phdru.name>
Sat, 21 Jun 2014 23:49:24 +0000 (03:49 +0400)
ChangeLog
TODO
parser/grammar
parser/parser.py
parser/test_lexer.py
parser/test_parser.py

index 30d3438821525aa32ea89dc216b6497f2ed31c37..b1310a4116b7044edb0a3536ac672276e5407a3f 100644 (file)
--- 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 12fe231194b12d0cb18f0a6920cc91db8b561499..2a96d7bf3c110d0530fd7e346c081d16f06ec84e 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,5 +1,3 @@
-Allow ' AND ' and ' and '.
-
 Allow ' OR ' and ' or '.
 
 Allow 'NOT ' and 'not '.
index 7c34096e3bdb10aaee6557185dcca6c60fb6d893..33326116c917ec5300f0df9b6ebbf95e44dfa65a 100644 (file)
@@ -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
 
index 0af77f65f2e99054ff568226658e66b8e4d7608b..d278e4b210681817be55a9624083d3b7d62747a7 100644 (file)
@@ -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"""
index ac3be813263eab36fc756d376b58e2ee8ffaa4fe..c31593eeaaff45439b00301951d3ac49c82e2cc7 100755 (executable)
@@ -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()
index d7363c019b09a1a4bfd9df87c5577122de8ab17d..50870e938e76dc5198df2d293a818fa1da4d6be4 100755 (executable)
@@ -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)