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

//#define DEBUGLEX

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
};

int Lexer(YYSTYPE* symbol, ParserData* parse)
{
  Char* string;

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

  string = parse->string;

  /* Eat up whitespace */
  while (string[parse->nextPos] == ' ' ||
	 string[parse->nextPos] == '\t')
    parse->nextPos++;

  /* Parse according to the next character */
  switch (charType[string[parse->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...
       */
#ifdef DEBUGLEX
      CalcPrint("NUMBER\n");
#endif

      symbol->ldata.type = Number;
      num = 0;
      flag = false;

	/* Parse the integer part of the number */
      while (charType[string[parse->nextPos]] == Number)
	{
	  x=0; temp=0; mult = 1;
	  /* Parse the next 8 characters */
	  while (charType[string[parse->nextPos]] == Number &&
		 x<8)
	    {
	      temp *= 10;
	      temp += (string[parse->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[parse->nextPos] == '.')
	{
	  mult = 0;
	  parse->nextPos++;
	  while (charType[string[parse->nextPos]] == Number)
	    {
	      x=0; temp=0;
	      /* Parse the next 8 characters */
	      while (charType[string[parse->nextPos]] == Number &&
		     x<8)
		{
		  temp *= 10;
		  temp += (string[parse->nextPos++] - 48);
		  x++;
		  mult++; /* Well, divide, really */
		}
	      /* Add them to the double */
	      /* FIXME: causes stack overflow (??!!) */
	      num += GetDouble(temp)/pow(10.0, GetDouble(mult));
	      flag = true;
	    }
	    
	  if (string[parse->nextPos] == '.')
	    symbol->ldata.type = Error;
	}

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

      symbol->ldata.data.number = num;
      return NUMBER;

    case Operator:
#ifdef DEBUGLEX
      CalcPrint("OPERATOR\n");
#endif
      symbol->ldata.type = Operator;
      symbol->ldata.data.operator = string[parse->nextPos];
      parse->nextPos++;
      return symbol->ldata.data.operator;

    case OpenBracket:
    case CloseBracket:
#ifdef DEBUGLEX
      CalcPrint("BRACKET\n");
#endif
      symbol->ldata.type = charType[string[parse->nextPos]];
      parse->nextPos++;
      return string[parse->nextPos-1];
      
    case Error:
      if (string[parse->nextPos] == '\0')
	{
#ifdef DEBUGLEX
	  CalcPrint("EOL\n");
#endif
	  symbol->ldata.type = EOL;
	  return 0;
	}
      
    default:
#ifdef DEBUGLEX
      CalcPrint("ERROR\n");
#endif
      symbol->ldata.type = Error;
      parse->nextPos++;
      return string[parse->nextPos-1];
    }
}

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

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

void CPerror(Char* s)
{
  parseError = s;
}

ParseTree* Parser(Char* string)
{
  ParserData pdata;

  parseError = NULL;

  if (parseTree == NULL)
    {
      parseTree = MemHandleNew(sizeof(ParseTree)*8);
    }

  pdata.string  = string;
  pdata.nextPos = 0;
  pdata.result  = NULL;

  pdata.storeHandle = parseTree;
  pdata.storeSize   = 8;
  pdata.storePos    = 0;
  pdata.store       = MemHandleLock(parseTree);
  
  CPparse(&pdata);

  parseTree = pdata.storeHandle;
  
  return pdata.result;
}

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;
}
