Implementation
void addBuiltIns(Expression e) {
e.addOperator(OperatorImpl("+", Expression.operatorPrecedenceAdditive, true,
fEval: (v1, v2) {
return v1 + v2;
}));
e.addOperator(OperatorImpl("-", Expression.operatorPrecedenceAdditive, true,
fEval: (v1, v2) {
return v1 - v2;
}));
e.addOperator(OperatorImpl(
"*", Expression.operatorPrecedenceMultiplicative, true, fEval: (v1, v2) {
return v1 * v2;
}));
e.addOperator(OperatorImpl(
"/", Expression.operatorPrecedenceMultiplicative, true, fEval: (v1, v2) {
if (v2 == Decimal.zero) {
throw new ExpressionException("Cannot divide by 0.");
}
return (v1 / v2).toDecimal(scaleOnInfinitePrecision: 16);
}));
e.addOperator(OperatorImpl(
"%", Expression.operatorPrecedenceMultiplicative, true, fEval: (v1, v2) {
return v1 % v2;
}));
e.addOperator(
OperatorImpl("^", e.powerOperatorPrecedence, false, fEval: (v1, v2) {
// Do an more high performance estimate to see if this should be request
// should be canned
double test = math.pow(v1.toDouble(), v2.toDouble()).toDouble();
if (test.isInfinite) {
throw new ExpressionException("Exponentiation too expensive");
} else if (test.isNaN) {
throw new ExpressionException("Exponentiation invalid");
}
// Thanks to Gene Marin:
// http://stackoverflow.com/questions/3579779/how-to-do-a-fractional-power-on-bigdecimal-in-java
int signOf2 = v2.signum;
double dn1 = v1.toDouble();
v2 = v2 * Decimal.fromInt(signOf2);
Decimal remainderOf2 = v2.remainder(Decimal.one);
Decimal n2IntPart = v2 - remainderOf2;
Decimal intPow = v1.pow(n2IntPart.toBigInt().toInt()).toDecimal();
Decimal doublePow =
Decimal.parse(math.pow(dn1, remainderOf2.toDouble()).toString());
Decimal result = intPow * doublePow;
if (signOf2 == -1) {
result = (Decimal.one / result).toDecimal(scaleOnInfinitePrecision: 16);
}
return result;
}));
e.addOperator(OperatorImpl("&&", Expression.operatorPrecedenceAnd, false,
booleanOperator: true, fEval: (v1, v2) {
bool b1 = v1.compareTo(Decimal.zero) != 0;
if (!b1) {
return Decimal.zero;
}
bool b2 = v2.compareTo(Decimal.zero) != 0;
return b2 ? Decimal.one : Decimal.zero;
}));
e.addOperator(OperatorImpl("||", Expression.operatorPrecedenceOr, false,
booleanOperator: true, fEval: (v1, v2) {
bool b1 = v1.compareTo(Decimal.zero) != 0;
if (b1) {
return Decimal.one;
}
bool b2 = v2.compareTo(Decimal.zero) != 0;
return b2 ? Decimal.one : Decimal.zero;
}));
e.addOperator(OperatorImpl(
">", Expression.operatorPrecedenceComparison, false, fEval: (v1, v2) {
return v1.compareTo(v2) > 0 ? Decimal.one : Decimal.zero;
}));
e.addOperator(OperatorImpl(
">=", Expression.operatorPrecedenceComparison, false,
booleanOperator: true, fEval: (v1, v2) {
return v1.compareTo(v2) >= 0 ? Decimal.one : Decimal.zero;
}));
e.addOperator(OperatorImpl(
"<", Expression.operatorPrecedenceComparison, false,
booleanOperator: true, fEval: (v1, v2) {
return v1.compareTo(v2) < 0 ? Decimal.one : Decimal.zero;
}));
e.addOperator(OperatorImpl(
"<=", Expression.operatorPrecedenceComparison, false,
booleanOperator: true, fEval: (v1, v2) {
return v1.compareTo(v2) <= 0 ? Decimal.one : Decimal.zero;
}));
e.addOperator(OperatorNullArgsImpl(
"=", Expression.operatorPrecedenceEquality, false, booleanOperator: true,
fEval: (v1, v2) {
if (v1 == v2) {
return Decimal.one;
}
if (v1 == null || v2 == null) {
return Decimal.zero;
}
return v1.compareTo(v2) == 0 ? Decimal.one : Decimal.zero;
}));
e.addOperator(OperatorNullArgsImpl(
"==", Expression.operatorPrecedenceEquality, false, booleanOperator: true,
fEval: (v1, v2) {
if (v1 == v2) {
return Decimal.one;
}
if (v1 == null || v2 == null) {
return Decimal.zero;
}
return v1.compareTo(v2) == 0 ? Decimal.one : Decimal.zero;
}));
e.addOperator(OperatorNullArgsImpl(
"!=", Expression.operatorPrecedenceEquality, false, booleanOperator: true,
fEval: (v1, v2) {
if (v1 == v2) {
return Decimal.zero;
}
if (v1 == null || v2 == null) {
return Decimal.one;
}
return v1.compareTo(v2) != 0 ? Decimal.one : Decimal.zero;
}));
e.addOperator(OperatorNullArgsImpl(
"<>", Expression.operatorPrecedenceEquality, false, booleanOperator: true,
fEval: (v1, v2) {
if (v1 == v2) {
return Decimal.zero;
}
if (v1 == null || v2 == null) {
return Decimal.one;
}
return v1.compareTo(v2) != 0 ? Decimal.one : Decimal.zero;
}));
e.addLazyFunction(LazyFunctionImpl("STREQ", 2, fEval: (params) {
if (params[0].getString() == params[1].getString()) {
return e.createLazyNumber(Decimal.one);
} else {
return e.createLazyNumber(Decimal.zero);
}
}));
e.addOperator(UnaryOperatorImpl(
"-", Expression.operatorPrecedenceUnary, false, fEval: (v1) {
return v1 * Decimal.fromInt(-1);
}));
e.addOperator(UnaryOperatorImpl(
"+", Expression.operatorPrecedenceUnary, false, fEval: (v1) {
return v1 * Decimal.one;
}));
e.addOperator(OperatorSuffixImpl("!", 61, false, fEval: (v) {
if (v.toDouble() > 50) {
throw new ExpressionException("Operand must be <= 50");
}
int number = v.toBigInt().toInt();
Decimal factorial = Decimal.one;
for (int i = 1; i <= number; i++) {
factorial = factorial * Decimal.fromInt(i);
}
return factorial;
}));
e.addFunc(FunctionImpl("FACT", 1, booleanFunction: false, fEval: (params) {
if (params.first.toDouble() > 50) {
throw new ExpressionException("Operand must be <= 50");
}
int number = params.first.toBigInt().toInt();
Decimal factorial = Decimal.one;
for (int i = 1; i <= number; i++) {
factorial = factorial * Decimal.fromInt(i);
}
return factorial;
}));
e.addFunc(FunctionImpl("NOT", 1, booleanFunction: true, fEval: (params) {
bool zero = params.first.compareTo(Decimal.zero) == 0;
return zero ? Decimal.one : Decimal.zero;
}));
e.addLazyFunction(LazyFunctionImpl("IF", 3, fEval: (params) {
return LazyIfNumber(params);
}));
e.addFunc(FunctionImpl("RANDOM", 0, fEval: (params) {
double d = math.Random().nextDouble();
return Decimal.parse(d.toString());
}));
/*
* Trig functions
*/
// Standard, radians
e.addFunc(FunctionImpl("SINR", 1, fEval: (params) {
double d = math.sin(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("COSR", 1, fEval: (params) {
double d = math.cos(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("TANR", 1, fEval: (params) {
double d = math.tan(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("COTR", 1, fEval: (params) {
double d = 1.0 / math.tan(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("SECR", 1, fEval: (params) {
double d = 1 / math.cos(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("CSCR", 1, fEval: (params) {
double d = 1.0 / math.sin(params.first.toDouble());
return Decimal.parse(d.toString());
}));
// Standard, degrees
e.addFunc(FunctionImpl("SIN", 1, fEval: (params) {
double d = math.sin(degreesToRads(params.first.toDouble()));
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("COS", 1, fEval: (params) {
double d = math.cos(degreesToRads(params.first.toDouble()));
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("TAN", 1, fEval: (params) {
double d = math.tan(degreesToRads(params.first.toDouble()));
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("COT", 1, fEval: (params) {
double d = 1.0 / math.tan(degreesToRads(params.first.toDouble()));
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("SEC", 1, fEval: (params) {
double d = 1 / math.cos(degreesToRads(params.first.toDouble()));
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("CSC", 1, fEval: (params) {
double d = 1.0 / math.sin(degreesToRads(params.first.toDouble()));
return Decimal.parse(d.toString());
}));
// Inverse arc functions, radians
e.addFunc(FunctionImpl("ASINR", 1, fEval: (params) {
double d = math.asin(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("ACOSR", 1, fEval: (params) {
double d = math.acos(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("ATANR", 1, fEval: (params) {
double d = math.atan(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("ACOTR", 1, fEval: (params) {
if (params.first.toDouble() == 0) {
throw new ExpressionException("Number must not be 0");
}
double d = math.atan(1.0 / params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("ATAN2R", 2, fEval: (params) {
double d = math.atan2(params[0].toDouble(), params[1].toDouble());
return Decimal.parse(d.toString());
}));
// Inverse arc functions, degrees
e.addFunc(FunctionImpl("ASIN", 1, fEval: (params) {
double d = degreesToRads(math.asin(params.first.toDouble()));
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("ACOS", 1, fEval: (params) {
double d = degreesToRads(math.acos(params.first.toDouble()));
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("ATAN", 1, fEval: (params) {
double d = radsToDegrees(math.atan(params.first.toDouble()));
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("ACOT", 1, fEval: (params) {
if (params.first.toDouble() == 0.0) {
throw new ExpressionException("Number must not be 0");
}
double d = radsToDegrees(math.atan(1.0 / params.first.toDouble()));
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("ATAN2", 2, fEval: (params) {
double d =
radsToDegrees(math.atan2(params[0].toDouble(), params[1].toDouble()));
return Decimal.parse(d.toString());
}));
// Conversions
e.addFunc(FunctionImpl("RAD", 1, fEval: (params) {
double d = degreesToRads(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("DEG", 1, fEval: (params) {
double d = radsToDegrees(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("MAX", -1, fEval: (params) {
if (params.isEmpty) {
throw new ExpressionException("MAX requires at least one parameter");
}
Decimal? max;
for (Decimal param in params) {
if (max == null || param.compareTo(max) > 0) {
max = param;
}
}
return max;
}));
e.addFunc(FunctionImpl("MIN", -1, fEval: (params) {
if (params.isEmpty) {
throw new ExpressionException("MIN requires at least one parameter");
}
Decimal? min;
for (Decimal param in params) {
if (min == null || param.compareTo(min) < 0) {
min = param;
}
}
return min;
}));
e.addFunc(FunctionImpl("ABS", 1, fEval: (params) {
return params.first.abs();
}));
e.addFunc(FunctionImpl("LOG", 1, fEval: (params) {
double d = math.log(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("LOG10", 1, fEval: (params) {
double d = log10(params.first.toDouble());
return Decimal.parse(d.toString());
}));
e.addFunc(FunctionImpl("ROUND", 2, fEval: (params) {
Decimal toRound = params.first;
return Decimal.parse(toRound.toStringAsFixed(params[1].toBigInt().toInt()));
}));
e.addFunc(FunctionImpl("FLOOR", 1, fEval: (params) {
return params.first.floor();
}));
e.addFunc(FunctionImpl("CEILING", 1, fEval: (params) {
return params.first.ceil();
}));
e.addFunc(FunctionImpl("SQRT", 1, fEval: (params) {
return Decimal.parse(math.sqrt(params.first.toDouble()).toString());
}));
e.variables["theAnswerToLifeTheUniverseAndEverything"] =
e.createLazyNumber(Decimal.fromInt(42));
e.variables["e"] = e.createLazyNumber(Expression.e);
e.variables["PI"] = e.createLazyNumber(Expression.pi);
e.variables["NULL"] = null;
e.variables["null"] = null;
e.variables["Null"] = null;
e.variables["TRUE"] = e.createLazyNumber(Decimal.one);
e.variables["true"] = e.createLazyNumber(Decimal.one);
e.variables["True"] = e.createLazyNumber(Decimal.one);
e.variables["FALSE"] = e.createLazyNumber(Decimal.zero);
e.variables["false"] = e.createLazyNumber(Decimal.zero);
e.variables["False"] = e.createLazyNumber(Decimal.zero);
}