#define __EXPR_CPP
#include <expr.h>
#include <date.h>
#include <validate.h>

///////////////////////////////////////////////////////////
// TValue
///////////////////////////////////////////////////////////

real& TValue::number()
{ 
  if (_t == _strexpr) 
  { 
    if (TDate::isdate(_s))
      _r = TDate(_s).date2ansi();
    else
      _r = real(_s); 
    _t = _numexpr; 
  } 
  return _r; 
}

TString& TValue::string() 
{ 
  if (_t == _numexpr) 
  { 
    _s = _r.string(); 
    _t = _strexpr; 
  } 
  return _s; 
}

///////////////////////////////////////////////////////////
// TCodearray
///////////////////////////////////////////////////////////

void TCodearray::clear()
{
  destroy();
  TArray::add(new TCode(_endsym));
}

void TCodearray::add(TCodesym sym, const TValue& val)
{
  TArray::insert(new TCode(sym, val), last());
}

///////////////////////////////////////////////////////////
// TCode
///////////////////////////////////////////////////////////

TObject* TCode::dup() const
{
  return new TCode(*this);
}    

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

///////////////////////////////////////////////////////////
// TVar
///////////////////////////////////////////////////////////

TObject* TVar::dup() const
{
  return new TVar(*this);
}    

int TVar::compare(const TSortable& s) const
{
  const TVar& v = (const TVar&)s;
  return _name.compare(v._name);
}

///////////////////////////////////////////////////////////
// TVararray
///////////////////////////////////////////////////////////

void TVararray::add(const char* name, const TValue& val)
{
  _vars.add(new TVar(name, val));
  _vars.sort();  // Brutto, ma per ora va bene qui;
}

TVar* TVararray::find(int i) const
{
  TVar* v = (TVar*)_vars.objptr(i);
  CHECKD(v, "Variabile non trovata:", i);
  return v;
}

TVar* TVararray::find(const char* name) const
{
  int mi = 0, ma = _vars.last(), i = 0;
  while(mi <= ma)
  {
    i = (mi+ma)/2;
    TVar* v = find(i);
    const int cmp = v->getname().compare(name);
    if (cmp == 0)
      return v;
    if (cmp < 0)
      mi = i+1;
    else
      ma = i-1;
  }

  for (i = _vars.last(); i >= 0; i--)
  {
    TVar* v = find(i);
    if (v->getname() == name)
      return v;  // Se passa di qua ... m'incazzo
  }  

  CHECKS(NULL, "Variabile non trovata:", name);
  return NULL;
}

void TVararray::set(const char* name, const real& val)
{
  TVar* v = find(name);
  if (v != NULL) 
    *v = val;
}


void TVararray::set(const char* name, const char* val)
{
  TVar* v = find(name);
  if (v != NULL) 
    *v = val;
}

const real& TVararray::getnum(const char* name)
{
  TVar* v = find(name);
  if (v != NULL)
    return v->number();
  return ZERO;
}

const TString& TVararray::getstring(const char* name)
{
  TVar* v = find(name);
  if (v != NULL)
    return v->string();
  return EMPTY_STRING;
}

///////////////////////////////////////////////////////////
// TEval_stack
///////////////////////////////////////////////////////////

void TEval_stack::push(bool b)
{
  if (_sp < _data.items())
  {
    _sp++;
    peek_real() = b ? 1.0 : 0.0;
  }
  else  
    TStack::push(new TValue(b ? real(1.0) : ZERO));
}

void TEval_stack::push(int n)
{
  if (_sp < _data.items())
  {
    _sp++;
    peek_real() = n;
  }
  else  
    TStack::push(new TValue(real(n)));
}


void TEval_stack::push(const real& r)
{
  if (_sp < _data.items())
  {
    _sp++;
    peek_real() = r;
  }
  else  
    TStack::push(new TValue(r));
}

void TEval_stack::push(const TString& s)
{   
  if (_sp < _data.items())
  {
    _sp++;
    peek_string() = s;
  }
  else  
    TStack::push(new TValue(s));
}

void TEval_stack::push(const char* s)
{
  if (_sp < _data.items())
  {
    _sp++;
    peek_string() = s;
  }
  else  
    TStack::push(new TValue(s));
}

real& TEval_stack::pop_real()
{          
  TValue& o = (TValue&)pop();
  return o.number();
}

real& TEval_stack::peek_real()
{          
  TValue& o = (TValue&)peek(0);
  return o.number();
}

TString& TEval_stack::pop_string()
{          
  TValue& o = (TValue&)pop();
  return o.string();
}

TString& TEval_stack::peek_string()
{          
  TValue& o = (TValue&)peek(0);
  return o.string();
}

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

TExpression::TExpression(const char* expression, TTypeexp type, bool ignore_err)
           : _original(expression)
{
  _ignore_error=ignore_err;
  _error=0;
  _dirty = true;
  _type = type;
  compile(_original, type);
}


TExpression::TExpression(TTypeexp type, bool ignore_err)
{
  _ignore_error=ignore_err;   
  _error=0;
  _dirty = FALSE;
  _type = type;
  _code.clear();
}

TExpression::TExpression(const TExpression & expr)
           : _code(expr._code), _var(expr._var), 
             _val(expr._val), _ignore_error(expr._ignore_error),
             _error(expr._error), _dirty(expr._dirty), 
             _user_func_defined(expr._user_func_defined),
             _type(expr._type), _original(expr._original)
{
}

TObject* TExpression::dup() const
{
  TExpression* o = new TExpression(*this);
  return o;
}    

const real & TExpression::as_real()
{            
  if (user_func_dirty() || _dirty) 
    eval();
  _dirty = FALSE;
  return _val.number();
}

const TString & TExpression::as_string()
{            
  if (user_func_dirty() || _dirty) 
    eval();
  _dirty = FALSE;
  return _val.string();
}

bool TExpression::as_bool()
{            
  return !as_real().is_zero();
}

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

void TExpression::evaluate_user_func(int index, int nparms, TEval_stack& stack, TTypeexp  curtype) const
{                
  NFCHECK("Unknown function %d.", index);
  for (int i = nparms; i > 0; i--)
    stack.pop(); 
  stack.push(ZERO);
}    

void TExpression::setvar(const char* varname, const real& val)
{
  if (_var.getnum(varname) != val)
  {
    _var.set(varname, val);
    _dirty = TRUE;
  }  
}

void TExpression::setvar(int varnum, const real& val)
{
  if (_var.getnum(varnum) != val)
  {
    _var.set(varnum, val);
    _dirty = TRUE;
  }  
}

void TExpression::setvar(const char* varname, const char* val)
{
  if (_var.getstring(varname) != val)
  {
    _var.set(varname, val);
    _dirty = true;
  }  
}


void TExpression::setvar(int varnum, const char* val)
{
  if (_var.getstring(varnum) != val)
  {
    _var.set(varnum, val);
    _dirty = true;
  }  
}

bool TExpression::print_error(const char* msg) const
{
  return yesnofatal_box("%s", msg);
}

static bool str2date(const TString& s, TDate& d)
{
  if (s.blank() || s == "0")
    return true;

  if (TDate::isdate(s))
  {
    d = s;
    return true;
  }

  return false;
}

static bool all_zeroes(const TString& str)
{
  bool yes = str.not_empty();
  for (const char* s = str; *s && yes; s++)
    yes = (*s == '0') || isspace(*s);
  return yes;
}

void TExpression::eval()
{                                   
  TEval_stack evalstack;
  TBit_array  types;
  int         type_pointer = 0;
  TCode       instr;
    
  types.set(type_pointer, (_type == _numexpr));
  
  TTypeexp type = _type;
  
  _code.begin();
  while (!_code.end())
  {
    instr = _code.step();
    switch (instr.getsym())
    {
    case _invalid:
    case _endsym:
      break;
    case _variable:
      if (type == _strexpr)
      {
        const TString & s1 = _var.getstring(instr.string());
        evalstack.push(s1);
      }
      else
      {
        const real & r1 = _var.getnum(instr.string());
        evalstack.push(r1);
      }
      break;
    case _number:    
      {
        const real & r1 = instr.number();
        evalstack.push(r1);
      }
      break;
    case _string:
      {
        const TString & s1 = instr.string();
        evalstack.push(s1);
      }
      break;
    case _plus:                    
      if (type == _strexpr)
      {  
        // Non unire le seguenti righe
        const TString& s = evalstack.pop_string();
        evalstack.peek_string() << s;
      }
      else
      {
        // Non unire le seguenti righe
        const real& r = evalstack.pop_real();
        evalstack.peek_real() += r;
      }
      break;
    case _minus:
      {
        const real& r = evalstack.pop_real();
        evalstack.peek_real() -= r;
      }
      break;
    case _multiply:
      {
        const real& r = evalstack.pop_real();
        evalstack.peek_real() *= r;
      }
      break;
    case _divide:
      {
        const real& r = evalstack.pop_real();
        if (r.is_zero())
        {
          if (!evalstack.peek_real().is_zero())
            if (_ignore_error)
              _error=1;
            else
              print_error("Divisione per zero!");
        } else
          evalstack.peek_real() /= r;
      }
      break;
    case _chgs:
      {
        real & r = evalstack.peek_real();
        r = -r;
      }
      break;
    case _and:
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();   
        r1 = (!r1.is_zero() && !r2.is_zero()) ? UNO : ZERO;
      }
      break;
    case _or:
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();   
        r1 = (r1 != ZERO || r2 != ZERO) ? UNO : ZERO;
      }
      break;
    case _not:
      {
        real & r1 = evalstack.peek_real();
        r1 = (r1 == ZERO) ? UNO : ZERO;
      }
      break;
    case _equal:
      if (type == _strexpr)
      {
        const TString & s2 = evalstack.pop_string();
        const TString & s1 = evalstack.pop_string(); 
        evalstack.push(s1 == s2);
      }
      else
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();
        r1 = (r1 == r2) ? 1.0 : 0.0;
      }
      break;
    case _match:                                    
     {
        const TString & s2 = evalstack.pop_string();
        const TString & s1 = evalstack.pop_string(); 
        evalstack.push(s1.match(s2, true)); // Match ignoring case
     }
     break;
    case _noteq:
      if (type == _strexpr)
      {
        const TString & s2 = evalstack.pop_string();
        TString & s1 = evalstack.pop_string(); 
        evalstack.push(s1 != s2);
      }
      else
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();
        r1 = (r1 != r2) ? 1.0 : 0.0;
      }
      break;
    case _lt:
      if (type == _strexpr)
      {
        const TString & s2 = evalstack.pop_string();
        const TString & s1 = evalstack.pop_string(); 
        evalstack.push(s1 < s2);
      }
      else
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();
        r1 = (r1 < r2) ? 1.0 : 0.0;
      }
      break;
    case _gt:
      if (type == _strexpr)
      {
        const TString & s2 = evalstack.pop_string();
        const TString & s1 = evalstack.pop_string(); 
        evalstack.push(s1 > s2);
      }
      else
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();
        r1 = (r1 > r2) ? 1.0 : 0.0;
      }
      break;
    case _lteq:
      if (type == _strexpr)
      {
        const TString& s2 = evalstack.pop_string();
        const TString& s1 = evalstack.pop_string(); 
        evalstack.push(s1 <= s2);
      }
      else
      {
        const real& r2 = evalstack.pop_real();
        real& r1 = evalstack.peek_real();
        r1 = (r1 <= r2) ? 1.0 : 0.0;
      }
      break;
    case _gteq:
      if (type == _strexpr)
      {
        const TString& s2 = evalstack.pop_string();
        const TString& s1 = evalstack.pop_string(); 
        evalstack.push(s1 >= s2);
      }
      else
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();
        r1 = (r1 >= r2) ? 1.0 : 0.0;
      }
      break;  
    case _userfunc: 
      {
        const int nparms = (int) evalstack.pop_real().integer();
        const int index = atoi(instr.string());
        
        evaluate_user_func(index, nparms, evalstack, type);
      }
      break;
    case _sqrt:
      {
        real& r = evalstack.peek_real();
        if (r < ZERO)
        {
          if (_ignore_error)
            _error=1;
          else
            print_error("Radice negativa!");
          r = -r;
        } 
        r = sqrt(r);
      }
      break;
    case _sqr:
      evalstack.peek_real() = sqr(evalstack.peek_real());
      break;
    case _exp10:
      evalstack.peek_real() = exp10(evalstack.peek_real());
      break;
    case _exp:
      evalstack.peek_real() = exp(evalstack.peek_real());
      break;
    case _log10:
      evalstack.peek_real() = log10(evalstack.peek_real());
      break;
    case _log:
      evalstack.peek_real() = log(evalstack.peek_real());
      break;
    case _sin:
      evalstack.peek_real() = sin(evalstack.peek_real());
      break;
    case _cos:
      evalstack.peek_real() = cos(evalstack.peek_real());
      break;
    case _tan:
      evalstack.peek_real() = tan(evalstack.peek_real());
      break;
    case _left:
      {
        const int len = (int)evalstack.pop_real().integer();
        TString & s1 = evalstack.peek_string();
        s1.cut(len);
      }
      break;
    case _right:
      {
        const int len = (int)evalstack.pop_real().integer();
        TString & s1 = evalstack.peek_string();
        s1 = s1.right(len);
      }
      break;
    case _mid:
      {
        int count = (int)evalstack.pop_real().integer();   
        if (count == 0) count--;
        int from = (int)evalstack.pop_real().integer() - 1; 
        if (from < 0) from = 0;
        TString & s1 = evalstack.peek_string();
        s1 = s1.mid(from, count);
      }  
      break;
    case _substr:
      {
        const int to = (int)evalstack.pop_real().integer();   
        int from = (int)evalstack.pop_real().integer() - 1; 
        if (from < 0) from = 0;
        TString & s1 = evalstack.peek_string();
        s1 = s1.sub(from, to);
      }  
      break;
    case _len:
      {
        const TString& s1 = evalstack.pop_string();
        evalstack.push(s1.len());
      }  
      break;
    case _pow:
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();
        r1 = pow(r1, r2);
      }
      break;
    case _min:
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();
        if (r2 < r1)
          r1 = r2;
      }
      break;
    case _max:
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();
        if (r2 > r1)
          r1 = r2;
      }
      break;
    case _upper:
      evalstack.peek_string().upper();
      break;
    case _trim:
      evalstack.peek_string().trim();
      break;
    case _round:
      {
        const int ndec = (int)(evalstack.pop_real()).integer();
        real & r = evalstack.peek_real();
        r.round(ndec);
      }
      break;
    case _trunc:
      {
        const int ndec = (int)(evalstack.pop_real()).integer();
        real & r = evalstack.peek_real();
        r.trunc(ndec);
      }
      break;
    case _ceil:
      {
        const int ndec = (int)(evalstack.pop_real()).integer();
        real & r = evalstack.peek_real();
        r.ceil(ndec);
      }
      break;
    case _perc:
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();
        r1 = real(r1 * r2 / 100.0);
      }
      break;
    case _scorp:
      {
        const real & r2 = evalstack.pop_real();
        real & r1 = evalstack.peek_real();
        r1 -= r1 * r2 / (r2 + 100.0);
      }
      break;
    case _if:
      {     
        if (type == _strexpr)
        {
          const TString & s1 = evalstack.pop_string();
          const TString & s2 = evalstack.pop_string();
          const real & cond = evalstack.pop_real();
          evalstack.push(cond.is_zero() ? s1 : s2);
        }
        else
        {
          const real & r1 = evalstack.pop_real();
          const real & r2 = evalstack.pop_real();
          real & cond = evalstack.peek_real();
          cond = cond.is_zero() ? r1 : r2;
        }
      }
      break;
    case _ansi:
      {            
        TString& s = evalstack.peek_string();
				if (!s.empty())
				{
					const TDate d(s);
					s = d.string(ANSI);
				}
      }
      break;     
    case _num:      
      type_pointer++;
      types.set(type_pointer, FALSE);
      type = _strexpr;
      break;
    case _endnum:
      type_pointer--;
      type = types[type_pointer] ? _numexpr : _strexpr;
      if (type == _numexpr)
        evalstack.peek_real();
      break;
    case _str:
      type_pointer++;
      types.set(type_pointer, TRUE);
      type = _numexpr;
      break;
    case _endstr:
      type_pointer--;
      type = types[type_pointer] ? _numexpr : _strexpr;
      if (type == _strexpr)
        evalstack.peek_string();
      break;
    case _between:
      {
        TString& s3 = evalstack.pop_string();
        if (all_zeroes(s3)) s3.cut(0); // Svuota una stringa finale di soli zeri
        TString& s2 = evalstack.pop_string();
        if (all_zeroes(s2)) s2.cut(0); // Svuota una stringa iniziale di soli zeri
        const TString& s1 = evalstack.pop_string();
        bool good = true;
        if (s2.full() || s3.full())
        {
          bool done = false;
          if (TDate::isdate(s1))
          {
            TDate d2, d3;
            done = str2date(s2, d2) && str2date(s3, d3);
            if (done)
            {
              const TDate d1(s1);
              good = (d1 >= d2) && (!d3.ok() || d1 <= d3);
            }
          }
          if (!done)
          {
            if (type == _strexpr)
              good = s1 >= s2 && (s3.empty() || s1 <= s3);
            else
            {
              const real r1(s1), r2(s2), r3(s3);
              good = (s2.empty() || r1 >= r2) && (s3.empty() || r1 <= r3);
            }
          }
        }
        evalstack.push(good ? UNO : ZERO);
      }
      break;
    case _cfcheck:
      {
        const TString& s1 = evalstack.pop_string();
        const TString& s2 = evalstack.pop_string();
        const bool good = s1.len() < s2.len() ? cf_check(s1, s2) : cf_check(s2, s1);
        evalstack.push(good ? UNO : ZERO);
      }
      break;
    case _picheck:
      {
        const TString& s1 = evalstack.pop_string();
        const TString& s2 = evalstack.pop_string();
        const bool good = s1.len() < s2.len() ? pi_check(s1, s2) : pi_check(s2, s1);
        evalstack.push(good ? UNO : ZERO);
      }
      break;
    case _year:
      {
        const TDate d = evalstack.pop_string();
        evalstack.push(d.year());
      }
      break;
    default:
      NFCHECK("operazione non valida %d", (int) instr.getsym());
      break;
    }
  }
  
  // Lo stack non e' vuoto
  if (_code.items() > 1)
    _val = (const TValue&)evalstack.pop();
  else
    _val = ZERO;    
}


HIDDEN const char* _s;
HIDDEN char  _tok[81];

TCodesym TExpression::tok2fun(const char* tok) const
{                                                   
  const int MAX_TOK = 32;
  HIDDEN const char* fnstr[MAX_TOK] = { "ANSI",    "BETWEEN","CEIL",    "CF_CHECK","COS",   
                                        "EXP",     "EXP10",  "IF",      "LEFT",    "LEN",     
                                        "LOG",     "LOG10",  "MAX",     "MID",     "MIN",     
                                        "NUM",     "PERC",   "PI_CHECK","POW",     "RIGHT",
                                        "ROUND",   "SCORP",  "SIN",     "SQR",     "SQRT",   
                                        "STR",     "SUBSTR", "TAN",     "TRIM",    "TRUNC",  
                                        "UPPER",   "YEAR" };
  
  HIDDEN TCodesym fntok[MAX_TOK] =    { _ansi,   _between, _ceil,     _cfcheck,  _cos,    
                                        _exp,    _exp10,   _if,       _left,     _len,      
                                        _log,    _log10,   _max,      _mid,      _min,      
                                        _num,    _perc,    _picheck,  _pow,      _right,    
                                        _round,  _scorp,   _sin,      _sqr,      _sqrt,   
                                        _str,    _substr,  _tan,      _trim,     _trunc,  
                                        _upper,  _year };
                                  
  int f = 0, l = MAX_TOK-1, i = MAX_TOK/2;                    
  for (;;)
  {
//    i = (f+l)>>1;                  
    i = f + (toupper(*tok) - *fnstr[f]) * (l - f + 1) / (*fnstr[l] - *fnstr[f] + 1);
      
    if (i < f || i > l)
      return _invalid;
      
    const int cmp = xvt_str_compare_ignoring_case(tok, fnstr[i]);

    if (cmp == 0) 
      break;
    if (cmp > 0) f = i+1;
    else         l = i-1;
    
    if (f > l) 
      return _invalid;
  }      
  CHECKD(i >= 0 && i < MAX_TOK, "Invalid function index ", i);
  return fntok[i];
}

                                  

TCodesym TExpression::__gettoken()
{
  TCodesym sym = _invalid;
  int i = 0;
  
  _tok[0] = '\0';
  while (isspace(*_s)) _s++;
  if (!*_s) return _endsym;

  bool square_bracket = FALSE;  
  
  if (isdigit(*_s) || (*_s == '.'))
  {
    sym = _number;
    while (isalnum(*_s) || (*_s == '_') || (*_s == '.') ||
           (*_s == ':')  || (*_s == '@')  || 
           (sym == _variable && *_s == '[') || 
           (sym == _number && *_s == '-' ) || 
           (square_bracket && (*_s == ',' || *_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 == ']') || (*_s == ',')  */ 
             ) 
            sym = _variable;
        }
        else
        {
          if (sym == _variable && *_s == '[')
            square_bracket = TRUE;
        }
        _tok[i++] = *(_s++);
      } 
    } 
    _tok[i] = '\0';
    return sym;
  }
  if (isalpha(*_s) || (*_s == '#') || (*_s == '_'))
  {                               
    if (*_s == '#')
      sym = _variable;
    
    _tok[i++] = *(_s++); 
    
    if (sym == _variable && *_s == '-')
    {
      _tok[i++] = *(_s++);
      if (*_s == '>' || isdigit(*_s)) 
	      _tok[i++] = *(_s++);
			else
				return _invalid;                // Non e' una -> (freccia)
    }
    while (isalnum(*_s) || (*_s == '-') || (*_s == '[') ||
           (*_s == ':') || (*_s == '_') || (*_s == '.') ||
           (square_bracket && (*_s == ',' || *_s == ']'))
          ) 
    {
      if (*_s == '-')
      {
        if (_s[1] != '>') break;
        _tok[i++] = *(_s++);
        _tok[i++] = *(_s++);
        sym = _variable;
      } else 
      if (*_s == '[')
      {
        sym = _variable;
        square_bracket = TRUE;
      } else
      if (*_s == '.')
        sym = _variable;

      _tok[i++] = *(_s++);
    } 
    _tok[i] = '\0';
    
    if (sym != _variable)     // So gia' tutto, inutile controllare oltre
    {
      sym = tok2fun(_tok);    
      if (sym != _invalid)
        return sym;
  		const char * p;
  		
      for (p = _s; isspace(*p); p++);
      if (*p == '(')
        return _userfunc; 
    }
    
    return _variable;
  }
  switch (*_s)
  {
  case '"' :
  case '\'' :
    {
      const char sep = *_s;
      _s++;
      while ((*_s) && (*_s != sep))
        _tok[i++] = *(_s++);
      _tok[i] = '\0';
      if (*_s == sep) _s++;
      return _string;
    }
  case ',' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _comma;
  case ';' :
    _tok[i++] = *(_s++);
    _tok[i] = '\0';
    return _semicolon;
  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(); 
    else sym = _invalid;
    break;
  case _variable:
    {     
      int from = 1, to = 0;
      char* quadra = strchr(_tok, '[');
      if (quadra)
      {
        if (sscanf(quadra, "[%d,%d]", &from, &to) == 0)
        {
          sym = _invalid;
          break;
        }
        *quadra = '\0';
      }                        
      _code.add(_variable, _tok);
      if (quadra)
      {
        _code.add(_number, real(from));
        _code.add(_number, real(to >= from ? to-from+1 : 0));
        _code.add(_mid);
      } 
      
      int i;
       
      for (i = numvar()-1 ; i >= 0; i--)
        if (strcmp(_tok, varname(i)) == 0) break;
      if (i < 0) _var.add(_tok);
      sym = __gettoken();
    }
    break;
  case _number:
  case _string:
    _code.add(startsym, _tok);
    sym = __gettoken();
    break;  
  case _userfunc:
    {       
      TValue val (_tok);
      int parms_found = 0;
      sym = __function(-1, &parms_found);
      if (sym != _invalid)
      {
        
        const int index = parse_user_func(val.string(), parms_found);
        if (index < 0) 
        {
          strncpy(_tok, val.string(), sizeof(_tok));
          sym = _invalid;
        }
        else
        {                     
          _code.add(_number, real(parms_found));
          val = real(index);
          _code.add(startsym, val);
          _user_func_defined = TRUE;                      
        }  
      }
    }
    break;  
  case _sqrt:
  case _sqr:
  case _exp10:
  case _exp:
  case _log10:
  case _log:
  case _sin:
  case _cos:
  case _tan:
  case _ansi:
  case _upper:
  case _trim:
  case _len:
  case _year:
    sym = __function(1);
    _code.add(startsym);
    break;
  case _left:
  case _right:
  case _pow:
  case _min:
  case _max:
  case _round: 
  case _ceil:
  case _trunc:
  case _perc:
  case _scorp:
  case _cfcheck:
  case _picheck:
    sym = __function(2);
    _code.add(startsym);
    break;
  case _between:
  case _mid:
  case _substr:
    sym = __function(3);
    _code.add(startsym);
    break;    
  case _if:
    sym = __function(3);
    _code.add(startsym);
    break;    
  case _num:
  case _str:
    _code.add(startsym);
    sym = __function(1);
    _code.add(startsym == _num ? _endnum : _endstr);
    break;
  default:
    break;
  }
  return sym;
}


TCodesym TExpression::__term(TCodesym startsym)
{
  TCodesym sym = __factor(startsym);
  while (sym == _multiply || sym == _divide)
  {
    const 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);
}

TCodesym TExpression::__function(int nparms, int* actual_nparams)
{
  TCodesym sym = __gettoken();
  if (sym != _lpar)
    return _invalid;
  int found = 0;
  sym = __gettoken();
  if (sym == _rpar)
  {
    if (actual_nparams) 
      *actual_nparams = found;
    return nparms <= 0 ? __gettoken() : _invalid;
  }
  found++;
  sym = __expression(sym);
  while (sym == _comma || sym == _semicolon)
  {
    sym = __gettoken();
    found++;
    sym = __expression(sym);
  }
  
  if (sym == _rpar)
  {
    if (actual_nparams) 
      *actual_nparams = found;
    return nparms < 0 || found == nparms ? __gettoken() : _invalid;
  }
  return _invalid;
}

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

bool TExpression::compile(const TString& expression, TTypeexp type)
{                      
  _user_func_defined = FALSE;                      
  _s = expression;
  _type = type;  
  _val = ZERO;
  _code.clear();
  if (expression.blank()) 
    return true;
 
  TCodesym currsym = __gettoken();                      
  bool ok = currsym != _invalid;
  if (ok) 
    ok = __expression(currsym) == _endsym;
  if (!ok)
  {
    _error = 2;
    if (!_ignore_error)
    {
      TString msg; 
      msg << "Espressione errata : " << _original;
      print_error(msg);
    }
  }  

  return ok;
}

const char* TExpression::last_token() const
{
  return _tok;
}