1515 lines
34 KiB
C++
Executable File
1515 lines
34 KiB
C++
Executable File
#define __EXPR_CPP
|
||
#include <expr.h>
|
||
#include <date.h>
|
||
#include <diction.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 ? UNO : ZERO;
|
||
}
|
||
else
|
||
TStack::push(new TValue(b ? UNO : 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()
|
||
{
|
||
if (count() == 0)
|
||
push(ZERO);
|
||
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()
|
||
{
|
||
if (count() == 0)
|
||
push(EMPTY_STRING);
|
||
TValue& o = (TValue&)peek(0);
|
||
return o.string();
|
||
}
|
||
|
||
TTypeexp TEval_stack::top1_type() const
|
||
{
|
||
return count() > 0 ? ((const TValue&)peek(0)).type() : _mixexpr;
|
||
}
|
||
|
||
TTypeexp TEval_stack::top2_type() const
|
||
{
|
||
if (count() >= 2)
|
||
{
|
||
const TValue& o1 = (const TValue&)peek(0);
|
||
const TValue& o2 = (const TValue&)peek(1);
|
||
if (o1.type() == o2.type())
|
||
return o1.type();
|
||
}
|
||
return _mixexpr;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// 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
|
||
{
|
||
return new TExpression(*this);
|
||
}
|
||
|
||
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")
|
||
{
|
||
d = 0L;
|
||
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 (evalstack.top2_type() == _numexpr)
|
||
{
|
||
const real & r2 = evalstack.pop_real();
|
||
real& r1 = evalstack.peek_real();
|
||
r1 = (r1 == r2) ? UNO : ZERO;
|
||
}
|
||
else
|
||
{
|
||
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) ? UNO : ZERO;
|
||
}
|
||
}
|
||
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) ? UNO : ZERO;
|
||
}
|
||
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) ? UNO : ZERO;
|
||
}
|
||
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) ? UNO : ZERO;
|
||
}
|
||
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) ? UNO : ZERO;
|
||
}
|
||
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) ? UNO : ZERO;
|
||
}
|
||
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 / CENTO);
|
||
}
|
||
break;
|
||
case _scorp:
|
||
{
|
||
const real & r2 = evalstack.pop_real();
|
||
real & r1 = evalstack.peek_real();
|
||
r1 -= r1 * r2 / (r2 + CENTO);
|
||
}
|
||
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 _bool:
|
||
{
|
||
if (evalstack.top1_type() == _numexpr)
|
||
{
|
||
real& r1 = evalstack.peek_real();
|
||
if (!r1.is_zero()) r1 = UNO;
|
||
}
|
||
else
|
||
{
|
||
const TString& s = evalstack.pop_string();
|
||
if (s.full() && strchr("1SXY", s[0]))
|
||
evalstack.push(UNO);
|
||
else
|
||
evalstack.push(ZERO);
|
||
}
|
||
}
|
||
break;
|
||
case _ansi:
|
||
{
|
||
TString& s = evalstack.peek_string();
|
||
TDate d;
|
||
if (str2date(s, d))
|
||
s = d.string(ANSI);
|
||
else
|
||
s.cut(0);
|
||
}
|
||
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.between(d2, 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;
|
||
case _zerofill:
|
||
{
|
||
const int len = evalstack.pop_real().integer();
|
||
TString& s = evalstack.peek_string();
|
||
s.right_just(len, '0');
|
||
}
|
||
break;
|
||
case _scon2perc:
|
||
{
|
||
const TString& s = evalstack.pop_string();
|
||
TString80 tmp;
|
||
real perc;
|
||
scontoexpr2perc(s, false, tmp, perc);
|
||
evalstack.push(perc);
|
||
}
|
||
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 = 35;
|
||
HIDDEN const char* fnstr[MAX_TOK] = { "ANSI", "BETWEEN","BOOL", "CEIL", "CF_CHECK",
|
||
"COS", "EXP", "EXP10", "IF", "LEFT", "LEN",
|
||
"LOG", "LOG10", "MAX", "MID", "MIN",
|
||
"NUM", "PERC", "PI_CHECK","POW", "RIGHT",
|
||
"ROUND", "SCON2PERC", "SCORP","SIN", "SQR", "SQRT",
|
||
"STR", "SUBSTR", "TAN", "TRIM", "TRUNC",
|
||
"UPPER", "YEAR", "ZEROFILL" };
|
||
|
||
HIDDEN TCodesym fntok[MAX_TOK] = { _ansi, _between, _bool, _ceil, _cfcheck,
|
||
_cos, _exp, _exp10, _if, _left, _len,
|
||
_log, _log10, _max, _mid, _min,
|
||
_num, _perc, _picheck, _pow, _right,
|
||
_round, _scon2perc,_scorp, _sin, _sqr,
|
||
_sqrt, _str, _substr, _tan, _trim,
|
||
_trunc, _upper, _year, _zerofill };
|
||
|
||
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 '(' :
|
||
case '{' :
|
||
_tok[i++] = *(_s++);
|
||
_tok[i] = '\0';
|
||
return _lpar;
|
||
case ')' :
|
||
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_s(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_s(_tok, sizeof(_tok), val.string(), sizeof(_tok)-1);
|
||
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:
|
||
case _scon2perc:
|
||
case _bool:
|
||
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:
|
||
case _zerofill:
|
||
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;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// Utility sugli sconti
|
||
///////////////////////////////////////////////////////////
|
||
|
||
bool scontoexpr2perc(const TString& exp, bool signal , TString & goodexp, real & val_perc )
|
||
{
|
||
bool valid = true;
|
||
|
||
goodexp.cut(0);
|
||
// Elimina gli spazi molesti
|
||
// work.strip_spaces( );
|
||
val_perc = UNO;
|
||
if (exp.full())
|
||
{
|
||
TString80 num;
|
||
bool dec = false; // Flag che indica se si attende l'inizio di un numero
|
||
bool startnum = true; // Flag che indica se siamo all'inizio di un numero
|
||
int errorchar = ' ';
|
||
|
||
// Flag che indica se sono nella parte decimale di un numero
|
||
for (const char* s = exp; *s && errorchar == ' '; s++)
|
||
{
|
||
const char c = *s;
|
||
switch(c)
|
||
{
|
||
case '+':
|
||
case '-':
|
||
// Se ero in in numero ...
|
||
if( !startnum )
|
||
{
|
||
// Aggiunge il numero alla sequenza
|
||
const real newval = num;
|
||
val_perc *= ( CENTO - newval ) / CENTO;
|
||
goodexp << num;
|
||
}
|
||
// Inizia il nuovo numero
|
||
num = (c == '-') ? "-" : "+";
|
||
startnum = true;
|
||
dec = false;
|
||
break;
|
||
case '0':
|
||
case '1':
|
||
case '2':
|
||
case '3':
|
||
case '4':
|
||
case '5':
|
||
case '6':
|
||
case '7':
|
||
case '8':
|
||
case '9':
|
||
num << c;
|
||
startnum = false;
|
||
break;
|
||
case '.':
|
||
case ',':
|
||
if(!dec)
|
||
{
|
||
if( startnum )
|
||
num << '0'; // Se occorreva un numero ci metto lo 0
|
||
num << '.'; // Interpreto la virgola come punto
|
||
dec = true;
|
||
startnum = true;
|
||
}
|
||
else
|
||
errorchar = c; // Se siamo gi<67> nella parte decimale segnala un errore
|
||
break;
|
||
case ' ':
|
||
break;
|
||
default:
|
||
errorchar = c;
|
||
break;
|
||
}
|
||
}
|
||
// Controlla la validit<69>
|
||
valid = errorchar == ' ';
|
||
|
||
if (valid)
|
||
{
|
||
// Aggiunge l'ultimo numero preso
|
||
const real lastval = num;
|
||
val_perc *= ( CENTO - lastval ) / CENTO;
|
||
goodexp << num; // Assegna la nuova espressione formattata bene
|
||
}
|
||
else
|
||
{
|
||
if (signal) // Se richiesto segnala l'errore
|
||
warning_box(FR("Espressione di sconto non valida. Errore sul carattere %c."), errorchar);
|
||
val_perc = UNO; // Azzera la sequenza di percentuali
|
||
goodexp = "";
|
||
}
|
||
}
|
||
return valid;
|
||
}
|
||
|
||
bool is_real_discount(const TString& exp)
|
||
{
|
||
if (exp.blank())
|
||
return false;
|
||
TString80 good;
|
||
real perc;
|
||
return scontoexpr2perc(exp, false , good, perc) && perc != UNO;
|
||
}
|
||
|
||
real prezzo_scontato(const real& prezzo, const TString& sconto)
|
||
{
|
||
if (sconto.full())
|
||
{
|
||
TString80 exp;
|
||
real val_sconto;
|
||
if (scontoexpr2perc(sconto, false , exp, val_sconto) && val_sconto != UNO)
|
||
return prezzo * val_sconto;
|
||
}
|
||
return prezzo;
|
||
}
|