#include <ctype.h>
#include <stdlib.h>

#define __EXPR_CPP
#include <expr.h>
#include <stack.h>
#include <utility.h>

TCodearray::TCodearray(int size) : _rpn(size)
{
  clear();
}


void TCodearray::clear()
{
  _rpn.destroy();
  _last = 0;
  TCode *c = new TCode(_endsym);
  _rpn.add(c, _last);
}


void TCodearray::add(TCodesym sym, const TValue& val)
{
  TCode *c = new TCode(sym, val);
  _rpn.add(c, _last++);
  TCode *c1 = new TCode(_endsym);
  _rpn.add(c1, _last);
}

TCode& TCode::operator =(const TCode& b)
{
  _sym = b._sym;
  _val = b._val;
  return *this;
}

TVararray::TVararray(int size) : _array(size)

{
  clear();
}


void TVararray::add(const char* name, const TValue& val)

{
  TVar* v = new TVar(name, val);
  _array.add(v, _last++);
}

void TVararray::set(const char* name, const real& val)
  
{
  for (int i = 0; i < _array.items(); i++)
    if (strcmp(((TVar&) _array[i]).getname(), name) == 0)
    {
      ((TVar&) _array[i]) = (TValue&) val;
      return;
    }
}


void TVararray::set(const char* name, const char* val)
  
{
  for (int i = 0; i < _array.items(); i++)
    if (strcmp(((TVar&) _array[i]).getname(), name) == 0)
    {
      ((TVar&) _array[i]) = (TValue&) val;
      return;
    }
}


real __r;

const real& TVararray::getnum(const char* name)
  
{
  int i;

  for (i = 0; i < _array.items(); i++)
  {
    if (strcmp(((TVar*) _array.objptr(i))->getname(), name) == 0)
    {
      __r  = ((TVar*) _array.objptr(i))->number();
      return __r;
    }
  }
  fatal_box("Unknown variable : %s", name);
  return __r;
}


const real& TVararray::getnum(int varnum)

{
  if (varnum >= _array.items())
    fatal_box("invalid variable number : %d", varnum);
  __r  = ((TVar*) _array.objptr(varnum))->number();
  return __r;
}


const char* TVararray::getstring(const char* name)

{
  const char* s = NULL;
  int   i;

  for (i = 0; i < _array.items(); i++)
  {
    if (strcmp(((TVar*) _array.objptr(i))->getname(), name) == 0)
      return ((TVar*) _array.objptr(i))->string();
  }
  fatal_box("Unknown variable : %s", name);
  return s;
}


const char* TVararray::getstring(int varnum)

{
  if (varnum >= _array.items())
    fatal_box("invalid variable number : %d", varnum);
  return ((TVar*) _array.objptr(varnum))->string();
}


///////////////////////////////////////////////////////////
// TExpression
///////////////////////////////////////////////////////////

TExpression::TExpression(const char* expression, TTypeexp type)
: _original(expression)

{
  _val = real(0.0);
  _dirty = TRUE;
  _type = type;
  if (!compile(expression, type))
  {
    error_box("Wrong expression : %s", expression);
    _code.clear();
  }
}


TExpression::TExpression(TTypeexp type)
: _original("")

{
  _val = real(0.0);
  _dirty = FALSE;
  _type = type;
  _code.clear();
}


TExpression::operator const real&()

{
  if (_dirty) eval();
  _dirty = FALSE;
  return _val.number();
}


TExpression::operator const char*()

{
  if (_dirty) eval();
  _dirty = FALSE;
  return _val.string();
}


void TExpression::print_on(ostream& out) const
{ out << _original; }


TExpression::operator bool()

{
  if (_dirty) eval();
  _dirty = FALSE;
  const real& r = _val.number();
  return !r.is_zero();
}


void TExpression::setvar(const char* varname, const real& val)

{
  _dirty = TRUE;
  _var.set(varname, val);
}


void TExpression::setvar(int varnum, const real& val)

{
  _dirty = TRUE;
  _var.set(varnum, val);
}


void TExpression::setvar(const char* varname, const char* val)

{
  _dirty = TRUE;
  _var.set(varname, val);
}


void TExpression::setvar(int varnum, const char* val)

{
  _dirty = TRUE;
  _var.set(varnum, val);
}


void TExpression::eval()

{
  TStack        evalstack(50);
  TCode         instr;
  real          o1, o2, zero(0.00);
  TString       s1, s2;

  _code.begin();
  while (!_code.end())
  {
    instr = _code.step();
    switch (instr.getsym())
    {
    case _invalid:
    case _endsym:
      break;
    case _variable:
      if (_type == _strexpr)
      {
        s1 = _var.getstring(instr.string());
        evalstack.push(s1);
      }
      else
      {
        o1 = _var.getnum(instr.string());
        evalstack.push(o1);
      }
      break;
    case _number:
      o1 = instr.number();
      evalstack.push(o1);
      break;
    case _string:
      s1 = instr.string();
      evalstack.push(s1);
      break;
    case _plus:
      if (_type == _strexpr)
      {
        s2 = (TString&) evalstack.pop();
        s1 = (TString&) evalstack.pop();
        s1 << s2;
        evalstack.push(s1);
      }
      else
      {
        o2 = (real&) evalstack.pop();
        o1 = (real&) evalstack.pop();
        evalstack.push(o1 + o2);
      }
      break;
    case _minus:
      o2 = (real&) evalstack.pop();
      o1 = (real&) evalstack.pop();
      evalstack.push(o1 - o2);
      break;
    case _multiply:
      o2 = (real&) evalstack.pop();
      o1 = (real&) evalstack.pop();
      evalstack.push(o1 * o2);
      break;
    case _divide:
      o2 = (real&) evalstack.pop();
      o1 = (real&) evalstack.pop();
      evalstack.push(o1 / o2);
      break;
    case _chgs:
      o1 = (real&) evalstack.pop();
      evalstack.push(-o1);
      break;
    case _and:
      if (_type == _strexpr)
      {
        o2 = real((const char*) (TString&)evalstack.pop());
        o1 = real((const char*) (TString&)evalstack.pop());
        evalstack.push(TString((o1 != zero && o2 != zero) ? "1" : "0"));
      }
      else
      {
        o2 = (real&) evalstack.pop();
        o1 = (real&) evalstack.pop();
        evalstack.push(real((o1 != zero && o2 != zero) ? 1.0 : 0.0));
      }
      break;
    case _or:
      if (_type == _strexpr)
      {
        o2 = real((const char*) (TString&)evalstack.pop());
        o1 = real((const char*) (TString&)evalstack.pop());
        evalstack.push(TString((o1 != zero || o2 != zero) ? "1" : "0"));
      }
      else
      {
        o2 = (real&) evalstack.pop();
        o1 = (real&) evalstack.pop();
        evalstack.push(real((o1 != zero || o2 != zero) ? 1.0 : 0.0));
      }
      break;
    case _not:
      o1 = (real&) evalstack.pop();
      evalstack.push(real((o1 == zero) ? 1.0 : 0.0));
      break;
    case _equal:
      if (_type == _strexpr)
      {
        s2 = (TString&) evalstack.pop();
        s1 = (TString&) evalstack.pop(); 
        evalstack.push(TString((s1 == s2) ? "1" : "0"));
      }
      else
      {
        o2 = (real&) evalstack.pop();
        o1 = (real&) evalstack.pop();
        evalstack.push(real((o1 == o2) ? 1.0 : 0.0));
      }
      break;
    case _match:
      s2 = (TString&) evalstack.pop();
      s1 = (TString&) evalstack.pop(); 
      evalstack.push(TString((s1.match(s2)) ? "1" : "0"));
      break;
    case _noteq:
      if (_type == _strexpr)
      {
        s2 = (TString&) evalstack.pop();
        s1 = (TString&) evalstack.pop();
        evalstack.push(TString(s1 != s2 ? "1" : "0"));
      }
      else
      {
        o2 = (real&) evalstack.pop();
        o1 = (real&) evalstack.pop();
        evalstack.push(real((o1 != o2) ? 1.0 : 0.0));
      }
      break;
    case _lt:
      if (_type == _strexpr)
      {
        s2 = (TString&) evalstack.pop();
        s1 = (TString&) evalstack.pop();
        evalstack.push(TString((s1 < (const char*)s2) ? "1" : "0"));
      }
      else
      {
        o2 = (real&) evalstack.pop();
        o1 = (real&) evalstack.pop();
        evalstack.push(real((o1 < o2) ? 1.0 : 0.0));
      }
      break;
    case _gt:
      if (_type == _strexpr)
      {
        s2 = (TString&) evalstack.pop();
        s1 = (TString&) evalstack.pop();
        evalstack.push(TString((s1 > (const char*)s2) ? "1" : "0"));
      }
      else
      {
        o2 = (real&) evalstack.pop();
        o1 = (real&) evalstack.pop();
        evalstack.push(real((o1 > o2) ? 1.0 : 0.0));
      }
      break;
    case _lteq:
      if (_type == _strexpr)
      {
        s2 = (TString&) evalstack.pop();
        s1 = (TString&) evalstack.pop();
        evalstack.push(TString((s1 <= (const char*)s2) ? "1" : "0"));
      }
      else
      {
        o2 = (real&) evalstack.pop();
        o1 = (real&) evalstack.pop();
        evalstack.push(real((o1 <= o2) ? 1.0 : 0.0));
      }
      break;
    case _gteq:
      if (_type == _strexpr)
      {
        s2 = (TString&) evalstack.pop();
        s1 = (TString&) evalstack.pop();
        evalstack.push(TString((s1 >= (const char*)s2) ? "1" : "0"));
      }
      else
      {
        o2 = (real&) evalstack.pop();
        o1 = (real&) evalstack.pop();
        evalstack.push(real((o1 >= o2) ? 1.0 : 0.0));
      }
      break;
    case _sqrt:
      evalstack.push(sqrt((real&) evalstack.pop()));
      break;
    case _sqr:
      evalstack.push(sqr((real&) evalstack.pop()));
      break;
    case _exp10:
      evalstack.push(exp10((real&) evalstack.pop()));
      break;
    case _exp:
      evalstack.push(exp((real&) evalstack.pop()));
      break;
    case _log10:
      evalstack.push(log10((real&) evalstack.pop()));
      break;
    case _log:
      evalstack.push(log((real&) evalstack.pop()));
      break;
    case _sin:
      evalstack.push(sin((real&) evalstack.pop()));
      break;
    case _cos:
      evalstack.push(cos((real&) evalstack.pop()));
      break;
    case _tan:
      evalstack.push(tan((real&) evalstack.pop()));
      break;
    case _left:
      o2 = (real&) evalstack.pop();
      s2 = (TString&) evalstack.pop();
      s1 = s2.left((int)o2.integer());
      evalstack.push(s1);
      break;
    case _right:
      o2 = (real&) evalstack.pop();
      s2 = (TString&) evalstack.pop();
      s1 = s2.right((int)o2.integer());
      evalstack.push(s1);
      break;
    case _mid:
    {
      int count = (int)((const real&)evalstack.pop()).integer();   
      if (count == 0) count--;
      const int from = (int)((const real&)evalstack.pop()).integer() - 1; 
      const TString& s = ((const TString&)evalstack.pop()).mid(from, count);
      evalstack.push(s);
    }  
      break;
    case _pow:
      o2 = (real&) evalstack.pop();
      o1 = (real&) evalstack.pop();
      evalstack.push(pow(o1, o2));
      break;
      o2 = (real&)evalstack.pop();
      o1 = (real&)evalstack.pop();
      evalstack.push(fnc_min(o1, o2));
      break;
    case _max:
      o2 = (real&) evalstack.pop();
      o1 = (real&) evalstack.pop();
      evalstack.push(fnc_min(o1, o2));
      break;
    case _upper:
      s1 = (TString&) evalstack.pop();
      s1.upper();
      evalstack.push(s1);
      break;
    case _round:
    {
      const int ndec = (int)((real&) evalstack.pop()).integer();
      o1 = (real&) evalstack.pop();
      o1.round(ndec);
      evalstack.push(o1);
    }
      break;
    default:
      break;
    }
  }
  if (_type == _strexpr)
    _val = (const char*) (TString&) evalstack.pop();
  else
    _val = (real&) evalstack.pop();

}


HIDDEN const char* _s;
HIDDEN char  _tok[81];
HIDDEN const char* _fntable[] = { "sqrt", "sqr", "exp10", "exp", "log10",
                                  "log", "sin", "cos", "tan", "left",
                                  "right", "pow", "min", "max", "mid", "upper", 
                                  "round", NULL };

HIDDEN TCodesym  _fntok[] = { _sqrt, _sqr, _exp10, _exp, _log10, _log,
                              _sin, _cos, _tan, _left, _right, _pow,
                              _min, _max, _mid, _upper, _round};

TCodesym TExpression::__gettoken(bool reduct)

{
  TCodesym      sym = _invalid;
  int                           i = 0;
  
  _tok[0] = '\0';
  while (isspace(*_s)) _s++;
  if (!*_s) return _endsym;
  if ((reduct) && (*_s == ','))
  {
    _s++;
    return _comma;
  }
  if (isdigit(*_s) || (*_s == '.') || (*_s == ','))
  {
    sym = _number;
    while (isalpha(*_s) || isdigit(*_s) || (*_s == '-') || (*_s == '[') ||
           (*_s == ']') || (*_s == '.') || (*_s == ':')  || 
           (!reduct && (*_s == ',')))
    {
      if (*_s == '-')
      {
        if (_s[1] != '>') break;
        _tok[i++] = *(_s++);
        _tok[i++] = *(_s++);
        sym = _variable;
      } 
      else 
      {   
        if (sym == _number)
          if ((isalpha(*_s)) ||(*_s == '[') || (*_s == ']') ||
              (*_s == ',') || (*_s == ':')) sym = _variable;
        _tok[i++] = *(_s++);
      } 
    } 
    _tok[i] = '\0';
    return sym;
  }
  if (isalpha(*_s) || (*_s == '#'))
  {
    _tok[i++] = *(_s++);
    while (isalpha(*_s) || isdigit(*_s) || (*_s == '-') || (*_s == '[') ||
           (*_s == ']') || (*_s == ':') || (!reduct && (*_s == ',')))
    {
      if (*_s == '-')
      {
        if (_s[1] != '>') break;
        _tok[i++] = *(_s++);
      } 
      _tok[i++] = *(_s++);
    } 
    _tok[i] = '\0';
    TString s(_tok);
    s.lower();
    for ( int j = 0; _fntable[j] != NULL; j++)
      if (s  == _fntable[j])  return _fntok[j];
    return _variable;
  }
  switch (*_s)
  {
  case '"' :
    _s++;
    while ((*_s) && (*_s != '"'))
      _tok[i++] = *(_s++);
    _tok[i] = '\0';
    if (*_s == '"') _s++;
    return _string;
  case ',' :
    _s++;
    return _comma;
  case '(' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _lpar;
  case ')' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _rpar;
  case '+' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _plus;
  case '-' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _minus;
  case '*' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _multiply;
  case '/' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _divide;
  case '&' :
    _tok[i++] = *(_s++);
    if (*_s != '&') break;
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _and;
  case '|' :
    _tok[i++] = *(_s++);
    if (*_s != '|') break;
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _or;
  case '!' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    if (*_s == '=')
    {
      _tok[i++] = *(_s++);
      _tok[i] = '\0';
      return _noteq;
    }
    return _not;
  case '=' :
    _tok[i++] = *(_s++);
    if (*_s == '=') _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _equal;
  case '?' :
    _tok[i++] = *(_s++);
    if (*_s == '=') _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _match;
  case '>' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    if (*_s == '=')
    {           
      _tok[i++] = *(_s++);
      _tok[i] = '\0';
      return _gteq;
    }
    return _gt;
  case '<' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    if (*_s == '=')
    {
      _tok[i++] = *(_s++);
      _tok[i] = '\0';
      return _lteq;
    }
    return _lt;
  default:
    break;
  }
  return _invalid;
}


TCodesym TExpression::__factor(TCodesym startsym)
  
{
  TCodesym      sym = _invalid;
  
  switch (startsym)
  {
  case _lpar:
    sym = __gettoken();
    sym = __expression(sym);
    if (sym == _rpar) sym = __gettoken();
    break;
  case _variable:
  case _number:
  case _string:
    _code.add(startsym, _tok);
    if (startsym == _variable)
    {
      for (int i = 0 ; i < numvar(); i++)
        if (strcmp(_tok, varname(i)) == 0) break;
      if (i == numvar()) _var.add(_tok);
    }
    sym = __gettoken(TRUE);
    break;
  case _sqrt:
  case _sqr:
  case _exp10:
  case _exp:
  case _log10:
  case _log:
  case _sin:
  case _cos:
  case _tan:
    sym = __gettoken();
    if (sym == _lpar) sym = __gettoken();
    else break;
    sym = __expression(sym);
    if (sym == _rpar) sym = __gettoken();
    else break;
    _code.add(startsym);
    break;
  case _upper:
    sym = __gettoken();
    if (sym == _lpar) sym = __gettoken();
    else break;
    if ((sym == _string) || (sym == _variable)) sym = __expression(sym);
    else break;
    if (sym == _rpar) sym = __gettoken();
    else break;
    _code.add(startsym);
    break;
  case _left:
  case _right:
  case _pow:
  case _min:
  case _max:
  case _round:
    sym = __gettoken();
    if (sym == _lpar) sym = __gettoken(TRUE);
    else break;
    sym = __expression(sym);
    if (sym == _comma) sym = __gettoken(TRUE);
    else break;
    sym = __expression(sym);
    if (sym == _rpar) sym = __gettoken();
    else break;
    _code.add(startsym);
    break;
  case _mid:
    sym = __gettoken();
    if (sym == _lpar) sym = __gettoken(TRUE);
    else break;
    if ((sym == _string) || (sym == _variable)) sym = __expression(sym);
    else break;
    if (sym == _comma) sym = __gettoken(TRUE);
    else break;
    sym = __expression(sym);
    if (sym == _comma) sym = __gettoken(TRUE);
    else break;
    sym = __expression(sym);
    if (sym == _rpar) sym = __gettoken();
    else break;
    _code.add(startsym);
    break;
  default:
    break;
  }
  return sym;
}


TCodesym TExpression::__term(TCodesym startsym)
  
{
  TCodesym      sym;

  sym = __factor(startsym);
  while ((sym != _endsym) && ((sym == _multiply) || (sym == _divide)))
  {
    TCodesym savedsym   = sym;
    sym = __gettoken();
    sym = __factor(sym);
    _code.add(savedsym);
  }

  return sym;
}


TCodesym TExpression::__expression(TCodesym startsym)
  
{
  TCodesym      sym;

  if ((startsym == _minus) || (startsym == _not) || (startsym == _plus))
    sym =__gettoken();
  else sym = startsym;
  sym = __term(sym);
  if ((startsym == _minus) || (startsym == _not))
    _code.add(startsym == _not ? startsym  : _chgs);
  while ((sym != _endsym) &&
         ((sym == _minus) || (sym == _plus) || (sym == _equal) ||
          (sym == _noteq) || (sym == _gt) || (sym == _lt) ||
          (sym == _gteq) || (sym == _lteq) || (sym == _and) ||
          (sym == _or) || (sym == _match)))
  {
    TCodesym savedsym   = sym;
    sym = __gettoken();
    sym = __term(sym);
    _code.add(savedsym);
  }
  return(sym);
}

bool TExpression::set(const char* expression, TTypeexp type) 
{ 
  _original = expression; 
  _type = type; 
  _dirty = TRUE; 
  return compile(expression, type); 
}

bool TExpression::compile(const char* expression, TTypeexp type)

{
  TString       sc(256);
  TCodesym      currsym;

  _s = expression;
  _type = type;
  while (((currsym = __gettoken()) != _endsym) && (currsym != _invalid))
  {
    if (currsym == _variable)
    {
      char *s1 = _tok;
      while ((*s1) && (*s1 != '[')) s1++;
      if (!*s1) sc << " " << _tok;
      else
      {
        *s1 = '\0';
        s1++;
        char *s2 = s1;
        while ((*s1) && (*s1 != ',')) s1++;
        if (!*s1) sc << " " << _tok;
        else
        {
          *s1 = '\0';
          s1++;
          int n1 = atoi(s2);
          s2 = s1;
          while ((*s1) && (*s1 != ']')) s1++;
          if (!*s1) sc << " " << _tok;
          else
          {
            const int n2 = atoi(s2);
            sc << format(" mid(%s,%d,%d)", _tok, n1, (n2 < n1) ? 0 : n2 - n1 + 1);
          }
        }
      }
    }
    else
      if (currsym == _string) sc << " \"" << _tok << "\"";
      else sc << " " << _tok;
  }
  _s = sc;
  _val = real(0.0);
  _code.clear();
  if (!*_s) return TRUE;
  if ((currsym = __gettoken()) == _invalid) return FALSE;
  return __expression(currsym) == _endsym;
}