/* The lexical analyser */
#include "evalint.h"
#include "symbol.h"
#include <ctype.h>
#include <string.h>
#include <math.h>
#include "support.h"

/* For predefined constants, eg pi, e */
/* A non-ansi construction  ... */
#define SYMCST(cst, val) if (strcmp(symbuf, "cst") == 0) { symbol.type = cste; symbol.cte = (val); }

static char *expr;
struct _value symbol;
enum symbol_type symbol_type;

/* Set string to analyse */
void init_symbol(_expr)
char *_expr;
{
    expr = _expr;
}

/* convert an integer string to a double */
/* returns nb of chars converted */
static int cvt_integer(_str, nb)
char *_str;
double *nb;
{
    register char *str = _str;

    *nb = 0.0;

    while (isdigit(*str)) *nb = 10.0 * *nb + (double)(*str++ - '0');

    return(str - _str);
}

/* cvt a double to a nb */
static int cvt_double(_str, nb)
char *_str;
double *nb;
{
    double val, dec, exponent;
    register int l;
    register char *str = _str;

    if ((l = cvt_integer(str, &val)) == 0) return(0);
    else
    {
	str += l;

	/* fractional part */
	if (*str == '.')
	{
	    str++;
	    l = cvt_integer(str, &dec);
	    if (l)
	    {
		str += l;
		val += dec * pow(10.0, (double)-l);
	    }
	}
	/* exponent */
	if (toupper(*str) == 'E')
	{
	    str++;
	    if ((l = cvt_integer(str, &exponent)) == 0) return(0);
	    else
	    {
		str += l;
		val *= pow(10.0, exponent);
	    }
	}
	*nb = val;
	return(str - _str);
    }
}

/* extract next symbol as a number */
static void advance_number()
{
    double nb;
    register int l;

    if ((l = cvt_double(expr, &nb)) == 0)
    {
	symbol_type = s_error;
    }
    else
    {
	symbol_type = s_value;
	expr += l;
	symbol.type = cste;
	symbol.cte = nb;
    }
}

/* next symbol is a keyword */
static void advance_var()
{
    static char symbuf[100];
    register onefunc *f;

    symbol_type = s_value;

    expr = stpsym(expr, symbuf, 100);

    strlwr(symbuf);

    f = locate_name(symbuf);
    if (f) /* this is a predefined function */
    {
	symbol.type = fonc;
	symbol.fn.func = f->func;
    }
    else SYMCST(pi, PI) /* check for pi */
    else SYMCST(e, E)  /* check for e */
    else /* it's a variable */
    {
	symbol.type = var;
	symbol.vr.name = symbuf;
    }
}

/* Read next symbol */
void advance()
{
    expr = stpblk(expr); /* skip blanks */

    switch (*expr) {
	case '\0':
	    symbol_type = s_none;
	    break;
	case '(':
	    symbol_type = s_left;
	    expr++;
	    break;
	case ')':
	    symbol_type = s_right;
	    expr++;
	    break;
	case '+':
	    symbol_type = s_value;
	    symbol.type = op;
	    symbol.opr.op = plus;
	    expr++;
	    break;
	case '-':
	    symbol_type = s_value;
	    symbol.type = op;
	    symbol.opr.op = minus;
	    expr++;
	    break;
	case '*':
	    symbol_type = s_value;
	    symbol.type = op;
	    symbol.opr.op = times;
	    expr++;
	    break;
	case '/':
	    symbol_type = s_value;
	    symbol.type = op;
	    symbol.opr.op = divide;
	    expr++;
	    break;
	case '^':
	    symbol_type = s_value;
	    symbol.type = op;
	    symbol.opr.op = power;
	    expr++;
	    break;
	default:
	    if (isdigit(*expr)) advance_number();
	    else if (isalpha(*expr)) advance_var();
	    else symbol_type = s_error;
	    break;
    }
}

