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库编写词法分析器时,上述模式代码用于生成令牌。
答案 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)
。)