我应该如何使用pyparsing从布尔表达式生成元素列表?

时间:2012-02-13 17:00:09

标签: python 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的方式已经破坏了我的脚本处理这些组的能力。有没有更好的方法来解决这个问题?

修改 喝完一杯咖啡后,我意识到我遇到的问题很容易解决。我的新功能和/或功能如下:

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]]。有没有一种典型的解决方法来处理这个问题?

1 个答案:

答案 0 :(得分:0)

为了将来参考,这里的方法适用于我的测试用例,但与上面建议的AST方法不同:

from pyparsing import Literal, Word, Optional, \
    Group, ZeroOrMore, Forward, alphanums
import ffparser, sys

exprStack = []

def myAnd(op1, op2):
    if isinstance(op1, str) and isinstance(op2, str):
        newlist = [[op1, op2]]

    elif isinstance(op1, str):
        newlist = op2
        [item.insert(0, op1) for item in newlist]

    elif isinstance(op2, str):
        newlist = op1
        [item.append(op2) for item in op1]

    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]]

    elif isinstance(op1, str):
        newlist = op2
        newlist.insert(0, [op1])

    elif isinstance(op2, str):
        newlist = op1
        newlist.append([op2])

    else:
        newlist = []
        [newlist.append(item) for item in op1]
        [newlist.append(item) for item in op2]

    return newlist

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.setParseAction(pushFirst) | (Optional(lpar) + expr + Optional(rpar))
    expr << atom + ZeroOrMore((boolop + expr).setParseAction(pushFirst))