/* 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 */

#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[] =
{ { "tgt_right",VAR|(29<<8),0,	NULL 	},  
  { "tgt_left", VAR|(28<<8),0,	NULL 	},
  { "der_step", VAR|(27<<8),0,	NULL 	},
  { "int_step", VAR|(26<<8),0,	NULL 	},  
  { "arcsin", FUN|(255<<8), 0,	asin	},
  { "arccos", FUN|(255<<8), 0,	acos	},
  { "arctan", FUN|(255<<8), 0,	atan	},
  { "arccsc", FUN|(255<<8), 0,	l_acsc	},
  { "arcsec", FUN|(255<<8), 0,	l_asec	},
  { "arccot", FUN|(255<<8), 0,	l_acot	},
  { "cotan",  FUN|(255<<8), 0,	l_cot,	},
  { "floor",  FUN|(255<<8), 0,	l_floor	},  
  { "gamma",  FUN|(255<<8), 0,	tgamma	},
  { "color",  VAR|(37<<8), 0,	NULL	},
  { "ptrad",  VAR|(38<<8), 0,	NULL	},
  { "polar",  FUN|(253<<8), 0,	NULL	},
  { "param",  FUN|(252<<8), 0,	NULL	},  
  { "sequ",   FUN|(251<<8), 0,	NULL	},
  { "cart",   FUN|(254<<8), 0,	NULL	},
  { "amin",   VAR|(39<<8), 0,	NULL	},
  { "amax",   VAR|(40<<8), 0,	NULL	},
  { "nmin",   VAR|(41<<8), 0,	NULL	},
  { "tmin",   VAR|(41<<8), 0,	NULL	},
  { "nmax",   VAR|(42<<8), 0,	NULL	},  
  { "tmax",   VAR|(42<<8), 0,	NULL	},  
  { "asin",   FUN|(255<<8), 0,	asin	},
  { "acos",   FUN|(255<<8), 0,	acos	},
  { "atan",   FUN|(255<<8), 0,	atan	},
  { "acsc",   FUN|(255<<8), 0,	l_acsc	},
  { "asec",   FUN|(255<<8), 0,	l_asec	},
  { "acot",   FUN|(255<<8), 0,	l_acot	},
  { "sinc",   FUN|(255<<8), 0,	l_sinc	},
  { "sinh",   FUN|(255<<8), 0,	sinh	},
  { "cosh",   FUN|(255<<8), 0,	cosh	},
  { "tanh",   FUN|(255<<8), 0,	tanh	},
  { "cbrt",   FUN|(255<<8), 0,	cbrt	},
  { "sqrt",   FUN|(255<<8), 0,	sqrt	},
  { "char",   FUN|(255<<8), 0,	l_char	},
  { "clip",   FUN|(255<<8), 0,	l_clip	},  
  { "sign",   FUN|(255<<8), 0,	l_sign	},
  { "cut",    FUN|(255<<8), 0,	l_cut	},
  { "int",    INT, 0,		NULL	},   
  { "tgt",    TGT, 0,		NULL	}, 
  { "nan",    NUM, NAN,		NULL	},
  { "out",    NUM, NAN,		NULL	},   /* result, index 44 */
  { "gap",    VAR|(35<<8), 0,	NULL	},  
  { "sin",    FUN|(255<<8), 0,	sin,	},
  { "cos",    FUN|(255<<8), 0,	cos,	},
  { "tan",    FUN|(255<<8), 0,	tan,	},
  { "csc",    FUN|(255<<8), 0,	l_csc	},
  { "sec",    FUN|(255<<8), 0,	l_sec	},
  { "cot",    FUN|(255<<8), 0,	l_cot	},
  { "exp",    FUN|(255<<8), 0,	exp	},
  { "phi",    FUN|(255<<8), 0,	l_phi	},  
  { "log",    FUN|(255<<8), 0,	log10	},  
  { "abs",    FUN|(255<<8), 0,	fabs	},
  { "ln",     FUN|(255<<8), 0,	log	},
  { "lw",     VAR|(36<<8),0,	NULL 	},  
  { "pi",     NUM, M_PI,	NULL	},
  { "fa",     FUN|(0<<8), 0,	NULL	},
  { "fb",     FUN|(1<<8), 0,	NULL 	},
  { "fc",     FUN|(2<<8), 0,	NULL 	},
  { "fd",     FUN|(3<<8), 0,	NULL 	},
  { "fe",     FUN|(4<<8), 0,	NULL 	},
  { "ff",     FUN|(5<<8), 0,	NULL 	},  
  { "fg",     FUN|(6<<8), 0,	NULL 	},
  { "fh",     FUN|(7<<8), 0,	NULL 	},
  { "fi",     FUN|(8<<8), 0,	NULL 	},
  { "fj",     FUN|(9<<8), 0,	NULL 	},
  { "fk",     FUN|(10<<8), 0,	NULL 	},
  { "fl",     FUN|(11<<8), 0,	NULL 	},
  { "fm",     FUN|(12<<8), 0,	NULL 	},
  { "fn",     FUN|(13<<8), 0,	NULL 	},
  { "fo",     FUN|(14<<8), 0,	NULL 	},
  { "fp",     FUN|(15<<8), 0,	NULL 	},
  { "fq",     FUN|(16<<8), 0,	NULL 	},
  { "fr",     FUN|(17<<8), 0,	NULL 	},
  { "fs",     FUN|(18<<8), 0,	NULL 	},
  { "ft",     FUN|(19<<8), 0,	NULL 	},
  { "fu",     FUN|(20<<8), 0,	NULL 	},
  { "fv",     FUN|(21<<8), 0,	NULL 	},
  { "fw",     FUN|(22<<8), 0,	NULL 	},
  { "fx",     FUN|(23<<8), 0,	NULL 	},
  { "fy",     FUN|(24<<8), 0,	NULL 	},
  { "fz",     FUN|(25<<8), 0,	NULL 	},
  { ";",      SEP, 0,		NULL	},
  { "=",      EQU, 0,		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	},
  { "?",      ASK, 0,		NULL	},
  { "#",      COM, 0,		NULL	},  
  { "&",      DEF, 0,		NULL	},
  { "'",      DER, 0,		NULL	},
  { "_",      RES, 0,		NULL	},    
  { "a",      VAR|(0<<8), 0,	NULL	},
  { "b",      VAR|(1<<8), 0,	NULL	},
  { "c",      VAR|(2<<8), 0,	NULL	},
  { "d",      VAR|(3<<8), 0,	NULL	},
  { "e",      VAR|(4<<8), 0,	NULL	},
  { "f",      VAR|(5<<8), 0,	NULL	},
  { "g",      VAR|(6<<8), 0,	NULL	},
  { "h",      VAR|(7<<8), 0,	NULL	},
  { "i",      VAR|(8<<8), 0,	NULL	},
  { "j",      VAR|(9<<8), 0,	NULL	},
  { "k",      VAR|(10<<8), 0,	NULL	},
  { "l",      VAR|(11<<8), 0,	NULL	},
  { "m",      VAR|(12<<8), 0,	NULL	},
  { "n",      VAR|(13<<8), 0,	NULL	},
  { "o",      VAR|(14<<8), 0,	NULL	},
  { "p",      VAR|(15<<8), 0,	NULL	},
  { "q",      VAR|(16<<8), 0,	NULL	},
  { "r",      VAR|(17<<8), 0,	NULL	},
  { "s",      VAR|(18<<8), 0,	NULL	},
  { "t",      VAR|(19<<8), 0,	NULL	},
  { "u",      VAR|(20<<8), 0,	NULL	},
  { "v",      VAR|(21<<8), 0,	NULL	},
  { "w",      VAR|(22<<8), 0,	NULL	},
  { "x",      VAR|(23<<8), 0,	NULL	},
  { "y",      VAR|(24<<8), 0,	NULL	},
  { "z",      VAR|(25<<8), 0,	NULL	},  
  { "",       EOE, 0,		NULL	} };

/* output */

void
set_output(gdouble x)
{
  tokens[44].value = x;
}

/* 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 = ERR;
    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);
}
