Clase NodeVisitor para el analizador PEG en Python

Imagina los siguientes tipos de cuerdas:

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

Ahora, me gustaría obtener las expresiones entre paréntesis, así que escribí un analizador PEG con la siguiente gramática:

 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*" """) 

que analiza bien con

 tree = grammar.parse(string) 

Ahora surge la pregunta: ¿cómo escribir una clase NodeVisitor para que este árbol obtenga solo los factores? Mi problema aquí es la segunda twig que puede ser profundamente anidada.


Lo intenté con

 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) 

Pero en vano, en realidad (los factores se multiplican por duplicado).
Nota: esta pregunta se basa en otra de StackOverflow.

¿Cómo lo haría para obtener ((a1 y b) o (a2 y c)), (cyd) y (e y f) como tres partes?

Podría crear un visitante que “escuche” cuando un nodo en el árbol de análisis es a ( , en el que aumenta la variable de profundidad, y cuando se encuentra a), disminuye la variable de profundidad. Luego, en el método que se llama que coincide con una expresión entre paréntesis, inspecciona la profundidad antes de agregarla a su lista de expresiones para devolverla del visitante.

Aquí hay un ejemplo rápido:

 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) 

que imprime:

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

Si solo desea devolver cada factor externo, return temprano y no descienda a sus hijos.

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

Salida:

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