I'd like to be able to parse two (or any number) of expressions, each with their own set of variable definitions or other context.
There doesn't seem to be an obvious way to associate a context with a particular invocation of pyparsing.ParseExpression.parseString()
. The most natural way seems to be to use an instancemethod of some class as the parse actions. The problem with this approach is that the grammar must be redefined for each parse context (for instance, in the class's __init__
), which seems terribly inefficient.
Using pyparsing.ParseExpression.copy()
on the rules doesn't help; the individual expressions get cloned alright, but the sub-expressions they are composed from don't get updated in any obvious way, and so none of the parse actions of any nested expression gets invoked.
我能想到的获得这种效果的唯一另一种方法是定义一个语法,该语法返回一个无上下文的抽象解析树,然后在第二步中对其进行处理。即使对于简单的语法,这似乎也很尴尬:在使用无法识别的名称的那一刻引发异常会很好,并且它仍然不会解析像 C 这样的语言,这些语言实际上需要有关之前内容的上下文才能知道哪个规则匹配。
是否有另一种方式将上下文(当然不使用全局变量)注入 pyparsing 表达式的解析操作中?
我不知道这是否一定能回答您的问题,但这是根据上下文自定义解析器的一种方法:
from pyparsing import Word, alphas, alphanums, nums, oneOf, ParseFatalException
var = Word(alphas+'_', alphanums+'_').setName("identifier")
integer = Word(nums).setName("integer").setParseAction(lambda t:int(t[0]))
operand = integer | var
operator = oneOf("+ - * /")
ops = {'+' : lambda a,b:a+b,
'-' : lambda a,b:a-b,
'*' : lambda a,b:a*b,
'/' : lambda a,b:a/b if b else "inf",
}
binop = operand + operator + operand
# add parse action that evaluates the binary operator by passing
# the two operands to the appropriate binary function defined in ops
binop.setParseAction(lambda t: ops[t[1]](t[0],t[2]))
# closure to return a context-specific parse action
def make_var_parseAction(context):
def pa(s,l,t):
varname = t[0]
try:
return context[varname]
except KeyError:
raise ParseFatalException("invalid variable '%s'" % varname)
return pa
def eval_binop(e, **kwargs):
var.setParseAction(make_var_parseAction(kwargs))
try:
print binop.parseString(e)[0]
except Exception as pe:
print pe
eval_binop("m*x", m=100, x=12, b=5)
eval_binop("z*x", m=100, x=12, b=5)
Run Code Online (Sandbox Code Playgroud)
印刷
1200
invalid variable 'z' (at char 0), (line:1, col:1)
Run Code Online (Sandbox Code Playgroud)