execerror("stack overflow", (char*)0); *stackp++ = d;}Datum pop() /* pop and return top elem from stack */{ if (stackp <= stack) execerror("stack underflow", (char*)0); return *--stackp;}Машинные команды создаются в процессе разбора при обращении к функции
codeproghoc4Inst *code(f) /* install one instruction or operand */ Inst f;{ Inst *oprogp = progp; if (progp >= &prog[NPROG]) execerror("program too big", (char*)0); *progp++ = f; return oprogp;}Выполнение машинной команды фантастически тривиально, а как мала процедура, которая "выполняет" машинные команды, когда уже определены все программы!
execute(p) /* run the machine */ Inst *p;{ for (pc = p; *pc != STOP; ) (*(*pc++))();}В цикле выполняется функция, указываемая командой, на которую в свою очередь указывает счетчик команд
pcpcSTOPconstpushvarpushpcconstpush() /* push constant onto stack */{ Datum d; d.val = ((Symbol*)*pc++)->u.val; push(d);}varpush() /* push variable onto stack */{ Datum d; d.sym = (Symbol*)(*pc++); push(d);}Оставшаяся часть описания машины проста. Так, арифметические операции в основном те же, и создаются они редактированием одного образца. Ниже показана операция
addadd() /* add top two elems on stack */{ Datum d1, d2; d2 = pop(); d1 = pop(); d1.val += d2.val; push(d1);}Другие процедуры также просты:
eval() /* evaluate variable on stack */{ Datum d; d = pop(); if (d.sym->type == UNDEF) execerror("undefined variable", d.sym->name); d.val = d.sym->u.val; push(d);}assign() /* assign top value to next value */{ Datum d1, d2; d1 = pop(); d2 = pop(); if (d1.sym->type != VAR && d1.sym->type != UNDEF) execerror("assignment to non-variable", d1.sym->name); d1.sym->u.val = d2.val; d1.sym->type = VAR; push(d2);}print() /* pop top value from stack, print it */{ Datum d; d = pop(); printf("\t%.8g\n", d.val);}bltin() /* evaluate built-in on top of stack */{ Datum d; d = pop(); d.val = (*(double (*)())(*pc++))(d.val); push(d);}Самый сложный момент здесь операция приведения в функции, которая требует, чтобы
*pcdoubled.valДиагностические сообщения от функций
evalassignИспользование языка Си дает возможность работать с указателем на функцию, что позволяет писать компактные и эффективные программы.
Альтернативное решение состоит в том, чтобы сделать операторы константами и сгруппировать семантические функции в большой переключатель в функции
execute