使用函数调用解析算术表达式

时间:2012-11-27 23:46:58

标签: python dsl pyparsing

我正在使用pyparsing,发现它非常适合开发一个简单的DSL,它允许我从MongoDB中提取数据字段并对它们进行简单的算术运算。我现在正在尝试扩展我的工具,以便我可以将Rank [Person:Height]形式的函数应用于字段,并可能包含简单表达式作为函数调用的参数。我正在努力使解析语法工作。以下是我到目前为止的情况:

# Define parser
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, 
# accounting for the fact that some fields contain whitespace
dbRef = Combine(Word(alphas) + ":" + Word(printables) + \
    Optional(" " + Word(alphas) + " " + Word(alphas)))
dbRef.setParseAction(EvalDBref)

# Handle function calls
functionCall = (Keyword("Rank") | Keyword("ZS") | Keyword("Ntile")) + "[" + expr + "]"
functionCall.setParseAction(EvalFunction)
operand =  functionCall | dbRef | (real | integer) 

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),
    ])

我的问题是,当我测试一个像Rank [Person:Height]这样的简单表达式时,我得到一个解析异常:

ParseException: Expected "]" (at char 19), (line:1, col:20)

如果我使用float或算术表达式作为Rank [3 + 1.1]这样的参数,解析工作正常,如果我简化了dbRef语法,那么它只是Word(alphas)它也可以工作。不能为我的生活弄清楚我的完整语法有什么不对。我已经尝试重新排列操作数的顺序以及简化functionCall语法无济于事。谁能看到我做错了什么?

一旦我开始工作,我想采取最后一步,并在表达式中引入对变量赋值的支持..

编辑:经过进一步测试,如果我从dbRef语法中删除了printables,那么一切正常:

 dbRef = Combine(Word(alphas) + OneOrMore(":") + Word(alphanums) + \
      Optional("_" + Word(alphas)))

然而,如果我将字符“ - ”添加到dbRef(我需要DB字段,如“Class:S-N”),解析器会再次失败。我认为“ - ”是由我的operatorPrecedence中的signop消耗的吗?

2 个答案:

答案 0 :(得分:2)

似乎发生的情况是,测试字符串末尾的]字符(Rank[Person:Height])被作为dbRef令牌的一部分消耗,因为此标记的部分已过去最初的:被声明为由Word(printables)组成(这个字符集,不幸的是包括方括号字符)

然后解析器尝试生成functionCall,但错过了结束]因此错误消息。

暂定的解决方法是使用不包含方括号的字符集,可能更明确,例如:

dbRef = Combine(Word(alphas) + ":" + Word(alphas, alphas+"-_./") + \
    Optional(" " + Word(alphas) + " " + Word(alphas)))

修改
仔细观察,上面的内容是松散的,但令牌层次结构是错误的(例如,解析器尝试生成functionCall作为expr等的一个操作数。) 此外,由于-符号的含糊不清,我的建议修补程序不起作用,在dbRef范围内应该被理解为普通字符,而在expr范围内时应理解为加号。这种类型的问题在解析器中很常见,并且有很多方法可以解决这个问题,尽管我不确定如何使用pyparsing。

答案 1 :(得分:0)

找到解决方案 - 问题是我的dbRef语法消耗了一些属于函数规范的字符。新语法正常工作:

dbRef = Combine(Word(alphas) + OneOrMore(":") + Word(alphanums) + \
    Optional(oneOf("_ -") + Word(alphas)))