#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