Разбор необязательной точки с запятой в конце оператора

Я писал синтаксический анализатор для разбора 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 } ';' { ... }

И добавьте правило лексера, которое может сопоставлять новую строку с новым состоянием:

rule(/\r\n|\r|\n/, :STATEMENT_END) { increase_lineno(); state = nil; return ';' }

Это означает, что лексер находится в состоянии :STATEMENT_END. он сначала увеличит номер строки, как обычно, а затем установит исходное состояние, а затем притворится точкой с запятой.

Странно, что на самом деле это не работает со следующим кодом:

a = 1
b = 2

Я отладил его и получил, что на самом деле не получается ';' как и ожидалось при сканировании новой строки после цифры 1, а указанное состояние правила на самом деле не выполняется.

И код для установки нового состояния выполняется после того, как он уже просканировал новую строку и ничего не вернул, то есть эти работы выполняются в следующем порядке:

  1. сканирование a, =и 1
  2. сканировать новую строку и пропустить, чтобы получить следующее значение b
  3. вставленный код ({ state = :STATEMENT_END }) выполняется
  4. поднимая ошибку -- неожиданно bздесь

Это то, что я ожидаю:

  1. scan a, = и 1
  2. обнаружил, что он соответствует правилу expr, поэтому уменьшите до stmt
  3. выполните вставленный код, чтобы установить новое состояние лексера
  4. просканируйте новую строку и верните ;в соответствии с новым правилом сопоставления состояний
  5. продолжайте сканировать и анализировать следующую строку

После самоанализа я обнаружил, что может быть вызвано тем, что YACC использует LALR (1), этот синтаксический анализатор будет сначала читать вперед для одного токена. Когда он сканирует туда, состояние еще не установлено, поэтому он не может получить правильный токен.

У меня вопрос: как заставить его работать так, как ожидалось? Я понятия не имею об этом.

Спасибо.

10
задан Shou Ya 20 September 2014 в 12:20
поделиться