X-Git-Url: https://git.phdru.name/?a=blobdiff_plain;f=parser%2Fparser.py;h=dc562dd5043783f632499d180d283591ccf46ccf;hb=7ddae4701a4608b91ac32bce958468682c1b7960;hp=1fb9903c62ee93794a22925678df963c728d4f2c;hpb=9d22f4f705db6a30fa2d49fdc074b901ec3cb3dc;p=phdru.name%2Fcgi-bin%2Fblog-ru%2Fsearch-tags.git diff --git a/parser/parser.py b/parser/parser.py index 1fb9903..dc562dd 100644 --- a/parser/parser.py +++ b/parser/parser.py @@ -1,62 +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 = '!' -t_ignore = '[ \t]+' +def cleanup_children(visited_children): + children = [c for c in visited_children if c] + if len(children) == 1: + return children[0] + else: + return children -def t_error(t): - """Avoid warnings on stderr""" -lexer = lex.lex() +class Compiler(NodeVisitor): + def generic_visit(self, node, visited_children): + return cleanup_children(visited_children) -def p_expression_name(p): - """expression : NAME""" - p[0] = ('NAME', p[1]) + def visit_or_expression(self, node, visited_children): + return ('OR', visited_children[0], visited_children[2]) -def p_expression_and_and(p): - """expression : expression AND_OP AND_OP expression""" - p[0] = ('AND', p[1], p[4]) + def visit_and_expression(self, node, visited_children): + return ('AND', visited_children[0], visited_children[2]) -def p_expression_and(p): - """expression : expression AND_OP expression""" - p[0] = ('AND', p[1], p[3]) + def visit_not_expression(self, node, visited_children): + return ('NOT', visited_children[2]) -def p_expression_not(p): - """expression : NOT_OP expression""" - p[0] = ('NOT', p[2]) + def visit_parens_expression(self, node, visited_children): + return ('PARENS', visited_children[2]) -def p_expression_or_or(p): - """expression : expression OR_OP OR_OP expression""" - p[0] = ('OR', p[1], p[4]) + def visit_name(self, node, visited_children): + return ('NAME', node.text) -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() +def compile(tree): + if isinstance(tree, str): + tree = parse(tree) + return Compiler().visit(tree)