我已经在Google上搜索了一段时间了,从here(归Paul先生所有)开始,我就知道如何将标识符传递给解析器。这是我到目前为止的内容:
from pyparsing import *
class Expressions:
ParserElement.enablePackrat()
arith_expr = Forward()
num = Word(nums) + Optional("." + OneOrMore(Word(nums)))
opmd = Word("*/", max=1)
opss = Word("+-", max=1)
ident = Word(alphas + "_", alphanums + "_")
fn_call = Group(Optional(delimitedList(arith_expr)))
arith_operand = fn_call | num | ident
arith_expr <<= infixNotation(arith_operand, [
('-', 1, opAssoc.RIGHT),
(opmd, 2, opAssoc.LEFT,),
(opss, 2, opAssoc.LEFT,)
])
def __init__(self, vars):
if isinstance(vars, list) and vars:
ids = []
for x in vars:
ids.append(x)
self.ident = MatchFirst(map(Keyword, ids))
def check(text):
try:
result = self.arith_expr.parseString(texto, True)
print(result)
except ParseException as exc:
print(exc)
然后,如果我转到python控制台并执行以下操作:
vars = ['v1', 'v2', 'v3,1']
e = Expressions(vars)
e.check('10+v1+v2+v3,1-whatever')
尽管whatever
中没有定义,它仍将vars
打印为正确的标记。我该怎么解决?
答案 0 :(得分:1)
在ident
类中定义的Expressions
变量不是占位符,因此,当您在__init__
方法中分配变量时,您并没有更改整体解析器,而只是更改了self.ident
(它将在Expressions实例上创建一个新属性,并且不会更改类级别的标识)。
为什么不只在__init__
中定义整个解析器?然后,您可以使用给定的变量名称定义ident
,并且可以绕过所有class-vs-instance属性问题以及“事实分析器更新”问题。>
这段代码应该做什么?
ids = []
for x in vars:
ids.append(x)
有很多简单的方法可以将值从一个列表复制到另一个列表,但是为什么还要进行复制呢?只需使用输入的变量名列表来定义ident
(您可以命名vars
以外的其他名称,因为这与有用的内置方法冲突-可以将其命名为var_names
?)。
编辑:更多注意事项
您需要修复fn_call。照原样,您将拥有无限递归,因为它只是arith_exprs的逗号分隔列表。由于使用fn_call定义arith_expr,因此存在左递归。我认为您从另一个示例中复制的内容不完整,您拥有的表达式对于函数的arg列表中的参数列表有效,但是您缺少函数名称和封闭的括号。添加这些,递归问题就消失了。
您的变量之一是“ v_3,1”。这是一个看起来很奇怪的标识符,但是幸运的是,它与解析器中的任何其他位都没有冲突。但是,如果您输入的标识符为“ 3.1”或“ 42”,则情况将非常混乱。您可能想要定义一个valid_identifier表达式,然后使用类似以下内容的方法来验证传入的var名称:
valid_expression = Word(alphas + '_', alphanums + '_,')
if not all(valid_expression.matches(varname) for varname in varnames):
raise WhatWereYouThinkingException("invalid identifier specified")