pip-services4-expressions-node
Version:
Tokenizers, parsers and expression calculators in Node.js / ES2017
382 lines (332 loc) • 20 kB
text/typescript
/** @module calculator */
import { FunctionCollection } from "./FunctionCollection";
import { DelegatedFunction } from "./DelegatedFunction";
import { Variant } from "../../variants/Variant";
import { VariantType } from "../../variants/VariantType";
import { IVariantOperations } from "../../variants/IVariantOperations";
import { ExpressionException } from "../ExpressionException";
/**
* Implements a list filled with standard functions.
*/
export class DefaultFunctionCollection extends FunctionCollection {
/**
* Constructs this list and fills it with the standard functions.
*/
public constructor() {
super();
this.add(new DelegatedFunction("Ticks", this.ticksFunctionCalculator, this));
this.add(new DelegatedFunction("TimeSpan", this.timeSpanFunctionCalculator, this));
this.add(new DelegatedFunction("Now", this.nowFunctionCalculator, this));
this.add(new DelegatedFunction("Date", this.dateFunctionCalculator, this));
this.add(new DelegatedFunction("DayOfWeek", this.dayOfWeekFunctionCalculator, this));
this.add(new DelegatedFunction("Min", this.minFunctionCalculator, this));
this.add(new DelegatedFunction("Max", this.maxFunctionCalculator, this));
this.add(new DelegatedFunction("Sum", this.sumFunctionCalculator, this));
this.add(new DelegatedFunction("If", this.ifFunctionCalculator, this));
this.add(new DelegatedFunction("Choose", this.chooseFunctionCalculator, this));
this.add(new DelegatedFunction("E", this.eFunctionCalculator, this));
this.add(new DelegatedFunction("Pi", this.piFunctionCalculator, this));
this.add(new DelegatedFunction("Rnd", this.rndFunctionCalculator, this));
this.add(new DelegatedFunction("Random", this.rndFunctionCalculator, this));
this.add(new DelegatedFunction("Abs", this.absFunctionCalculator, this));
this.add(new DelegatedFunction("Acos", this.acosFunctionCalculator, this));
this.add(new DelegatedFunction("Asin", this.asinFunctionCalculator, this));
this.add(new DelegatedFunction("Atan", this.atanFunctionCalculator, this));
this.add(new DelegatedFunction("Exp", this.expFunctionCalculator, this));
this.add(new DelegatedFunction("Log", this.logFunctionCalculator, this));
this.add(new DelegatedFunction("Ln", this.logFunctionCalculator, this));
this.add(new DelegatedFunction("Log10", this.log10FunctionCalculator, this));
this.add(new DelegatedFunction("Ceil", this.ceilFunctionCalculator, this));
this.add(new DelegatedFunction("Ceiling", this.ceilFunctionCalculator, this));
this.add(new DelegatedFunction("Floor", this.floorFunctionCalculator, this));
this.add(new DelegatedFunction("Round", this.roundFunctionCalculator, this));
this.add(new DelegatedFunction("Trunc", this.truncFunctionCalculator, this));
this.add(new DelegatedFunction("Truncate", this.truncFunctionCalculator, this));
this.add(new DelegatedFunction("Cos", this.cosFunctionCalculator, this));
this.add(new DelegatedFunction("Sin", this.sinFunctionCalculator, this));
this.add(new DelegatedFunction("Tan", this.tanFunctionCalculator, this));
this.add(new DelegatedFunction("Sqr", this.sqrtFunctionCalculator, this));
this.add(new DelegatedFunction("Sqrt", this.sqrtFunctionCalculator, this));
this.add(new DelegatedFunction("Empty", this.emptyFunctionCalculator, this));
this.add(new DelegatedFunction("Null", this.nullFunctionCalculator, this));
this.add(new DelegatedFunction("Contains", this.containsFunctionCalculator, this));
this.add(new DelegatedFunction("Array", this.arrayFunctionCalculator, this));
}
/**
* Checks if params contains the correct number of function parameters (must be stored on the top of the params).
* @param params A list of function parameters.
* @param expectedParamCount The expected number of function parameters.
*/
protected checkParamCount(params: Variant[], expectedParamCount: number): void {
const paramCount = params.length;
if (expectedParamCount != paramCount) {
throw new ExpressionException(null, "WRONG_PARAM_COUNT",
"Expected " + expectedParamCount
+ " parameters but was found " + paramCount);
}
}
/**
* Gets function parameter by it's index.
* @param params A list of function parameters.
* @param paramIndex Index for the function parameter (0 for the first parameter).
* @returns Function parameter value.
*/
protected getParameter(params: Variant[], paramIndex: number): Variant {
return params[paramIndex];
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async ticksFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 0);
return Variant.fromLong(new Date().getTime());
}
private async timeSpanFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
const paramCount = params.length;
if (paramCount != 1 && paramCount != 3 && paramCount != 4 && paramCount != 5) {
throw new ExpressionException(null, "WRONG_PARAM_COUNT", "Expected 1, 3, 4 or 5 parameters");
}
const result = new Variant();
if (paramCount == 1) {
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Long);
result.asTimeSpan = value.asLong;
} else if (paramCount > 2) {
const value1 = variantOperations.convert(this.getParameter(params, 0), VariantType.Long);
const value2 = variantOperations.convert(this.getParameter(params, 1), VariantType.Long);
const value3 = variantOperations.convert(this.getParameter(params, 2), VariantType.Long);
const value4 = paramCount > 3 ? variantOperations.convert(this.getParameter(params, 3), VariantType.Long) : Variant.fromLong(0);
const value5 = paramCount > 4 ? variantOperations.convert(this.getParameter(params, 4), VariantType.Long) : Variant.fromLong(0);
result.asTimeSpan = (((value1.asLong * 24 + value2.asLong) * 60 + value3.asLong) * 60 + value4.asLong) * 1000 + value5.asLong;
}
return result;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async nowFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 0);
return Variant.fromDateTime(new Date());
}
private async dateFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
const paramCount = params.length;
if (paramCount < 1 || paramCount > 7) {
throw new ExpressionException(null, "WRONG_PARAM_COUNT", "Expected from 1 to 7 parameters");
}
if (paramCount == 1) {
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Long);
return Variant.fromDateTime(new Date(value.asLong));
}
const value1 = variantOperations.convert(this.getParameter(params, 0), VariantType.Integer);
const value2 = paramCount > 1 ? variantOperations.convert(this.getParameter(params, 1), VariantType.Integer) : Variant.fromInteger(1);
const value3 = paramCount > 2 ? variantOperations.convert(this.getParameter(params, 2), VariantType.Integer) : Variant.fromInteger(1);
const value4 = paramCount > 3 ? variantOperations.convert(this.getParameter(params, 3), VariantType.Integer) : Variant.fromInteger(0);
const value5 = paramCount > 4 ? variantOperations.convert(this.getParameter(params, 4), VariantType.Integer) : Variant.fromInteger(0);
const value6 = paramCount > 5 ? variantOperations.convert(this.getParameter(params, 5), VariantType.Integer) : Variant.fromInteger(0);
const value7 = paramCount > 6 ? variantOperations.convert(this.getParameter(params, 6), VariantType.Integer) : Variant.fromInteger(0);
const date = new Date(value1.asInteger, value2.asInteger-1, value3.asInteger,
value4.asInteger, value5.asInteger, value6.asInteger, value7.asInteger);
return Variant.fromDateTime(date);
}
private async dayOfWeekFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.DateTime);
const date = value.asDateTime;
return Variant.fromInteger(date.getDay());
}
private async minFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
const paramCount = params.length;
if (paramCount < 2) {
throw new ExpressionException(null, "WRONG_PARAM_COUNT",
"Expected at least 2 parameters");
}
let result = this.getParameter(params, 0);
for (let i = 1; i < paramCount; i++) {
const value = this.getParameter(params, i);
if (variantOperations.more(result, value).asBoolean) {
result = value;
}
}
return result;
}
private async maxFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
const paramCount = params.length;
if (paramCount < 2) {
throw new ExpressionException(null, "WRONG_PARAM_COUNT",
"Expected at least 2 parameters");
}
let result = this.getParameter(params, 0);
for (let i = 1; i < paramCount; i++) {
const value = this.getParameter(params, i);
if (variantOperations.less(result, value).asBoolean) {
result = value;
}
}
return result;
}
private async sumFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
const paramCount = params.length;
if (paramCount < 2) {
throw new ExpressionException(null, "WRONG_PARAM_COUNT",
"Expected at least 2 parameters");
}
let result = this.getParameter(params, 0);
for (let i = 1; i < paramCount; i++) {
const value = this.getParameter(params, i);
result = variantOperations.add(result, value);
}
return result;
}
private async ifFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 3);
const value1 = this.getParameter(params, 0);
const value2 = this.getParameter(params, 1);
const value3 = this.getParameter(params, 2);
const condition = variantOperations.convert(value1, VariantType.Boolean);
return condition.asBoolean ? value2 : value3;
}
private async chooseFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
const paramCount = params.length;
if (paramCount < 3) {
throw new ExpressionException(null, "WRONG_PARAM_COUNT",
"Expected at least 3 parameters");
}
const value1 = this.getParameter(params, 0);
const condition = variantOperations.convert(value1, VariantType.Integer);
const paramIndex = condition.asInteger;
if (paramCount < paramIndex + 1) {
throw new ExpressionException(null, "WRONG_PARAM_COUNT",
"Expected at least " + (paramIndex + 1) + " parameters");
}
return this.getParameter(params, paramIndex);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async eFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 0);
return new Variant(Math.E);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async piFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 0);
return new Variant(Math.PI);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async rndFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 0);
return new Variant(Math.random());
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async absFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
let value = this.getParameter(params, 0);
const result = new Variant();
switch (value.type) {
case VariantType.Integer:
result.asInteger = Math.abs(value.asInteger);
break;
case VariantType.Long:
result.asLong = Math.abs(value.asLong);
break;
case VariantType.Float:
result.asFloat = Math.abs(value.asFloat);
break;
case VariantType.Double:
result.asDouble = Math.abs(value.asDouble);
break;
default:
value = variantOperations.convert(value, VariantType.Double);
result.asDouble = Math.abs(value.asDouble);
break;
}
return result;
}
private async acosFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.acos(value.asDouble));
}
private async asinFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.asin(value.asDouble));
}
private async atanFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.atan(value.asDouble));
}
private async expFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.exp(value.asDouble));
}
private async logFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.log(value.asDouble));
}
private async log10FunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.log10(value.asDouble));
}
private async ceilFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.ceil(value.asDouble));
}
private async floorFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.floor(value.asDouble));
}
private async roundFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.round(value.asDouble));
}
private async truncFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return Variant.fromInteger(Math.trunc(value.asDouble));
}
private async cosFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.cos(value.asDouble));
}
private async sinFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.sin(value.asDouble));
}
private async tanFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.tan(value.asDouble));
}
private async sqrtFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = variantOperations.convert(this.getParameter(params, 0), VariantType.Double);
return new Variant(Math.sqrt(value.asDouble));
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async emptyFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 1);
const value = this.getParameter(params, 0);
return new Variant(value.isEmpty());
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async nullFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 0);
return new Variant();
}
private async containsFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
this.checkParamCount(params, 2);
const containerstr = variantOperations.convert(this.getParameter(params, 0), VariantType.String);
const substring = variantOperations.convert(this.getParameter(params, 1), VariantType.String);
if (containerstr.isEmpty() || containerstr.isNull()) {
return Variant.fromBoolean(false);
}
return Variant.fromBoolean(containerstr.asString.indexOf(substring.asString) >= 0);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private async arrayFunctionCalculator(params: Variant[], variantOperations: IVariantOperations): Promise<Variant> {
return Variant.fromArray(params);
}
}