Pyparsing将关键字传递给解析器的麻烦

时间:2019-07-05 18:58:48

标签: python-3.x pyparsing

我已经在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打印为正确的标记。我该怎么解决?

1 个答案:

答案 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")