/*.py[co]
+/parser.out
+/parsetab.py
--- /dev/null
+# Grammar rules for tag searching; BNF.
+
+# The grammar defines expressions in the following forms:
+# TAG - search blog posts that contain the tag;
+# !TAG - search blog posts that don't contain the tag;
+# TAG & TAG - search blog posts that contain both tags;
+# TAG | TAG - search blog posts that contain one of the tags or both;
+# Parentheses are allowed to group expressions:
+# TAG & (TAG | TAG)
+# !(TAG | TAG)
+# and so on. This is the first 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 stricter and more complex.
+
+NAME : '[a-z][a-z0-9_]+'
+
+AND_OP : '&'
+
+OR_OP : '|'
+
+NOT_OP : '!'
+
+expression : NAME
+ | expression AND_OP expression
+ | NOT_OP expression
+ | expression OR_OP expression
+ | '(' expression ')'
"""Avoid warnings on stderr"""
lexer = lex.lex()
+
+def p_expression_name(p):
+ """expression : NAME"""
+ p[0] = ('NAME', p[1])
+
+def p_expression_and(p):
+ """expression : expression AND_OP expression"""
+ p[0] = ('AND', p[1], p[3])
+
+def p_expression_not(p):
+ """expression : NOT_OP expression"""
+ p[0] = ('NOT', p[2])
+
+def p_expression_or(p):
+ """expression : expression OR_OP expression"""
+ p[0] = ('OR', p[1], p[3])
+
+def p_expression_parens(p):
+ """expression : '(' expression ')'"""
+ p[0] = ('PARENS', p[2])
+
+def p_error(p):
+ """Avoid warnings on stderr"""
+ yacc.restart()
+
+precedence = (
+ ('left', 'OR_OP'),
+ ('left', 'AND_OP'),
+ ('right', 'NOT_OP'),
+)
+
+parser = yacc.yacc()
--- /dev/null
+#! /usr/bin/env python
+
+import unittest
+
+class TestParser(unittest.TestCase):
+ def test_01_import(self):
+ global parser
+ from parse_query import parser
+
+ def test_02_tag(self):
+ self.assertEqual(parser.parse('xxx'), ('NAME', 'xxx'))
+
+ def test_03_bad_tag(self):
+ from ply.lex import LexError
+ self.assertRaises(LexError, parser.parse, 'XXX')
+
+ def test_04_expression(self):
+ self.assertEqual(parser.parse('!(xxx&yyy)'),
+ ('NOT', ('PARENS', ('AND', ('NAME', 'xxx'), ('NAME', 'yyy'))))
+ )
+
+ def test_05_bad_expression(self):
+ self.assertIs(parser.parse('!(xxx&yyy'), None)
+
+ def test_06_expression2(self):
+ self.assertEqual(parser.parse('!xxx&yyy&zzz|ooo'),
+ ('OR', ('AND', ('AND', ('NOT', ('NAME', 'xxx')), ('NAME', 'yyy')), ('NAME', 'zzz')), ('NAME', 'ooo'))
+ )
+
+if __name__ == "__main__":
+ unittest.main()