Files correlati : ve0.exe ve0100a.msk Commento : Migliorata la segnalazione quando c'é una divisione per zero Corretta la ricerca per ragione sociale nella maschera di quey dei documenti interattivi in modo che sia case insensitive (Metacalabria)
1520 lines
34 KiB
C++
Executable File
1520 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
|
||
{
|
||
TString msg("Espressione : ");
|
||
|
||
msg << _original << ": Divisione per zero!";
|
||
print_error(msg);
|
||
}
|
||
} 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[255];
|
||
|
||
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;
|
||
}
|