Я писал парсер для синтаксического анализа грамматик типа C.
Во-первых, теперь он может разобрать код, например:
a = 1;
b = 2;
Теперь я хочу сделать точку с запятой в конце строки опциональной.
Исходное правило YACC:
stmt: expr ';' { ... }
Если новая строка обрабатывается лексером, написанным мной (код упрощен):
rule(/\r\n|\r|\n/) { increase_lineno(); return :PASS }
инструкция: PASS здесь эквивалентна возврату ничего в LEX, который отбрасывает текущий согласованный текст и переходит к следующему правилу, точно так же, как обычно делается с пробелами.
Из-за этого я не могу просто изменить правило YACC на:
stmt: expr end_of_stmt { ... }
;
end_of_stmt: ';'
| '\n'
;
Итак, я решил динамически изменять состояние лексера с помощью синтаксического анализатора.
Вот так:
stmt: expr { state = :STATEMENT_END } ';' { ... }
И добавьте правило lexer, которое может соответствовать новой строке с новым состоянием:
rule(/\r\n|\r|\n/, :STATEMENT_END) { increase_lineno(); state = nil; return ';' }
Это означает, что когда lexer находится в состоянии: STATEMENT_END. он сначала увеличит номер строки, как обычно, а затем установит состояние в начальное, а затем притворится, что это точка с запятой.
Странно, что на самом деле он не работает со следующим кодом:
a = 1
b = 2
Я отлаживал его и получал, что на самом деле не получается ';' как и ожидалось при сканировании новой строки после номера 1, и указанное правило не выполняется.
И код для установки нового состояния выполняется после того, как он уже отсканировал новую строку и ничего не вернул, это означает, что эти работы выполняются следующим образом:
- scan
a
,=
и1
- сканировать новую строку и пропустить, так что получите следующее значение
b
- выполняется вставленный код (
{ state = :STATEMENT_END }
) - ошибка при поднятии - неожиданно
b
здесь
Это то, что я ожидаю:
- scan
a
,=
и1
- обнаружил, что он соответствует правилу
expr
, поэтому уменьшите доstmt
- выполнить вставленный код, чтобы установить новое лексерское состояние
- сканируйте новую строку и верните
;
в соответствии с новым правилом сопоставления состояний - продолжить сканирование и анализ следующей строки
После интроспекции я обнаружил, что это может быть вызвано тем, что YACC использует LALR (1), этот синтаксический анализатор будет читать вперед для первого токена. Когда он сканирует туда, состояние еще не установлено, поэтому он не может получить правильный токен.
Мой вопрос: как заставить его работать должным образом? Я понятия не имею об этом.
Спасибо.