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

/*
 * Corrected grammar
 * Kris Jurka 27/7/1999
 */

/*
 * Corrected more grammar
 * Torsten Landschoff 24/11/1999
 */

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

#include <math.h>
#include "parser.h"
#include "prefs.h"
#include "tokeniser.h"
#include "log.h"
#include "i18n.h"

/* #define DEBUG 1 */

/*
 *
 * EBNF:
 *  <expr>     ::= +|- <term> { +|- <term> }
 *  <term>     ::= <product> { *|/ <product> }
 *  <product>  ::= <factor> { ^ <factor> }
 *  <factor>   ::= <number> | <function> | (<expr>)
 *  <number>   ::= <float> | x
 *  <function> ::= <function_name>(<expression>)
 */

static parse_tree *split(void);
static parse_tree *expr(void);
static parse_tree *term(void);
static parse_tree *factor(void);
static parse_tree *product(void);

static token_list *list_g;      /* global list, makes life easier */
static gint parse_error;	/* flag this when a parse error occurs */

/* Coefficients of Gauss-Legendre method */
#define ORDER 5
static double gauss_x[ORDER] = {
  0.046910077030668,
  0.23076534494715845,
  0.5,
  0.76923465505284155,
  0.953089922969332
};
static double gauss_y[ORDER] = {
  0.11846344252809454,
  0.23931433524968324,
  0.28444444444444444,
  0.23931433524968324,
  0.11846344252809454  
};
    
/* main variables a-z */
extern gdouble var[ENDVARIABLES];
extern parse_tree *fun[26];
extern struct_ranges ranges;
extern void set_mode(gint i);

/* some convenience functions follow, these stop seg faults and other stuff,
 * when the user enters un-parsable equations.
 * There is probably a fair bit of redundancy in the NULL pointer checking
 * but leave it there for now.
 */
static gint
token_type_is(gint type)
{
  if(!list_g)
    return(FALSE);
  return((list_g->type & 255) == type);
}

static gint
token_subtype()
{
  if(!list_g)
    return(-1);
  return(list_g->type >> 8);
}

static gint
token_type(void)
{
  if(!list_g)
    return(EOE);
  return(list_g->type);
}

static gdouble
token_value(void)
{
  if(!list_g)
    return(0);
  return(list_g->value);
}

static void
get_next_token(void)
{
  if(list_g)
    list_g = list_g->next;
}

static void
skip_token(gint type)
{
  if(!list_g) {
    parse_error = TRUE;
    return;
  }
  else
    if((list_g->type & 255)!= type)
      parse_error = TRUE;
  list_g = list_g->next;
}

/* end of convenience functions
 */

/* split all semicolon separated expressions
 */
static parse_tree *
split(void)
{
  parse_tree *tree = NULL, *temp_tree;
  while(!token_type_is(EOE)) {
    if (token_type_is(SEP)) {
      temp_tree = tree;
      tree = g_new(parse_tree, 1);
      tree->left = temp_tree;
      tree->type = token_type();
      get_next_token();
      if (!list_g) {
	tree->right = NULL;
	break;
      }
      tree->right = expr();
      if (!tree->right) break;
    } else {
      tree = expr();
      if (!tree) break;      
    }
  }
  return(tree);
}

/* expr, starting non-terminal for generating parse tree
 */
static parse_tree *
expr(void)
{
  parse_tree *tree = NULL, *temp_tree;

#if DEBUG  
  fprintf(stderr, "expr %d\n", token_type());
#endif

  if(token_type_is(DEF)) {
    get_next_token();
    if (token_type_is(EQU)) {
      tree = g_new(parse_tree, 1);
      tree->left = NULL;
      tree->type = token_type();
      get_next_token();
      tree->right = expr();
      return(tree);
    } else {
      parse_error = TRUE;
      return(NULL);
    }
  }

  if(token_type_is(NUM) ||
     token_type_is(VAR) ||
     token_type_is(FUN) ||
     token_type_is(SUB) ||	/* negate, (not minus) */
     token_type_is(LB)	||
     token_type_is(LCB) ||
     token_type_is(LSB)) {
    tree = term();
  }
  else {
    parse_error = TRUE;
    return(NULL);
  }

  while(token_type_is(ADD) ||
	token_type_is(SUB)) {
    temp_tree = tree;		/* save the old tree */
    tree = g_new(parse_tree, 1);
    tree->type = token_type();
    tree->left = temp_tree;	/* push old tree down a 'left branch' */
    get_next_token();
    tree->right = term();	/* 'right branch' */
  }

  if((tree == NULL) || (token_type_is(ERR)))
    parse_error = TRUE;
  return(tree);
}

/* term, non-terminal
 */
static parse_tree *
term(void)
{
  parse_tree *tree = NULL, *temp_tree;

#if DEBUG  
  fprintf(stderr, "term %d\n", token_type());
#endif

  if(token_type_is(NUM) ||
     token_type_is(VAR) ||
     token_type_is(FUN) ||
     token_type_is(SUB) ||	/* negate, (not minus) */
     token_type_is(LB)	||
     token_type_is(LCB) ||
     token_type_is(LSB)) {
    if(token_type_is(SUB)) {	/* negate */
      tree = g_new(parse_tree, 1);
      tree->type = SUB;	/* token_type(); */
      tree->left = g_new(parse_tree, 1);
      tree->left->type = NUM;
      tree->left->left = NULL;
      tree->left->right = NULL;
      get_next_token();		/* skip the minus-sign */
      tree->right = product();
    }
    else {
      tree = product();
    }
  }
  else {
    parse_error = TRUE;
    return(NULL);
  }
  
  while(token_type_is(MUL) ||
	token_type_is(DIV) ||	
	token_type_is(VAR) ||
	token_type_is(FUN) ||
	token_type_is(LB)  ||
	token_type_is(LCB) ||
	token_type_is(LSB)) {
    if(token_type_is(VAR) ||	/* assume multiplication */
       token_type_is(FUN) ||
       token_type_is(LB)  ||
       token_type_is(LCB) ||
       token_type_is(LSB)) {
      temp_tree = tree;
      tree = g_new(parse_tree, 1);
      tree->type = MUL;   /* set multiplication */
      tree->left = temp_tree;
      tree->right = term();
    }
    else {
      temp_tree = tree;
      tree = g_new(parse_tree, 1);
      tree->type = token_type();
      tree->left = temp_tree;
      get_next_token();
      tree->right = term();
    }
  }

  if(tree == NULL)
    parse_error = TRUE;
  return(tree);
}

/* product, non-terminal
 */
static parse_tree *
product(void)
{
  parse_tree *tree = NULL, *temp_tree;

#if DEBUG  
  fprintf(stderr, "prod %d\n", token_type());
#endif

  if(token_type_is(NUM) ||
     token_type_is(VAR) ||
     token_type_is(FUN) ||
     token_type_is(LB)	||
     token_type_is(LCB) ||
     token_type_is(LSB)) {
    tree = factor();
  }
  else {
    parse_error = TRUE;
    return(NULL);
  }

  while(token_type_is(POW)) {
    temp_tree = tree;
    tree = g_new(parse_tree, 1);
    tree->type = token_type();
    tree->left = temp_tree;
    get_next_token();
    tree->right = product();
  }

  if(tree == NULL)
    parse_error = TRUE;
  return(tree);
}

/* factor, non-terminal
 * negation is a bit of a hack, if we get a negator, we create a left tree,
 * with number=0, and a negation node, and continue down the right tree....
 * ie, we subtract the following factor from 0
 */
static parse_tree *
factor(void)
{
  parse_tree *tree;
  gdouble *val;
  
#if DEBUG  
  fprintf(stderr, "factor %d\n", token_type());
#endif

  if(token_type_is(NUM) ||
     token_type_is(VAR)) {
    tree = g_new(parse_tree, 1);
    tree->right = NULL;
    tree->type = token_type();
    if (token_type_is(NUM)) {
      /* use left field as pointer to value */
      val = g_new(double, 1);
      *val = token_value();
      tree->left = (parse_tree *)val;
    } else
      tree->left = NULL;
    get_next_token();
    return(tree);
  }
  else if(token_type_is(FUN)) {
    tree = g_new(parse_tree, 1);
    tree->left = (parse_tree *)list_g->func;
    tree->type = token_type();
    skip_token(FUN);
    if(token_type_is(LB)) {
      skip_token(LB);
      tree->right = expr();
      skip_token(RB);
    }
    else if(token_type_is(LSB)) {
      skip_token(LSB);
      tree->right = expr();
      skip_token(RSB);
    }
    else {
      skip_token(LCB);
      tree->right = expr();
      skip_token(RCB);
    }
    return(tree);
  }
  else if(token_type_is(LB)) {
    skip_token(LB);
    tree = expr();
    skip_token(RB);
    return(tree);
  }
  else if(token_type_is(LSB)) {
    skip_token(LSB);
    tree = expr();
    skip_token(RSB);
    return(tree);
  }
  else if(token_type_is(LCB)) {
    skip_token(LCB);
    tree = expr();
    skip_token(RCB);
    return(tree);
  }
  else {
    parse_error = TRUE;
    return(NULL);
  }
}

static gint
fix_equal()
{
  token_list *list, *listp = NULL, *listpp;
  list = list_g;
  
  while(1) {
    /* search for x = ? tokens, and set variable to appropriate value 
       from previous token if it is a VAR preceded by a SEP, 
       otherwise do parse error */
    listpp = listp;
    listp = list;
    list = list->next;
    if (!list) break;
    if (list->type == EQU) {
      if (listpp && ((listpp->type & 255) != SEP)) {
        return(parse_error = TRUE);	
      }
      if ((listp->type & 255) != VAR) {
        return(parse_error = TRUE);
      }
      /* attach assigned variable to = operator */
      list->type |= listp->type & 0xff00;
      listp->type = DEF;
    }
  }
  return parse_error;
}

static parse_tree *
def_tree(glong i, parse_tree *tree)
{
  parse_tree *temp_tree;
  temp_tree = g_new(parse_tree, 1);
  temp_tree->type = DEF;
  temp_tree->left = (parse_tree *) i;
  temp_tree->right = tree;
  return(temp_tree);
}

static parse_tree *
directive(void)
{
  token_list *list;
  parse_tree *tree = NULL, *temp_tree = NULL;
  gint t, u, v, w;

  if (token_type_is(VAR)) {
    /* assignment of variable */
    /* &z=... */
    list = list_g;
    get_next_token();
    if (!token_type_is(EQU)) return(NULL);
    tree = make_parse_tree(list);
    if (!tree) return(NULL);
    return(def_tree(1, tree));
  }

  if (!token_type_is(FUN)) return(NULL);
  
  v = token_subtype();
  /* predefined functions not allowed */
  if (v == 255) return(NULL);
  
  if (v >= 251 && v <= 254) {
    /* cart, polar, param, sequ and nothing more */
    list = list_g;
    get_next_token();
    if (token_type_is(EOE)) {
      set_mode(254 - v);
      return(def_tree(0, NULL));
    }
    return(NULL);      
  }

  /* assignment of user defined function ? */
  /* &fa... */
  if (token_type_is(FUN) && v >= 0 && v <= 25)
    get_next_token();
  else return(NULL);

  if (!token_type_is(EQU)) {
    if (token_type_is(LB)) get_next_token(); else return(NULL);
    /* syntax should be &fa(p)=... */
    if (token_type_is(VAR)) {
      w = token_subtype();
      get_next_token();
    } else return(NULL);
    if (token_type_is(RB)) get_next_token(); else return(NULL);
    if (token_type_is(EQU)) get_next_token(); else return(NULL);
    if (token_type_is(EOE)) return(NULL);
    if (fix_equal()) return(NULL);
    temp_tree = split();
    if((parse_error)||(!token_type_is(EOE))) {
      if (temp_tree) free_tree(temp_tree);
      return(NULL);
    }
    if (fun[v]) free_tree(fun[v]);
    tree = g_new(parse_tree, 1);
    tree->type = FUN | (v << 8) | (w << 16);
    tree->left = NULL;
    tree->right = temp_tree;
    fun[v] = tree;
    return(def_tree(0, NULL));
  }

  /* so token type is EQU */
  /* &fa=... */
  get_next_token();

  if (token_type_is(FUN)) {
    /* &fa=fb...   or  &fa=<predef> */
    w = token_subtype();
    list = list_g;
    get_next_token();
    if (!token_type_is(DER)) return NULL;
    get_next_token();
    if (!token_type_is(EOE)) return(NULL);
    if (fun[v]) { free_tree(fun[v]); fun[v] = NULL; }
    if (w == 255) {
      /* derivative of predefined function */
      tree = g_new(parse_tree, 1);
      tree->type = FUN | (v << 8) | (26 << 16);
      tree->left = (parse_tree *) list->func;
      tree->right = NULL;
      fun[v] = tree;
      return(def_tree(0, NULL));
    } else
    if (w >= 0 && w <= 25) {      
      /* derivative of user defined function */
      tree = g_new(parse_tree, 1);      
      tree->type = FUN | (v << 8) | ((27+w) << 16);      
      tree->left = NULL;
      tree->right = NULL;
      fun[v] = tree;
      return(def_tree(0, NULL));
    }
    return(NULL);
  }
  
  if (token_type_is(INT) || token_type_is(TGT)) {
    /* &fa=tgt_c_function */
    t = (token_type_is(INT))? 81 : 54;
    get_next_token();
    if(!token_type_is(RES)) return(NULL);
    get_next_token();
    if (!token_type_is(VAR)) return(NULL);
    u = list_g->type >> 8;      
    get_next_token();
    if (!token_type_is(RES)) return(NULL);
    get_next_token();
    if(!token_type_is(FUN)) return(NULL);
    w = token_subtype();
    list = list_g;
    get_next_token();
    if (!token_type_is(EOE)) return(NULL);
    if (w == 255) {
      /* tangent/integral of predefined function */
      if (fun[v]) { free_tree(fun[v]); fun[v] = NULL; }
      tree = g_new(parse_tree, 1);      
      tree->type = FUN | (v << 8) | ((t-1) << 16) | (u << 24);
      tree->left = (parse_tree *) list->func;      
      tree->right = NULL;
      fun[v] = tree;
      return(def_tree(0, NULL));      
    } else
    if (w >= 0 && w <= 25) {      
      /* tangent/integral of user defined function */
      tree = g_new(parse_tree, 1);      
      tree->type = FUN | (v << 8) | ((t+w) << 16) | (u << 24);
      tree->left = NULL;
      tree->right = NULL;
      fun[v] = tree;
      return(def_tree(0, NULL));
    }
  }
  return(NULL);
}

/* make_parse_tree, creates a parse tree from a list of tokens
 */

parse_tree *
make_parse_tree(token_list *list)
{
  parse_tree *tree = NULL, *temp_tree;  
  parse_error = FALSE;
  list_g = list; 	/* assign global list */

  /* Check if initial token is #, ?, = or & */
  if (token_type_is(COM)) {
    /* comment */
    tree = g_new(parse_tree, 1);
    tree->left = NULL;
    tree->right = NULL;
    tree->type = COM;
    return(tree);
  } else
  if (token_type_is(ASK)) {
    /* evaluation request */
    get_next_token();
    if (fix_equal()) return(NULL);  
    temp_tree = split();
    if((!temp_tree) || (parse_error) || (!token_type_is(EOE))) return(NULL);
    tree = g_new(parse_tree, 1);
    tree->left = NULL;
    tree->right = temp_tree;
    tree->type = ASK;
    return(tree);    
  } else
  if (token_type_is(EQU)) {
    /* solving equation */
    get_next_token();
    if (fix_equal()) return(NULL);  
    temp_tree = split();
    if((!temp_tree) || (parse_error) || (!token_type_is(EOE))) return(NULL);
    tree = g_new(parse_tree, 1);
    tree->left = NULL;
    tree->right = temp_tree;
    tree->type = SOL;
    return(tree);    
  } else
  if (token_type_is(DEF)) {
    /* directive : change of coordinates or definition of function */
    get_next_token();  
    tree = directive();
    return(tree);    
  }

  if (fix_equal()) return(NULL);
  tree = split();
  if((parse_error)||(!token_type_is(EOE))) return(NULL);
  return(tree);
}

/* free_tree, frees the memory allocated to the tree using a recursive
 * algorithm
 */
void
free_tree(parse_tree *tree)
{
  gint i;
#if DEBUG
  printf("tree = %d type = %d %d left = %d right = %d\n",
	 tree, tree->type & 255, tree->type >> 8, tree->left, tree->right);
#endif
  if(tree->left) {
    i = tree->type & 255;
    if (i == NUM || i == VAR)
      g_free(tree->left);
    else
    if (i != DEF && i != FUN)
      free_tree(tree->left);
  }
  if(tree->right)
    free_tree(tree->right);
  g_free(tree);
}

#if 0
/* dup_tree, duplicates the tree using a recursive algorithm
 */
parse_tree *
dup_tree(parse_tree *tree)
{
  gint i;
  parse_tree * new_tree;
  new_tree = g_new(parse_tree, 1);
  new_tree->type = tree->type;
  new_tree->right = (tree->right)? dup_tree(tree->right) : NULL;
  if (tree->left) {
    i = tree->type & 255;
    if (i == NUM || i == VAR) {
      new_tree->left = (parse_tree *)g_new(gdouble, 1);
      *(gdouble *)new_tree->left = *(gdouble *)tree->left;
    } else
    if (i != DEF && i != FUN)
      new_tree->left = dup_tree(tree->left);
  } else
    new_tree->left = NULL;
  return(new_tree);
}
#endif

gint
tan_check(gdouble x)
{
  if (!isnan(var[TGTR]) && x > var[TGTR]) return 1;
  if (!isnan(var[TGTL]) && x < var[TGTL]) return 1;
  return 0;
}

static gdouble
eval_function(gint v, gdouble x) {
  gdouble A, c, y, h, s, t;
  gint k, l, m, n, w;
  Func func;

  if (v < 0 || v > 25) return(NAN);
  if (!fun[v]) return(NAN);

  w = ((fun[v]->type) >> 16) & 255;

  if (w >= 0 && w <= 25) {
    s = var[w];
    var[w] = x;
    y = eval_tree(fun[v]->right);
    var[w] = s;
    return(y);
  }

  w -= 27;
  /* derivative function */
  
  if (w >= 0 && w <= 25) {
    /* derivative of user defined function */
    y = eval_function(w, x + 0.5 * var[INCR]);
    y = 8.0 * (y - eval_function(w, x - 0.5 * var[INCR]));
    y = y - eval_function(w, x + var[INCR]);
    y = var[MULT] * (y + eval_function(w, x - var[INCR]));
    return(y);
  }

  if (w == -1) {
    /* derivative of predefined function */    
    func = (Func)(fun[v]->left);
    if (!func) return(NAN);    
    y = var[MULT] * ( 8.0 *
		      (func(x + 0.5 * var[INCR]) - func(x - 0.5 * var[INCR]))
		     -(func(x + var[INCR]) - func(x - var[INCR])));
    return(y);
  }

  w -= 27;
  /* tangent function */
  
  if (w >= 0 && w <= 25) {
    /* tangent of user defined function */
    k = (fun[v]->type) >> 24;
    if (k > 25) return(NAN);
    c = var[k];
    if (var[ACTV] && tan_check(x - c)) return(NAN);  
    y = eval_function(w, c + 0.5 * var[INCR]);
    y = 8.0 * (y - eval_function(w, c - 0.5 * var[INCR]));
    y = y - eval_function(w, c + var[INCR]);
    y = var[MULT] * (y + eval_function(w, c - var[INCR]));
    y = eval_function(w, c) + (x - c) * y;
    return(y);
  }

  if (w == -1) {
    /* tangent of predefined function */
    k = (fun[v]->type) >> 24;
    if (k > 25) return(NAN);
    c = var[k];
    if (var[ACTV] && tan_check(x - c)) return(NAN);    
    func = (Func)(fun[v]->left);
    if (!func) return(NAN);    
    y = var[MULT] * ( 8.0 *
		      (func(c + 0.5 * var[INCR]) - func(c - 0.5 * var[INCR]))
		     -(func(c + var[INCR]) - func(c - var[INCR])));
    y = func(c) + (x - c) * y; 
    return(y);
  }

  w -= 27;
  /* indefinite integral */

  if (w >= 0 && w <= 25) {
    /* integral of user defined function */
    k = (fun[v]->type) >> 24;
    if (k > 25) return(NAN);
    c = var[k];  
    if (fun[v] == integr[v].ptr && fabs(x-integr[v].c) < fabs(x-c)) {
      c = integr[v].c;
      A = integr[v].A;
    } else {
      A = 0;
    }
    m = (int) (fabs(x - c) / var[INTSTEP]) + 1;
    h = (x - c) / m;
    y = 0;
    /* Gauss-Legendre method */	  
    for (l = 0; l < m; l++) {
      for (n = 0; n < ORDER; n++) {
        t = eval_function(w, c + gauss_x[n] * h);
	if (!isfinite(t)) return(NAN);
	y = y + gauss_y[n] * t;
      }
      c = c + h;	    
    }
    A = A + h * y;
    integr[v].ptr = fun[v];
    integr[v].c = x;
    integr[v].A = A;
    return(A);    
  }

  if (w == -1) {
    /* indefinite integral of predefined function */
    k = (fun[v]->type) >> 24;
    if (k > 25) return(NAN);
    func = (Func)(fun[v]->left);
    if (!func) return(NAN);
    c = var[k];  
    if (fun[v] == integr[v].ptr && fabs(x-integr[v].c) < fabs(x-c)) {
      c = integr[v].c;
      A = integr[v].A;
    } else {
      A = 0;
    }
    m = (int) (fabs(x - c) / var[INTSTEP]) + 1;
    h = (x - c) / m;
    y = 0;
    /* Gauss-Legendre method */	  
    for (l = 0; l < m; l++) {
      for(n = 0; n < ORDER; n++) {
	t = func(c + gauss_x[n] * h);
	if (!isfinite(t)) return(NAN);	      
	y = y + gauss_y[n] * t;
      }
      c = c + h;  
    }
    A = A + h * y;
    integr[v].ptr = fun[v];
    integr[v].c = x;	  
    integr[v].A = A;	  
    return(A);	  
  }

  return(NAN);
}
 
/* eval_tree,       this function recursively evaluates the parse tree using
 * LRN notation. Currently only unary functions are supported, where the
 * argument to the function is assumed to be the left child branch of the
 * function node.... in the future binary functions might exist, like mod,
 * where the arguments are the children branches from left to right
 */
double
eval_tree(parse_tree *tree)
{
  static gint  v;
  static gdouble x, y;
  static Func func;
  /*
  printf("Tree[%d,%d,%d] = %d left = %d right = %d\n", 
         tree->token.type & 255, 
         (tree->token.type >> 8) & 255,
         (tree->token.type >> 16) & 255,
         tree, tree->left, tree->right);
  */
  switch(tree->type & 255) {
  case ADD:
    return(eval_tree(tree->left) + eval_tree(tree->right));
  case SUB:
    return(eval_tree(tree->left) - eval_tree(tree->right));
  case MUL:
    return(eval_tree(tree->left) * eval_tree(tree->right));
  case DIV:
    x = eval_tree(tree->left) ; y = eval_tree(tree->right);
    if (y == 0) return(NAN);
    return(x / y);
  case POW:
    return(pow(eval_tree(tree->left) , eval_tree(tree->right)));
  case SEP:
    (void) eval_tree(tree->left);
    return(eval_tree(tree->right));    
  case FUN:
    v = (tree->type >> 8) & 255;
    if (v == 255) {
      /* predefined function */
      func = (Func) tree->left;    
      return(func(eval_tree(tree->right)));      
    }
    return(eval_function(v, eval_tree(tree->right)));
  case NUM:
    if (tree->left)
      return(*(gdouble *)tree->left);
    else
      return(0);
  case VAR:
    return(var[tree->type>>8]);
  case EQU:
    v = tree->type>>8;
    if (v < ENDVARIABLES)
      return(var[v] = eval_tree(tree->right));
  default:
    write_log(_("Eval Error"));
    return(0);
  }
}
