Skip to main content
 首页 » 编程设计

python之在 DSL 的算术语法中评估函数

2025年05月04日10luoye11

我正在开发一个简单的 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)