#include "c.h"
#include "y.tab.h"
#include <stdio.h>

#define NSTACK	256
static	Datum	stack[NSTACK];	/* the stack */
static	Datum	*stackp;	/* next free spot on stack */

#define NPROG	2000
Inst	prog[NPROG];	/* the machine */
Inst	*progp;		/* next free spot for code generation */
Inst	*pc;		/* program counter during execution */
Inst	*progbase = prog; /* start of current subprogram */

initcode()	/* initialize for code generation */
{
	progp = progbase;
	stackp = stack;
}

push(d)		/* push d onto stack */
	Datum d;
{
	if (stackp >= &stack[NSTACK])
		execerror("stack overflow", (char *) 0);
	*stackp++ = d;
}

Datum top()	/* pop and return top elem from stack */
{
	if (stackp <= stack)
		execerror("stack underflow", (char *) 0);
	return *--stackp;
}

pop()	/* pop and return top elem from stack */
{
	if (stackp <= stack)
		execerror("stack underflow", (char *) 0);
	*--stackp;
}

branch() 	/* unconditional branch */
{
pc = *((Inst **)(pc));
}

cbranch()	/* conditional branch */
{
	Datum d;
	d = top();
	if (d.val) pc++;		/* if condition true, do not branch */
	else pc = *((Inst **)(pc));	/* if condition false, branch */
}

Inst *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++))();
}

constpush()	/* 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);
}

add()
{
	Datum d1,d2;
	d2 = top();
	d1 = top();
	d1.val += d2.val;
	push(d1);
}

sub()
{
	Datum d1,d2;
	d2 = top();
	d1 = top();
	d1.val -= d2.val;
	push(d1);
}

mul()
{
	Datum d1,d2;
	d2 = top();
 	d1 = top();
	d1.val *= d2.val;
	push(d1);
}

divv()
{
	Datum d1,d2;
	d2 = top();
	if (d2.val == 0.0)
		execerror("division by zero", (char *) 0);
	d1 = top();
	d1.val /= d2.val;
	push(d1);
}

negate()
{
	Datum d;
	d = top();
	d.val = -d.val;
	push(d);
}

power()
{
	Datum d1,d2;
	extern double Pow();
	d2 = top();
	d1 = top();
	d1.val = Pow(d1.val, d2.val);
	push(d1);
}

gt()
{
	Datum d1,d2;
	d2 = top();
	d1 = top();
	d1.val = (double)(d1.val > d2.val);
	push(d1);
}

lt()
{
	Datum d1,d2;
	d2 = top();
	d1 = top();
	d1.val = (double)(d1.val < d2.val);
	push(d1);
}

ge()
{
	Datum d1,d2;
	d2 = top();
	d1 = top();
	d1.val = (double)(d1.val >= d2.val);
	push(d1);
}

le()
{
	Datum d1,d2;
	d2 = top();
	d1 = top();
	d1.val = (double)(d1.val <= d2.val);
	push(d1);
}

ne()
{
	Datum d1,d2;
	d2 = top();
	d1 = top();
	d1.val = (double)(d1.val != d2.val);
	push(d1);
}

eq()
{
	Datum d1,d2;
	d2 = top();
	d1 = top();
	d1.val = (double)(d1.val == d2.val);
	push(d1);
}

eval()		/* evaluate variable on stack */
{
	Datum d;
	d = top();
	if (d.sym->type != ID && d.sym->type != UNDEF)
		execerror("attempt to evaluate non-variable", d.sym->name);
	d.val = d.sym->u.val;
	push(d);
}

assign()	/* assign top value to next value */
{
	Datum d1, d2;
	d1 = top();
	d2 = top();
	if (d1.sym->type != ID && d1.sym->type != UNDEF)
		execerror("assignment to non-variable",
			d1.sym->name);
	d1.sym->u.val = d2.val;
	d1.sym->type = ID;
}

prexpr()	/* print numeric value */
{
	Datum d;
	extern FILE *out;
	d = top();
	printf("%.8g\n", d.val);
	fprintf(out, "%.8g\n", d.val);
}

varread()	/* read into variable */
{
	Datum d;
	extern FILE *in;
	Symbol *var = (Symbol *) *pc++;
	switch (fscanf(in, "%lf", &var->u.val)) {
	case EOF:
		d.val = var->u.val = 0.0;
		break;
	case 0:
		execerror("non-number read into", var->name);
		break;
	default:
		d.val = 1.0;
		break;
	}
	var->type = ID;
	push(d);
}

bltin()		/* evaluate built-in on top of stack */
{
	Datum d;
	d = top();
	d.val = (*(double(*)())(*pc++))(d.val);
	push(d);
}

