UNIX — универсальная среда программирования
Шрифт:
Первая модификация позволяет существенно экономить время в случае больших программ, когда грамматика постоянна, а семантические действия меняются (обычная ситуация). Второе изменение обеспечивает экономию бумаги.
Приведем
makefile
для hoc4
:
YFLAGS = -d
OBJS = hoc.o code.o init.o math.o symbol.o
hoc4: $(OBJS)
cc $(OBJS) -lm -o hoc4
hoc.o code.o init.o symbol.o: hoc.h
code.o init.o symbol.o: x.tab.h
x.tab.h: y.tab.h
– cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h
pr: hoc.y hoc.h code.c init.c math.c symbol.c
@pr $?
@touch pr
clean:
rm -f $(OBJS) [xy].tab.[ch]
Символ
'-'
cmp
дает указание make
продолжать выполнение даже в случае неудачи cmp
; это позволяет не останавливать работу и при несуществующем файле x.tab.h
(флаг – s
предписывает команде cmp
не производить вывод, но установить код завершения). Комбинация $?
раскрывается как список элементов из правила с устаревшей версией. К сожалению, форма записи в makefile
слабо связана с обозначениями в интерпретаторе. Проиллюстрируем изложенное выше на примере (в предположении, что все файлы последней версии):
$ touch hoc.y
Изменим время для файла hoc.y
$ make
yacc -d hoc.y
conflicts: 1 shift/reduce
сс -с y.tab.c
rm y.tab.c
mv y.tab.o hoc.o
cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h
cc hoc.o code.o init.o math.o symbol.o -lm -o hoc4
$ make -n p
r Печать измененных файлов
pr hoc.y
touch pr
$
Отметим, что, кроме
hoc.y
, файлы не перетранслировались, поскольку файл y.tab.h
остался тем же. Упражнение 8.10
Сделайте размеры стека и массива
prog
динамическими, чтобы для hoc4
всегда хватало объема памяти, если только ее можно получить, обращаясь к функции malloc
. Упражнение 8.11
Измените
hoc4
так, чтобы использовать в функции execute
вместо вызова функций переключатель по виду операции +
. Каково соотношение версий по размеру исходного текста и по времени выполнения? Как приблизительно их сопоставить по сложности развития и поддержания? 8.5 Этап 5: структуры управления и операции отношений
Версия
hoc5
оправдывает все затраты, связанные с созданием интерпретатора. В нее допустимо включать операторы if-else
и while
, аналогичные операторам языка Си, группировать операторы с помощью {
и }
и использовать оператор print
. Она содержит полный набор операций отношений (>
, >=
, и т.д.), а также операций AND
, OR
, &&
и ||
. (Две последние операции не гарантируют
вычисления слева направо, хотя такой подход принят в Си; вычисляются оба условия, даже если в этом нет необходимости.) Грамматику
hoc5
дополняют лексемы, нетерминальные символы и правила для if
, while
, фигурных скобок и операций отношений. Поэтому она получилась несколько больше, но не намного сложнее предыдущих версий (возможно, за исключением правил для if
и while
):
$ cat hoc.y
%{
#include "hoc.h"
#define code2(c1,c2) code(c1); code(c2)
#define code3(c1,c2,c3) code(c1); code(c2); code(c3)
%}
%union {
Symbol *sym; /* symbol table pointer */
Inst *inst; /* machine instruction */
}
%token <sym> NUMBER PRINT VAR BLTIN UNDEF WHILE IF ELSE
%type <inst> stmt asgn expr stmtlist cond while if end
%right '='
%left OR
%left AND
%left GT GE LT LE EQ NE
%left '+'
%left '*' '/'
%left UNARYMINUS NOT
%right '^'
%%
list: /* nothing */
| list '\n'
| list asgn '\n' { code2(pop, STOP); return 1; }
| list stmt '\n' { code(STOP); return 1; }
| list expr '\n' { code2(print, STOP); return 1; }
| list error '\n' { yyerrok; }
;
asgn: VAR '=' expr { $$=$3; code3(varpush, (Inst)$1, assign); }
;
stmt: expr { code(pop); }
| PRINT expr { code(prexpr); $$ = $2; }
| while cond stmt end {
($1)[1] = (Inst)$3; /* body of loop */
($1)[2] = (Inst)$4; } /* end, if cond fails */
| if cond stmt end { /* else-less if */
($1)[1] = (Inst)$3; /* thenpart */
($1)[3] = (Inst)$4; } /* end, if cond fails */
| if cond stmt end ELSE stmt end { /* if with else */
($1)[1] = (Inst)$3; /* thenpart */
($1)[2] = (Inst)$6; /* elsepart */
($1)[3] = (Inst)$7; } /* end, if cond fails */
| '{' stmtlist '}' { $$ = $2; }
;
cond: '(' expr ')' { code(STOP); $$ = $2; }
;
while: WHILE { $$ = code3(whilecode, STOP, STOP); }
;
if: IF { $$=code(ifcode); code3(STOP, STOP, STOP); }
;
end: /* nothing */ { code(STOP); $$ = progp; }
;
stmtlist: /* nothing */ { $$ = progp; }
| stmtlist '\n'
| stmtlist stmt
;
expr: NUMBER { $$ = code2(constpush, (Inst)$1); }
Поделиться с друзьями:
- Telegram
- Viber
- Skype
- ВКонтакте