#include "hoc.h"

#ifndef MSDOS
#include "y.tab.h"
#else
#include "y.h"
#endif

#include <stdio.h>
#include <ctype.h>

#ifdef STDC_HEADERS
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#else
char *malloc(),*memcpy();
double fmod();
#endif


static Datum stack[NSTACK];
static Datum *stackp;

extern FILE *fin;
#include <setjmp.h>
extern jmp_buf begin;

Inst prog[NPROG];
Inst *progp;
Inst *pc;
Inst *progbase=prog;
int returning,whilebreak,whilecont;
int inwhile;
Frame frame[NFRAME];
Frame *fp;

/* set by Set() and used by other routines */
static int Sprcalls=0,prcalls=0;
static int Signoreundef=0,ignoreundef=0;
static int Sallnames=0,allnames=0;

void
initcode()
{
	progp=progbase;
	stackp=stack;
	fp=frame;
	returning=0;
	whilebreak=0;
	whilecont=0;
	inwhile=0;
	indef=0;
}
static void
push(d)
	Datum d;
{
	if(stackp>=&stack[NSTACK])
		execerror("stack too deep", (char *)0);
	*stackp++=d;
}
static void
pushd(d)
	double d;
{
	if(stackp>=&stack[NSTACK])
		execerror("stack too deep", (char *)0);
	(*stackp++).val=d;
}
static Datum
pop()
{
	if(stackp==stack)
		execerror("stack underflow", (char *)0);
	return *--stackp;
}
void
define(sp)
	Symbol *sp;
{
	Inst *tmp;
	Inst *tmp1;
	if(sp->u.defn!=(Inst)deffunc&&sp->u.defn!=(Inst)defproc){
		free((Inst*)sp->u.defn);
	}

	tmp1=(Inst*)emalloc((progp-prog)*sizeof(Inst));
	sp->u.defn=(Inst)tmp1;
	tmp=prog;
	while(tmp<progp)
		*tmp1++= *tmp++;
	progp=prog;
}
static
double *
getarg()
{
	int nargs=(int)*pc++;
	/* we assume that the given argument number nargs can never be
	   <=0 (since that is checked in the lexical analyzer yylex())
	   so we only have to check whether it is too large.
	*/
	if(nargs>fp->nargs)
		execerror(fp->sp->name, "not enough arguments");
	return &fp->argn[nargs-fp->nargs].val;
}
static double
getval(v)
	Symbol *v;
{
	switch(v->type){
	case UNDEF:
		if(ignoreundef)
			return 0;
		else
			execerror(v->name,"is used before defined");
	case VAR:
		return v->u.val;
	case LOCAL:
		if(v->u.localptr->p.frame!=fp){
			/* found local from another function. look for the
			   global with the same name */
			/* ->nextdef because global can't be first */
			Local *x=v->u.localptr->nextdef;
			while(x->nextdef)
				x=x->nextdef;
			if(x->p.type==UNDEF){
				if(ignoreundef){
					return 0;
				} else
					execerror(v->name,"is used before defined");
			} else
				return x->val;
		} else {
			return v->u.localptr->val;
		}
		break;	
	default:
		execerror(v->name,"is not a varible");
		break;
	}
}	
/* getvar pops a variable (pushed by varpush) from the stack, and returns the
   address of the value of the variable, so it can be used and assigned. it
   gives an error if the variable is undefined, and an assignment to
   non-variable error if it isn't a variable.
   Because of the errors it prints, it was meant to be called by functions
   which both use the value of the variable and assign it a new variable,
   like in ++, +=, and etc.
*/
static double *
getvar()
{
	Datum d;
	d=pop();
	switch(d.sym->type){
	case UNDEF:
		if(ignoreundef){
			d.sym->u.val=0;
			d.sym->type=VAR;
			return &(d.sym->u.val);
		} else
			execerror(d.sym->name,"is used before defined");
		break;
	case VAR:
		return &(d.sym->u.val);
	case LOCAL:
		if(d.sym->u.localptr->p.frame!=fp){
			Local *x=d.sym->u.localptr->nextdef;
			while(x->nextdef)
				x=x->nextdef;
			if(x->p.type==UNDEF){
				if(ignoreundef){
					x->val=0;
					x->p.type=VAR;
				} else
					execerror(d.sym->name,"is used before defined");
			}
			return &(x->val);
		} else {
			return &(d.sym->u.localptr->val);
		}
	default:
		execerror("assignment to non-variable",d.sym->name);
	}
}
void
Line(lineinfile,filename)
	Inst filename, lineinfile;
{
	infile=((char *)filename);
	if(infile[0]=='\0')
		infile=(char*)0;
	lineno=((int)(((Constant *)lineinfile)->d))-1;
}
Inst *
code(f)
	Inst f;
{
	Inst *oprogp=progp;
	if(progp>=&prog[NPROG])
		execerror("program too big", (char *)0);
	*progp++=f;
	return oprogp;
}
void
move(p,n)
	Inst *p;
	int n;
{
	Inst *tmp;
	tmp=progp-1;
	progp+=n;
	if(progp>=&prog[NPROG])
		execerror("program too big", (char *)0);
	for(;tmp>=p;tmp--){
		tmp[n]=tmp[0];
	}
}
void
execute(p)
	Inst *p;
{
	/* By definition in the construct (*(*pc++))(), the function should
	   see the increased pc (with this construct the function pointed to
	   in the current position of pc is called, as in (*pc[0])(), but the
	   pc passed to it is the next pc).
	   In fact not only does it makes sense, the ANSI C standard defines
	   the concept "sequence point" which states that after a sequence
	   point (and only after one) all side effects are guaranteed to have
	   happened. As it turns out one sequence point is after the function's
	   parameters AND THE EXPRESSION OF THE FUNCTION ITSELF are evaluated -
	   and that is before the function is actually called.
	   However, IBM AIX 2.3's compiler has a bug that causes the side
	   effect to happen only after the function call. This is the only
	   compiler I know with that bug.

	   I could have used the alternate expression in all cases, instead
	   of having a compilation flag, since the alternate expression
	   always works.
	*/
#ifndef COMPILER_BUG_INCR
	for(pc=p;*pc!=STOP && !returning && !whilebreak && !whilecont;)
		(*(*pc++))();
#else
	for(pc=p;*pc!=STOP && !returning && !whilebreak && !whilecont;){
		++pc;
		(*(pc[-1]))();
	}
#endif
}
/****************************************************************************/
/* Stack machine instructions. Pointers to the following functions can be
   are used as instructions for our stack machines. Note that all these
   functions must be of type SMI (stack machine instruction), since
   the pointer, Inst, is declared as a pointer to a function returning SMI.
   The size of this return value is very important in execute(), where the
   C compiler needs to prepare memory for the return value. (This importance
   is illustrated by the need of popandforget() instead of using using a
   pointer to pop() - which caused a serious and hard-to-find bug in hoc8).
   SMI is typically void.
*/
SMI
constpush()
{
	Datum d;
	d.val=((Constant *)*pc++)->d;
	push(d);
}

SMI
Set()
{
	switch((int)(*pc++)){
	case S_IGNOREUNDEF:
		ignoreundef=1;break;
	case -S_IGNOREUNDEF:
		ignoreundef=0;break;
	case S_TRACECALLS:
		prcalls=1;break;
	case -S_TRACECALLS:
		prcalls=0;break;
	case S_ALLNAMES:
		allnames=1;break;
	case -S_ALLNAMES:
		allnames=0;break;
	case S_ALL:
		fprintf(stderr,"%signoreundef\n%stracecalls\n%sallnames\n",
			ignoreundef?"":"no",prcalls?"":"no",allnames?"":"no");
#if YYDEBUG
		{	extern int yydebug;
			if(!yydebug)fprintf(stderr,"nodebug\n");
			else
				fprintf(stderr,"debug%d\n",yydebug);
		}
#endif
		break;
	case S_DEFAULT:
		prcalls=Sprcalls; ignoreundef=Signoreundef; allnames=Sallnames;
#if YYDEBUG
		{	extern int yydebug;
			yydebug=0;
		}
#endif
		break;
	case S_CHANGED:
		if(prcalls!=Sprcalls)
			fprintf(stderr,"%stracecalls\n",prcalls?"":"no");
		if(ignoreundef!=Signoreundef)
			fprintf(stderr,"%signoreundef\n",ignoreundef?"":"no");
		if(allnames!=Sallnames)
			fprintf(stderr,"%sallnames\n",allnames?"":"no");
#if YYDEBUG
		{	extern int yydebug;
			if(yydebug!=0)
				fprintf(stderr,"debug%d\n",yydebug);
		}
#endif
		break;
	}
}
SMI
varpush()
{
	Datum d;
	d.sym=(Symbol *)(*pc++);
	push(d);
}
SMI
whilecode()
{
	Datum d;
	Inst *savepc=pc;
	Inst *PA=savepc+(int)*(savepc);
	Inst *PB=savepc+2;
	int z;
	z=inwhile;
	execute(PB);
	d=pop();
	inwhile++;
	while(d.val){
		execute(PA);
		if(whilebreak){
			whilebreak--;
			break;
		} else if(returning){
			goto RETURNING;
		}
		whilecont=0;
		execute(PB);
		d=pop();
	}
	pc=savepc+(int)*(savepc+1);
RETURNING:
	inwhile=z;
}
SMI
forcode()
{
	Datum d;
	Inst *savepc=pc;
	int z;
	int B=((int)*(savepc+1));
	int C=((int)*(savepc+2));
	Inst *PB=savepc+B;
	Inst *PC=savepc+C;
	Inst *PD=savepc+((int)*(savepc+3));
	z=inwhile;
	inwhile=0;
	if((int)*(savepc)) execute(savepc+(int)*(savepc));
	if(returning)goto RETURNING;
	inwhile=z+1;
	if(B){
		execute(PB);
		d=pop();
	} else {
		d.val = 1.0;
	}
	while(d.val){
		execute(PD);
		if(whilebreak){
			whilebreak--;
			break;
		} else if(returning){
			goto RETURNING;
		}
		whilecont=0;
		if(C){
			execute(PC);
			if(whilebreak){
				whilebreak--;
				break;
			} else if(returning){
				goto RETURNING;
			}
			whilecont=0;
		}
		if(B){
			execute(PB);
			d=pop();
		}
	}
	pc=savepc+(int)*(savepc+4);
RETURNING:
	inwhile=z;
}	
SMI
ifcode()
{
	Datum d;
	Inst *savepc=pc;
	execute(savepc+3);
	d=pop();
	if (d.val)
		execute(savepc+(int)*(savepc));
	else if ((int)*(savepc+1))
		execute(savepc+(int)*(savepc+1));
	if(!returning)
		pc=savepc+(int)*(savepc+2);
}
SMI
conditional()
{
	Datum d;
	Inst *savepc=pc;
	execute(savepc+(int)*(savepc));
	d=pop();
	if(d.val)execute(savepc+3);
	else execute(savepc+(int)*(savepc+1));
	pc=savepc+(int)*(savepc+2);
}
SMI
call()
{
	Symbol *sp=(Symbol *)pc[0];
	int sinwhile;

	if((int)pc[2]!=sp->type)
		execerror(sp->name,"called after type changed");

	if(fp++>=&frame[NFRAME-1]){
		/* the fp--; must be done because execerror frees the locals
		    and fp must be in the frame. */
		fp--;
		execerror(sp->name,"call nested too deeply");
	}
	fp->sp=sp;
	fp->nargs=(int)pc[1];
	fp->retpc=pc+3;
	fp->argn=stackp-1;
	fp->locals=0;
	if(prcalls){
		int i,z;
		Datum *x,*y;
		y=stackp-1;
		z=fp-&frame[0];
		for(i=0;i<z;i++){
			putc('-',stderr);
		}
		fprintf(stderr,"%s(",fp->sp->name);
		for(x=stackp-(int)pc[1];x<=y;x++){
			fprintf(stderr,"%g",x->val);
			if(x!=y)putc(',',stderr);
		}
		putc(')',stderr);
		putc('\n',stderr);
	}
	sinwhile=inwhile;
	inwhile=0;
	execute((Inst *)sp->u.defn);
	inwhile=sinwhile;
	returning=0;
}
SMI
ret()
{
	int i;
	if(fp->locals){
		/* free all local variables of current function */
		/* same code as in execerror() */
		Local *tmp=fp->locals, *tmp1;
		while(tmp1=tmp){
			tmp=tmp1->nextlocal;
			if(!tmp1->nextdef->nextdef){
				/* next one is global/undef */
				tmp1->real->u.val=
					tmp1->nextdef->val;
				tmp1->real->type=
					tmp1->nextdef->p.type;
				FREELOCAL(tmp1->nextdef);
				FREELOCAL(tmp1);
				continue;
			}
			tmp1->real->u.localptr=tmp1->nextdef;
			FREELOCAL(tmp1);
		}
	}
	for(i=0;i<fp->nargs; i++)
		pop();
	pc=(Inst *)fp->retpc;
	--fp;
	returning=1;
}
/* makelocal: make some variable local, and assign it a value */
SMI
makelocal()
{
	Datum d1,d2;
	double tmp1;
	Local *tmp;

	d1=pop();	/* var to make local */
	d2=pop();	/* initial value of the local */

	switch(d1.sym->type){
	case UNDEF:	/* variable undefined */
		/* put the new local var */
		d1.sym->type=LOCAL;
		d1.sym->u.localptr=NEWLOCAL();
		d1.sym->u.localptr->val=d2.val;
		d1.sym->u.localptr->p.frame=fp;
		d1.sym->u.localptr->real=d1.sym;
		/* move the undef var to a Local structure */
		d1.sym->u.localptr->nextdef=NEWLOCAL();
		d1.sym->u.localptr->nextdef->real=d1.sym;
		d1.sym->u.localptr->nextdef->p.type=UNDEF;
		break;
	case VAR:	/* variable already defined as global */
		tmp1=d1.sym->u.val;
		/* put the new local var */
		d1.sym->type=LOCAL;
		d1.sym->u.localptr=NEWLOCAL();
		d1.sym->u.localptr->val=d2.val;
		d1.sym->u.localptr->p.frame=fp;
		d1.sym->u.localptr->real=d1.sym;
		/* move the global var to a Local structure */
		d1.sym->u.localptr->nextdef=NEWLOCAL();
		d1.sym->u.localptr->nextdef->val=tmp1;
		d1.sym->u.localptr->nextdef->real=d1.sym;
		d1.sym->u.localptr->nextdef->p.type=VAR;
		break;
	case LOCAL:	/* variable already defined as local */
		if(d1.sym->u.localptr->p.frame==fp){/* same local defined twice
						       in same function */
			d1.sym->u.localptr->val=d2.val;
			goto PUSH;
		}
		tmp=d1.sym->u.localptr;
		d1.sym->u.localptr=NEWLOCAL();
		d1.sym->u.localptr->val=d2.val;
		d1.sym->u.localptr->nextdef=tmp;
		d1.sym->u.localptr->p.frame=fp;
		d1.sym->u.localptr->real=d1.sym;
		break;
	default:	/* ERROR */
		execerror("attempt to make a local with a non-variable name",
			d1.sym->name);
		break;
	}
	/* put local on the locals-to-free list */
	tmp=fp->locals;
	fp->locals=d1.sym->u.localptr;
	d1.sym->u.localptr->nextlocal=tmp;
PUSH:
	push(d2);
}
SMI 
Break()
{
	if(inwhile)
		whilebreak++;
	else
		execerror("break","not in while");
}
SMI
Breakn()
{
	Datum d;
	d=pop();
	if(inwhile<whilebreak+d.val)
		execerror("break","level greater than loop level");
	whilebreak+=d.val;
}
SMI
cont()
{
	if(inwhile)
		whilecont=1;
	else
		execerror("continue", "not in while");
}
SMI
contn()
{
	Datum d;
	d=pop();
	if(inwhile<whilebreak+d.val)
		execerror("continue", "level greater than loop level");
	whilebreak+=d.val-1;
	whilecont=1;
}
SMI
Abort()
{
	if(fp==frame)
		execerror("program aborted",(char*)0);
	else
		execerror(fp->sp->name, "aborted");
}
/* end current file, even if it is stdin */
SMI
Doeof()
{
	execerror((char*)0,(char*)0);
}
SMI
funcret()
{
	Datum d;
	if(fp->sp->type==PROCEDURE)
		execerror(fp->sp->name, "(proc) returns value");
	d=pop();
	if(prcalls){
		int i,z;
		z=fp-&frame[0];
		for(i=0;i<z;i++){
			putc('-',stderr);
		}
		fprintf(stderr,"%s returns %g\n",fp->sp->name,d.val);
	}
	ret();
	push(d);
}
SMI
procret()
{
	if(fp->sp->type==FUNCTION)
		execerror(fp->sp->name, "(func) returns no value");
	if(prcalls){
		int i,z;
		z=fp-&frame[0];
		for(i=0;i<z;i++){
			putc('-',stderr);
		}
		fprintf(stderr,"%s returns\n",fp->sp->name);
	}
	ret();
}
SMI
arg()
{
	Datum d;
	/* the following complication is because a bug in unixpc C */
	register double *temp;
	temp = getarg ();
	d.val = *temp;
	push(d);
}
SMI
argcnt()
{
	Datum d;
	d.val = fp->nargs;
	push(d);
}
SMI
argassign()
{
#ifdef BUG1
	Datum d;
	register double *temp;
	temp = getarg();
	d=pop();
	push(d);
	*temp=d.val;
#else
	Datum d;
	push(d=pop());
	*getarg()=d.val;
#endif
}
SMI
argaddeq()
{
#ifdef BUG1
	register double *temp=getarg();
	Datum d;
	d=pop();
	*temp+=d.val;
	pushd(*temp);
#else
	Datum d;
	d=pop();
	pushd(*getarg()+=d.val);
#endif
}
SMI
argsubeq()
{
#ifdef BUG1
	register double *temp=getarg();
	Datum d;
	d=pop();
	*temp-=d.val;
	pushd(*temp);
#else
	Datum d;
	d=pop();
	pushd(*getarg()-=d.val);
#endif
}
SMI
argmuleq()
{
#ifdef BUG1
	register double *temp=getarg();
	Datum d;
	d=pop();;
	*temp*=d.val;
	pushd(*temp);
#else
	Datum d;
	d=pop();
	pushd(*getarg()*=d.val);
#endif
}
SMI
argdiveq()
{
#ifdef BUG1
	register double *temp=getarg();
	Datum d;
	d=pop();
	if(d.val==0.0)
		execerror("division by zero", (char *)0);
	*temp/=d.val;
	pushd(*temp);
#else
	Datum d;
	d=pop();
	if(d.val==0.0)
		execerror("division by zero", (char *)0);
	pushd(*getarg()/=d.val);
#endif
}
SMI
argpoweq()
{
#ifdef BUG1
	register double *temp=getarg();
	Datum d;
	d=pop();
	*temp=Pow(*temp,d.val);
	pushd(*temp);
#else
	register double *temp=getarg();
	Datum d;
	d=pop();
	pushd(*temp=Pow(*temp,d.val));
#endif
}
SMI
argmodeq()
{
#ifdef BUG1
	register double *temp=getarg();
	Datum d;
	d=pop();
	if(d.val==0.0)
		execerror("division by zero",(char *)0);
	*temp=fmod(*temp,d.val);
	pushd(*temp);
#else
	register double *temp=getarg();
	Datum d;
	d=pop();
	if(d.val==0.0)
		execerror("division by zero",(char *)0);
	pushd(*temp=fmod(*temp,d.val));
#endif
}
SMI
argpreinc()
{
#ifdef BUG1
	register double *temp=getarg();
	++(*temp);
	pushd(*temp);
#else
	pushd(++(*getarg()));
#endif
}
SMI
argpostinc()
{
#ifdef BUG1
	register double *temp=getarg();
	pushd(*temp);
	++(*temp);
#else
	pushd((*getarg())++);
#endif
}
SMI
argpredec()
{
#ifdef BUG1
	register double *temp=getarg();
	--(*temp);
	pushd(*temp);
#else
	pushd(--(*getarg()));
#endif
}
SMI
argpostdec()
{
#ifdef BUG1
	register double *temp=getarg();
	pushd(*temp);
	--(*temp);
#else
	pushd((*getarg())--);
#endif
}
SMI
bltin()
{
	Datum d;
	d=pop();
	d.val=(*(double (*)())*pc++)(d.val);
	push(d);
}
SMI
eval()
{
	Datum d;
	d=pop();
	switch(d.sym->type){
	case UNDEF:
		if(ignoreundef)
			d.val=0;
		else
			execerror(d.sym->name,"is used before defined");
		break;
	case VAR:
		d.val=d.sym->u.val;
		break;
	case LOCAL:
		if(d.sym->u.localptr->p.frame!=fp){
			/* found local from another function. look for the
			   global with the same name */
			/* ->nextdef because global can't be first */
			Local *x=d.sym->u.localptr->nextdef;
			while(x->nextdef)
				x=x->nextdef;
			if(x->p.type==UNDEF){
				if(ignoreundef){
					d.val=0;
				} else
					execerror(d.sym->name,"is used before defined");
			} else
				d.val=x->val;
		} else {
			d.val=d.sym->u.localptr->val;
		}
		break;	
	default:
		execerror("attempt to evaluate non-varible",d.sym->name);
		break;
	}
	push(d);
}
SMI
add()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	d1.val+=d2.val;
	push(d1);
}
SMI
sub()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	d1.val-=d2.val;
	push(d1);
}
SMI
mul()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	d1.val*=d2.val;
	push(d1);
}
SMI
Div()
{
	Datum d1,d2;
	d2=pop();
	if(d2.val==0.0)
		execerror("division by zero", (char *)0);
	d1=pop();
	d1.val/=d2.val;
	push(d1);
}
SMI
mod()
{	/* remainder */
	Datum d1,d2;
	d2=pop();
	if(d2.val==0.0)
		execerror("division by zero", (char *)0);
	d1=pop();
	d1.val=fmod(d1.val,d2.val);
	push(d1);
}
SMI
Mod()
{	/* modulo: like in math */
	Datum d1,d2;
	d2=pop();
	if(d2.val==0.0)
		execerror("division by zero", (char *)0);
	d1=pop();
	d1.val=fmod(d2.val+fmod(d1.val,d2.val),d2.val);
	push(d1);
}
SMI
negate()
{
	Datum d;
	d=pop();
	d.val= -d.val;
	push(d);
}
SMI
gt()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	d1.val=(double)(d1.val > d2.val);
	push(d1);
}
SMI
lt()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	d1.val=(double)(d1.val < d2.val);
	push(d1);
}
SMI
ge()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	d1.val=(double)(d1.val >= d2.val);
	push(d1);
}
SMI
le()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	d1.val=(double)(d1.val <= d2.val);
	push(d1);
}
SMI
eq()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	d1.val=(double)(d1.val == d2.val);
	push(d1);
}
#define ABS(x) ((x)<0 ? -(x) : (x))
SMI
approxeq()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	pushd((double)(ABS(d1.val-d2.val)<=getval(vpERROR)));
}
SMI
notapproxeq()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	pushd((double)(ABS(d1.val-d2.val)>=getval(vpERROR)));
}
SMI
ne()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	d1.val=(double)(d1.val != d2.val);
	push(d1);
}
SMI
and()
{
	Datum d;
	Inst *savepc=pc;
	execute(savepc+2);
	d=pop();
	if(d.val){
		execute(savepc+(int)*(savepc));
		d=pop();
	}
	pc=savepc+(int)*(savepc+1);
	/* and must return 1 on true and 0 on false, so we change
	   non-zero values to 1 and keep zero as 0 */
	d.val=!!d.val;
	push(d);
}
SMI
or()
{
	Datum d;
	Inst *savepc=pc;
	execute(savepc+2);
	d=pop();
	if(!d.val){
		execute(savepc+(int)*(savepc));
		d=pop();
	}
	pc=savepc+(int)*(savepc+1);
	/* or must return 1 on true and 0 on false, so we change
	   non-zero values to 1 and keep zero as 0 */
	d.val=!!d.val;
	push(d);
}
SMI
not()
{
	Datum d;
	d=pop();
	d.val=(double)(d.val == 0.0);
	push(d);
}
SMI
power()
{
	Datum d1,d2;
	d2=pop();
	d1=pop();
	d1.val=Pow(d1.val,d2.val);
	push(d1);
}
SMI
assign()
{
	Datum d1,d2;
	d1=pop();
	d2=pop();
	switch(d1.sym->type){
	case UNDEF:
		d1.sym->u.val=d2.val;
		d1.sym->type=VAR;
		break;
	case VAR:
		d1.sym->u.val=d2.val;
		break;
	case LOCAL:
		if(d1.sym->u.localptr->p.frame!=fp){
			/* found local from another function. look for the
			   global with the same name */
			/* ->nextdef because global can't be first */
			Local *x=d1.sym->u.localptr->nextdef;
			while(x->nextdef)
				x=x->nextdef;
			x->val=d2.val;
			x->p.type=VAR;
		} else {
			d1.sym->u.localptr->val=d2.val;
		}
		break;	
	default:
		execerror("assignment to non-varible",d1.sym->name);
		break;
	}
	push(d2);
}
SMI
makeconst()
{
	Datum d1,d2;
	d1=pop();
	d2=pop();
	switch(d1.sym->type){
	case UNDEF:
		d1.sym->u.Const=installd(d2.val);
		d1.sym->type=CONST;
		break;
	case CONST:
		d1.sym->u.Const=installd(d2.val);
		break;
	default:
		execerror("constant assignment to non-constant",d1.sym->name);
		break;
	}
	push(d2);
}
SMI
preinc()
{
#ifdef BUG1
	register double *temp=getvar();
	++(*temp);
	pushd(*temp);
#else
	pushd(++(*(getvar())));
#endif
}
SMI
postinc()
{
#ifdef BUG1
	register double *temp=getvar();
	pushd(*temp);
	++(*temp);
#else
	pushd((*(getvar()))++);
#endif
}
SMI
predec()
{
#ifdef BUG1
	register double *temp=getvar();
	--(*temp);
	pushd(*temp);
#else
	pushd(--(*(getvar())));
#endif
}
SMI
postdec()
{
#ifdef BUG1
	register double *temp=getvar();
	pushd(*temp);
	--(*temp);
#else
	pushd((*(getvar()))--);
#endif
}
SMI
addeq()
{
	register double *temp=getvar();
	Datum d;
	d=pop();
#ifdef BUG1
	*temp+=d.val;
	pushd(*temp);
#else
	pushd(*temp+=d.val);
#endif
}
SMI
subeq()
{
	register double *temp=getvar();
	Datum d;
	d=pop();
#ifdef BUG1
	*temp-=d.val;
	pushd(*temp);
#else
	pushd(*temp-=d.val);
#endif
}
SMI
muleq()
{
	register double *temp=getvar();
	Datum d;
	d=pop();
#ifdef BUG1
	*temp*=d.val;
	pushd(*temp);
#else
	pushd(*temp*=d.val);
#endif
}
SMI
diveq()
{
	register double *temp=getvar();
	Datum d;
	d=pop();
	if(d.val==0.0)
		execerror("division by zero", (char *)0);
#ifdef BUG1
	*temp/=d.val;
	pushd(*temp);
#else
	pushd(*temp/=d.val);
#endif
}
SMI
poweq()
{
	register double *temp=getvar();
	Datum d;
	d=pop();
#ifdef BUG1
	*temp=Pow(*temp,d.val);
	pushd(*temp);
#else
	pushd(*temp=Pow(*temp,d.val));
#endif
}
SMI
modeq()
{
	register double *temp=getvar();
	Datum d;
	d=pop();
	if(d.val==0.0)
	execerror("division by zero", (char *)0);
#ifdef BUG1
	*temp=fmod(*temp,d.val);
	pushd(*temp);
#else
	pushd(*temp=fmod(*temp,d.val));
#endif
}
double last;
SMI
print()
{
	Datum d;
	d=pop();
	printf("\t%.*g\n",(int)getval(vpDIGITS), last=d.val);
	fflush(stdout);
}
SMI
prexpr()
{
	Datum d;
	d=pop();
	printf("%.*g",(int)getval(vpDIGITS), d.val);
}
SMI
prstr()
{
	printf("%s", (char *)*pc++);
}
SMI
Chdir()
{
	char *s=(char *)*pc++;
	if(chdir(s)==(-1)) execerror("invalid directory",s);
}
SMI
doflush()
{
	fflush(stdout);
}
SMI
System()
{
	system((char *)*pc++);
}
SMI
Include()
{
	/* part of this code is similar to moreinput() */
	char *oldinfile;
	int oldlineno;
	FILE *oldfin;
	int oldnlstate,oldinteractive;
	jmp_buf oldbegin;

	oldinfile=infile;
	oldlineno=lineno;
	oldfin=fin;
	oldnlstate=nlstate;
	oldinteractive=interactive;
	/* because jmp_buf is an array (at least on a VAX it is an array
	   and I am not sure what it is on other computers), I use memcpy
	   to copy it, not assignment. 
	*/
	memcpy((char*)oldbegin,(char *)begin,sizeof(jmp_buf));

	interactive=0;
	infile=((char *)*pc++);
	if(strcmp(infile,"-")==0){
		fin=stdin;
		infile=0;
		/* this is an interactive hoc if we're taking input from
		   stdin, and stdin is connected to a terminal
		*/
		interactive=isatty(fileno(fin));
#ifndef ALWAYSPRVER
		if(interactive) versioninfo();
#endif
		goto OPENED;
	}
	if(infile[0]=='-'&&infile[1]=='l'){
		char *tmp;
#ifdef NOLIBS
		infile=oldinfile;
		execerror("libraries not available on this system",(char*)0);
#else
#ifndef LIBPREFIX
#define LIBPREFIX "/usr/lib/hoclibs/LIB"
#endif
		tmp=malloc(strlen(infile+2)+strlen(LIBPREFIX)+1);
		tmp[0]='\0';
		strcat(tmp,LIBPREFIX);
		strcat(tmp,infile+2);
		infile=tmp;
#endif
	}
	if((fin=fopen(infile,"r"))==NULL){
		char *saveinfile=infile;
		infile=oldinfile;
		fin=oldfin;
		nlstate=oldnlstate;
		interactive=oldinteractive;
		execerror("can't open",saveinfile);
	}
OPENED:
	lineno=1;
	run();	/* fatal if interrupted between this and the memcpy. */

	if(fin!=stdin)
		fclose(fin);
	
	infile=oldinfile;
	lineno=oldlineno;
	fin=oldfin;
	nlstate=oldnlstate;
	interactive=oldinteractive;
	/* again memcpy */
	memcpy(begin,oldbegin,sizeof(jmp_buf));; /*now it's ok to interrupt*/

	/* put STOP code in beggining of prog, because the call to run changed
	   it, and we want execute who called Include to find a STOP */
	pc=progbase;
	*pc=STOP;
}
SMI
varread()
{
	/* read: read number into variable. return:
		0 on EOF
		1 on success
		-1 on input of other than a string.
	   on error, don't change the value of variable.
	*/
	Datum d;
	int x;
	Symbol *var = (Symbol *)*pc++;
	double rv;		/* value read */
	switch(fscanf(stdin, "%lf", &rv)){
	case EOF:
		d.val = 0.0;
		break;
	case 0:
		warning("non-number read into", var->name);
		while((!isspace(x=getchar()))&&x!=EOF);
		d.val= -1.0;
		break;
	default:
		switch(var->type){
		case UNDEF:
			var->u.val=rv;
			var->type=VAR;
			break;
		case VAR:
			var->u.val=rv;
			break;
		case LOCAL:
			if(var->u.localptr->p.frame!=fp){
				/* found local from another function. look for the
				   global with the same name */
				/* ->nextdef because global can't be first */
				Local *x=var->u.localptr->nextdef;
				while(x->nextdef)
					x=x->nextdef;
				x->val=rv;
				x->p.type=VAR;
			} else {
				var->u.localptr->val=rv;
			}
			break;	
		default:
			execerror("value read into non-varible",var->name);
			break;
		}
	d.val=1.0;
	break;
	}
	push(d);
}
SMI
findtype()
{
	/* findtype: return the type of the item. */
	switch(((Symbol *)*pc++)->type){
	case UNDEF:	pushd(0.); break;
	case VAR:	pushd(1.); break;
	case FUNCTION:	pushd(2.); break;
	case PROCEDURE:	pushd(3.); break;
	case LOCAL:	pushd(4.); break;
	case BLTIN:	pushd(5.); break;
	default:	pushd(6.); break;
	}
}
SMI
Free()
{
	printf("recursion: %d (+%d)\t",fp-&frame[0],&frame[NFRAME-1]-fp);
	printf("stack: %d (+%d)\n",stackp-&stack[0],&stack[NSTACK]-stackp);
}
SMI
exitn()
{
	Datum d;
	d=pop();
	exit((int)((double)d.val));
}
SMI
exit0(){
	exit(0);
}
static void
sortnames(s)
	Symbol *s;
{
	if(s->right)sortnames(s->right);
	if(!(s->name[0]=='_') || allnames)
	switch(s->type){
	case UNDEF:	break;
	case VAR:	fprintf(stderr,"%s = %.15g\n", s->name, s->u.val);
			break;
	case CONST:	fprintf(stderr,"%s = %.15g (constant)\n", s->name,
				s->u.Const->d); break;
	case FUNCTION:	fprintf(stderr,"%s: function\n", s->name); break;
	case PROCEDURE:	fprintf(stderr,"%s: procedure\n", s->name); break;
	case BLTIN:	fprintf(stderr,"%s: builtin function\n", s->name); break;
	case LOCAL:	if(s->u.localptr->p.frame!=fp){
				/* founf local from another function. look for the
				    global with the same name */
				/*->nextdef because global can't be first*/
				Local *x=s->u.localptr->nextdef;
				while(x->nextdef)
					x=x->nextdef;
				if(x->p.type==UNDEF)
					fprintf(stderr,"%s: undefined for this func/proc\n",
						s->name);
				else
					fprintf(stderr,"%s = %.15g (global for this func/proc)\n",
						s->name, x->val);
			} else {
				fprintf(stderr,"%s = %.15g (local for this func/proc)\n",
					s->name, s->u.localptr->val);
			}
			break;
	default:	fprintf(stderr,"%s: keyword\n", s->name); break;
	}
	if(s->left)sortnames(s->left);
}
SMI
printnames()
{
	sortnames(symstart());
}
SMI
printversion()
{
	int i=0;
	fprintf(stderr,"\nNYH HOC, by Brian Kernighan, Rob Pike, and Nadav Har'El.\n");
	fprintf(stderr,"Copyright (C) 1995 AT&T, (C) 1986-1997, 2007 Nadav Har'El.\n");
	while(version_array[i])
		fprintf(stderr,"%s\n",version_array[i++]+4);
	fprintf(stderr,"\n");
}
SMI
popandforget(){
	pop();
}
/****************************************************************************/
/* "Builtins", called by bltin(). These functions must all take one argument
   and return a double.
   More builtins are in math.c.
*/
double
Arg(num)
	double num;
{
	int nargs;
	nargs=(int)num;
	if(!(fp-frame))
		execerror("arg", "used outside definition");
	if(nargs<1)
		execerror(fp->sp->name, "no such argument");
	if(nargs>fp->nargs)
		execerror(fp->sp->name, "not enough arguments");
	return fp->argn[nargs-fp->nargs].val;
}
/****************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1