Как я должен использовать pyparsing для генерации списков элементов из логических выражений? - PullRequest
2 голосов
/ 13 февраля 2012

У меня есть список логических выражений, которые представляют физические объекты, которые можно комбинировать для представления более крупных объектов. Они выглядят примерно так: ((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 голосов
/ 31 июля 2012

Для дальнейшего использования, вот подход, который работает для моих тестовых случаев, но который отличается от подхода 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))
...