/*
	eval.c	-	Bill Nickerson, 1988

	Evaluates a sequence of p-code instructions representing an
	expression as a function of "fnvar".
	If an error has occurred, errno will be a non-zero value, otherwise
	it will be zero.

	Export: evaluate
	Static: none
*/

#include <math.h>
#include "stack.h"
#include "kwords.h"

/*
	Imported functions and variables.
*/
extern void Push(), MakeAStack();
extern anElement Pop();

extern int errno;

/*
	pcode	- array of p-code for interpretation. End delimited by STOPt.
	fnvar	- variable that this p-code is a function of. ie pcode(fnvar)

	If successful, returns result of evaluation and errno will be zero.
	If some sort of arithmetic error occurred (like div by zero), then
	zero is returned and errno will be non-zero.

	NOTE: This function does absolutely no error checking and assumes
	that the p-code array is a valid function.
*/
double evaluate( pcode, fnvar )
double pcode[];
double fnvar;
{
	double a1, a2, tmp;
	int pc = 0;

	errno = 0;
	MakeAStack();

	while ((int)pcode[pc] != STOPt)
	{
		if (errno != 0)
			break;

		switch ((int)pcode[pc++])
		{
		case PLUSt:	Push(Pop() + Pop());
				break;
		case MINUSt:	a1 = Pop();
				Push(Pop() - a1);
				break;
		case TIMESt:	Push(Pop() * Pop());
				break;
		case DIVt:	a1 = Pop();
				Push(Pop() / a1);
				break;
		case MODt:	a1 = Pop();
				Push(fmod(Pop(), a1));
				break;
		case ACOSt:	Push(acos(Pop()));
				break;
		case COSHt:	Push(cosh(Pop()));
				break;
		case COSt:	Push(cos(Pop()));
				break;
		case ASINt:	Push(asin(Pop()));
				break;
		case SINHt:	Push(sinh(Pop()));
				break;
		case SINt:	Push(sin(Pop()));
				break;
		case ATANt:	Push(atan(Pop()));
				break;
		case TANHt:	Push(tanh(Pop()));
				break;
		case TANt:	Push(tan(Pop()));
				break;
		case CEILt:	Push(ceil(Pop()));
				break;
		case FLOORt:	Push(floor(Pop()));
				break;
		case EXPt:	Push(exp(Pop()));
				break;
		case LNt:	Push(log(Pop()));
				break;
		case LOGt:	Push(log10(Pop()));
				break;
		case SQRTt:	Push(sqrt(Pop()));
				break;
		case POWERt:	a1 = Pop();
				Push(pow(Pop(), a1));
				break;
		case ABSt:	Push(fabs(Pop()));
				break;
		case FACTt:	a1 = floor(Pop());
				for (tmp = 1.0; a1 > 1.0; a1 -= 1.0)
					tmp *= a1;
				Push(tmp);
				break;
		case SUMt:	a2 = floor(Pop());
				a1 = floor(Pop());
				if (a1 > a2)
				{
					tmp = a1;
					a1 = a2;
					a2 = tmp;
				}
				for (tmp = 0.0; a1 <= a2; a1 += 1.0)
					tmp += a1;
				Push(tmp);
				break;
		case NEGt:	Push(-1 * Pop());
				break;
		case PUSHt:	Push(pcode[pc++]);
				break;
		case FNVARt:	Push(fnvar);
				break;
		default:	break;
		}
	}

	if (errno == 0)
		tmp = Pop();
	else	tmp = 0.0;

	return(tmp);
}
