/* tokeniser.c
 * geg, a GTK+ Equation Grapher
 * David Bryant 1998
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include <string.h>
#include <math.h>

#include "tokeniser.h"
#include "localfunctions.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif /* M_PI */

#ifndef M_E
#define M_E 2.7182818284590452354
#endif /* M_E */

#define NUMBER(c) (((c) >= '0') && ((c) <= '9'))

typedef struct {
  char *string;		/* the identifier string in the equation */
  gint type;		/* eg NUM, FUN */
  double value;		/* used if it is a number or a constant */
  Func func;		/* the address of the unary function to call */
} token_db;

static gint compare(token_db *a, gchar *b);

/* these must be in descending order of string length          */
static token_db tokens[] =
{ { "arcsin", FUN, 0,		asin	},
  { "arccos", FUN, 0,		acos	},
  { "arctan", FUN, 0,		atan	},
  { "arccsc", FUN, 0,		l_acsc	},
  { "arcsec", FUN, 0,		l_asec	},
  { "arccot", FUN, 0,		l_acot	},
  { "asin",   FUN, 0,		asin	},
  { "acos",   FUN, 0,		acos	},
  { "atan",   FUN, 0,		atan	},
  { "acsc",   FUN, 0,		l_acsc	},
  { "asec",   FUN, 0,		l_asec	},
  { "acot",   FUN, 0,		l_acot	},
  { "sinc",   FUN, 0, 		l_sinc	},
  { "sinh",   FUN, 0,		sinh	},
  { "cosh",   FUN, 0,		cosh	},
  { "tanh",   FUN, 0,		tanh	},
  { "cbrt",   FUN, 0,		l_cbrt	},
  { "sqrt",   FUN, 0,		sqrt	},
  { "sin",    FUN, 0,		sin,	},
  { "cos",    FUN, 0,		cos,	},
  { "tan",    FUN, 0,		tan,	},
  { "csc",    FUN, 0,		l_csc,	},
  { "sec",    FUN, 0,		l_sec,	},
  { "cot",    FUN, 0,		l_cot,	},
  { "log",    FUN, 0,		log10	},
  { "abs",    FUN, 0,		fabs	},
  { "ln",     FUN, 0,		log	},
  { "pi",     NUM, M_PI,	NULL	},
  { "u",      FUN, 0,		l_u	},
  { "e",      NUM, M_E,		NULL	},
  { "+",      ADD, 0,		NULL	},
  { "-",      SUB, 0,		NULL	},
  { "*",      MUL, 0,		NULL	},
  { "/",      DIV, 0,		NULL	},
  { "^",      POW, 0,		NULL	},
  { "[",      LSB, 0,		NULL	},
  { "]",      RSB, 0,		NULL	},
  { "{",      LCB, 0,		NULL	},
  { "}",      RCB, 0,		NULL	},
  { "(",      LB,  0,		NULL	},
  { ")",      RB,  0,		NULL	},
  { "x",      X,   0,		NULL	}, 
  { "",       EOE, 0,		NULL	} };

/* compare, */
static gint
compare(token_db *a, gchar *b)
{
  if((*a->string == '\0') ^ (*b == '\0'))
    return(0);
  else
    return(!strncasecmp(a->string, b, strlen(a->string)));
}

/* make_token_list, this function builds a token list out of the equation   
 */
token_list *
make_token_list(gchar *equation)
{
  gint i;
  token_list *list;

  list = (token_list *)g_new(token_list, 1);

  /* skip any whitespace */
  while(*equation == ' ')
    equation++;

  /* check for the token in the token database */
  for(i = 0; i < (sizeof(tokens) / sizeof(token_db)); i++) {
    if(compare(&tokens[i], equation))
    {
      list->type = tokens[i].type;
      list->value = tokens[i].value;
      list->func = tokens[i].func;
      equation += strlen(tokens[i].string);
      if(list->type != EOE)
        list->next = make_token_list(equation);
      else
        list->next = NULL;
      return(list);
    }
  }

  /* if it wasn't in the token database, it's either a number, or the token */
  /* can't be recognised and is therefore an error */
  if(NUMBER(*equation)) {
    list->type = NUM;
    list->value = 0;
    while(NUMBER(*equation)) {
      list->value = list->value * 10 +  *equation++ - '0';
    }
    if(*equation == '.') {	/* a real number */
      equation++;
      i = 0;		/* use i to count the decimal places */
      while(NUMBER(*equation))
        list->value += (*equation++ - '0') * pow(10, --i);
    }
  list->next = make_token_list(equation);
  return(list);
  }
  else {	/* it's an error .. */
    list->type = ERROR;
    list->value = 0;
    list->next = NULL;
    return(list);
  }
}


/* free_list, a recursive list freeing algorithm  
 */
void free_list(token_list *list)
{
  if(list->next != NULL)
    free_list(list->next);
  g_free(list);
}


syntax highlighted by Code2HTML, v. 0.9.1