从字典python的键动态生成正则表达式

时间:2019-01-06 02:46:09

标签: python regex python-3.x lexer ply

def t_FUNC_(self, t):
        r'(?i)I|(?i)J|(?i)K|(?i)L|(?i)M|(?i)N|(?i)Y'
        return t

在上面的函数中,我返回一个正则表达式,这意味着FUNC可以是I或J或K或L或M或N或Y。

现在,我有一个像这样的字典:

dic = { 'k1':'v1', 'k2':'v2' }

我可以在上述功能中访问此词典。我如何从字典的键动态生成正则表达式。字典的大小也不固定。

因此,我想将r'(?i)I|(?i)J|(?i)K|(?i)L|(?i)M|(?i)N|(?i)Y'替换为r'(?i)k1|(?i)k2

PS:当我们使用python中的ply库编写词法分析器时,上述模式代码用于生成令牌。

3 个答案:

答案 0 :(得分:2)

将字典的键放入正则表达式很简单:

代码:

regex = '|'.join('(?i){}'.format(k) for k in data)

测试代码:

data = {'k1': 'v1', 'k2': 'v2'}
regex = '|'.join('(?i){}'.format(k) for k in data)
print(regex)

结果:

(?i)k1|(?i)k2

答案 1 :(得分:1)

正如@AustinHastings在评论中所说,Ply通过组合lexer类中提供的正则表达式(作为类成员的值或作为类成员函数的docstring)来构建词法扫描器。构建扫描程序后,将不会对其进行修改,因此,实际上至少在生成扫描程序之后,您才真正无法动态调整正则表达式。

但是,对于您想到的特定应用程序,没有必要创建自定义正则表达式。您可以使用the Ply manual中所示的更为简单的过程,该过程显示了如何识别保留的单词,而无需为每个单词使用自定义的正则表达式。

这个想法很简单。保留字(在您的情况下为函数名)通常是词法扫描器中已使用的一些更通用模式的特定示例。几乎可以肯定是这样,因为词法扫描器必须以某种方式识别每个标记,因此在将动态生成的单词添加到扫描器之前,必须先将其识别为其他事物。而不是尝试针对特定实例覆盖其他模式,我们只是让令牌被识别,然后在返回令牌之前更正其类型(可能还有其值)。

这是Ply手册中示例的略微修改版本:

def t_ID(t):
     r'[a-zA-Z_][a-zA-Z_0-9]*'
     # Apparently case insensitive recognition is desired, so we use
     # the lower-case version of the token as a lookup key. This means
     # that all the keys in the dictionary must be in lower-case
     token = t.value.lower()
     if token in self.funcs:
         t.type = 'FUNC'
     return t

(您可能希望调整以上内容,以使其与funcs字典中的键相关联的值起作用,尽管稍后在语义分析期间也可以这样做。)

由于funcs字典不以任何方式参与词法分析器(或解析器)的生成,因此不需要特殊的技巧即可将其传递到词法分析器对象中。实际上,它甚至不需要在lexer对象中;您可以在构造lexer对象时将解析器对象添加到lexer对象,从而使您可以将字典放入解析器对象中,在该对象中解析器操作更易于访问。

与试图构建自定义正则表达式相比,这是一种更好的解决方案,其原因之一是它无法识别保留字,而保留字恰好是非保留字的前缀。例如,如果cos是函数之一,而您设法产生了与之等效的

t_ID = r'[a-zA-Z_][a-zA-Z_0-9]*'
def t_FUNC(t):
    r'(?i)sin|cos|tan'
    # do something

然后您会发现:

cost = 3

被扫描为FUNC(cos), ID(t), '=', NUMBER(3),几乎可以肯定这不是您想要的。将逻辑放在t_ID函数中可以完全避免此问题,因为将只考虑完整的令牌。

答案 2 :(得分:0)

'(?i)'+'|'.join(re.escape(k) for k in dic)

如果re.escape键之一正好包含正则表达式语言的控制字符(例如dic),则需要|。另外,不建议在模式中除开始处的任何位置使用全局内联标志,例如(?i)。 (如果只希望将其应用于表达式的一部分,则可以使用新的本地标志语法(?i:foo)。)