UNIX — универсальная среда программирования
Шрифт:
| VAR '=' expr { $$ = mem[$1] = $3; }
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr {
if ($3 == 0.0)
execerror("division by zero", "");
$$ = $1 / $3;
}
| '(' expr ')' { $$ = $2; }
| '-' expr %prec UNARYMINUS { $$ = -$2; }
;
%%
/* end of grammar */
...
Из
%union
следует, что элементы стека содержат или число с двойной точностью (обычный случай), или целое, являющееся индексом в массиве mem
. В описании %token
дополнительно указывается тип значения. В описании %type
есть сведения о том, что выраж является элементом объединения <val>
, т.е. double
. Информация о типе позволяет yacc
обращаться к нужному элементу объединения. Обратите внимание: "="
представляет собой правоассоциативную операцию, тогда как другие операции — левоассоциативные. Обработка ошибок происходит в несколько этапов. Прежде всего производится проверка на нулевой делитель: если делитель равен нулю, вызывается процедура обработки ошибок
execerror
. Второй этап заключается в перехвате сигнала "переполнение вещественного" ("floating point exception"), который возникает при переполнении вещественного числа. Сигнал устанавливается в функции main
. Последний шаг восстановления после ошибки заключается в добавлении к грамматике правила вывода для ошибки. В грамматике yacc
слово error
зарезервировано; оно дает возможность анализатору осознать синтаксическую ошибку и восстановиться после нее. Если произойдет ошибка, yacc
в конце концов использует это правило, распознает ошибку как грамматически "правильную" конструкцию и, таким образом, восстановится. Действие yyerrok
заключается в установке признака в анализаторе, который позволяет вернуться ему назад в состояние осмысленного разбора. Восстановление после ошибки сложная проблема для всех анализаторов. Мы показали вам здесь лишь самые элементарные приемы и только обозначили возможности yacc
. В грамматике
hoс2
произошли незначительные изменения. Ниже приведена функция main
, дополненная обращением к setjmp
. Оно позволяет запомнить то нормальное состояние, которое будет использовано при восстановлении после ошибки. В функции execerror
происходит соответствующее обращение к longjmp
. (Описание setjmp
и longjmp
см. в разд. 7.5.)
...
#include <stdio.h>
#include <ctype.h>
char *progname;
int lineno = 1;
#include <signal.h>
#include <setjmp.h>
jmp_buf begin;
main(argc, argv) /* hoc2 */
char *argv[];
{
int fpecatch;
progname = argv[0];
setjmp(begin);
signal(SIGFPE, fpecatch);
yyparse;
}
execerror(s, t) /* recover from run-time error */
char *s, *t;
{
warning(s, t);
longjmp(begin, 0);
}
fpecatch /* catch floating point exceptions */
{
execerror("floating point exception", (char*)0);
}
В
execerror
вызывала abort
(см. справочное руководство по abort(3)
), что приведет к распечатке содержимого памяти, которую затем смогут использовать программы adb
и sdb
. Когда разработка программы полностью завершится, обращение к abort
будет заменено на longjmp
. В программе
hoc2
лексический анализатор несколько иной. В нем учтено различие строчных и прописных букв, а поскольку теперь yyval
является объединением, нужно выбрать подходящий элемент перед выходом из yylex
. Ниже показаны измененные фрагменты:
yylex /* hoc2 */
{
...
if (с == '.' || isdigit(c)) { /* number */
ungetc(c, stdin);
scanf("%lf", &yylval.val);
return NUMBER;
}
if (islower(c)) {
yylval.index = с - 'a'; /* ASCII only */
return VAR;
}
...
Еще раз отметим, что тип лексемы (т.е.
NUMBER
) не совпадает с ее значением (например, 3.1416). Продемонстрируем новые возможности
hoc2
переменные и способность восстановления после ошибки:
$ hoc2
x = 355
355
y = 113
113
p = x/z
z не определено, а значит, равно 0 hoc2: division by zero near line 4
Восстановление после ошибки
x/y
3.1415929
1е30 * 1е30
Переполнение
hoc2: floating point exception near line 5
...
В самом деле, для PDP-11 требуются вполне конкретные меры, чтобы обнаружить переполнение вещественного, но на большинстве других машин
hoc2
действует так, как показано выше. Упражнение 8.3
Обеспечьте возможность запоминания последнего вычисленного значения, чтобы его не приходилось вводить снова для последовательности связанных вычислений. Одним из решений может быть использование какой-либо переменной, например
'p'
, в качестве "предыдущего" (previous) значения.
Поделиться:
Популярные книги
Последний Паладин. Том 2
2. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Зубных дел мастер
1. Зубных дел мастер
Фантастика:
научная фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Истребитель. Ас из будущего
Фантастика:
боевая фантастика
попаданцы
альтернативная история
5.25
рейтинг книги
Честное пионерское! Часть 3
3. Честное пионерское!
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Обгоняя время
13. Девяностые
Фантастика:
попаданцы
5.00
рейтинг книги
Страж. Тетралогия
Страж
Фантастика:
фэнтези
9.11
рейтинг книги
Магия чистых душ
Любовные романы:
любовно-фантастические романы
5.40
рейтинг книги
Имя нам Легион. Том 4
4. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Девятый
1. Девятый
Фантастика:
боевая фантастика
попаданцы
9.15
рейтинг книги
Морской волк. 1-я Трилогия
1. Морской волк
Фантастика:
альтернативная история
8.71
рейтинг книги
Отмороженный 8.0
8. Отмороженный
Фантастика:
постапокалипсис
рпг
аниме
5.00
рейтинг книги
Совершенный: охота
3. Совершенный
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Калибр Личности 1
1. Калибр Личности
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Личник
3. Ермак
Фантастика:
альтернативная история
6.33