Expressions: Added support for aggregate functions and ranges.

This commit is contained in:
Eivind Kvedalen
2016-03-26 15:46:28 +01:00
committed by wmayer
parent 6c80dd4eae
commit 08692f811b
10 changed files with 1269 additions and 525 deletions

View File

@@ -581,18 +581,50 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f
, args(_args)
{
switch (f) {
case NONE:
throw ExpressionError("Unknown function");
case ACOS:
case ASIN:
case ATAN:
case ABS:
case EXP:
case LOG:
case LOG10:
case SIN:
case SINH:
case TAN:
case TANH:
case SQRT:
case COS:
case COSH:
case ROUND:
case TRUNC:
case CEIL:
case FLOOR:
if (args.size() != 1)
throw ExpressionError("Invalid number of arguments: exactly one required.");
break;
case MOD:
case ATAN2:
case POW:
if (args.size() != 2)
throw ExpressionError("Invalid number of arguments.");
throw ExpressionError("Invalid number of arguments: eaxctly two required.");
break;
case STDDEV:
if (args.size() < 2)
throw ExpressionError("Invalid number of arguments: at least two required.");
break;
case SUM:
case AVERAGE:
case COUNT:
case MIN:
case MAX:
if (args.size() == 0)
throw ExpressionError("Invalid number of arguments: at least one required.");
break;
case NONE:
case AGGREGATES:
case LAST:
default:
if (args.size() != 1)
throw ExpressionError("Invalid number of arguments.");
break;
throw ExpressionError("Unknown function");
}
}
@@ -625,6 +657,136 @@ bool FunctionExpression::isTouched() const
return false;
}
Expression * FunctionExpression::evalAggregate() const
{
class Collector {
public:
Collector(Function _f)
: f(_f)
, first(true)
, n(0) {
}
void collect(Quantity value) {
if (first) {
q.setUnit(value.getUnit());
mean.setUnit(value.getUnit());
M2.setUnit(value.getUnit());
}
switch (f) {
case AVERAGE:
n++;
case SUM:
q = q + value;
break;
case STDDEV: {
n++;
const Quantity delta = value - mean;
mean = mean + delta / n;
M2 = M2 + delta * (value - mean);
break;
}
case COUNT:
q = q + 1;
break;
case MIN:
if (first || value < q)
q = value;
break;
case MAX:
if (first || value > q)
q = value;
break;
default:
break;
}
first = false;
}
Quantity getQuantity() const {
switch (static_cast<Function>(f)) {
case AVERAGE:
return q / (double)n;
case STDDEV:
if (n < 2)
return Quantity();
else
return (M2 / (n - 1.0)).pow(Quantity(0.5));
default:
break;
}
return q;
}
private:
Function f;
bool first;
int n;
Quantity q;
Quantity mean;
Quantity M2;
};
switch (f) {
case SUM:
case AVERAGE:
case STDDEV:
case COUNT:
case MIN:
case MAX: {
Collector c(f);
for (size_t i = 0; i< args.size(); ++i) {
if (args[i]->isDerivedFrom(RangeExpression::getClassTypeId())) {
RangeExpression * v = static_cast<RangeExpression*>(args[i]);
Range range(v->getRange());
do {
Property * p = owner->getPropertyByName(range.address().c_str());
PropertyQuantity * qp;
PropertyFloat * fp;
if (!p)
continue;
if ((qp = freecad_dynamic_cast<PropertyQuantity>(p)) != 0)
c.collect(qp->getQuantityValue());
else if ((fp = freecad_dynamic_cast<PropertyFloat>(p)) != 0)
c.collect(fp->getValue());
else
throw Exception("Invalid property type for aggregate");
} while (range.next());
}
else if (args[i]->isDerivedFrom(App::VariableExpression::getClassTypeId())) {
std::auto_ptr<Expression> e(args[i]->eval());
NumberExpression * n(freecad_dynamic_cast<NumberExpression>(e.get()));
if (n)
c.collect(n->getQuantity());
}
else if (args[i]->isDerivedFrom(App::NumberExpression::getClassTypeId())) {
c.collect(static_cast<NumberExpression*>(args[i])->getQuantity());
}
}
return new NumberExpression(owner, c.getQuantity());
}
default:
assert(0);
return 0;
}
}
/**
* Evaluate function. Returns a NumberExpression if evaluation is successfuly.
* Throws an ExpressionError exception if something fails.
@@ -634,6 +796,10 @@ bool FunctionExpression::isTouched() const
Expression * FunctionExpression::eval() const
{
// Handle aggregate functions
if (f > AGGREGATES)
return evalAggregate();
std::auto_ptr<Expression> e1(args[0]->eval());
std::auto_ptr<Expression> e2(args.size() > 1 ? args[1]->eval() : 0);
NumberExpression * v1 = freecad_dynamic_cast<NumberExpression>(e1.get());
@@ -873,49 +1039,69 @@ Expression *FunctionExpression::simplify() const
std::string FunctionExpression::toString() const
{
std::stringstream ss;
for (size_t i = 0; i < args.size(); ++i) {
ss << args[i]->toString();
if (i != args.size() - 1)
ss << "; ";
}
switch (f) {
case ACOS:
return "acos(" + args[0]->toString() + ")";
return "acos(" + ss.str() + ")";
case ASIN:
return "asin(" + args[0]->toString() + ")";
return "asin(" + ss.str() + ")";
case ATAN:
return "atan(" + args[0]->toString() + ")";
return "atan(" + ss.str() + ")";
case ABS:
return "abs(" + args[0]->toString() + ")";
return "abs(" + ss.str() + ")";
case EXP:
return "exp(" + args[0]->toString() + ")";
return "exp(" + ss.str() + ")";
case LOG:
return "log(" + args[0]->toString() + ")";
return "log(" + ss.str() + ")";
case LOG10:
return "log10(" + args[0]->toString() + ")";
return "log10(" + ss.str() + ")";
case SIN:
return "sin(" + args[0]->toString() + ")";
return "sin(" + ss.str() + ")";
case SINH:
return "sinh(" + args[0]->toString() + ")";
return "sinh(" + ss.str() + ")";
case TAN:
return "tan(" + args[0]->toString() + ")";
return "tan(" + ss.str() + ")";
case TANH:
return "tanh(" + args[0]->toString() + ")";
return "tanh(" + ss.str() + ")";
case SQRT:
return "sqrt(" + args[0]->toString() + ")";
return "sqrt(" + ss.str() + ")";
case COS:
return "cos(" + args[0]->toString() + ")";
return "cos(" + ss.str() + ")";
case COSH:
return "cosh(" + args[0]->toString() + ")";
return "cosh(" + ss.str() + ")";
case MOD:
return "mod(" + args[0]->toString() + ", " + args[1]->toString() + ")";
return "mod(" + ss.str() + ")";
case ATAN2:
return "atan2(" + args[0]->toString() + ", " + args[1]->toString() + ")";
return "atan2(" + ss.str() + ")";
case POW:
return "pow(" + args[0]->toString() + ", " + args[1]->toString() + ")";
return "pow(" + ss.str() + ")";
case ROUND:
return "round(" + args[0]->toString() + ")";
return "round(" + ss.str() + ")";
case TRUNC:
return "trunc(" + args[0]->toString() + ")";
return "trunc(" + ss.str() + ")";
case CEIL:
return "ceil(" + args[0]->toString() + ")";
return "ceil(" + ss.str() + ")";
case FLOOR:
return "floor(" + args[0]->toString() + ")";
return "floor(" + ss.str() + ")";
case SUM:
return "sum(" + ss.str() + ")";
case COUNT:
return "count(" + ss.str() + ")";
case AVERAGE:
return "average(" + ss.str() + ")";
case STDDEV:
return "stddev(" + ss.str() + ")";
case MIN:
return "min(" + ss.str() + ")";
case MAX:
return "max(" + ss.str() + ")";
default:
assert(0);
return std::string();
@@ -1303,6 +1489,62 @@ Expression *BooleanExpression::copy() const
return new BooleanExpression(owner, getValue() > 0.5 ? true : false);
}
TYPESYSTEM_SOURCE(App::RangeExpression, App::Expression);
RangeExpression::RangeExpression(const DocumentObject *_owner, const std::string &begin, const std::string &end)
: Expression(_owner)
, range((begin + ":" + end).c_str())
{
}
bool RangeExpression::isTouched() const
{
Range i(range);
do {
Property * prop = owner->getPropertyByName(i.address().c_str());
if (prop && prop->isTouched())
return true;
} while (i.next());
return false;
}
Expression *RangeExpression::eval() const
{
throw Exception("Range expression cannot be evaluated");
}
std::string RangeExpression::toString() const
{
return range.rangeString();
}
Expression *RangeExpression::copy() const
{
return new RangeExpression(owner, range.fromCellString(), range.toCellString());
}
void RangeExpression::getDeps(std::set<ObjectIdentifier> &props) const
{
Range i(range);
do {
props.insert(ObjectIdentifier(owner, i.address()));
} while (i.next());
}
Expression *RangeExpression::simplify() const
{
return copy();
}
void RangeExpression::setRange(const Range &r)
{
range = r;
}
namespace App {
namespace ExpressionParser {
@@ -1311,7 +1553,7 @@ namespace ExpressionParser {
* Error function for parser. Throws a generic Base::Exception with the parser error.
*/
void ExpressionParser_yyerror(char *errorinfo)
void ExpressionParser_yyerror(const char *errorinfo)
{
}
@@ -1404,6 +1646,14 @@ static void initParser(const App::DocumentObject *owner)
registered_functions["ceil"] = FunctionExpression::CEIL;
registered_functions["floor"] = FunctionExpression::FLOOR;
// Aggregates
registered_functions["sum"] = FunctionExpression::SUM;
registered_functions["count"] = FunctionExpression::COUNT;
registered_functions["average"] = FunctionExpression::AVERAGE;
registered_functions["stddev"] = FunctionExpression::STDDEV;
registered_functions["min"] = FunctionExpression::MIN;
registered_functions["max"] = FunctionExpression::MAX;
has_registered_functions = true;
}
}