/* Below is the worlds sleaziest little numerical expression
 * evaluator. Used to do only ints, now does doubles as well.
 *
 * This file is copyright 2002 Jim Kent, but license is hereby
 * granted for all use - public, private or commercial. */

#include "common.h"
#include "kxTok.h"


static struct kxTok *tok;

#define nextTok() (tok = tok->next)

#ifdef DEBUG
static void nextTok()
/* Advance to next token. */
{
if (tok == NULL)
    printf("(null)");
else
    {
    printf("'%s' -> ", tok->string);
    if (tok->next == NULL)
        printf("(null)\n");
    else
        printf("'%s'\n", tok->next->string);
    }
tok = tok->next;
}
#endif /* DEBUG */


static double expression();
/* Forward declaration of main expression handler. */

static double number()
/* Return number. */
{
double val;
if (tok == NULL)
    errAbort("Parse error in numerical expression");
if (!isdigit(tok->string[0]))
    errAbort("Expecting number, got %s", tok->string);
val = atof(tok->string);
nextTok();
return val;
}

static double atom()
/* Return parenthetical expression or number. */
{
double val;
if (tok->type == kxtOpenParen)
    {
    nextTok();
    val = expression();
    if (tok->type == kxtCloseParen)
	{
        nextTok();
	return val;
	}
    else
	{
        errAbort("Unmatched parenthesis");
	return 0;
	}
    }
else
    return number();
}


static double uMinus()
/* Unary minus. */
{
double val;
if (tok->type == kxtSub)
    {
    nextTok();
    val = -atom();
    return val;
    }
else
    return atom();
}

static double mulDiv()
/* Multiplication or division. */
{
double val = uMinus();
for (;;)
    {
    if (tok->type == kxtMul)
	{
	nextTok();
	val *= uMinus();
	}
    else if (tok->type == kxtDiv)
	{
	nextTok();
	val /= uMinus();
	}
    else
        break;
    }
return val;
}

static double addSub()
/* Addition or subtraction. */
{
double val;
val = mulDiv();
for (;;)
    {
    if (tok->type == kxtAdd)
	{
	nextTok();
	val += mulDiv();
	}
    else if (tok->type == kxtSub)
	{
	nextTok();
	val -= mulDiv();
	}
    else
        break;
    }
return val;
}

static double expression()
/* Wraps around lowest level of expression. */
{
return addSub();
}

double doubleExp(char *text)
/* Convert text to double expression and evaluate. */
{
double val;
struct kxTok *tokList = tok = kxTokenize(text, FALSE);
val = expression();
slFreeList(&tokList);
return val;
}

int intExp(char *text)
/* Convert text to int expression and evaluate. */
{
return cround(doubleExp(text));
}
