Pyparsing infixNotation into a parse tree: Only one operator ending up in tree?

痞子三分冷 提交于 2019-12-24 07:30:12

问题


My eventual goal is to apply sql-where-clause-style queries to filter pandas dataframes. Some searching led me to pyparsing's infixNotation method.

I found an example of infix notation here: http://nullege.com/codes/show/src%40p%40y%40pyparsing-2.0.2%40examples%40simpleBool.py/15/pyparsing.infixNotation/python#

but that actually processes the notation, and I need to use the tree multiple times on different data. Looking for help with parse trees I found this: http://pyparsing.wikispaces.com/file/view/fourFn.py/30154950/fourFn.py

and I took the stack implementation from there. However, it isn't behaving as I expect so I am hoping someone can tell me what I am doing wrong.

Here is the simplest case I could come up with:

from pyparsing import Word, alphas, infixNotation, opAssoc

exprStack=[]
def pushFirst( strg, loc, toks ):
    exprStack.append( toks[0] )

def addAnd():
    exprStack.append("and")

varname = Word(alphas).setParseAction( pushFirst )

operators=[("and", 2, opAssoc.LEFT,addAnd)]

whereExpr = infixNotation(varname,operators)

exprStack=[]
teststring = "cheese and crackers and wine"
parsed=whereExpr.parseString(teststring)
for p in exprStack:
    print p

What I get from this code is:

cheese
crackers
wine
and

What I expect to get, from my understanding of how the infix notation method should work, is:

cheese
crackers
wine
and
and

I also tried running it with "cheese and crackers and wine and whine" but I still only got one "and" in my list.

What am I misunderstanding in using the infixNotation method?

Thank you


回答1:


Start by decorating your parse action with pyparsing's traceParseAction diagnostic deccorator.

@traceParseAction
def addAnd():
    exprStack.append("and")

traceParseAction will show the matched source line, the starting location of the matched tokens, and tokens passed to the parse action, and the value that is returned by the parse action:

>>entering addAnd(line: 'cheese and crackers and wine', 0, 
                 ([(['cheese', 'and', 'crackers', 'and', 'wine'], {})], {}))
<<leaving addAnd (ret: None)

The tokens are a little confusing-looking, since what you are getting is a pyparsing ParseResults object, which has both list and dict semantics, so the Python repr of the object first shows its list contents, and then its named contents. What looks like a tuple with a list and a dict is really the ParseResults, and in this case, it is a ParseResults whose first element is another ParseResults, and this nested object is the one containing your list of matched tokens.

This is a little easier to see if you add a tokens argument to your parse action, and then print out tokens.dump():

def addAnd(tokens):
    print(tokens.dump())
    exprStack.append("and")

And you'll get the more readable:

[['cheese', 'and', 'crackers', 'and', 'wine']]
[0]:
  ['cheese', 'and', 'crackers', 'and', 'wine']

You can see that the matched tokens contains not just 'and', but all of the and-ed terms together, so you will need to push as many 'and's onto your exprStack as there are in the matched tokens.

def addAnd(tokens):
    exprStack.extend(tokens[0][1::2])

With this change, you should now see this as your returned stack:

cheese
crackers
wine
and
and


来源:https://stackoverflow.com/questions/46251750/pyparsing-infixnotation-into-a-parse-tree-only-one-operator-ending-up-in-tree

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!