【问题标题】:NodeVisitor class for PEG parser in PythonPython 中 PEG 解析器的 NodeVisitor 类
【发布时间】:2019-08-19 15:56:28
【问题描述】:

想象以下类型的字符串:

if ((a1 and b) or (a2 and c)) or (c and d) or (e and f)

现在,我想得到括号中的表达式,所以我写了一个PEG解析器,语法如下:

from parsimonious.grammar import Grammar

grammar = Grammar(
    r"""
    program     = if expr+
    expr        = term (operator term)*
    term        = (factor operator factor) / factor
    factor      = (lpar word operator word rpar) / (lpar expr rpar)

    if          = "if" ws
    and         = "and"
    or          = "or"
    operator    = ws? (and / or) ws?

    word        = ~"\w+"
    lpar        = "("
    rpar        = ")"

    ws          = ~"\s*"
    """)

解析就好了

tree = grammar.parse(string)

现在问题来了:如何为这棵树编写一个NodeVisitor 类以仅获取因子?我的问题是第二个可以深度嵌套的分支。


我试过了
def walk(node, level = 0):
    if node.expr.name == "factor":
        print(level * "-", node.text)

    for child in node.children:
        walk(child, level + 1)

walk(tree)

但实际上无济于事(因素会重复出现)。
注意:本题基于 StackOverflow 上的another one

【问题讨论】:

    标签: python regex parsing parsimonious


    【解决方案1】:

    我将如何将 ((a1 和 b) 或 (a2 和 c))、(c 和 d) 和 (e 和 f) 作为三个部分?

    当解析树中的节点是( 时,您可以创建一个“监听”访问者,其中深度变量增加,当遇到) 时,深度变量减少。然后在被调用的匹配括号表达式的方法中,在将深度添加到要从访问者返回的表达式列表之前检查深度。

    这是一个简单的例子:

    from parsimonious.grammar import Grammar
    from parsimonious.nodes import NodeVisitor
    
    grammar = Grammar(
        r"""
        program     = if expr+
        expr        = term (operator term)*
        term        = (lpar expr rpar) / word
    
        if          = "if" ws
        and         = "and"
        or          = "or"
        operator    = ws? (and / or) ws?
    
        word        = ~"\w+"
        lpar        = "("
        rpar        = ")"
    
        ws          = ~"\s*"
        """)
    
    
    class ParExprVisitor(NodeVisitor):
    
        def __init__(self):
            self.depth = 0
            self.par_expr = []
    
        def visit_term(self, node, visited_children):
            if self.depth == 0:
                self.par_expr.append(node.text)
    
        def visit_lpar(self, node, visited_children):
            self.depth += 1
    
        def visit_rpar(self, node, visited_children):
            self.depth -= 1
    
        def generic_visit(self, node, visited_children):
            return self.par_expr
    
    
    tree = grammar.parse("if ((a1 and b) or (a2 and c)) or (c and d) or (e and f)")
    visitor = ParExprVisitor()
    
    for expr in visitor.visit(tree):
        print(expr)
    

    哪个打印:

    ((a1 and b) or (a2 and c))
    (c and d)
    (e and f)
    

    【讨论】:

    • 谢谢。我将如何将((a1 and b) or (a2 and c))(c and d)(e and f) 作为三个部分?很抱歉在这里的原始问题不清楚。 imo 的主要问题是如何编写递归NodeVisitor 类。
    • 啊,我明白了。让我快速浏览一下。
    • 这很聪明!我会在赏金中再等几天,但这绝对很有趣。
    • 当然,没问题,我回答是因为我不知道这个看起来不错的parsimonious 库。我可能会仔细看看。
    【解决方案2】:

    如果您只想返回每个最外层因素,请尽早返回return,不要下降到其子级。

    def walk(node, level = 0):
        if node.expr.name == "factor":
            print(level * "-", node.text)
            return
        for child in node.children:
            walk(child, level + 1)
    

    输出:

    ----- ((a1 and b) or (a2 and c))
    ----- (c and d)
    ------ (e and f)
    

    【讨论】:

    • 感谢您的回复。但是,我只是在寻找NodeVisitor 解决方案或重新制定语法的提示。对于早期的回报,人们无法对输出做任何事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多