[英]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.