#ifndef __EXPR_H
#define __EXPR_H

#ifndef __STRINGS_H
#include <strings.h>
#endif

#ifndef __REAL_H
#include <real.h>
#endif

#ifndef __ARRAY_H
#include <array.h>
#endif

// @T 
// @DES I simboli/istruzioni possibili
// @T 

enum TCodesym { _invalid, _endsym, _comma, _lpar, _rpar, _variable,
                _number, _string, _plus, _minus, _multiply, _divide,
                _chgs, _and, _or, _not, _equal, _match, _noteq, _lt, _gt,
                _lteq, _gteq, _sqrt, _sqr, _exp10, _exp, _log10, _log,
                _sin, _cos, _tan, _left, _right, _pow, _min, _max, _mid,
                _upper } ;

                // @DES I tipi di espressioni definiti
                // @T 
                enum TTypeexp { _numexpr, _strexpr } ;
                // @END 

                // @C
                // Classe TValue : public TObject
                // @END

                class TValue : public TObject
{
  // @DPRIV
  real    _r;     // Valore real
  TString _s;     // Valore in formato stringa 

public:
  // @FPUB

  TValue& operator =(const TValue& val) { _s = val._s; _r = val._r; return *this;} // Operatore = tra oggetti TValue
  const real& number() const { return _r;}  // Ritorna il valore numerico
  const char* string() const { return (const char*) _s;}  // Ritorna il valore come stringa
  void set(const real& val) { _r = val; _s = val.string();}       // Real
  void set(const char* val) { _s = val; _r = real(val);}  // Stringa

  // @DES Costruttori. Inizializzano TValue con vari oggetti
  // @FPUB
  TValue(const real& val) { _r = val; _s = val.string();}         // Real
  TValue(const char* val) { _s = val; _r = real(val);}    // Stringa
  TValue(const TValue& val) { *this = val; }      // Altro TValue
  TValue() { _r = 0.00; _s = ""; }        // 0,0 e ""
};


#ifdef __EXPR_CPP
#define extern
#endif

// @DPUB
extern TValue nulltvalue;
// @END

#undef extern

// @C
// Classe TCode : public TObject
// @END

class TCode : public TObject
{
  // @DPRIV
  TCodesym        _sym;   // Simbolo-istruzione-codice
  TValue          _val;   // Valore

public:
  // @FPUB
  TCode& operator =(const TCode& b);
  void set(TCodesym sym, const TValue& val = nulltvalue) { _sym = sym; _val = val; } // Inizializza simbolo = sym e valore = val
  TCodesym getsym() const { return _sym;} // Ritorna il simbolo _sym
  const real& number() const { return _val.number();} // Ritorna il valore _val come real
  const char* string() const { return _val.string();} // Ritorna il valore _val come stringa

  TCode() {set(_invalid);} // Costruttore, inizializza simbolo con "invalid", valore a nullvalue
  TCode(TCodesym sym, const TValue& val = nulltvalue) { set(sym, val);} // Costruttore, inizializza simbolo con sym e valore con val
};

// @C
// Classe TCodearray : public TObject
// 
// L'array di istruzioni
//
// @END

class TCodearray : public TObject
{
  // @DPRIV
  int             _last;  // Numero di istruzioni
  int             _ip;    // Puntatore all'istruzione corrente (Istruction pointer)
  TArray          _rpn;   // Array
  // @END

public:
  // @FPUB
  void clear();           // Cancella contenuto array
  void add(TCodesym sym, const TValue& val = nulltvalue); // Aggiunge un'istruzione all'array
  void begin() { _ip = 0;}        // Mette all'inizio il puntatore all'istruzione corrente
  TCode& step() { return (TCode&) _rpn[end() ? _ip : _ip++];}     // Incrementa istruction pointer
  bool end() const { return ((TCode&) _rpn[_ip]).getsym() == _endsym;} // Ritorna vero se _ip ha raggiunto il simbolo di fine codice
  void backtrace(int step = 1) { _ip > step ? _ip -= step : begin(); }
  TCodearray(int size = 50); // Il costruttore crea un array di 10 elementi
};

// @C
// Classe TVar : public TObject
// @END

class TVar : public TObject
{
  // @DPRIV
  TString _name;  // Nome variabile
  TValue  _val;   // Valore

public:

  // @DES Operatore = tra vari oggetti
  // @FPUB
  const char* operator =(const char* val) { _val.set(val); return val;}
  const real& operator =(const real& val) { _val.set(val); return val;}
  TVar& operator =(const TValue& val) { _val = val; return *this;}
  TVar& operator =(const TVar& var) { _name = var._name ; _val = var._val; return *this;}
  void set(const char* name, const TValue& val = nulltvalue) { _name = name ; _val = val;}
  void setname(const char* name) { _name = name;} // Setta a name il nome della variabile
  const char* getname() const { return _name;} // Ritorna il nome della variabile

  operator TValue&() { return _val;}  // Ritorna _val (un TValue)
  const real& number() const { return _val.number();} // Ritorna il valore real della variabile
  const char* string() const { return _val.string();} // Ritorna il valore stringa della variabile

  // @DES Costruttori
  // @FPUB
  TVar() { _name = ""; _val = nulltvalue;}
  TVar(const char* name, const TValue& val = nulltvalue) { _name = name; _val = val;}
  TVar(TVar& v) { _name = v._name; _val = v._val;}
};

// @C
// class TVararray : public TObject
// @END

class TVararray : public TObject
{
  // @DPRIV
  int             _last;  // Numero di variabili
  TArray           _array; // Array
  // @END

public:
  // @FPUB
  void clear()    { _last = 0; } // Cancella contenuto array
  void add(const TVar& var); // Aggiunge un oggetto TVar
  void add(const char* name, const TValue& val = nulltvalue); // Aggiunge un nome di variabile e il suo valore
  const char* varname(int varnum) const { return  varnum < _array.items() ? ((TVar&) _array[varnum]).getname() : "";} // Ritorna il nome della variabile di posto varnum

  // @DES Metodi di inizializzazione
  // @FPUB
  void set(const char* varname, const real& val);
  void set(const char* varname, const char* val);
  void set(int varnum, const real& val) { if (varnum < _array.items()) ((TVar&) _array[varnum]) = val;}
  void set(int varnum, const char* val) { if (varnum < _array.items()) ((TVar&) _array[varnum]) = val;}

  // @DES Metodi di interrogazione
  // @FPUB
  const real& getnum(const char* varname);
  const real& getnum(int varnum);
  const char* getstring(const char* varname);
  const char* getstring(int varnum);

  int   numvar() const { return _last;} // Ritorna il numero di variabili utilizzate

  TVararray(int size = 10);
};

// @C
// class TExpression : public TObject
// @END

class TExpression : public TObject
{
  // @DPRIV
  TCodearray _code;     // Array di codice
  TVararray   _var;     // Array di variabili 
  TValue      _val;     // Valore dell'espressione
  bool      _dirty;     // Vero se l'espressione e' stata modificata
  TTypeexp   _type;     // Tipo dell'espressione
  TString    _original; // stringa originale

  // @END

  // @FPROT
protected:
  void eval();  // Valuta l'espressione
  TCodesym __gettoken(bool reduct = FALSE);
  TCodesym __factor(TCodesym startsym);
  TCodesym __term(TCodesym startsym);
  TCodesym __expression(TCodesym startsym);
  virtual void print_on(ostream& out) const ;
  bool compile(const char* expression, TTypeexp type); // Compila l'espressione

public:
  // @FPUB
  operator const real&(); // Ritorna il valore real dell'espressione
  operator const char*(); // Ritorna il valore come stringa
  operator bool();        // Ritorna il valore come booleano
  // @DES Metodi di interrogazione
  // @FPUB
  // Ritorna il nome della variabile di posto varnum
  const char* varname(int varnum) const { return _var.varname(varnum); }
  // Ritorna il numero di variabili nell'espressione
  int numvar() const { return _var.numvar(); } 
  const TTypeexp type() const { return _type; }
  TCodearray& code() const { return (TCodearray&)_code; }
  const TVararray& vars() const { return _var; }

  // @DES Metodi di inizializzazione
  // @FPUB
  void setvar(const char* varname, const real& val);
  void setvar(int varnum, const real& val);
  void setvar(const char* varname, const char* val);
  void setvar(int varnum, const char* val);
  void set_type(const TTypeexp type) { _type = type; }

  bool set(const char* expression, TTypeexp type = _numexpr);
  TExpression(const char* expression, TTypeexp type = _numexpr);
  TExpression(TTypeexp type = _numexpr);
};

#endif // __EXPR_H