/*
 * Expression parser/evaluator
 * Copyright (C) 2000 Andrew Hunter
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */

/*
 * A YACC LALR parser seemed a little over the top for this
 * application. This provides a hand-written LL parser for
 * Screen Calculator expressions. (Well, it's essentially an LL
 * parser, but we don't use a prediction stack...)
 */

#include <PalmOS.h>

#include "calc.h"
#include "parse.h"
#include "number.h"
#include "MathLib.h"

#define __LITTLE_ENDIAN 1
#define __BIG_ENDIAN    2
#define __BYTE_ORDER __BIG_ENDIAN

/*                          ----=====88888=====----                          */

static const LexerType charType[256] =
{
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 
  0,0,0,0, 0,2,0,0, 3,4,2,2, 0,2,100,2, 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0, 
  5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,0, 0,0,2,0, 
  0,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
};

void Lexer(Char* string, LexerData* data)
{
  /* Eat up whitespace */
  while (string[data->nextPos] == ' ' ||
	 string[data->nextPos] == '\t')
    data->nextPos++;

  /* Parse according to the next character */
  switch (charType[string[data->nextPos]])
    {
    case Number:
    case Point:
      /*
       * Eep! Complicated
       *
       * PalmOS's floating point support is, not to put too fine a
       * point on it, piss poor. So, we parse the number ourselves. We 
       * even convert from integer to double ourselves, as _d_itod
       * doesn't seem to work...
       */
      {
	double num;      /* The final number */
	Int32 temp;      /* Integer parsing is much quicker... */
	UInt32 x, mult;  /* Position and multiplier */
	Boolean flag;    /* False if this is the first chunk of 8 characters */

	data->type = Number;
	num = 0;
	flag = false;

	/* Parse the integer part of the number */
	while (charType[string[data->nextPos]] == Number)
	  {
	    x=0; temp=0; mult = 1;
	    /* Parse the next 8 characters */
	    while (charType[string[data->nextPos]] == Number &&
		   x<8)
	      {
		temp *= 10;
		temp += (string[data->nextPos++] - 48);
		x++;
		mult *= 10;
	      }
	    /* Add them to the double */
	    if (flag == true)
	      num *= GetDouble(mult);
	    num += GetDouble(temp);
	    flag = true;
	  }

	/* Parse the fractional part of the number */
	if (string[data->nextPos] == '.')
	  {
	    mult = 0;
	    data->nextPos++;
	    while (charType[string[data->nextPos]] == Number)
	      {
		x=0; temp=0;
		/* Parse the next 8 characters */
		while (charType[string[data->nextPos]] == Number &&
		       x<8)
		  {
		    temp *= 10;
		    temp += (string[data->nextPos++] - 48);
		    x++;
		    mult++; /* Well, divide, really */
		  }
		/* Add them to the double */
		num += GetDouble(temp)/pow(10.0, GetDouble(mult));
		flag = true;
	      }
	    
	    if (string[data->nextPos] == '.')
	      data->type = Error;
	  }

	/* Exponent part */
	if (string[data->nextPos] == 'e' ||
	    string[data->nextPos] == 'E')
	  {
	    data->nextPos++;
	    if (charType[string[data->nextPos]] != Number)
	      data->type = Error;
	    else
	      {
		temp = 0;
		while (charType[string[data->nextPos]] == Number &&
		       string[data->nextPos] != '.')
		  {
		    temp *= 10;
		    temp += (string[data->nextPos++] - 48);
		  }
		num *= pow(10.0, GetDouble(temp));
	      }
	  }

	data->data.number = num;
      }
      break;

    case Operator:
      data->type = Operator;
      data->data.operator = string[data->nextPos];
      data->nextPos++;
      break;

    case OpenBracket:
    case CloseBracket:
      data->type = charType[string[data->nextPos]];
      data->nextPos++;
      break;
      
    case Error:
      if (string[data->nextPos] == '\0')
	{
	  data->type = EOL;
	  return;
	}
      
    default:
      data->type = Error;
      data->nextPos++;
      break;
    }
}

/*                          ----=====88888=====----                          */

static MemHandle parseTree = NULL;
static Char*     parseError = NULL;

/* #define TRACEPARSE */

/* Declared static to avoid clogging up the small palmOS stack */
static Int16      rightStack[64], rootStack[64];

ParseTree* Parser(Char* string)
{
  LexerData  data;
  Int16      newNode, lastNode, numNodes;
  ParseTree* parse;
  Int16      root;
  Int16      rightMost;
  Int16      stackPos;

#define NextNode newNode++; \
  if (newNode >= numNodes) \
    { \
      numNodes += 32; \
      MemHandleUnlock(parseTree); \
      if (MemHandleResize(parseTree, numNodes*sizeof(ParseTree))) \
	{ \
          MemHandleFree(parseTree); \
          parseTree = NULL; \
          parseError = "Out of memory"; \
	  return NULL; \
	} \
      parse = MemHandleLock(parseTree); \
    }
  
  data.nextPos = 0;
  newNode      = 0;
  root         = rightMost = -1;
  numNodes     = 8;
  parseError   = NULL;
  stackPos     = 0;
  
  /* Initialise */
  if (parseTree == NULL)
    {
      parseTree = MemHandleNew(sizeof(ParseTree)*numNodes);
      if (!parseTree)
	{
	  parseError = "Out of memory";
	  return NULL;
	}
    }
  else
    {
      if (MemHandleResize(parseTree, sizeof(ParseTree)*numNodes))
	{
	  parseError = "Can't allocate memory";
	  return NULL;
	}
    }

  parse = MemHandleLock(parseTree);

  /*
   * For you 'eek! he used a goto' fanatics out there, please note
   * this is a state machine. Oh, and also note the small size of the
   * Palm's stack...
   */

 initialState: /* Start of expression */
  Lexer(string, &data); /* Look ahead */

  switch (data.type)
    {
    case Number:
    case Variable:
#ifdef TRACEPARSE
      CalcPrint("Number\n");
#endif
      parse[newNode].nodeData = data;
      parse[newNode].left     = parse[newNode].right = NULL;

      root = rightMost = newNode;

      NextNode;
      goto operatorState;

    case OpenBracket:
#ifdef TRACEPARSE
      CalcPrint("(\n");
#endif
      rootStack[stackPos]    = root;
      rightStack[stackPos++] = rightMost;
      goto initialState;

    case EOL:
#ifdef TRACEPARSE
      CalcPrint("EOL\n");
#endif
      goto parseEnd;
      
    default:
      parseError = "Parse Error";
      return NULL;
    }
      
 numState: /* Collecting a number */
  Lexer(string, &data); /* Look ahead */

  switch (data.type)
    {
    case Number:
    case Variable:
#ifdef TRACEPARSE
      CalcPrint("Number\n");
#endif
      /* Push this at the rightmost node */
      parse[newNode].nodeData = data;
      parse[newNode].parent   = &parse[rightMost];
      parse[newNode].left     = NULL;
      parse[newNode].right    = NULL;
      parse[rightMost].right = &parse[newNode];
      
      NextNode;

      if (parse[rightMost].nodeData.type == Operator)
	{
	  /* '^' is handled slightly differently */
	  if (parse[rightMost].nodeData.data.operator == '^')
	    goto operatorState;
	}
      rightMost = newNode-1;

      goto operatorState;

    case OpenBracket:
#ifdef TRACEPARSE
      CalcPrint("(\n");
#endif
      rootStack[stackPos]    = root;
      rightStack[stackPos++] = rightMost;
      root = -1;
      goto initialState;

    default:
      parseError = "Parse Error";
      return NULL;
    }

 operatorState: /* Getting an operator */
  Lexer(string, &data);

  switch (data.type)
    {
    case EOL:
#ifdef TRACEPARSE
      CalcPrint("EOL\n");
#endif
      goto parseEnd;
      
    case Operator:
#ifdef TRACEPARSE
      CalcPrint("Operator\n");
#endif
      parse[newNode].nodeData = data;
      
      switch (data.data.operator)
	{
	case '+':
	case '-':
	  /* Push these at the root */
	  parse[newNode].left   = &parse[root];
	  parse[root].parent    = &parse[newNode];
	  parse[newNode].right  = NULL;
	  parse[newNode].parent = NULL;
	  
	  rightMost = root = newNode;

	  NextNode;
	  break;

	case '*':
	case '/':
	case '^':
	  /* Push these at the rightmost node */
	  parse[newNode].left     = &parse[rightMost];
	  parse[newNode].parent   = parse[rightMost].parent;
	  parse[rightMost].parent = &parse[newNode];
	  parse[newNode].right    = NULL;

	  if (parse[newNode].parent != NULL)
	    {
	      parse[newNode].parent->right = &parse[newNode];
	    }

	  if (root == rightMost)
	    root = newNode;
	  rightMost = newNode;

	  NextNode;
	  break;
	}
      goto numState;

    case CloseBracket:
#ifdef TRACEPARSE
      CalcPrint(")\n");
#endif
      /* Push this tree as the rightmost node */
      if (stackPos == 0)
	{
	  parseError = "Too many '('s";
	  return NULL;
	}
      stackPos--;

      rightMost = rightStack[stackPos];

      if (rightMost != -1)
	{
	  /* This node gets put at the rightmost position in the current tree */
	  parse[rightMost].right = &parse[root];
	  parse[root].parent = &parse[rightMost];
	  
	  /* This node becomes the rightmost node in the current tree */
	  rightMost = root;
	  /* The root goes back to what it was before */
	  root = rootStack[stackPos];
	}
      else
	{
	  /* This node becomes the root of the current tree */
	  rightMost = root;
	}
      goto operatorState;

    default:
      parseError = "Parse Error";
      return NULL;
    }

 parseEnd:
  return &parse[root];
}

void ParseFinished(void)
{
  if (parseTree != NULL)
    {
      MemHandleUnlock(parseTree);
      MemHandleFree(parseTree);
      parseTree = NULL;
    }
}

Char* ParseError(void)
{
  return parseError;
}

/*                          ----=====88888=====----                          */

static Char* evalError = NULL;

struct EvaluateStack
{
  enum
  {
    returnLeft, returnRight, returnDone
  } whereTo;

  double     left;
  ParseTree* expr;
};

static double NaN(void)
{
  union DoubleType nan;

  nan.bits.sign = 0;
  nan.bits.exp  = 0x7ff;
  nan.bits.manH = 0;
  nan.bits.manL = 0;
  
  return nan.d;
}

double Evaluate(ParseTree* expression)
{
  MemHandle evalStack = NULL;
  UInt16 stackPos;
  UInt16 stackSize;
  struct EvaluateStack* stack;
  double result;

  evalError = NULL;
  stackSize = 8;
  
  evalStack = MemHandleNew(sizeof(struct EvaluateStack)*stackSize);

#define NextStack stackPos++; \
  if (stackPos >= stackSize) \
    { \
      stackSize+=8; \
      MemHandleUnlock(evalStack); \
      if (MemHandleResize(evalStack, sizeof(struct EvaluateStack)*stackSize)) \
	{ \
	  evalError = "Out of memory"; \
	  result = NaN(); \
          goto finished; \
	} \
      stack = MemHandleLock(evalStack); \
    }

  stack = MemHandleLock(evalStack);

  stackPos = 0;
  stack[stackPos].expr = expression;
  stack[stackPos].whereTo = returnDone;
  
  /*
   * Once again, gotos to avoid overflowing the stack. O'course, we're now
   * faced with the problem of overflowing the rather titchy dynamic
   * memory area, but at least that is dealt with nicely.
   *
   * I can hear you jabbering 'impose a limit!', but different palm
   * devices have different stack sizes and heap sizes, and (it seems) 
   * no API to find out about them. Plus this all fails cleanly, which
   * the alternative doesn't. Unless you consider barfing all over the 
   * stack 'clean'.
   */

  /*
   * Macros to simulate function calls
   */
#define EvalReturn \
  if (stackPos == 0) goto finished; \
  stackPos--; \
  expression = stack[stackPos].expr; \
  if (stack[stackPos].whereTo == returnLeft) \
    goto gotleft; \
  goto gotright;
  
 evaluate:
  if (expression == NULL)
    {
      result = NaN();
      evalError = "(Oops) NULL expression reached";
      goto finished;
    }
  
  switch (expression->nodeData.type)
    {
    case Operator:
      /* Evaluate the left hand side */
      NextStack;
      expression = stack[stackPos].expr = expression->left;
      stack[stackPos-1].whereTo = returnLeft;
      goto evaluate;
      
    case Number:
      result = expression->nodeData.data.number;
      EvalReturn;

    default:
      result = NaN();
      evalError = "(Oops) Parse type not supported";
      goto finished;
    }

 gotleft:
  stack[stackPos].left = result;
  
  /* Evaluate the right hand side */
  NextStack;
  expression = stack[stackPos].expr = expression->right;
  stack[stackPos-1].whereTo = returnRight;
  goto evaluate;

 gotright:
  switch (expression->nodeData.data.operator)
    {
    case '+':
      result = stack[stackPos].left + result;
      break;

    case '-':
      result = stack[stackPos].left - result;
      break;

    case '/':
      if (result == 0.0)
	{
	  evalError = "Division by zero";
	  result = NaN();
	  goto finished;
	}
      result = stack[stackPos].left / result;
      break;

    case '*':
      result = stack[stackPos].left * result;
      break;

    case '^':
      result = pow(stack[stackPos].left, result);
      break;
      
    default:
      result = NaN();
      evalError = "(Oops) Unimplemented operation";
      goto finished;
    }
  EvalReturn;

 finished:
  MemHandleUnlock(evalStack);
  MemHandleFree(evalStack);

  return result;
}

Char* EvaluateError(void)
{
  return evalError;
}
