我正在开发一个简单的 DSL 来转换从 MongoDB 中提取的数据。我正在使用 python 和 pyparsing,并且从提供的示例开始,在创建适用于 +/-* 等基本运算符的语法方面取得了相当大的进展。我目前一直在研究如何让我的程序评估 Rank[dbRef] 形式的函数。我可以通过简单的运算符对 dbRefs 进行求值和算术运算,但是在求值函数时我的递归无法正常工作。我不知道如何访问在函数调用中传递的 dbRef 参数。
这是语法和相关的 setParseActions:
# Define parser, accounting for the fact that some fields contain whitespace
chars = Word(alphanums + "_-/")
expr = Forward()
integer = Word(nums).setParseAction(EvalConstant)
real = Combine(Word(nums) + "." + Word(nums)).setParseAction(EvalConstant)
# Handle database field references that are coming out of Mongo
dbRef = Combine(chars + OneOrMore(":") + chars)
dbRef.setParseAction(EvalDBref)
# Handle function calls
functionCall = (Keyword("Rank") | Keyword("ZS") | Keyword("Ntile")) + "[" + dbRef + "]"
functionCall.setParseAction(EvalFunction)
operand = (real | integer) | functionCall | dbRef
signop = oneOf('+ -')
multop = oneOf('* /')
plusop = oneOf('+ -')
# Use parse actions to attach Eval constructors to sub-expressions
expr << operatorPrecedence(operand,
[
(signop, 1, opAssoc.RIGHT, EvalSignOp),
(multop, 2, opAssoc.LEFT, EvalMultOp),
(plusop, 2, opAssoc.LEFT, EvalAddOp),
])
formulas = ['Rank[Person:Height]']
for f in formulas:
ret = expr.parseString(f)[0]
print p + ": " + line + " --> " + str(ret.eval())
这里是我的评估类的相关代码。该类确实被解析器调用,但我如何访问传递给函数的参数?
# Executes functions contained in expressions
class EvalFunction(object):
def __init__(self, tokens):
self.value = tokens[0]
def eval(self):
func = self.value
if func == 'Rank':
# How to evaluate the token that is arg of Function?
return 'Rank Found';
我认为我只需要朝着正确的方向轻推才能进入下一阶段..
请您参考如下方法:
我整理了一下,想提供一个答案。我的函数评估类如下所示:
# Executes functions contained in expressions
class EvalFunction(object):
pop_ = {}
def __init__(self, tokens):
self.func_ = tokens.funcname
self.field_ = tokens.arg
def eval(self):
# Get the name of the requested field and source db
# Functions can only be called on dbRef so this always done
v = self.field_.value
fieldRef = v.split(':')
source = fieldRef[0]
field = fieldRef[1]
# Evaluate the dbRef (get the value from the db)
val = self.field_.eval()
if self.func_ == 'Avg':
rec = db['Stats'].find_one({'_id' : field})
return rec['value']['avg']
elif self.func_ == 'Root':
return math.sqrt(val)
我的函数语法是:
functionCall = funcNames("funcname") + "[" + dbRef("arg") + "]"
functionCall.setParseAction(EvalFunction)
