I love the ability to define a parseAction with pyarsing, but I’ve run into a roadblock for a particular use case. Take the input string and the following simple grammar:
from pyparsing import *
line = "[[one [two [three] two [three [four]]] one] zero]"
token = Word(alphas)
# Define the simple recursive grammar
grammar = Forward()
nestedBrackets = nestedExpr('[', ']', content=grammar)
grammar << (token | nestedBrackets)
P = grammar.parseString(line)
print P
I’d like the results to be:
[('one',1), [('two',2), [('three',3)], ('two',2), [('three',3), [('four',4)]]] one], ('zero',0)]
i.e parse each token and return a tuple with the token and the depth. I know that this can be done post-parse, but I want to know if it is possible to do with a parseAction. Here way my incorrect attempt with a global variable:
# Try to count the depth
counter = 0
def action_token(x):
global counter
counter += 1
return (x[0],counter)
token.setParseAction(action_token)
def action_nest(x):
global counter
counter -= 1
return x[0]
nestedBrackets.setParseAction(action_nest)
Giving:
[('one', 1), ('two', 2), ('three', 3), ('two', 3), ('three', 4), ('four', 5), ('one', 3), ('zero', 3)]
Do this (leaving the rest as you have it):
The issue is that the nesting depends not on the number of nested groups matched, but on the number of open brackets matched versus the number of closed brackets matched. Therefore, you need to adjust the count when you parse the open/close brackets, not when you parse the group. So you need to set the parseAction on the group delimiters, not the group itself.
Also, your example has the nesting off by one level (at least by my eyes). The ‘zero” should really be one, since it is inside one level of brackets, and likewise everything else should be shifted up by one. If you really want that outermost “zero” to have level zero and so on, you need to initialize
countto -1.