我应该如何使用 pyparsing 从布尔表达式生成元素列表?
我有一个布尔表达式列表,这些表达式表示可以组合起来表示更大对象的物理对象。它们看起来像这样:((A 和 B) 或 C)。该对象可以用A和B的组合来表示,也可以单独用C来表示。我想生成可用于创建对象的字符串列表列表。在本例中我想要 [[A,B], [C]]。
Pyparsing 看起来非常有趣,所以我决定尝试一下解决这个问题。经过几次失败的尝试后,我决定改编网站上的 fourFn.py 示例。这就是我到目前为止所得到的:
from pyparsing import Literal, CaselessLiteral, Word, Combine, \
Group, Optional, ZeroOrMore, Forward, alphanums
exprStack = []
def myAnd(op1, op2):
if isinstance(op1, str):
return([op1, op2])
else:
return op1.append(op2)
def myOr(op1, op2):
if isinstance(op1, str):
return([[op1], [op2]])
else:
return op1.append([op2])
def pushFirst(strg, loc, toks):
exprStack.append(toks[0])
bnf = None
def BNF():
"""
boolop :: 'and' | 'or'
gene :: alphanum
atom :: gene | '(' expr ')'
"""
global bnf
if not bnf:
element = Word(alphanums)
andop = Literal( "and" )
orop = Literal( "or" )
lpar = Literal( "(" ).suppress()
rpar = Literal( ")" ).suppress()
boolop = andop | orop
expr = Forward()
atom = ((element | lpar + expr + rpar).setParseAction(pushFirst) | (lpar + expr.suppress() + rpar))
expr << atom + ZeroOrMore((boolop + expr).setParseAction(pushFirst))
bnf = expr
return bnf
# map operator symbols to corresponding arithmetic operations
fn = {"or": myOr,
"and": myAnd}
def evaluateStack( s ):
op = s.pop()
if op in fn:
op2 = evaluateStack(s)
op1 = evaluateStack(s)
return fn[op](op1, op2)
else:
return op
if __name__ == "__main__":
def test(s, expVal):
global exprStack
exprStack = []
results = BNF().parseString(s)
val = evaluateStack(exprStack[:])
if val == expVal:
print s, "=", val, results, "=>", exprStack
else:
print "!!! "+s, val, "!=", expVal, results, "=>", exprStack
test("((A and B) or C)", [['A','B'], ['C']])
test("(A and B) or C", [['A','B'], ['C']])
test("(A or B) and C", [['A', 'C'], ['B', 'C']])
test("A and B", ['A', 'B'])
test("A or B", [['A'], ['B']])
前三个测试在这里失败,只返回括号中每个表达式的第一个元素。 A 将被多次压入堆栈。看来我修改 fourFn.py 的方式破坏了我的脚本处理这些组的能力。有没有更好的方法来解决这个问题?
编辑 喝完一杯咖啡后,我意识到我遇到的问题很容易解决。我的新的 and 和 or 函数如下:
def myAnd(op1, op2):
if isinstance(op1, str) and isinstance(op2, str):
newlist = [op1, op2]
elif isinstance(op1, str):
newlist = [op1]
newlist.append(op2)
elif isinstance(op2, str):
newlist = op1
newlist.append(op2)
else:
newlist = [op1.append(item) for item in op2]
return newlist
def myOr(op1, op2):
if isinstance(op1, str) and isinstance(op2, str):
newlist = [[op1], [op2]]
r
elif isinstance(op1, str):
newlist = [op1]
newlist.append([op2])
elif isinstance(op2, str):
newlist = [op1]
newlist.append([op2])
else:
newlist = [op1, [op2]]
return newlist1
解析器的构造如下:
expr = Forward()
atom = element.setParseAction(pushFirst) | (lpar + expr + rpar)
expr << atom + ZeroOrMore((boolop + expr).setParseAction(pushFirst))
一个新的更有趣的问题涉及如何处理像这样的情况(A 或 B)和 C。结果应该是 [[A, C ],[B,C]]。有没有典型的 pyparsing 方法来处理这个问题?
I have a list of Boolean expressions that represent physical objects that can be combined to represent larger objects. They look something like this: ((A and B) or C). This object can be represented by a combination of A and B or by C alone. I would like to generate a list of lists of strings that can be used to create the object. In this case I want [[A,B], [C]].
Pyparsing looks pretty intriguing so I've decided to give it a shot for this problem. After a few failed attempts I've settled on adapting the fourFn.py example from the website. This is what I have so far:
from pyparsing import Literal, CaselessLiteral, Word, Combine, \
Group, Optional, ZeroOrMore, Forward, alphanums
exprStack = []
def myAnd(op1, op2):
if isinstance(op1, str):
return([op1, op2])
else:
return op1.append(op2)
def myOr(op1, op2):
if isinstance(op1, str):
return([[op1], [op2]])
else:
return op1.append([op2])
def pushFirst(strg, loc, toks):
exprStack.append(toks[0])
bnf = None
def BNF():
"""
boolop :: 'and' | 'or'
gene :: alphanum
atom :: gene | '(' expr ')'
"""
global bnf
if not bnf:
element = Word(alphanums)
andop = Literal( "and" )
orop = Literal( "or" )
lpar = Literal( "(" ).suppress()
rpar = Literal( ")" ).suppress()
boolop = andop | orop
expr = Forward()
atom = ((element | lpar + expr + rpar).setParseAction(pushFirst) | (lpar + expr.suppress() + rpar))
expr << atom + ZeroOrMore((boolop + expr).setParseAction(pushFirst))
bnf = expr
return bnf
# map operator symbols to corresponding arithmetic operations
fn = {"or": myOr,
"and": myAnd}
def evaluateStack( s ):
op = s.pop()
if op in fn:
op2 = evaluateStack(s)
op1 = evaluateStack(s)
return fn[op](op1, op2)
else:
return op
if __name__ == "__main__":
def test(s, expVal):
global exprStack
exprStack = []
results = BNF().parseString(s)
val = evaluateStack(exprStack[:])
if val == expVal:
print s, "=", val, results, "=>", exprStack
else:
print "!!! "+s, val, "!=", expVal, results, "=>", exprStack
test("((A and B) or C)", [['A','B'], ['C']])
test("(A and B) or C", [['A','B'], ['C']])
test("(A or B) and C", [['A', 'C'], ['B', 'C']])
test("A and B", ['A', 'B'])
test("A or B", [['A'], ['B']])
The first three tests fail here and only return the first element of each expression in parentheses. A will get pushed to the stack multiple times. It seems that the way I modified fourFn.py has broken my script's ability to handle these groups. Is there a better way to approach this problem?
edit
After a cup of coffee I realized the problems I was having were pretty easy to solve. My new and and or functions are as follows:
def myAnd(op1, op2):
if isinstance(op1, str) and isinstance(op2, str):
newlist = [op1, op2]
elif isinstance(op1, str):
newlist = [op1]
newlist.append(op2)
elif isinstance(op2, str):
newlist = op1
newlist.append(op2)
else:
newlist = [op1.append(item) for item in op2]
return newlist
def myOr(op1, op2):
if isinstance(op1, str) and isinstance(op2, str):
newlist = [[op1], [op2]]
r
elif isinstance(op1, str):
newlist = [op1]
newlist.append([op2])
elif isinstance(op2, str):
newlist = [op1]
newlist.append([op2])
else:
newlist = [op1, [op2]]
return newlist1
And the parser is constructed as follows:
expr = Forward()
atom = element.setParseAction(pushFirst) | (lpar + expr + rpar)
expr << atom + ZeroOrMore((boolop + expr).setParseAction(pushFirst))
A new and more interesting question involves how to deal with a case like this (A or B) and C. The result should be [[A, C], [B, C]]. Is there a typical pyparsing way of dealing with this issue?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
为了供将来参考,这里有一种适用于我的测试用例的方法,但与上面建议的 AST 方法不同:
For future reference, here's an approach that works for my test cases but that deviates from AST approach suggested above: