]> git.phdru.name Git - phdru.name/cgi-bin/blog-ru/search-tags.git/blobdiff - parser/parser.py
Use parsimonious instead of grako
[phdru.name/cgi-bin/blog-ru/search-tags.git] / parser / parser.py
index c74eb78d21a29a5d33ec85af523ed85266b41796..dc562dd5043783f632499d180d283591ccf46ccf 100644 (file)
@@ -1,52 +1,54 @@
-# Parse query
+import os
+from parsimonious import Grammar, NodeVisitor
 
-from ply import lex, yacc
 
-literals = '()'
+# cache
+_grammar = None
 
-tokens = ('NAME', 'AND_OP', 'OR_OP', 'NOT_OP')
 
-t_NAME = '[a-z][a-z0-9_]+'
+def load_grammar():
+    global _grammar
+    parser_dir = os.path.dirname(__file__)
+    with open(os.path.join(parser_dir, 'grammar.ebnf'), 'rt') as grammar_file:
+        grammar_text = grammar_file.read()
+    _grammar = Grammar(grammar_text)
 
-t_AND_OP = '&'
 
-t_OR_OP = r'\|'
+def parse(input):
+    if _grammar is None:
+        load_grammar()
+    return _grammar.parse(input)
 
-t_NOT_OP = '!'
 
-def t_error(t):
-    """Avoid warnings on stderr"""
+def cleanup_children(visited_children):
+    children = [c for c in visited_children if c]
+    if len(children) == 1:
+        return children[0]
+    else:
+        return children
 
-lexer = lex.lex()
 
-def p_expression_name(p):
-    """expression : NAME"""
-    p[0] = ('NAME', p[1])
+class Compiler(NodeVisitor):
+    def generic_visit(self, node, visited_children):
+        return cleanup_children(visited_children)
 
-def p_expression_and(p):
-    """expression : expression AND_OP expression"""
-    p[0] = ('AND', p[1], p[3])
+    def visit_or_expression(self, node, visited_children):
+        return ('OR', visited_children[0], visited_children[2])
 
-def p_expression_not(p):
-    """expression : NOT_OP expression"""
-    p[0] = ('NOT', p[2])
+    def visit_and_expression(self, node, visited_children):
+        return ('AND', visited_children[0], visited_children[2])
 
-def p_expression_or(p):
-    """expression : expression OR_OP expression"""
-    p[0] = ('OR', p[1], p[3])
+    def visit_not_expression(self, node, visited_children):
+        return ('NOT', visited_children[2])
 
-def p_expression_parens(p):
-    """expression : '(' expression ')'"""
-    p[0] = ('PARENS', p[2])
+    def visit_parens_expression(self, node, visited_children):
+        return ('PARENS', visited_children[2])
 
-def p_error(p):
-    """Avoid warnings on stderr"""
-    yacc.restart()
+    def visit_name(self, node, visited_children):
+        return ('NAME', node.text)
 
-precedence = (
-    ('left', 'OR_OP'),
-    ('left', 'AND_OP'),
-    ('right', 'NOT_OP'),
-)
 
-parser = yacc.yacc()
+def compile(tree):
+    if isinstance(tree, str):
+        tree = parse(tree)
+    return Compiler().visit(tree)