Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing nested ternary expressions

The continuation of my PyParsing quest is parsing nested ternary expressions (e.g. (x == 1 ? true : (y == 10 ? 100 : 200))). As such, I constructed the following expression. Which, to me, seems intuitive. However I get no matches:

any = Word(printables)

conditional = Forward()
sub_exp = (conditional | any)
conditional = Literal('(') + sub_exp + Literal('?') + sub_exp + Literal(':') + sub_exp + Literal(')')

    for exp in conditional.scanString(block_str):
        print exp

Initially I thought that the problem was with printables consuming everything; I set the excludeChars to not match :?)(, but that didn't help either. The alternative was to construct nested expressions, one each for the "( ?", "? : " and ": )" blocks. But this approach is very messy. Does anybody have any recommendations on parsing ternary expressions?

UPDATE Using the answer from below, but modified to work with scanString:

When using scanString however, it returns a lot of other matches too (basically, anything matching atom).

lpar = Literal('(').suppress()
rpar = Literal(')').suppress()
any = Combine(OneOrMore(Word(printables, excludeChars='()?:') | White(' ', max=1)))
expr = Forward()
atom = any | Group(lpar + expr + Literal('?') + expr + Literal(':') + expr + rpar)
expr << Literal('(') + atom + ZeroOrMore(expr) + Literal('?') + atom + ZeroOrMore(expr) + Literal(':') + atom + ZeroOrMore(expr) + Literal(')')

for ternary_exp in expr.scanString(block_str):
  print ternary_exp
like image 796
JB2 Avatar asked Sep 30 '22 04:09

JB2


1 Answers

I think your problem is two-fold: the whitespace (which isn't handled well by your any definition), and the recursion (which should use the << operator):

lpar  = Literal( '(' ).suppress()
rpar  = Literal( ')' ).suppress()
any = Combine(OneOrMore(Word(printables, excludeChars='()?:') | White(' ',max=1)))
expr = Forward()
atom = any | Group( lpar + expr + Literal('?') + expr + Literal(':') + expr + rpar )
expr << atom + ZeroOrMore( expr )

For example,

t2 = '(x == 1 ? true : (y == 10 ? 100 : 200))'
expr.parseString(t2)
([(['x == 1 ', '?', 'true ', ':', (['y == 10 ', '?', '100 ', ':', '200'], {})], {})], {})
like image 162
xnx Avatar answered Oct 04 '22 03:10

xnx