Обработка ошибок в yacc

При чтении анализатором входного потока данные из этого потока могут не соответствовать ни одному правилу файла грамматики.

Анализатор должен обнаруживать подобные ошибки как можно раньше. Если в файле грамматики присутствует функция обработки ошибок, то с ее помощью можно запросить повторный ввод данных, пропустить данные с ошибкой или выполнить очистку и восстановление. Например, при обнаружении ошибки может потребоваться освобождение памяти, выделенной для дерева разбора, удаление или изменение записей таблицы символов и установка флагов для предотвращения дальнейшего разбора.

Если функция обработки ошибок не определена, то при обнаружении ошибки анализатор прекращает работу. Для дальнейшей обработки входного потока с целью обнаружения других ошибок необходимо перезапустить анализатор с той точки, в которой он сможет продолжить разбор входного потока. Одним из способов такого перезапуска является пропуск определенного числа лексем, следующих за лексемой, вызывавшей ошибку.

В команде yacc для обработки ошибок применяется специальная лексема error. Эту лексему помещают в раздел правил в тех местах, где может возникнуть ошибка и требуются специальные действия по ее обработке. Если ошибка возникает в таком месте, анализатор выполняет вместо обычного действия действие лексемы error.

При обработке ошибок в действиях yacc могут применяться следующие макроопределения:
Макрокоманды Описание
YYERROR Вызывает обработку ошибки анализатором.
YYABORT Анализатор возвращает значение 1.
YYACCEPT Анализатор возвращает значение 0.
YYRECOVERING() Возвращает 1, если была обнаружена синтаксическая ошибка и анализатор еще не завершил процесс исправления.

Для того чтобы одна ошибка не приводила к возникновению нескольких сообщений об ошибках, анализатор остается в состоянии ошибки до те пор, пока он не обработает три лексемы, следующие за лексемой, вызвавшей ошибку. Если в течение этого времени возникнет еще одна ошибка, анализатор пропустит соответствующую лексему без выдачи сообщения.

Например, правило:
stat  :  error ';'

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

Исправление ошибок

При возникновении ошибки вы можете предоставить пользователю, работающему с программой в диалоговом режиме, возможность исправить входной текст, запросив повторный ввод строки. Ниже приведен пример такого кода:

input : error '\n'
        {
          printf(" Введите последнюю строку еще раз: " );
         }
         input
       {
         $$ = $4;
       }
       ;
Однако в этом варианте анализатор остается в состоянии ошибки на протяжении еще трех лексем. Если в одной из 3 первых лексем исправленной строки присутствует ошибка, анализатор пропустит их, не выдав сообщение об ошибке. Для того чтобы учесть подобную ситуацию, используйте следующий оператор yacc:
yyerrok;

Этот оператор переводит анализатор в обычное состояние. Процедура исправления ошибок с этим оператором выглядит так:

input : error '\n'
        {
          yyerrok;
          printf(" Введите последнюю строку еще раз: " );
         }
         input
       {
         $$ = $4;
       }
         ;

Очистка следующей лексемы

Под следующей лексемой подразумевается лексема, которая будет проанализирована программой на следующем шаге. При возникновении ошибки следующая лексема - это лексема, в которой анализатор обнаружил ошибку. Если действие по восстановлению после ошибки определяет точку, с которой обработка будет возобновлена, его код должен очищать следующую лексему. Для очистки значения следующей лексемы применяется следующий оператор:

yyclearin ;