Чтение онлайн

ЖАНРЫ

UNIX — универсальная среда программирования
Шрифт:

Inst *progbase = prog; /* start of current subprogram */

int returning; /* 1 if return stmt seen */

typedef struct Frame { /* proc/func call stack frame */

 Symbol *sp; /* symbol table entry */

 Inst *retpc; /* where to resume after return */

 Datum *argn; /* n-th argument on stack */

 int nargs; /* number of arguments */

} Frame;

#define NFRAME 100 Frame frame[NFRAME];

Frame *fp; /* frame pointer */

initcode {

 progp = progbase;

 stackp = stack;

 fp = frame;

 returning = 0;

}

...

$

Поскольку

теперь в таблице имен хранятся указатели на функции и процедуры, а также на строки для печати, необходимо расширить определение типа объединения в файле
hoc.h
:

$ cat hoc.h

typedef struct Symbol { /* symbol table entry */

 char *name;

 short type;

 union {

double val; /* VAR */

double (*ptr); /* BLTIN */

int (*defn); /* FUNCTION, PROCEDURE */

char *str; /* STRING */

 } u;

 struct Symbol *next; /* to link to another */

} Symbol;

$

В процессе трансляции функция

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

define(sp) /* put func/proc in symbol table */

 Symbol *sp;

{

 sp->u.defn = (Inst)progbase; /* start of code */

 progbase = progp; /* next code starts here */

}

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

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

call /* call a function */

{

 Symbol *sp = (Symbol*)pc[0]; /* symbol table entry */

 /* for function */

 if (fp++ >= &frame[NFRAME-1])

execerror(sp->name, "call nested too deeply");

 fp->sp = sp;

 fp->nargs = (int)pc[1];

 fp->retpc = pc + 2;

 fp->argn = stackp - 1; /* last argument */

 execute(sp->u.defn);

 returning = 0;

}

Создаваемая

структура показана на рис. 8.2.

Рис. 8.2: Структуры данных для вызова процедуры

В конце концов произойдет возврат из вызываемой программы при выполнении

procret
или
funcret
:

funcret /* return from a function */

{

 Datum d;

 if (fp->sp->type == PROCEDURE)

execerror(fp->sp->name, "(proc) returns value");

 d = pop; /* preserve function return value */

 ret;

 push(d);

}

procret /* return from a procedure */

{

 if (fp->sp->type == FUNCTION)

execerror(fp->sp->name(func) returns no value");

 ret;

}

Функция

ret
удаляет аргументы из стека, сохраняет указатель на образ стека
fp
и устанавливает счетчик команд:

ret /* common return from func or proc */

{

 int i;

 for (i = 0; i < fp->nargs; i++)

pop; /* pop arguments */

 pc = (Inst*)fp->retpc;

 --fp;

 returning = 1;

}

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

returning
, который принимает значение 1 при обнаружении оператора
return
. Выполнение, организуемое функциями
ifcode
,
whilecode
,
execute
, завершается раньше, если установлен признак
returning
; в функции
call
он обнуляется.

ifcode {

 Datum d;

 Inst *savepc = pc; /* then part */

 execute(savepc+3); /* condition */

 d = pop;

 if (d.val)

execute(*((Inst**)(savepc)));

 else if (*((Inst**)(savepc+1))) /* else part? */

execute(*((Inst**)(savepc+1)));

 if (!returning)

pc = *((Inst**)(savepc+2)); /* next stmt */

Поделиться с друзьями: