如何从正则表达式中排除某些可能性?

How to exclude certain possibilities from a regular expression?

对于我正在创建的解析器,我使用此正则表达式作为 ID 的定义:

ID: /[a-z_][a-z0-9]*/i

(对于不熟悉我正在使用的特定解析器语法的任何人,"i" 标志仅表示不区分大小写。)

我还有一些关键词,像这样:

CALL_KW: "call"
PRINT_KW: "print"

问题是,由于语法中的一些歧义,有时关键字被视为 ID,而我真的不希望它们如此。所以我在想我是否可以重写 ID 的正则表达式,使关键字完全不匹配。这样的事情可能吗?

为了提供更多上下文,我正在使用 Python 的 Lark 解析器库。 Lark 提供的 Earley 解析器(与动态词法分析器一起)在处理歧义语法方面非常灵活和强大,但它们有时会做这样奇怪的事情(而且是非确定性的!)。因此,我试图通过使关键字永远不匹配 ID 规则来为解析器提供一些帮助。

我相信 Lark 使用普通的 Python 正则表达式,所以你可以使用否定前瞻断言来排除关键字。但您必须注意不要拒绝以关键字开头的名称:

ID: /(?!(else|call)\b)[a-z_][a-z0-9]*/i

这个正则表达式肯定适用于 Python3:

>>> # Test with just the word
>>> for test_string in ["x", "xelse", "elsex", "else"]:
...   m = re.match(r"(?!(else|call)\b)[a-z_][a-z0-9]*", test_string)
...   if m: print("%s: Matched %s" % (test_string, m.group(0)))
...   else: print("%s: No match" % test_string)
... 
x: Matched x
xelse: Matched xelse
elsex: Matched elsex
else: No match

>>> # Test with the word as the first word in a string
... for test_string in [word + " and more stuff" for word in ["x", "xelse", "elsex", "else"]]:
...   m = re.match(r"(?!(else|call)\b)[a-z_][a-z0-9]*", test_string)
...   if m: print("%s: Matched %s" % (test_string, m.group(0)))
...   else: print("%s: No match" % test_string)
... 
x and more stuff: Matched x
xelse and more stuff: Matched xelse
elsex and more stuff: Matched elsex
else and more stuff: No match

有几种方法可以不将您的相似值传递给您的 ID。

正则表达式 1

例如,您可以在表达式中使用捕获组,也许是 similar to:

    ([a-z]+_[a-z0-9]+)

正则表达式电路

这个 link 可以帮助您形象化表达:

正则表达式 2

另一种方法是使用:从右边绑定你的表达式,然后你可以使用an expression similar to:

(\w+):

或带有 i 标志的原始表达式:

([a-z0-9_]+):

如果愿意,您可以为其添加更多边界。