Как исключить определенные возможности из регулярного выражения?
Для парсера, который я создаю, я использую это регулярное выражение в качестве определения идентификатора:
ID: /[a-z_][a-z0-9]*/i
(Для тех, кто не знаком с синтаксисом конкретного парсера, который я использую, флаг "i" просто означает без учета регистра.)
У меня также есть ряд ключевых слов, например:
CALL_KW: "call"
PRINT_KW: "print"
Проблема в том, что из-за некоторых двусмысленностей в грамматике иногда ключевые слова рассматриваются как идентификаторы, в то время как я действительно не хочу, чтобы они были. Поэтому я подумал, можно ли переписать регулярное выражение для идентификатора таким образом, чтобы ключевые слова вообще не сопоставлялись с ним. Это возможно?
Чтобы дать больше контекста, я использую библиотеку синтаксического анализатора Lark для Python. Парсер Earley, который обеспечивает Lark (вместе с динамическим лексером), достаточно гибок и силен в обработке неоднозначных грамматик, но иногда они делают странные вещи, подобные этой (и недетерминированно, в этом!). Итак, я пытаюсь помочь парсеру, делая ключевые слова никогда не совпадающими с правилом идентификатора.
2 ответа
Я полагаю, что Ларк использует обычные регулярные выражения 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
Есть несколько способов не передавать аналогичные значения вашим идентификаторам.
RegEx 1
Например, вы можете использовать группы захвата в своем выражении, может быть что-то похожее на
([a-z]+_[a-z0-9]+)
RegEx Circuit
Эта ссылка поможет вам визуализировать ваши выражения:
RegEx 2
Другим способом было бы связать ваше выражение справа, используя :, тогда вы можете использовать выражение, подобное:
(\w+):
или ваше оригинальное выражение с i
флаг:
([a-z0-9_]+):
Вы можете добавить больше границ, если хотите.