Python parser ply соответствует неправильному регулярному выражению
Я пытаюсь создать парсер с помощью Ply, но столкнулся со странной ошибкой. Вот MCVE, где происходит ошибка соответствия:
лексер
import ply.lex as lex
tokens = (
'IDENTIFIER',
'NAME',
'EQUALS'
)
def t_IDENTIFIER(t):
r'\* *[a-zA-Z_]+'
print("identifier")
return t
def t_NAME(t):
r"[a-zA-Z_]+"
print("name")
return t
t_EQUALS = r"="
t_ignore = ' \t'
def t_newline(t):
r'\n+'
t.lexer.lineno += len(t.value)
# Error handling rule
def t_error(t):
print("Illegal character '%s' at line' %s'" % (t.value[0] , t.lexer.lineno ))
t.lexer.skip(1)
# Build the lexer
lexer = lex.lex()
синтаксический анализатор
import ply.yacc as yacc
from l import tokens
def p_main(p):
'''
main : NAME EQUALS NAME
'''
def p_error(p):
if p is not None:
print ("Line %s, illegal token %s" % (p.lineno, p.value))
else:
print("Syntax error in input!")
parser = yacc.yacc()
with open('simple2','r') as f:
result = parser.parse(f.read())
Мой входной файл содержит только это:
A = B
И что происходит, это то, что первое слово A
соответствует токену IDENTIFIER
даже если это не должно делать, так как регулярное выражение требует *
перед буквами. После этого парсер не сможет распознать выражение, так как лексер не вернет нужные токены.
Что случилось? Регулярное выражение, используемое для IDENTIFIER токена, прекрасно работает в Python.
2 ответа
Согласно руководству PLY: (выделение добавлено)
Внутренне
lex.py
используетre
модуль для сопоставления с образцом. Шаблоны компилируются с использованиемre.VERBOSE
флаг, который можно использовать для удобства чтения. Однако следует помнить, что неэкранированные пробелы игнорируются, и в этом режиме разрешены комментарии. Если ваш шаблон содержит пробелы, убедитесь, что вы используете\s
, Если вам нужно соответствовать#
характер, использование[#]
,
Таким образом, символ пробела в вашем регулярном выражении \* *[a-zA-Z_]+
игнорируется, делая регулярное выражение, эффективно, \**[a-zA-Z_]+
; то есть ноль или более звезд. Если вы действительно хотите, чтобы это была звезда, за которой следовал один или несколько пробелов, вам нужно что-то вроде: \*\ [a-zA-Z_]+
,
Я думаю, что я нашел проблему и решение.
Проблема в '*'
в ' *'
потому что это лечит '\* '
как одна строка - так '\* *'
средства '\* '
много раз или ни одного (как 'abc*'
средства 'abc'
много раз или нет).
Тебе нужно '\*[ ]*'
или же '\*\s*'