2012-02-13 61 views
2

我有一個布爾表達式列表,表示可以組合以表示較大對象的物理對象。他們看起來像這樣:((A和B)或C)。這個對象可以用A和B的組合來表示,也可以用C單獨來表示。我想生成可用於創建對象的字符串列表的列表。在這種情況下,我想[[A,B],[C]]。如何pyparsing從布爾表達式生成元素列表?

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]]。有沒有一種處理這個問題的典型方法?

+0

如果我是你,我會剛剛創建了一個簡單的經典AST與操作爲節點,操作數作爲葉子,然後遍歷它來構建我喜歡的構造。在你的情況下,它只是用它們的操作數列表替換'和'節點。 – 9000 2012-02-13 21:54:29

+3

查看pyparsing wiki上的SimpleBool.py示例。它使用了一個pyparsing helper方法'operatorPrecedence',它爲你做了大量的原子/因子/項。 SimpleBool.py將爲您提供Bool對象的結構,類似於@ 9000提出的AST,但具有更豐富的對象API,因此您可以直接評估結果。 – PaulMcG 2012-02-13 22:48:01

+0

感謝您的意見。我喜歡AST的建議,並且一定會看看SimpleBool.py示例。我確實設法使上述方法適用於我的測試用例。 – 2012-02-13 23:07:33

回答

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