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*'

Другие вопросы по тегам