Как исключить определенные возможности из регулярного выражения? - PullRequest
1 голос
/ 12 мая 2019

Для анализатора, который я создаю, я использую это регулярное выражение в качестве определения идентификатора:

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

(Для тех, кто не знаком с синтаксисом конкретного анализатора, который я использую, флаг "i" просто означает без учета регистра).

У меня также есть несколько ключевых слов, например:

CALL_KW: "call"
PRINT_KW: "print"

Проблема в том, что из-за некоторых неясностей в грамматике иногда ключевые слова обрабатываются как идентификаторы, хотя я действительно не хочу, чтобы они были. Поэтому я подумал, можно ли переписать регулярное выражение для идентификатора таким образом, чтобы ключевые слова вообще не сопоставлялись с ним. Возможна ли такая вещь?

Чтобы получить больше контекста, я использую библиотеку синтаксического анализатора Lark для Python. Парсер Earley, который обеспечивает Lark (вместе с динамическим лексером), достаточно гибок и силен в обработке неоднозначных грамматик, но иногда они делают странные вещи, подобные этой (и недетерминированно, в этом!). Поэтому я пытаюсь помочь парсеру, сделав ключевые слова никогда не совпадающими с правилом идентификатора.

Ответы [ 2 ]

2 голосов
/ 12 мая 2019

Я полагаю, что 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
0 голосов
/ 12 мая 2019

Есть несколько способов не передавать аналогичные значения вашим идентификаторам.

RegEx 1

Вы можете, например, использовать в своем выражении группы захвата, может быть что-то похожее на :

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

enter image description here

RegEx Circuit

Эта ссылка поможет вам визуализировать ваши выражения:

enter image description here

RegEx 2

Другим способом было бы связать ваше выражение справа, используя : , тогда вы можете использовать выражение, подобное :

(\w+):

enter image description here

или ваше оригинальное выражение с флагом i:

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

Вы можете добавить больше границ, если хотите.

...