簡體   English   中英

pyParsing 評估表達式

[英]pyParsing Evaluating Expression

在瀏覽了本網站上的幾個 pyparsing 和帖子示例后,我設法編寫了可以完全按照我想要的方式解析表達式的代碼。 但是現在我堅持如何評估它,因此在這里尋求您的幫助。

所以當我給出這個字符串時:“(Number(3) > Number(5)) AND (Time(IST) < Number(1030))”解析將輸出我:
[[[['Number', [3]], '>', ['Number', [5]]], 'AND', [['Time', ['IST']], '<', [ '號碼', [1030]]]]]

Number 是一個自定義函數,它接受 int 輸入並返回 int。 Time 是一個自定義函數,它接受字符串輸入並以 int 形式返回當前系統時間。 像這樣,我將有許多自定義函數,它們將接受一些輸入和返回值。

所以有人可以幫我評估解析出來的結果,所以最后我應該得到一個 True 或 False 作為最終結果?

這是我的python腳本的副本:

from pyparsing import (
    CaselessKeyword,Suppress,Word,alphas,alphanums,nums,Optional,Group,oneOf,Forward,infixNotation,
    opAssoc,dblQuotedString,delimitedList,Combine,Literal,QuotedString,ParserElement,Keyword,
    OneOrMore,pyparsing_common as ppc,
)

ParserElement.enablePackrat()

LPAR, RPAR = map(Suppress, "()")

expr = Forward()

alps = Word(alphas, alphanums) 

def stat_function(name):
    return ( 
        Group(CaselessKeyword(name) + Group(LPAR + delimitedList(expr) + RPAR)) |
        Group(CaselessKeyword(name) + Group(LPAR + delimitedList(alps) + RPAR))
    )

timeFunc = stat_function("Time")
dateFunc = stat_function("Date")
numFunc  = stat_function("Number")
funcCall = timeFunc | dateFunc | numFunc

Compop = oneOf("< = > >= <= != <>")
 
multOp = oneOf("* /")
addOp = oneOf("+ -")
logicalOp = oneOf("and or AND OR")
numericLiteral = ppc.number

operand = numericLiteral | funcCall | alps  
arithExpr = infixNotation(
    operand, [(multOp, 2, opAssoc.LEFT), 
              (addOp, 2, opAssoc.LEFT),
              (Compop, 2, opAssoc.LEFT),
              (logicalOp, 2, opAssoc.LEFT),
              ]  
)

expr <<= arithExpr  

s1 = "(Number(3) > Number(5)) AND (Time(IST) < Number(1030))"
result = expr.parseString(s1)
print(result)

解析您的表達式后,您的工作只完成了一半(如果那樣的話)。

在將輸入字符串轉換為標記的嵌套結構后,典型的下一步是遞歸遍歷此結構並解釋標記(例如“數字”、“與”等)並執行相關功能。

但是,這樣做幾乎只是追溯 pyparsing 已經完成的步驟,以及生成的 infixNotation 表達式。

我建議為語法中的每個表達式定義可評估的節點類,並將它們作為解析操作傳遞給 pyparsing。 然后當你完成后,你可以調用result.eval() 評估的實際功能在每個相關的節點類中實現。 這樣,pyparsing 會在解析時構建節點,而不必事后再次構建。

以下是您的函數的類(我稍微修改了stat_function以適應它們):

class Node:
    """
    Base class for all of the parsed node classes.
    """
    def __init__(self, tokens):
        self.tokens = tokens[0]

class Function(Node):
    def __init__(self, tokens):
        super().__init__(tokens)
        self.arg = self.tokens[1][0]

class Number(Function):
    def eval(self):
        return self.arg

class Time(Function):
    def eval(self):
        from datetime import datetime
        if self.arg == "IST":
            return int(datetime.now().strftime("%H%M"))
        else:
            return 0

class Date(Function):
    def eval(self):
        return 0

def stat_function(name, eval_fn):
    return ( 
        Group(CaselessKeyword(name) + Group(LPAR + delimitedList(expr) + RPAR)) |
        Group(CaselessKeyword(name) + Group(LPAR + delimitedList(alps) + RPAR))
    ).addParseAction(eval_fn)

timeFunc = stat_function("Time", Time)
dateFunc = stat_function("Date", Date)
numFunc  = stat_function("Number", Number)
funcCall = timeFunc | dateFunc | numFunc

(我不得不猜測“時間(IST)”應該做什么,但由於您正在與 1030 進行比較,我認為它會以整數形式返回 24 小時 HHMM 時間。)

現在您可以解析這些簡單的操作數並評估它們:

s1 = "Number(27)"
s1 = "Time(IST)"
result = expr.parseString(s1)
print(result)
print(result[0].eval())

現在同樣適用於您的邏輯、比較和算術節點:

class Logical(Node):
    def eval(self):
        # do not make this a list comprehension, else
        # you will defeat any/all short-circuiting
        eval_exprs = (t.eval() for t in self.tokens[::2])
        return self.logical_fn(eval_exprs)

class AndLogical(Logical):
    logical_fn = all
    
class OrLogical(Logical):
    logical_fn = any

class Comparison(Node):
    op_map = {
        '<': operator.lt,
        '>': operator.gt,
        '=': operator.eq,
        '!=': operator.ne,
        '<>': operator.ne,
        '>=': operator.ge,
        '<=': operator.le,
    }
    def eval(self):
        op1, op, op2 = self.tokens
        comparison_fn = self.op_map[op]
        return comparison_fn(op1.eval(), op2.eval())

class BinArithOp(Node):
    op_map = {
        '*': operator.mul,
        '/': operator.truediv,
        '+': operator.add,
        '-': operator.sub,
    }
    def eval(self):
        # start by eval()'ing the first operand
        ret = self.tokens[0].eval()

        # get following operators and operands in pairs
        ops = self.tokens[1::2]
        operands = self.tokens[2::2]
        for op, operand in zip(ops, operands):
            # update cumulative value by add/subtract/mult/divide the next operand
            arith_fn = self.op_map[op]
            ret = arith_fn(ret, operand.eval())
        return ret

operand = numericLiteral | funcCall | alps  

infixNotation每個運算符級別infixNotation一個可選的第四個參數,用作該操作的解析操作。 (我還將 AND 和 OR 作為單獨的級別進行了划分,因為 AND 通常比 OR 的優先級更高)。

arithExpr = infixNotation(
    operand, [(multOp, 2, opAssoc.LEFT, BinArithOp), 
              (addOp, 2, opAssoc.LEFT, BinArithOp),
              (Compop, 2, opAssoc.LEFT, Comparison),
              (CaselessKeyword("and"), 2, opAssoc.LEFT, AndLogical),
              (CaselessKeyword("or"), 2, opAssoc.LEFT, OrLogical),
              ]  
)

expr <<= arithExpr

現在評估您的輸入字符串:

s1 = "(Number(3) > Number(5)) AND (Time(IST) < Number(1030))"
result = expr.parseString(s1)
print(result)
print(result[0].eval())

印刷:

[<__main__.AndLogical object at 0xb64bc370>]
False

如果您將 s1 更改為比較 3 < 5(並在上午 10:30 之前運行此示例):

s1 = "(Number(3) < Number(5)) AND (Time(IST) < Number(1030))"

你得到:

[<__main__.AndLogical object at 0xb6392110>]
True

pyparsing examples目錄下有類似的例子,使用infixNotation搜索。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM