
/**************************************************************************
 *                                                                        *
 * Copyright (c) 1996 Michael Richmond and Richard Treffers               *
 *                                                                        *
 *                    This software may be copied and distributed for     *
 *                    educational, research and not for profit services   *
 *                    provided that this copyright and statement are      *
 *                    included in all such copies.                        *
 *                                                                        *
 **************************************************************************/


/*	
	string evaluator
	returns value of arithmetic expression possible
	involving symbol table variables
	trig functions take arguments in radians, not degrees 

	7/7/94 - isdelim changed to take an int instead of char
*/


#include <math.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "pcvista.h"

#ifdef PROTO
static double eval(char *);
static double getval(char *);
static int isdelim(int);
static void left_shift(char *,int);
#else
static double eval();
static double getval();
static int isdelim();
static void left_shift();
#endif

static int paren_level, call_level;

double 
evaluate(s)
char *s;
{
	char buf[NBUF];

	strcpy(buf, s);
	paren_level = 0;
	call_level = -1;
	return(eval(buf));
}

static double 
eval(s)
char *s;
{
	int done = 0, delim, entrance_level;
	double rval, temp;
	
	call_level++;
	entrance_level = paren_level;
	rval = getval(s);
	
	do {
		delim = s[0];
		switch (delim) {
			case 0:
				done = 1;
				break;
			case '+':
				left_shift(s, 1);
				rval += eval(s);
				break;
			case '-':
				left_shift(s, 1);
				rval -= eval(s);
				break;
			case '*':
				left_shift(s, 1);
				rval *= getval(s);
				break;
			case '/':
				left_shift(s, 1);
				temp = getval(s);
				if (temp == 0.0) {
					puts("divide by zero\n");
					exit(1);
				}
				rval /= temp;
				break;
			case ')':
				if (call_level == entrance_level) {
					left_shift(s, 1);
					paren_level--;
				}
				done = 1;
				break;
			default:
				printf("unknown case in eval '%c' %d", delim, delim);
				exit(1);
		}		
	} while(!done);
	call_level--;
	return(rval);
}		


static int
isdelim(c)
int c;
{
	int rval = 0;

	switch(c) {
		case 0:
		case '+':
		case '-':
		case '*':
		case '/':
		case ')':
			rval = 1;
	}
	return(rval);
}

static double 
getval(s)			/* returns value of first token in string s */
char *s;			/* advances the string to the next delimiter */	
{
	int loc, delim;
	double temp;
	char *gotit;

	if (s[0] == '(') {
		paren_level++;
		left_shift(s, 1);
		temp = eval(s);
		return(temp);
	}
	if(strncmp(s, "sqrt(", 5) == 0) {
		left_shift(s, 5);
		paren_level++;
		return(sqrt(eval(s)));
	}
	if(strncmp(s, "sin(", 4) == 0) {
		left_shift(s, 4);
		paren_level++;
		return(sin(eval(s)));
	}
	if(strncmp(s, "cos(", 4) == 0) {
		left_shift(s, 4);
		paren_level++;
		return(cos(eval(s)));
	}
	if(strncmp(s, "tan(", 4) == 0) {
		left_shift(s, 4);
		paren_level++;
		return(tan(eval(s)));
	}
	if(strncmp(s, "exp(", 4) == 0) {
		left_shift(s, 4);
		paren_level++;
		return(exp(eval(s)));
	}
	if(strncmp(s, "log(", 4) == 0) {		/* log base e */
		left_shift(s, 4);
		paren_level++;
		return(log(eval(s)));
	}
	if(strncmp(s, "log10(", 6) == 0) {		/* log base 10 */
		left_shift(s, 6);
		paren_level++;
		return(log10(eval(s)));
	}


	for (loc = 0; ; loc++)		/* find position of delimiter */
		if (isdelim(s[loc]))
			break;
	delim = s[loc];					/* store delimiter value */
	s[loc] = 0;	
	if ((gotit = getsym(s)) != NULL)		/* get symbol if available */
		temp = evaluate(gotit);
	else
		temp = atof(s);				/* evaluate value */
	s[loc] = (char) delim;
	left_shift(s, loc);
	return(temp);
}

static void 
left_shift(s, n)		/* left shifts string n characters */	
char *s;
int n;
{
	int i = 0;

	for (i = 0; s[n]; i++, n++)
		s[i] = s[n];
	s[i] = 0;
}

