Брайан Керниган - UNIX — универсальная среда программирования
- Название:UNIX — универсальная среда программирования
- Автор:
- Жанр:
- Издательство:Финансы и статистика
- Год:1992
- Город:Москва
- ISBN:5-289-00253-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Брайан Керниган - UNIX — универсальная среда программирования краткое содержание
В книге американских авторов — разработчиков операционной системы UNIX — блестяще решена проблема автоматизации деятельности программиста, системной поддержки его творчества, выходящей за рамки языков программирования. Профессионалам открыт богатый "встроенный" арсенал системы UNIX. Многочисленными примерами иллюстрировано использование языка управления заданиями shell.
Для программистов-пользователей операционной системы UNIX.
UNIX — универсальная среда программирования - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Особенно полезно обращаться к make
, когда создаваемая программа настолько велика, что "располагается" в нескольких исходных файлах. Однако она удобна и для таких малых программ, как hoc1
. Ниже приведены описания команд для make
, рассчитанные на hoc1
, которые make
предполагает найти в файле с именем makefile
.
$ cat makefile
hoc1: hoc.o
cc hoc.o -o hoc1
$
Здесь сообщается, что hoc1
зависит от hoc.o
и что hoc1
создается из hoc.o
с помощью команды сс
, которая запускает компилятор Си, помещая выходной поток в файл hoc1
. Программа make
уже "знает", как преобразовать входной файл для yacc
hoc.y
в выходной файл hoc.o
:
$ make
Проделаем первый раз получение hoc1 с помощью make
yacc hoc.y
сс -с y.tab.c
rm y.tab.c
mv y.tab.o hoc.о
сс hoc.о -о hoc1
$ make
Попробуем еще раз
'hoc1' is up to date make понимает, что это не нужно
$
8.2 Этап 2: переменные и восстановление после ошибки
Следующий шаг переход от hoc1
к hoc2
, который сводится к расширению памяти (в памяти хранится 26 переменных с именами от а
до z
). Это довольно несложный и весьма полезный промежуточный этап. Мы также введем здесь процесс обработки ошибок. Если вы проверите hoc1
, то убедитесь, что реакцией на синтаксические ошибки являются вывод сообщения и прекращение работы. Поведение же hoc1
в случае арифметических ошибок типа деления на нуль достойно всяческого порицания:
$ hoc1
1/0
Floating exception - core dump
$
Для реализации новых возможностей требуются лишь небольшие изменения: приблизительно 35 строк текста. Лексический анализатор yylex
должен распознавать буквы как переменные, а грамматика содержать правила вывода вида
expr: VAR
| VAR '=' expr
Выражение может содержать операцию присваивания; разрешены также многократные присваивания типа
x = y = z = 0
Простейший способ хранения значений переменных создать массив из 26 элементов; однобуквенную переменную можно использовать в качестве индекса массива. Однако если анализатору предстоит обрабатывать и имена переменных, и значения в одном стеке, необходимо сообщить yacc
, что элемент стека является объединением double
и int
, а не просто элементом типа double
. Это делается с помощью описания %union
. Описания #define
или typedef
подходят для определения стека из базовых типов как double
, но для типов объединения требуется описание %union
, поскольку yacc
осуществляет контроль типов в выражениях вида $$ = $2
.
Ниже приведена часть определения грамматики hoc.y
для программы hoc2
:
$ cat hoc.y
%{
double mem[26]; /* memory for variables 'a'..'z' */
%}
%union { /* stack type */
double val; /* actual value */
int index; /* index into mem[] */
}
%token NUMBER
%token VAR
%type expr
%right '='
%left '+'
%left '*' '/'
%left UNARYMINUS
%%
list: /* nothing */
| list '\n'
| list expr '\n' { printf ("\t%.8g\n", $2); }
| list error '\n' { yyerrok; }
;
expr: NUMBER
| VAR { $$ = mem[$1]; }
| 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
есть сведения о том, что выраж является элементом объединения , т.е. double
. Информация о типе позволяет yacc
обращаться к нужному элементу объединения. Обратите внимание: "="
представляет собой правоассоциативную операцию, тогда как другие операции — левоассоциативные.
Обработка ошибок происходит в несколько этапов. Прежде всего производится проверка на нулевой делитель: если делитель равен нулю, вызывается процедура обработки ошибок execerror
. Второй этап заключается в перехвате сигнала "переполнение вещественного" ("floating point exception"), который возникает при переполнении вещественного числа. Сигнал устанавливается в функции main
. Последний шаг восстановления после ошибки заключается в добавлении к грамматике правила вывода для ошибки. В грамматике yacc
слово error
зарезервировано; оно дает возможность анализатору осознать синтаксическую ошибку и восстановиться после нее. Если произойдет ошибка, yacc
в конце концов использует это правило, распознает ошибку как грамматически "правильную" конструкцию и, таким образом, восстановится. Действие yyerrok
заключается в установке признака в анализаторе, который позволяет вернуться ему назад в состояние осмысленного разбора. Восстановление после ошибки сложная проблема для всех анализаторов. Мы показали вам здесь лишь самые элементарные приемы и только обозначили возможности yacc
.
В грамматике hoс2
произошли незначительные изменения. Ниже приведена функция main
, дополненная обращением к setjmp
. Оно позволяет запомнить то нормальное состояние, которое будет использовано при восстановлении после ошибки. В функции execerror
происходит соответствующее обращение к longjmp
. (Описание setjmp
и longjmp
см. в разд. 7.5.)
...
#include
#include
char *progname;
int lineno = 1;
#include
#include
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
. Ниже показаны измененные фрагменты:
Интервал:
Закладка: