Россия, Челябинск, Южно-Уральский Государственный Университет (НИУ) |
Грамматики и YACC
Error-правила
Если мы в состоянии понять, в каких ситуациях могут встретиться ошибки, то мы можем добавить к грамматике языка правила, которые будут использоваться в случае ошибки. Эти правила называются "error productions". В частности, добавлять такие правила позволяет YACC.
Error-правила в YACC'е имеет один из следующих видов:
- A: w error
- A: w1 error w2
Имя error зарезервировано для обработки ошибок. Это имя может использоваться в грамматических правилах; в сущности, это имя сообщает о месте, где ожидаются ошибки, и может происходить восстановление. YACC обрабатывает эти правила как обычные правила. Однако, когда анализатор сгенерированный YACC'ом, встречает ошибку, он обрабатывает состояние специальным образом. Из магазина извлекаются символы до тех пор, пока не будет найдено такое состояние, которое лежит как можно ближе к вершине магазина и под которым находится элемент вида: A: w1 ^ error w2 . Затем анализатор переносит фиктивный лексический класс error на стек, как будто этот терминальный символ находился во входной цепочке.
- Если w2 - пусто, то свертка к А выполняется незамедлительно и исполняется семантика, связанная с правилом A: w error. Затем анализатор сбрасывает символы входной цепочки до тех пор, пока он не отыщет символ, с которым нормальная обработка может быть продолжена.
- Если w2 - непусто, то YACC пропускает первые символы входной цепочки, пока не будет найдена, которая может быть свернута в w2 . Затем анализатор сворачивает A: w1 error w2 в нетерминал А и восстанавливает нормальную обработку. Например, правило stmt: error ';' указывает анализатору, что он должен пропустить все литеры до ближайшей точки с запятой.
Пример использования error-правил
Пример использования error-правил
lines: lines expr '\n' { printf ("%d \n", $2); } | lines '\n' | /* empty */ | error '\n' { yyerror ("reenter last line:"); yyerrok; } ;
Ниже приведен пример использования error-правил в рассмотренной ранее программе, вычисляющей арифметическое выражение. Теперь, если поданная на вход строка не распознана как выражение, выведется сообщение с предложением ввести последнюю строку заново.
%union { int myValue; } /* Terminals */ %token <myValue> Number_LC %left '+' '-' %left '*' '/' %right UNARYMINUS /* Nonterminals */ %type <myValue> expr %start lines %% /* Grammar rules */ lines: lines expr '\n' { printf ("%d \n", &2); } | lines '\n' | /* empty */ | error '\n' { yyerror ("reenter last line:"); yyerrok; } ; expr: Number_LC { && = &1; } | expr '*' expr { && = &1*&3; } | expr '/' expr { && = &1/&3; } | expr '+' expr { && = &1+&3; } | expr '-' expr { && = &1-&3; } | '-' expr %prec UNARYMINUS { $$ = -&2; } ;
Литература к лекции
- А. Ахо, Р. Сети, Дж. Ульман. "Компиляторы: принципы, технологии и инструменты", М.: "Вильямс", 2001. 768 стр.
- D. Grune, G. H. J. Jacobs "Parsing Techniques - A Practical Guide", Ellis Horwood, 1990. 320 pp.