From 3b22bb874f890fb802361560f283a2ebd7056322 Mon Sep 17 00:00:00 2001 From: Alexander Schenkel Date: Sat, 24 Aug 2024 17:02:35 +0200 Subject: [PATCH] Finalize the implementation of Luigi Pulcini's logical operator implementation --- README.md | 57 ++- dist/fparser.js | 853 ++++++++++++++++++++++++------------- dist/fparser.js.map | 2 +- dist/fparser.umd.cjs | 826 ++++++++++++++++++++++++++++++++++- dist/fparser.umd.cjs.map | 2 +- spec/specs/exprTreeSpec.js | 6 + src/fparser.ts | 2 +- 7 files changed, 1434 insertions(+), 314 deletions(-) diff --git a/README.md b/README.md index 066ad74..0fd460b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Parses a mathematical formula from a string. Known expressions: - _Numbers_ in the form [-]digits[.digits], e.g. "-133.2945" - _simple operators_: '+','-','\*','/', '^' expanded in correct order +- _logical operators_: '<','<=','>','>=', '=', '!=', which evaluate to 1 or 0. Useful for implementing conditional logic - _parentheses_ '(', ')' for grouping (e.g. "5\*(3+2)") - all _JavaScript Math object functions_ (e.g. "sin(3.14)") - all _JavaScript Math constants_ like PI, E @@ -27,7 +28,7 @@ Parses a mathematical formula from a string. Known expressions: - the use of path named variables and functions (like '2\*[myVar.property.innerProperty]') - _memoization_: store already evaluated results for faster re-calcs - use it in Web pages, as ES6 module or as NodeJS module -- Example:
-1*(sin(2^x)/(PI*x))\*cos(x)) +- Example:
-1*(sin(2^x)/(PI*x))\*cos(x) ## Usage @@ -175,6 +176,43 @@ let result = fObj.evaluate({ var1: 'FooBar', longer: (s1, s2) => s1.length > s2. // --> 14 ``` +### Using of logical operators + +Logical operators allow for conditional logic. The result of the evaluation is always `0` (expression is false) or `1` (expression is true). + +Example: + +Calculate a percentage value based on a variable `x`, but only if `x` is between 0 and 1: + +```javascript +const fObj = new Formula('x >= 0 * x <= 1 * x * 100'); +let result = fObj.evaluate([{ x: 0.5 }, { x: 0.7 }, { x: 1.5 }, { x: -0.5 }, { x: -1.7 }]); +// --> [50, 70, 0, 0, 0] +``` + +This could be used to simulate or "shortcut" comparison functions. The same could be achieved with a user-definded function: + +```javascript +const fObj = new Formula('withinOne(x) * 100'); +fObj.withinOne = (x) => (x >= 0 && x <= 1 ? x : 0); +let result = fObj.evaluate([{ x: 0.5 }, { x: 0.7 }, { x: 1.5 }, { x: -0.5 }, { x: -1.7 }]); +// --> [50, 70, 0, 0, 0] +``` + +### Conditional evaluation + +The previous chapter introduced logical operators. This can be used to implement a conditional function, or `if` function: + +Example: Kids get a 50% discount on a price if they are under 18: + +```javascript +const fObj = new Formula('ifElse([age] < 18, [price]*0.5, [price])'); +fObj.ifElse = (predicate, trueValue, falseValue) => (predicate ? trueValue : falseValue); +const res = fObj.evaluate([{ price: 100, age: 17 }, { price: 100, age: 20 }]); +// --> res = [50, 100] +``` + + ### Re-use a Formula object You can instantiate a Formula object without formula, and set it later, and even re-use the existing object: @@ -327,13 +365,22 @@ edge cases. Thanks to all the additional contributors: -- [LuigiPulcini](https://github.com/LuigiPulcini) for the Strings support +- [LuigiPulcini](https://github.com/LuigiPulcini) for: + - the Strings support + - the Logical Operator support ## TODOs, Whishlist - -* support for double- and single quote strings (now: only double quotes) -* make parser state names via enum, instead of error-prone strings +* [ ] support for double- and single quote strings (now: only double quotes) +* [ ] make parser state names via enum, instead of error-prone strings +* [ ] Implement standard logic functions: + * [ ] `and(...args)`: if all given arguments are trueish (> 0), then the last arg is returned as value + * [ ] `or(...args)`: the first trueish (> 0) arg is returned as value + * [ ] `ifElse(predicate, trueValue, falseValue)`: returns the trueValue if the predicate is trueish (> 0), else the falseValue is returned +* [ ] Refactor / rebuild parser: + * separate tokenize step + * then use Djikstra's Shunting Yard algorithm to convert the Inifix notation to Postfix, which is + way simpler to execute (See https://en.wikipedia.org/wiki/Shunting_yard_algorithm) ## License diff --git a/dist/fparser.js b/dist/fparser.js index 63cdb25..4c41b94 100644 --- a/dist/fparser.js +++ b/dist/fparser.js @@ -1,7 +1,10 @@ -var $ = Object.defineProperty; -var O = (l, i, e) => i in l ? $(l, i, { enumerable: !0, configurable: !0, writable: !0, value: e }) : l[i] = e; -var n = (l, i, e) => (O(l, typeof i != "symbol" ? i + "" : i, e), e); -const g = { +var __defProp = Object.defineProperty; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __publicField = (obj, key, value) => { + __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); + return value; +}; +const MATH_CONSTANTS = { PI: Math.PI, E: Math.E, LN2: Math.LN2, @@ -11,68 +14,81 @@ const g = { SQRT1_2: Math.SQRT1_2, SQRT2: Math.SQRT2 }; -class p { - static throwIfNotNumber(i) { - if (typeof i === "string") +class MathOperatorHelper { + static throwIfNotNumber(value) { + const valueType = typeof value; + if (valueType === "string") { throw new Error("Strings are not allowed in math operations"); + } } } -class V { - static throwIfNotNumber(i) { - if (typeof i === "string") +class MathFunctionHelper { + static throwIfNotNumber(value) { + const valueType = typeof value; + if (valueType === "string") { throw new Error("Strings are not allowed in math operations"); + } } } -class h { - static createOperatorExpression(i, e, t) { - if (i === "^") - return new b(e, t); - if (["*", "/"].includes(i)) - return new x(i, e, t); - if (["+", "-"].includes(i)) - return new E(i, e, t); - if (["<", ">", "<=", ">=", "=", "!="].includes(i)) - return new d(i, e, t); - throw new Error(`Unknown operator: ${i}`); - } - evaluate(i = {}) { +class Expression { + static createOperatorExpression(operator, left, right) { + if (operator === "^") { + return new PowerExpression(left, right); + } + if (["*", "/"].includes(operator)) { + return new MultDivExpression(operator, left, right); + } + if (["+", "-"].includes(operator)) { + return new PlusMinusExpression(operator, left, right); + } + if (["<", ">", "<=", ">=", "=", "!="].includes(operator)) { + return new LogicalExpression(operator, left, right); + } + throw new Error(`Unknown operator: ${operator}`); + } + evaluate(params = {}) { throw new Error("Empty Expression - Must be defined in child classes"); } toString() { return ""; } } -class S extends h { - constructor(e) { +class BracketExpression extends Expression { + constructor(expr) { super(); - n(this, "innerExpression"); - if (this.innerExpression = e, !(this.innerExpression instanceof h)) + __publicField(this, "innerExpression"); + this.innerExpression = expr; + if (!(this.innerExpression instanceof Expression)) { throw new Error("No inner expression given for bracket expression"); + } } - evaluate(e = {}) { - return this.innerExpression.evaluate(e); + evaluate(params = {}) { + return this.innerExpression.evaluate(params); } toString() { return `(${this.innerExpression.toString()})`; } } -class w extends h { - constructor(e, t = "number") { +class ValueExpression extends Expression { + constructor(value, type = "number") { super(); - n(this, "value"); - n(this, "type"); - switch (this.value = Number(e), t) { + __publicField(this, "value"); + __publicField(this, "type"); + this.value = Number(value); + switch (type) { case "number": - if (this.value = Number(e), isNaN(this.value)) - throw new Error("Cannot parse number: " + e); + this.value = Number(value); + if (isNaN(this.value)) { + throw new Error("Cannot parse number: " + value); + } break; case "string": - this.value = String(e); + this.value = String(value); break; default: - throw new Error("Invalid value type: " + t); + throw new Error("Invalid value type: " + type); } - this.type = t; + this.type = type; } evaluate() { return this.value; @@ -82,96 +98,120 @@ class w extends h { case "number": return String(this.value); case "string": - return '"' + this.value + '"'; + return String('"' + this.value + '"'); default: throw new Error("Invalid type"); } } } -class E extends h { - constructor(e, t, r) { +class PlusMinusExpression extends Expression { + constructor(operator, left, right) { super(); - n(this, "operator"); - n(this, "left"); - n(this, "right"); - if (!["+", "-"].includes(e)) - throw new Error(`Operator not allowed in Plus/Minus expression: ${e}`); - this.operator = e, this.left = t, this.right = r; - } - evaluate(e = {}) { - const t = this.left.evaluate(e), r = this.right.evaluate(e); - if (p.throwIfNotNumber(t), p.throwIfNotNumber(r), this.operator === "+") - return Number(t) + Number(r); - if (this.operator === "-") - return Number(t) - Number(r); + __publicField(this, "operator"); + __publicField(this, "left"); + __publicField(this, "right"); + if (!["+", "-"].includes(operator)) { + throw new Error(`Operator not allowed in Plus/Minus expression: ${operator}`); + } + this.operator = operator; + this.left = left; + this.right = right; + } + evaluate(params = {}) { + const leftValue = this.left.evaluate(params); + const rightValue = this.right.evaluate(params); + MathOperatorHelper.throwIfNotNumber(leftValue); + MathOperatorHelper.throwIfNotNumber(rightValue); + if (this.operator === "+") { + return Number(leftValue) + Number(rightValue); + } + if (this.operator === "-") { + return Number(leftValue) - Number(rightValue); + } throw new Error("Unknown operator for PlusMinus expression"); } toString() { return `${this.left.toString()} ${this.operator} ${this.right.toString()}`; } } -class x extends h { - constructor(e, t, r) { +class MultDivExpression extends Expression { + constructor(operator, left, right) { super(); - n(this, "operator"); - n(this, "left"); - n(this, "right"); - if (!["*", "/"].includes(e)) - throw new Error(`Operator not allowed in Multiply/Division expression: ${e}`); - this.operator = e, this.left = t, this.right = r; - } - evaluate(e = {}) { - const t = this.left.evaluate(e), r = this.right.evaluate(e); - if (p.throwIfNotNumber(t), p.throwIfNotNumber(r), this.operator === "*") - return Number(t) * Number(r); - if (this.operator === "/") - return Number(t) / Number(r); + __publicField(this, "operator"); + __publicField(this, "left"); + __publicField(this, "right"); + if (!["*", "/"].includes(operator)) { + throw new Error(`Operator not allowed in Multiply/Division expression: ${operator}`); + } + this.operator = operator; + this.left = left; + this.right = right; + } + evaluate(params = {}) { + const leftValue = this.left.evaluate(params); + const rightValue = this.right.evaluate(params); + MathOperatorHelper.throwIfNotNumber(leftValue); + MathOperatorHelper.throwIfNotNumber(rightValue); + if (this.operator === "*") { + return Number(leftValue) * Number(rightValue); + } + if (this.operator === "/") { + return Number(leftValue) / Number(rightValue); + } throw new Error("Unknown operator for MultDiv expression"); } toString() { return `${this.left.toString()} ${this.operator} ${this.right.toString()}`; } } -class b extends h { - constructor(e, t) { +class PowerExpression extends Expression { + constructor(base, exponent) { super(); - n(this, "base"); - n(this, "exponent"); - this.base = e, this.exponent = t; - } - evaluate(e = {}) { - const t = this.base.evaluate(e), r = this.exponent.evaluate(e); - return p.throwIfNotNumber(t), p.throwIfNotNumber(r), Math.pow(Number(t), Number(r)); + __publicField(this, "base"); + __publicField(this, "exponent"); + this.base = base; + this.exponent = exponent; + } + evaluate(params = {}) { + const baseValue = this.base.evaluate(params); + const exponentValue = this.exponent.evaluate(params); + MathOperatorHelper.throwIfNotNumber(baseValue); + MathOperatorHelper.throwIfNotNumber(exponentValue); + return Math.pow(Number(baseValue), Number(exponentValue)); } toString() { return `${this.base.toString()}^${this.exponent.toString()}`; } } -class d extends h { - constructor(e, t, r) { +class LogicalExpression extends Expression { + constructor(operator, left, right) { super(); - n(this, "operator"); - n(this, "left"); - n(this, "right"); - if (!["<", ">", "<=", ">=", "=", "!="].includes(e)) - throw new Error(`Operator not allowed in Logical expression: ${e}`); - this.operator = e, this.left = t, this.right = r; - } - evaluate(e = {}) { - const t = this.left.evaluate(e), r = this.right.evaluate(e); + __publicField(this, "operator"); + __publicField(this, "left"); + __publicField(this, "right"); + if (!["<", ">", "<=", ">=", "=", "!="].includes(operator)) { + throw new Error(`Operator not allowed in Logical expression: ${operator}`); + } + this.operator = operator; + this.left = left; + this.right = right; + } + evaluate(params = {}) { + const leftValue = this.left.evaluate(params); + const rightValue = this.right.evaluate(params); switch (this.operator) { case "<": - return t < r ? 1 : 0; + return leftValue < rightValue ? 1 : 0; case ">": - return t > r ? 1 : 0; + return leftValue > rightValue ? 1 : 0; case "<=": - return t <= r ? 1 : 0; + return leftValue <= rightValue ? 1 : 0; case ">=": - return t >= r ? 1 : 0; + return leftValue >= rightValue ? 1 : 0; case "=": - return t === r ? 1 : 0; + return leftValue === rightValue ? 1 : 0; case "!=": - return t !== r ? 1 : 0; + return leftValue !== rightValue ? 1 : 0; } throw new Error("Unknown operator for Logical expression"); } @@ -179,92 +219,112 @@ class d extends h { return `${this.left.toString()} ${this.operator} ${this.right.toString()}`; } } -class k extends h { - constructor(e, t, r = null) { +class FunctionExpression extends Expression { + constructor(fn, argumentExpressions, formulaObject = null) { super(); - n(this, "fn"); - n(this, "varPath"); - n(this, "argumentExpressions"); - n(this, "formulaObject"); - n(this, "blacklisted"); - this.fn = e != null ? e : "", this.varPath = this.fn.split("."), this.argumentExpressions = t || [], this.formulaObject = r, this.blacklisted = void 0; - } - evaluate(e = {}) { - var a; - e = e || {}; - const t = this.argumentExpressions.map((s) => s.evaluate(e)); + __publicField(this, "fn"); + __publicField(this, "varPath"); + __publicField(this, "argumentExpressions"); + __publicField(this, "formulaObject"); + __publicField(this, "blacklisted"); + this.fn = fn != null ? fn : ""; + this.varPath = this.fn.split("."); + this.argumentExpressions = argumentExpressions || []; + this.formulaObject = formulaObject; + this.blacklisted = void 0; + } + evaluate(params = {}) { + var _a; + params = params || {}; + const paramValues = this.argumentExpressions.map((a) => a.evaluate(params)); try { - let s = m(e, this.varPath, this.fn); - if (s instanceof Function) - return s.apply(this, t); - } catch (s) { + let fn = getProperty(params, this.varPath, this.fn); + if (fn instanceof Function) { + return fn.apply(this, paramValues); + } + } catch (e) { } - let r; + let objFn; try { - r = m((a = this.formulaObject) != null ? a : {}, this.varPath, this.fn); - } catch (s) { + objFn = getProperty((_a = this.formulaObject) != null ? _a : {}, this.varPath, this.fn); + } catch (e) { } - if (this.formulaObject && r instanceof Function) { - if (this.isBlacklisted()) + if (this.formulaObject && objFn instanceof Function) { + if (this.isBlacklisted()) { throw new Error("Blacklisted function called: " + this.fn); - return r.apply(this.formulaObject, t); + } + return objFn.apply(this.formulaObject, paramValues); } try { - const s = m(Math, this.varPath, this.fn); - if (s instanceof Function) - return t.forEach((o) => { - V.throwIfNotNumber(o); - }), s.apply(this, t); - } catch (s) { + const mathFn = getProperty(Math, this.varPath, this.fn); + if (mathFn instanceof Function) { + paramValues.forEach((paramValue) => { + MathFunctionHelper.throwIfNotNumber(paramValue); + }); + return mathFn.apply(this, paramValues); + } + } catch (e) { } throw new Error("Function not found: " + this.fn); } toString() { - return `${this.fn}(${this.argumentExpressions.map((e) => e.toString()).join(", ")})`; + return `${this.fn}(${this.argumentExpressions.map((a) => a.toString()).join(", ")})`; } isBlacklisted() { - return this.blacklisted === void 0 && (this.blacklisted = N.functionBlacklist.includes( - this.formulaObject ? this.formulaObject[this.fn] : null - )), this.blacklisted; + if (this.blacklisted === void 0) { + this.blacklisted = Formula.functionBlacklist.includes( + this.formulaObject ? this.formulaObject[this.fn] : null + ); + } + return this.blacklisted; } } -function m(l, i, e) { - let t = l; - for (let r of i) { - if (typeof t != "object") - throw new Error(`Cannot evaluate ${r}, property not found (from path ${e})`); - if (t[r] === void 0) - throw new Error(`Cannot evaluate ${r}, property not found (from path ${e})`); - t = t[r]; - } - if (typeof t == "object") +function getProperty(object, path, fullPath) { + let curr = object; + for (let propName of path) { + if (typeof curr !== "object") { + throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`); + } + if (curr[propName] === void 0) { + throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`); + } + curr = curr[propName]; + } + if (typeof curr === "object") { throw new Error("Invalid value"); - return t; + } + return curr; } -class y extends h { - constructor(e, t = null) { +class VariableExpression extends Expression { + constructor(fullPath, formulaObj = null) { super(); - n(this, "fullPath"); - n(this, "varPath"); - n(this, "formulaObject"); - this.formulaObject = t, this.fullPath = e, this.varPath = e.split("."); - } - evaluate(e = {}) { - var r; - let t; + __publicField(this, "fullPath"); + __publicField(this, "varPath"); + __publicField(this, "formulaObject"); + this.formulaObject = formulaObj; + this.fullPath = fullPath; + this.varPath = fullPath.split("."); + } + evaluate(params = {}) { + var _a; + let value = void 0; try { - t = m(e, this.varPath, this.fullPath); - } catch (a) { + value = getProperty(params, this.varPath, this.fullPath); + } catch (e) { } - if (t === void 0 && (t = m((r = this.formulaObject) != null ? r : {}, this.varPath, this.fullPath)), typeof t == "function" || typeof t == "object") + if (value === void 0) { + value = getProperty((_a = this.formulaObject) != null ? _a : {}, this.varPath, this.fullPath); + } + if (typeof value === "function" || typeof value === "object") { throw new Error(`Cannot use ${this.fullPath} as value: It contains a non-numerical value.`); - return t; + } + return value; } toString() { return `${this.varPath.join(".")}`; } } -const u = class u { +const _Formula = class _Formula { /** * Creates a new Formula instance * @@ -277,13 +337,18 @@ const u = class u { * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters * @param {Formula} parentFormula Internally used to build a Formula AST */ - constructor(i, e = {}) { - n(this, "formulaExpression"); - n(this, "options"); - n(this, "formulaStr"); - n(this, "_variables"); - n(this, "_memory"); - this.formulaExpression = null, this.options = { memoization: !1, ...e }, this.formulaStr = "", this._variables = [], this._memory = {}, this.setFormula(i); + constructor(fStr, options = {}) { + __publicField(this, "formulaExpression"); + __publicField(this, "options"); + __publicField(this, "formulaStr"); + __publicField(this, "_variables"); + __publicField(this, "_memory"); + this.formulaExpression = null; + this.options = { ...{ memoization: false }, ...options }; + this.formulaStr = ""; + this._variables = []; + this._memory = {}; + this.setFormula(fStr); } /** * Re-sets the given String and parses it to a formula expression. Can be used after initialization, @@ -292,55 +357,80 @@ const u = class u { * @param {String} formulaString The formula string to set/parse * @return {this} The Formula object (this) */ - setFormula(i) { - return i && (this.formulaExpression = null, this._variables = [], this._memory = {}, this.formulaStr = i, this.formulaExpression = this.parse(i)), this; + setFormula(formulaString) { + if (formulaString) { + this.formulaExpression = null; + this._variables = []; + this._memory = {}; + this.formulaStr = formulaString; + this.formulaExpression = this.parse(formulaString); + } + return this; } /** * Enable memoization: An expression is only evaluated once for the same input. * Further evaluations with the same input will return the in-memory stored result. */ enableMemoization() { - this.options.memoization = !0; + this.options.memoization = true; } /** * Disable in-memory memoization: each call to evaluate() is executed from scratch. */ disableMemoization() { - this.options.memoization = !1, this._memory = {}; + this.options.memoization = false; + this._memory = {}; } /** * Splits the given string by ',', makes sure the ',' is not within * a sub-expression * e.g.: str = "x,pow(3,4)" returns 2 elements: x and pow(3,4). */ - splitFunctionParams(i) { - let e = 0, t = ""; - const r = []; - for (let a of i.split("")) - if (a === "," && e === 0) - r.push(t), t = ""; - else if (a === "(") - e++, t += a; - else if (a === ")") { - if (e--, t += a, e < 0) + splitFunctionParams(toSplit) { + let pCount = 0, paramStr = ""; + const params = []; + for (let chr of toSplit.split("")) { + if (chr === "," && pCount === 0) { + params.push(paramStr); + paramStr = ""; + } else if (chr === "(") { + pCount++; + paramStr += chr; + } else if (chr === ")") { + pCount--; + paramStr += chr; + if (pCount < 0) { throw new Error("ERROR: Too many closing parentheses!"); - } else - t += a; - if (e !== 0) + } + } else { + paramStr += chr; + } + } + if (pCount !== 0) { throw new Error("ERROR: Too many opening parentheses!"); - return t.length > 0 && r.push(t), r; + } + if (paramStr.length > 0) { + params.push(paramStr); + } + return params; } /** * Cleans the input string from unnecessary whitespace, * and replaces some known constants: */ - cleanupInputFormula(i) { - const e = []; - return i.split('"').forEach((r, a) => { - a % 2 === 0 && (r = r.replace(/[\s]+/g, ""), Object.keys(g).forEach((s) => { - r = r.replace(new RegExp(`\\b${s}\\b`, "g"), `[${s}]`); - })), e.push(r); - }), e.join('"'); + cleanupInputFormula(s) { + const resParts = []; + const srcParts = s.split('"'); + srcParts.forEach((part, index) => { + if (index % 2 === 0) { + part = part.replace(/[\s]+/g, ""); + Object.keys(MATH_CONSTANTS).forEach((c) => { + part = part.replace(new RegExp(`\\b${c}\\b`, "g"), `[${c}]`); + }); + } + resParts.push(part); + }); + return resParts.join('"'); } /** * Parses the given formula string by using a state machine into a single Expression object, @@ -382,93 +472,183 @@ const u = class u { * @param {String} str The formula string, e.g. '3*sin(PI/x)' * @returns {Expression} An expression object, representing the expression tree */ - parse(i) { - return i = this.cleanupInputFormula(i), this._do_parse(i); + parse(str) { + str = this.cleanupInputFormula(str); + return this._do_parse(str); } /** * @see parse(): this is the recursive parse function, without the clean string part. * @param {String} str * @returns {Expression} An expression object, representing the expression tree */ - _do_parse(i) { - let e = i.length - 1, t = 0, r = "initial", a = [], s = "", o = "", v = null, f = 0, c = ""; - for (; t <= e; ) { - switch (r) { + _do_parse(str) { + let lastChar = str.length - 1, act = 0, state = "initial", expressions = [], char = "", tmp = "", funcName = null, pCount = 0, pStringDelimiter = ""; + while (act <= lastChar) { + switch (state) { case "initial": - if (s = i.charAt(t), s.match(/[0-9.]/)) - r = "within-nr", o = "", t--; - else if (this.isOperator(s)) { - if (s === "-" && (a.length === 0 || this.isOperatorExpr(a[a.length - 1]))) { - r = "within-nr", o = "-"; - break; + char = str.charAt(act); + if (char.match(/[0-9.]/)) { + state = "within-nr"; + tmp = ""; + act--; + } else if (this.isOperator(char)) { + if (char === "-") { + if (expressions.length === 0 || this.isOperatorExpr(expressions[expressions.length - 1])) { + state = "within-nr"; + tmp = "-"; + break; + } } - if (t === e || this.isOperatorExpr(a[a.length - 1])) { - r = "invalid"; + if (act === lastChar || this.isOperatorExpr(expressions[expressions.length - 1])) { + state = "invalid"; break; - } else - a.push( - h.createOperatorExpression(s, new h(), new h()) - ), r = "initial"; - } else if ([">", "<", "=", "!"].includes(s)) - if (t === e) { - r = "invalid"; + } else { + expressions.push( + Expression.createOperatorExpression(char, new Expression(), new Expression()) + ); + state = "initial"; + } + } else if ([">", "<", "=", "!"].includes(char)) { + if (act === lastChar) { + state = "invalid"; break; - } else - r = "within-logical-operator", o = s; - else - s === "(" ? (r = "within-parentheses", o = "", f = 0) : s === "[" ? (r = "within-named-var", o = "") : s.match(/["']/) ? (r = "within-string", c = s, o = "") : s.match(/[a-zA-Z]/) && (t < e && i.charAt(t + 1).match(/[a-zA-Z0-9_.]/) ? (o = s, r = "within-func") : (a.length > 0 && a[a.length - 1] instanceof w && a.push( - h.createOperatorExpression("*", new h(), new h()) - ), a.push(new y(s, this)), this.registerVariable(s), r = "initial", o = "")); + } else { + state = "within-logical-operator"; + tmp = char; + } + } else if (char === "(") { + state = "within-parentheses"; + tmp = ""; + pCount = 0; + } else if (char === "[") { + state = "within-named-var"; + tmp = ""; + } else if (char.match(/["']/)) { + state = "within-string"; + pStringDelimiter = char; + tmp = ""; + } else if (char.match(/[a-zA-Z]/)) { + if (act < lastChar && str.charAt(act + 1).match(/[a-zA-Z0-9_.]/)) { + tmp = char; + state = "within-func"; + } else { + if (expressions.length > 0 && expressions[expressions.length - 1] instanceof ValueExpression) { + expressions.push( + Expression.createOperatorExpression("*", new Expression(), new Expression()) + ); + } + expressions.push(new VariableExpression(char, this)); + this.registerVariable(char); + state = "initial"; + tmp = ""; + } + } break; case "within-nr": - s = i.charAt(t), s.match(/[0-9.]/) ? (o += s, t === e && (a.push(new w(o)), r = "initial")) : (o === "-" && (o = "-1"), a.push(new w(o)), o = "", r = "initial", t--); + char = str.charAt(act); + if (char.match(/[0-9.]/)) { + tmp += char; + if (act === lastChar) { + expressions.push(new ValueExpression(tmp)); + state = "initial"; + } + } else { + if (tmp === "-") { + tmp = "-1"; + } + expressions.push(new ValueExpression(tmp)); + tmp = ""; + state = "initial"; + act--; + } break; case "within-func": - if (s = i.charAt(t), s.match(/[a-zA-Z0-9_.]/)) - o += s; - else if (s === "(") - v = o, o = "", f = 0, r = "within-func-parentheses"; - else - throw new Error("Wrong character for function at position " + t); + char = str.charAt(act); + if (char.match(/[a-zA-Z0-9_.]/)) { + tmp += char; + } else if (char === "(") { + funcName = tmp; + tmp = ""; + pCount = 0; + state = "within-func-parentheses"; + } else { + throw new Error("Wrong character for function at position " + act); + } break; case "within-named-var": - if (s = i.charAt(t), s === "]") - a.push(new y(o, this)), this.registerVariable(o), o = "", r = "initial"; - else if (s.match(/[a-zA-Z0-9_.]/)) - o += s; - else - throw new Error("Character not allowed within named variable: " + s); + char = str.charAt(act); + if (char === "]") { + expressions.push(new VariableExpression(tmp, this)); + this.registerVariable(tmp); + tmp = ""; + state = "initial"; + } else if (char.match(/[a-zA-Z0-9_.]/)) { + tmp += char; + } else { + throw new Error("Character not allowed within named variable: " + char); + } break; case "within-string": - s = i.charAt(t), s === c ? (a.push(new w(o, "string")), o = "", r = "initial", c = "") : o += s; + char = str.charAt(act); + if (char === pStringDelimiter) { + expressions.push(new ValueExpression(tmp, "string")); + tmp = ""; + state = "initial"; + pStringDelimiter = ""; + } else { + tmp += char; + } break; case "within-parentheses": case "within-func-parentheses": - if (s = i.charAt(t), c) - s === c && (c = ""), o += s; - else if (s === ")") - if (f <= 0) { - if (r === "within-parentheses") - a.push(new S(this._do_parse(o))); - else if (r === "within-func-parentheses") { - let M = this.splitFunctionParams(o).map((P) => this._do_parse(P)); - a.push(new k(v, M, this)), v = null; + char = str.charAt(act); + if (pStringDelimiter) { + if (char === pStringDelimiter) { + pStringDelimiter = ""; + } + tmp += char; + } else if (char === ")") { + if (pCount <= 0) { + if (state === "within-parentheses") { + expressions.push(new BracketExpression(this._do_parse(tmp))); + } else if (state === "within-func-parentheses") { + let args = this.splitFunctionParams(tmp).map((a) => this._do_parse(a)); + expressions.push(new FunctionExpression(funcName, args, this)); + funcName = null; } - r = "initial"; - } else - f--, o += s; - else - s === "(" ? (f++, o += s) : (s.match(/["']/) && (c = s), o += s); + state = "initial"; + } else { + pCount--; + tmp += char; + } + } else if (char === "(") { + pCount++; + tmp += char; + } else if (char.match(/["']/)) { + pStringDelimiter = char; + tmp += char; + } else { + tmp += char; + } break; case "within-logical-operator": - s = i.charAt(t), s === "=" && (o += s, t++), a.push(h.createOperatorExpression(o, new h(), new h())), o = "", r = "initial", t--; + char = str.charAt(act); + if (char === "=") { + tmp += char; + act++; + } + expressions.push(Expression.createOperatorExpression(tmp, new Expression(), new Expression())); + tmp = ""; + state = "initial"; + act--; break; } - t++; + act++; } - if (r !== "initial") + if (state !== "initial") { throw new Error("Could not parse formula: Syntax error."); - return this.buildExpressionTree(a); + } + return this.buildExpressionTree(expressions); } /** * @see parse(): Builds an expression tree from the given expression array. @@ -479,51 +659,90 @@ const u = class u { * @param {*} expressions * @return {Expression} The root Expression of the built expression tree */ - buildExpressionTree(i) { - if (i.length < 1) + buildExpressionTree(expressions) { + if (expressions.length < 1) { throw new Error("No expression given!"); - const e = [...i]; - let t = 0, r = null; - for (; t < e.length; ) - if (r = e[t], r instanceof b) { - if (t === 0 || t === e.length - 1) + } + const exprCopy = [...expressions]; + let idx = 0; + let expr = null; + while (idx < exprCopy.length) { + expr = exprCopy[idx]; + if (expr instanceof PowerExpression) { + if (idx === 0 || idx === exprCopy.length - 1) { throw new Error("Wrong operator position!"); - r.base = e[t - 1], r.exponent = e[t + 1], e[t - 1] = r, e.splice(t, 2); - } else - t++; - for (t = 0, r = null; t < e.length; ) - if (r = e[t], r instanceof x) { - if (t === 0 || t === e.length - 1) + } + expr.base = exprCopy[idx - 1]; + expr.exponent = exprCopy[idx + 1]; + exprCopy[idx - 1] = expr; + exprCopy.splice(idx, 2); + } else { + idx++; + } + } + idx = 0; + expr = null; + while (idx < exprCopy.length) { + expr = exprCopy[idx]; + if (expr instanceof MultDivExpression) { + if (idx === 0 || idx === exprCopy.length - 1) { throw new Error("Wrong operator position!"); - r.left = e[t - 1], r.right = e[t + 1], e[t - 1] = r, e.splice(t, 2); - } else - t++; - for (t = 0, r = null; t < e.length; ) - if (r = e[t], r instanceof E) { - if (t === 0 || t === e.length - 1) + } + expr.left = exprCopy[idx - 1]; + expr.right = exprCopy[idx + 1]; + exprCopy[idx - 1] = expr; + exprCopy.splice(idx, 2); + } else { + idx++; + } + } + idx = 0; + expr = null; + while (idx < exprCopy.length) { + expr = exprCopy[idx]; + if (expr instanceof PlusMinusExpression) { + if (idx === 0 || idx === exprCopy.length - 1) { throw new Error("Wrong operator position!"); - r.left = e[t - 1], r.right = e[t + 1], e[t - 1] = r, e.splice(t, 2); - } else - t++; - for (t = 0, r = null; t < e.length; ) - if (r = e[t], r instanceof d) { - if (t === 0 || t === e.length - 1) + } + expr.left = exprCopy[idx - 1]; + expr.right = exprCopy[idx + 1]; + exprCopy[idx - 1] = expr; + exprCopy.splice(idx, 2); + } else { + idx++; + } + } + idx = 0; + expr = null; + while (idx < exprCopy.length) { + expr = exprCopy[idx]; + if (expr instanceof LogicalExpression) { + if (idx === 0 || idx === exprCopy.length - 1) { throw new Error("Wrong operator position!"); - r.left = e[t - 1], r.right = e[t + 1], e[t - 1] = r, e.splice(t, 2); - } else - t++; - if (e.length !== 1) + } + expr.left = exprCopy[idx - 1]; + expr.right = exprCopy[idx + 1]; + exprCopy[idx - 1] = expr; + exprCopy.splice(idx, 2); + } else { + idx++; + } + } + if (exprCopy.length !== 1) { throw new Error("Could not parse formula: incorrect syntax?"); - return e[0]; + } + return exprCopy[0]; } - isOperator(i) { - return typeof i == "string" && i.match(/[+\-*/^]/); + isOperator(char) { + return typeof char === "string" && char.match(/[+\-*/^]/); } - isOperatorExpr(i) { - return i instanceof E || i instanceof x || i instanceof b || i instanceof d; + isOperatorExpr(expr) { + return expr instanceof PlusMinusExpression || expr instanceof MultDivExpression || expr instanceof PowerExpression || expr instanceof LogicalExpression; } - registerVariable(i) { - this._variables.indexOf(i) < 0 && this._variables.push(i); + registerVariable(varName) { + if (this._variables.indexOf(varName) < 0) { + this._variables.push(varName); + } } getVariables() { return this._variables; @@ -539,27 +758,40 @@ const u = class u { * also returned as array. * @return {Number|String|(Number|String)[]} The evaluated result, or an array with results */ - evaluate(i) { - if (i instanceof Array) - return i.map((t) => this.evaluate(t)); - let e = this.getExpression(); - if (!(e instanceof h)) + evaluate(valueObj) { + if (valueObj instanceof Array) { + return valueObj.map((v) => this.evaluate(v)); + } + let expr = this.getExpression(); + if (!(expr instanceof Expression)) { throw new Error("No expression set: Did you init the object with a Formula?"); + } if (this.options.memoization) { - let t = this.resultFromMemory(i); - return t !== null || (t = e.evaluate({ ...g, ...i }), this.storeInMemory(i, t)), t; + let res = this.resultFromMemory(valueObj); + if (res !== null) { + return res; + } else { + res = expr.evaluate({ ...MATH_CONSTANTS, ...valueObj }); + this.storeInMemory(valueObj, res); + return res; + } + } + return expr.evaluate({ ...MATH_CONSTANTS, ...valueObj }); + } + hashValues(valueObj) { + return JSON.stringify(valueObj); + } + resultFromMemory(valueObj) { + let key = this.hashValues(valueObj); + let res = this._memory[key]; + if (res !== void 0) { + return res; + } else { + return null; } - return e.evaluate({ ...g, ...i }); - } - hashValues(i) { - return JSON.stringify(i); - } - resultFromMemory(i) { - let e = this.hashValues(i), t = this._memory[e]; - return t !== void 0 ? t : null; } - storeInMemory(i, e) { - this._memory[this.hashValues(i)] = e; + storeInMemory(valueObj, value) { + this._memory[this.hashValues(valueObj)] = value; } getExpression() { return this.formulaExpression; @@ -567,14 +799,25 @@ const u = class u { getExpressionString() { return this.formulaExpression ? this.formulaExpression.toString() : ""; } - static calc(i, e = null, t = {}) { - return e = e != null ? e : {}, new u(i, t).evaluate(e); + static calc(formula, valueObj = null, options = {}) { + valueObj = valueObj != null ? valueObj : {}; + return new _Formula(formula, options).evaluate(valueObj); } }; -n(u, "Expression", h), n(u, "BracketExpression", S), n(u, "PowerExpression", b), n(u, "MultDivExpression", x), n(u, "PlusMinusExpression", E), n(u, "LogicalExpression", d), n(u, "ValueExpression", w), n(u, "VariableExpression", y), n(u, "FunctionExpression", k), n(u, "MATH_CONSTANTS", g), // Create a function blacklist: -n(u, "functionBlacklist", Object.getOwnPropertyNames(u.prototype).filter((i) => u.prototype[i] instanceof Function).map((i) => u.prototype[i])); -let N = u; +__publicField(_Formula, "Expression", Expression); +__publicField(_Formula, "BracketExpression", BracketExpression); +__publicField(_Formula, "PowerExpression", PowerExpression); +__publicField(_Formula, "MultDivExpression", MultDivExpression); +__publicField(_Formula, "PlusMinusExpression", PlusMinusExpression); +__publicField(_Formula, "LogicalExpression", LogicalExpression); +__publicField(_Formula, "ValueExpression", ValueExpression); +__publicField(_Formula, "VariableExpression", VariableExpression); +__publicField(_Formula, "FunctionExpression", FunctionExpression); +__publicField(_Formula, "MATH_CONSTANTS", MATH_CONSTANTS); +// Create a function blacklist: +__publicField(_Formula, "functionBlacklist", Object.getOwnPropertyNames(_Formula.prototype).filter((prop) => _Formula.prototype[prop] instanceof Function).map((prop) => _Formula.prototype[prop])); +let Formula = _Formula; export { - N as default + Formula as default }; //# sourceMappingURL=fparser.js.map diff --git a/dist/fparser.js.map b/dist/fparser.js.map index 9420dd6..583e5f1 100644 --- a/dist/fparser.js.map +++ b/dist/fparser.js.map @@ -1 +1 @@ -{"version":3,"file":"fparser.js","sources":["../src/fparser.ts"],"sourcesContent":["/**\n * JS Formula Parser\n * -------------------\n * (c) 2012-2023 Alexander Schenkel, alex@alexi.ch\n *\n * JS Formula Parser takes a string, parses its mathmatical formula\n * and creates an evaluatable Formula object of it.\n *\n * Example input:\n *\n * var fObj = new Formula('sin(PI*x)/(2*PI)');\n * var result = fObj.evaluate({x: 2});\n * var results = fObj.evaluate([\n * {x: 2},\n * {x: 4},\n * {x: 8}\n * ]);\n *\n * LICENSE:\n * -------------\n * MIT license, see LICENSE file\n */\nconst MATH_CONSTANTS = {\n PI: Math.PI,\n E: Math.E,\n LN2: Math.LN2,\n LN10: Math.LN10,\n LOG2E: Math.LOG2E,\n LOG10E: Math.LOG10E,\n SQRT1_2: Math.SQRT1_2,\n SQRT2: Math.SQRT2\n};\n\ndeclare global {\n interface Math {\n [key: string]: number | Function;\n }\n}\n\ntype FormulaOptions = {\n memoization?: boolean;\n};\n\ntype ValueObject = {\n [key: string]: number | string | Function | ValueObject;\n};\n\nclass MathOperatorHelper {\n static throwIfNotNumber(value: number | string) {\n const valueType = typeof value;\n if (valueType === 'string') {\n throw new Error('Strings are not allowed in math operations');\n }\n }\n}\n\nclass MathFunctionHelper {\n static throwIfNotNumber(value: number | string) {\n const valueType = typeof value;\n if (valueType === 'string') {\n throw new Error('Strings are not allowed in math operations');\n }\n }\n}\n\nclass Expression {\n static createOperatorExpression(operator: string, left: Expression, right: Expression) {\n if (operator === '^') {\n return new PowerExpression(left, right);\n }\n if ( [ '*', '/' ].includes( operator ) ) {\n return new MultDivExpression(operator, left, right);\n }\n if ( [ '+', '-' ].includes( operator ) ) {\n return new PlusMinusExpression(operator, left, right);\n }\n if ( [ '<', '>', '<=', '>=', '=', '!=' ].includes( operator ) ) {\n return new LogicalExpression(operator, left, right);\n }\n throw new Error(`Unknown operator: ${operator}`);\n }\n\n evaluate(params: ValueObject = {}): number | string {\n throw new Error('Empty Expression - Must be defined in child classes');\n }\n\n toString() {\n return '';\n }\n}\n\nclass BracketExpression extends Expression {\n innerExpression: Expression;\n\n constructor(expr: Expression) {\n super();\n this.innerExpression = expr;\n if (!(this.innerExpression instanceof Expression)) {\n throw new Error('No inner expression given for bracket expression');\n }\n }\n evaluate(params = {}): number | string {\n return this.innerExpression.evaluate(params);\n }\n toString() {\n return `(${this.innerExpression.toString()})`;\n }\n}\n\nclass ValueExpression extends Expression {\n value: number | string;\n type: string;\n\n constructor(value: number | string, type: string = 'number') {\n super();\n this.value = Number(value);\n switch (type) {\n case 'number':\n this.value = Number(value);\n if (isNaN(this.value)) {\n throw new Error('Cannot parse number: ' + value);\n }\n break;\n case 'string':\n this.value = String(value);\n break;\n default:\n throw new Error('Invalid value type: ' + type);\n }\n this.type = type;\n }\n evaluate(): number | string {\n return this.value;\n }\n toString() {\n switch (this.type) {\n case 'number':\n return String(this.value);\n case 'string':\n return String('\"' + this.value + '\"');\n default:\n throw new Error('Invalid type');\n }\n }\n}\n\nclass PlusMinusExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n\n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if (!['+', '-'].includes(operator)) {\n throw new Error(`Operator not allowed in Plus/Minus expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(leftValue);\n MathOperatorHelper.throwIfNotNumber(rightValue);\n if (this.operator === '+') {\n return Number(leftValue) + Number(rightValue);\n }\n if (this.operator === '-') {\n return Number(leftValue) - Number(rightValue);\n }\n throw new Error('Unknown operator for PlusMinus expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\n\nclass MultDivExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n\n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if (!['*', '/'].includes(operator)) {\n throw new Error(`Operator not allowed in Multiply/Division expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(leftValue);\n MathOperatorHelper.throwIfNotNumber(rightValue);\n if (this.operator === '*') {\n return Number(leftValue) * Number(rightValue);\n }\n if (this.operator === '/') {\n return Number(leftValue) / Number(rightValue);\n }\n throw new Error('Unknown operator for MultDiv expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\n\nclass PowerExpression extends Expression {\n base: Expression;\n exponent: Expression;\n\n constructor(base: Expression, exponent: Expression) {\n super();\n this.base = base;\n this.exponent = exponent;\n }\n\n evaluate(params: ValueObject = {}): number {\n const baseValue = this.base.evaluate(params);\n const exponentValue = this.exponent.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(baseValue);\n MathOperatorHelper.throwIfNotNumber(exponentValue);\n\n return Math.pow(Number(baseValue), Number(exponentValue));\n }\n\n toString() {\n return `${this.base.toString()}^${this.exponent.toString()}`;\n }\n}\n\nclass LogicalExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n \n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if ( ! [ '<', '>', '<=', '>=', '=', '!=' ].includes( operator ) ) {\n throw new Error(`Operator not allowed in Logical expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n switch ( this.operator ) {\n case '<':\n return leftValue < rightValue ? 1 : 0;\n case '>':\n return leftValue > rightValue ? 1 : 0;\n case '<=':\n return leftValue <= rightValue ? 1 : 0;\n case '>=':\n return leftValue >= rightValue ? 1 : 0;\n case '=':\n return leftValue === rightValue ? 1 : 0;\n case '!=':\n return leftValue !== rightValue ? 1 : 0;\n }\n throw new Error('Unknown operator for Logical expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\nclass FunctionExpression extends Expression {\n fn: string;\n varPath: string[];\n argumentExpressions: Expression[];\n formulaObject: Formula | null;\n blacklisted: boolean | undefined;\n\n constructor(fn: string | null, argumentExpressions: Expression[], formulaObject: Formula | null = null) {\n super();\n this.fn = fn ?? '';\n this.varPath = this.fn.split('.');\n this.argumentExpressions = argumentExpressions || [];\n this.formulaObject = formulaObject;\n this.blacklisted = undefined;\n }\n\n evaluate(params: ValueObject = {}): number | string {\n params = params || {};\n const paramValues = this.argumentExpressions.map((a) => a.evaluate(params));\n\n // If the params object itself has a function definition with\n // the function name, call this one:\n // let fn = params[this.fn];\n try {\n let fn = getProperty(params, this.varPath, this.fn);\n if (fn instanceof Function) {\n return fn.apply(this, paramValues);\n }\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n\n let objFn;\n try {\n // perhaps the Formula object has the function? so call it:\n objFn = getProperty(this.formulaObject ?? {}, this.varPath, this.fn);\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n if (this.formulaObject && objFn instanceof Function) {\n // Don't, if it is blacklisted:\n if (this.isBlacklisted()) {\n throw new Error('Blacklisted function called: ' + this.fn);\n }\n return objFn.apply(this.formulaObject, paramValues);\n }\n\n try {\n // Has the JS Math object a function as requested? Call it:\n const mathFn = getProperty(Math, this.varPath, this.fn);\n if (mathFn instanceof Function) {\n paramValues.forEach((paramValue) => {\n MathFunctionHelper.throwIfNotNumber(paramValue);\n });\n\n return mathFn.apply(this, paramValues);\n }\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n // No more options left: sorry!\n throw new Error('Function not found: ' + this.fn);\n }\n\n toString() {\n return `${this.fn}(${this.argumentExpressions.map((a) => a.toString()).join(', ')})`;\n }\n\n isBlacklisted() {\n // cache evaluation of blacklisted function, to save call time:\n if (this.blacklisted === undefined) {\n this.blacklisted = Formula.functionBlacklist.includes(\n this.formulaObject ? this.formulaObject[this.fn] : null\n );\n }\n return this.blacklisted;\n }\n}\n\nfunction getProperty(object: ValueObject, path: string[], fullPath: string) {\n let curr: number | string | Function | ValueObject = object;\n for (let propName of path) {\n if (typeof curr !== 'object') {\n throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`);\n }\n if (curr[propName] === undefined) {\n throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`);\n }\n curr = curr[propName];\n }\n\n if (typeof curr === 'object') {\n throw new Error('Invalid value');\n }\n\n return curr;\n}\n\nclass VariableExpression extends Expression {\n fullPath: string;\n varPath: string[];\n formulaObject: Formula | null;\n\n constructor(fullPath: string, formulaObj: Formula | null = null) {\n super();\n this.formulaObject = formulaObj;\n this.fullPath = fullPath;\n this.varPath = fullPath.split('.');\n }\n\n evaluate(params = {}): number | string {\n // params contain variable / value pairs: If this object's variable matches\n // a varname found in the params, return the value.\n // eg: params = {x: 5,y:3}, varname = x, return 5\n // Objects and arrays are also supported:\n // e.g. params = {x: {y: 5}}, varname = x.y, return 5\n // or params = {x: [2,4,6]}, varname = x.2, return 6\n\n // Let's look in the value object first:\n let value = undefined;\n try {\n value = getProperty(params, this.varPath, this.fullPath);\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n if (value === undefined) {\n // Now have a look at the formula object:\n // This will throw an error if the property is not found:\n value = getProperty(this.formulaObject ?? {}, this.varPath, this.fullPath);\n }\n if (typeof value === 'function' || typeof value === 'object') {\n throw new Error(`Cannot use ${this.fullPath} as value: It contains a non-numerical value.`);\n }\n\n return value;\n }\n toString() {\n return `${this.varPath.join('.')}`;\n }\n}\n\nexport default class Formula {\n [key: string]: any;\n static Expression = Expression;\n static BracketExpression = BracketExpression;\n static PowerExpression = PowerExpression;\n static MultDivExpression = MultDivExpression;\n static PlusMinusExpression = PlusMinusExpression;\n static LogicalExpression = LogicalExpression;\n static ValueExpression = ValueExpression;\n static VariableExpression = VariableExpression;\n static FunctionExpression = FunctionExpression;\n static MATH_CONSTANTS = MATH_CONSTANTS;\n\n // Create a function blacklist:\n static functionBlacklist = Object.getOwnPropertyNames(Formula.prototype)\n .filter((prop) => Formula.prototype[prop] instanceof Function)\n .map((prop) => Formula.prototype[prop]);\n\n public formulaExpression: Expression | null;\n public options: FormulaOptions;\n public formulaStr: string;\n private _variables: string[];\n private _memory: { [key: string]: number | string };\n\n /**\n * Creates a new Formula instance\n *\n * Optional configuration can be set in the options object:\n *\n * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters\n *\n * @param {String} fStr The formula string, e.g. 'sin(x)/cos(y)'\n * @param {Object} options An options object. Supported options:\n * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters\n * @param {Formula} parentFormula Internally used to build a Formula AST\n */\n constructor(fStr: string, options: FormulaOptions | null = {}) {\n this.formulaExpression = null;\n this.options = { ...{ memoization: false }, ...options };\n this.formulaStr = '';\n this._variables = [];\n this._memory = {};\n this.setFormula(fStr);\n }\n\n /**\n * Re-sets the given String and parses it to a formula expression. Can be used after initialization,\n * to re-use the Formula object.\n *\n * @param {String} formulaString The formula string to set/parse\n * @return {this} The Formula object (this)\n */\n setFormula(formulaString: string) {\n if (formulaString) {\n this.formulaExpression = null;\n this._variables = [];\n this._memory = {};\n this.formulaStr = formulaString;\n this.formulaExpression = this.parse(formulaString);\n }\n return this;\n }\n\n /**\n * Enable memoization: An expression is only evaluated once for the same input.\n * Further evaluations with the same input will return the in-memory stored result.\n */\n enableMemoization() {\n this.options.memoization = true;\n }\n\n /**\n * Disable in-memory memoization: each call to evaluate() is executed from scratch.\n */\n disableMemoization() {\n this.options.memoization = false;\n this._memory = {};\n }\n\n /**\n * Splits the given string by ',', makes sure the ',' is not within\n * a sub-expression\n * e.g.: str = \"x,pow(3,4)\" returns 2 elements: x and pow(3,4).\n */\n splitFunctionParams(toSplit: string) {\n // do not split on ',' within matching brackets.\n let pCount = 0,\n paramStr = '';\n const params = [];\n for (let chr of toSplit.split('')) {\n if (chr === ',' && pCount === 0) {\n // Found function param, save 'em\n params.push(paramStr);\n paramStr = '';\n } else if (chr === '(') {\n pCount++;\n paramStr += chr;\n } else if (chr === ')') {\n pCount--;\n paramStr += chr;\n if (pCount < 0) {\n throw new Error('ERROR: Too many closing parentheses!');\n }\n } else {\n paramStr += chr;\n }\n }\n if (pCount !== 0) {\n throw new Error('ERROR: Too many opening parentheses!');\n }\n if (paramStr.length > 0) {\n params.push(paramStr);\n }\n return params;\n }\n\n /**\n * Cleans the input string from unnecessary whitespace,\n * and replaces some known constants:\n */\n cleanupInputFormula(s: string) {\n const resParts: string[] = [];\n const srcParts = s.split('\"');\n srcParts.forEach((part, index) => {\n // skip parts marked as string\n if (index % 2 === 0) {\n part = part.replace(/[\\s]+/g, '');\n // surround known math constants with [], to parse them as named variables [xxx]:\n Object.keys(MATH_CONSTANTS).forEach((c) => {\n part = part.replace(new RegExp(`\\\\b${c}\\\\b`, 'g'), `[${c}]`);\n });\n }\n resParts.push(part);\n });\n return resParts.join('\"');\n }\n\n /**\n * Parses the given formula string by using a state machine into a single Expression object,\n * which represents an expression tree (aka AST).\n *\n * First, we split the string into 'expression': An expression can be:\n * - a number, e.g. '3.45'\n * - an unknown variable, e.g. 'x'\n * - a single char operator, such as '*','+' etc...\n * - a named variable, in [], e.g. [myvar]\n * - a function, such as sin(x)\n * - a parenthessed expression, containing other expressions\n *\n * We want to create an expression tree out of the string. This is done in 2 stages:\n * 1. form single expressions from the string: parse the string into known expression objects:\n * - numbers/[variables]/\"strings\"\n * - operators\n * - braces (with a sub-expression)\n * - functions (with sub-expressions (aka argument expressions))\n * This will lead to an array of expressions.\n * As an example:\n * \"2 + 3 * (4 + 3 ^ 5) * sin(PI * x)\" forms an array of the following expressions:\n * `[2, +, 3, *, bracketExpr(4,+,3,^,5), * , functionExpr(PI,*,x)]`\n * 2. From the raw expression array we form an expression tree by evaluating the expressions in the correct order:\n * e.g.:\n * the expression array `[2, +, 3, *, bracketExpr(4,+,3,^,5), * , functionExpr(PI,*,x)]` will be transformed into the expression tree:\n * ```\n * root expr: (+)\n * / \\\n * 2 (*)\n * / \\\n * (*) functionExpr(...)\n * / \\\n * 3 (bracket(..))\n * ```\n *\n * In the end, we have a single root expression node, which then can be evaluated in the evaluate() function.\n *\n * @param {String} str The formula string, e.g. '3*sin(PI/x)'\n * @returns {Expression} An expression object, representing the expression tree\n */\n parse(str: string) {\n // clean the input string first. spaces, math constant replacements etc.:\n str = this.cleanupInputFormula(str);\n // start recursive call to parse:\n return this._do_parse(str);\n }\n\n /**\n * @see parse(): this is the recursive parse function, without the clean string part.\n * @param {String} str\n * @returns {Expression} An expression object, representing the expression tree\n */\n _do_parse(str: string): Expression {\n let lastChar = str.length - 1,\n act = 0,\n state:\n | 'initial'\n | 'within-nr'\n | 'within-parentheses'\n | 'within-func-parentheses'\n | 'within-named-var'\n | 'within-string'\n | 'within-expr'\n | 'within-bracket'\n | 'within-func'\n | 'within-logical-operator'\n | 'invalid' = 'initial',\n expressions = [],\n char = '',\n tmp = '',\n funcName = null,\n pCount = 0,\n pStringDelimiter = '';\n\n while (act <= lastChar) {\n switch (state) {\n case 'initial':\n // None state, the beginning. Read a char and see what happens.\n char = str.charAt(act);\n if (char.match(/[0-9.]/)) {\n // found the beginning of a number, change state to \"within-number\"\n state = 'within-nr';\n tmp = '';\n act--;\n } else if (this.isOperator(char)) {\n // Simple operators. Note: '-' must be treaten specially,\n // it could be part of a number.\n // it MUST be part of a number if the last found expression\n // was an operator (or the beginning):\n if (char === '-') {\n if (expressions.length === 0 || this.isOperatorExpr(expressions[expressions.length - 1])) {\n state = 'within-nr';\n tmp = '-';\n break;\n }\n }\n\n // Found a simple operator, store as expression:\n if (act === lastChar || this.isOperatorExpr(expressions[expressions.length - 1])) {\n state = 'invalid'; // invalid to end with an operator, or have 2 operators in conjunction\n break;\n } else {\n expressions.push(\n Expression.createOperatorExpression(char, new Expression(), new Expression())\n );\n state = 'initial';\n }\n } else if ( [ '>', '<', '=', '!' ].includes(char)) {\n // found the beginning of a logical operator, change state to \"within-logical-operator\"\n if (act === lastChar) {\n state = 'invalid'; // invalid to end with a logical operator\n break;\n } else {\n state = 'within-logical-operator';\n tmp = char;\n }\n } else if (char === '(') {\n // left parenthes found, seems to be the beginning of a new sub-expression:\n state = 'within-parentheses';\n tmp = '';\n pCount = 0;\n } else if (char === '[') {\n // left named var separator char found, seems to be the beginning of a named var:\n state = 'within-named-var';\n tmp = '';\n } else if (char.match(/[\"']/)) {\n // left string separator char found\n state = 'within-string';\n pStringDelimiter = char;\n tmp = '';\n } else if (char.match(/[a-zA-Z]/)) {\n // multiple chars means it may be a function, else its a var which counts as own expression:\n if (act < lastChar && str.charAt(act + 1).match(/[a-zA-Z0-9_.]/)) {\n tmp = char;\n state = 'within-func';\n } else {\n // Single variable found:\n // We need to check some special considerations:\n // - If the last char was a number (e.g. 3x), we need to create a multiplication out of it (3*x)\n if (\n expressions.length > 0 &&\n expressions[expressions.length - 1] instanceof ValueExpression\n ) {\n expressions.push(\n Expression.createOperatorExpression('*', new Expression(), new Expression())\n );\n }\n expressions.push(new VariableExpression(char, this));\n this.registerVariable(char);\n state = 'initial';\n tmp = '';\n }\n }\n break;\n case 'within-nr':\n char = str.charAt(act);\n if (char.match(/[0-9.]/)) {\n //Still within number, store and continue\n tmp += char;\n if (act === lastChar) {\n expressions.push(new ValueExpression(tmp));\n state = 'initial';\n }\n } else {\n // Number finished on last round, so add as expression:\n if (tmp === '-') {\n // just a single '-' means: a variable could follow (e.g. like in 3*-x), we convert it to -1: (3*-1x)\n tmp = '-1';\n }\n expressions.push(new ValueExpression(tmp));\n tmp = '';\n state = 'initial';\n act--;\n }\n break;\n\n case 'within-func':\n char = str.charAt(act);\n if (char.match(/[a-zA-Z0-9_.]/)) {\n tmp += char;\n } else if (char === '(') {\n funcName = tmp;\n tmp = '';\n pCount = 0;\n state = 'within-func-parentheses';\n } else {\n throw new Error('Wrong character for function at position ' + act);\n }\n\n break;\n\n case 'within-named-var':\n char = str.charAt(act);\n if (char === ']') {\n // end of named var, create expression:\n expressions.push(new VariableExpression(tmp, this));\n this.registerVariable(tmp);\n tmp = '';\n state = 'initial';\n } else if (char.match(/[a-zA-Z0-9_.]/)) {\n tmp += char;\n } else {\n throw new Error('Character not allowed within named variable: ' + char);\n }\n break;\n\n case 'within-string':\n char = str.charAt(act);\n if (char === pStringDelimiter) {\n // end of string, create expression:\n expressions.push(new ValueExpression(tmp, 'string'));\n tmp = '';\n state = 'initial';\n pStringDelimiter = '';\n } else {\n tmp += char;\n }\n break;\n\n case 'within-parentheses':\n case 'within-func-parentheses':\n char = str.charAt(act);\n if (pStringDelimiter) {\n // If string is opened, then:\n if (char === pStringDelimiter) {\n // end of string\n pStringDelimiter = '';\n }\n // accumulate string chars\n tmp += char;\n } else if (char === ')') {\n //Check if this is the matching closing parenthesis.If not, just read ahead.\n if (pCount <= 0) {\n // Yes, we found the closing parenthesis, create new sub-expression:\n if (state === 'within-parentheses') {\n expressions.push(new BracketExpression(this._do_parse(tmp)));\n } else if (state === 'within-func-parentheses') {\n // Function found: create expressions from the inner argument\n // string, and create a function expression with it:\n let args = this.splitFunctionParams(tmp).map((a) => this._do_parse(a));\n expressions.push(new FunctionExpression(funcName, args, this));\n funcName = null;\n }\n state = 'initial';\n } else {\n pCount--;\n tmp += char;\n }\n } else if (char === '(') {\n // begin of a new sub-parenthesis, increase counter:\n pCount++;\n tmp += char;\n } else if (char.match(/[\"']/)) {\n // start of string\n pStringDelimiter = char;\n tmp += char;\n } else {\n // all other things are just added to the sub-expression:\n tmp += char;\n }\n break;\n \n case 'within-logical-operator':\n char = str.charAt(act);\n if (char === '=') {\n // the second char of a logical operator\n // can only be an equal sign\n tmp += char;\n act++;\n }\n // logical operator finished, create expression:\n expressions.push(Expression.createOperatorExpression(tmp, new Expression(), new Expression()));\n tmp = '';\n state = 'initial';\n act--;\n break;\n }\n act++;\n }\n\n if (state !== 'initial') {\n throw new Error('Could not parse formula: Syntax error.');\n }\n\n return this.buildExpressionTree(expressions);\n }\n\n /**\n * @see parse(): Builds an expression tree from the given expression array.\n * Builds a tree with a single root expression in the correct order of operator precedence.\n *\n * Note that the given expression objects are modified and linked.\n *\n * @param {*} expressions\n * @return {Expression} The root Expression of the built expression tree\n */\n buildExpressionTree(expressions: Expression[]): Expression {\n if (expressions.length < 1) {\n throw new Error('No expression given!');\n }\n const exprCopy = [...expressions];\n let idx = 0;\n let expr = null;\n // Replace all Power expressions with a partial tree:\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof PowerExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.base = exprCopy[idx - 1];\n expr.exponent = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Mult/Div expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof MultDivExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Plus/Minus expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof PlusMinusExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Logical expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof LogicalExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n if (exprCopy.length !== 1) {\n throw new Error('Could not parse formula: incorrect syntax?');\n }\n return exprCopy[0];\n }\n\n isOperator(char: string | null) {\n return typeof char === 'string' && char.match(/[+\\-*/^]/);\n }\n\n isOperatorExpr(expr: Expression) {\n return (\n expr instanceof PlusMinusExpression || expr instanceof MultDivExpression || expr instanceof PowerExpression || expr instanceof LogicalExpression\n );\n }\n\n registerVariable(varName: string) {\n if (this._variables.indexOf(varName) < 0) {\n this._variables.push(varName);\n }\n }\n\n getVariables() {\n return this._variables;\n }\n\n /**\n * Evaluates a Formula by delivering values for the Formula's variables.\n * E.g. if the formula is '3*x^2 + 2*x + 4', you should call `evaulate` as follows:\n *\n * evaluate({x:2}) --> Result: 20\n *\n * @param {ValueObject|Array} valueObj An object containing values for variables and (unknown) functions,\n * or an array of such objects: If an array is given, all objects are evaluated and the results\n * also returned as array.\n * @return {Number|String|(Number|String)[]} The evaluated result, or an array with results\n */\n evaluate(valueObj: ValueObject | ValueObject[]): number | string | (number | string)[] {\n // resolve multiple value objects recursively:\n if (valueObj instanceof Array) {\n return valueObj.map((v) => this.evaluate(v)) as (number | string)[];\n }\n let expr = this.getExpression();\n if (!(expr instanceof Expression)) {\n throw new Error('No expression set: Did you init the object with a Formula?');\n }\n if (this.options.memoization) {\n let res = this.resultFromMemory(valueObj);\n if (res !== null) {\n return res;\n } else {\n res = expr.evaluate({ ...MATH_CONSTANTS, ...valueObj });\n this.storeInMemory(valueObj, res);\n return res;\n }\n }\n return expr.evaluate({ ...MATH_CONSTANTS, ...valueObj });\n }\n\n hashValues(valueObj: ValueObject) {\n return JSON.stringify(valueObj);\n }\n\n resultFromMemory(valueObj: ValueObject): number | string | null {\n let key = this.hashValues(valueObj);\n let res = this._memory[key];\n if (res !== undefined) {\n return res;\n } else {\n return null;\n }\n }\n\n storeInMemory(valueObj: ValueObject, value: number | string) {\n this._memory[this.hashValues(valueObj)] = value;\n }\n\n getExpression() {\n return this.formulaExpression;\n }\n\n getExpressionString() {\n return this.formulaExpression ? this.formulaExpression.toString() : '';\n }\n\n static calc(formula: string, valueObj: ValueObject | null = null, options = {}) {\n valueObj = valueObj ?? {};\n return new Formula(formula, options).evaluate(valueObj);\n }\n}\n"],"names":["MATH_CONSTANTS","MathOperatorHelper","value","MathFunctionHelper","Expression","operator","left","right","PowerExpression","MultDivExpression","PlusMinusExpression","LogicalExpression","params","BracketExpression","expr","__publicField","ValueExpression","type","leftValue","rightValue","base","exponent","baseValue","exponentValue","FunctionExpression","fn","argumentExpressions","formulaObject","_a","paramValues","a","getProperty","e","objFn","mathFn","paramValue","Formula","object","path","fullPath","curr","propName","VariableExpression","formulaObj","_Formula","fStr","options","formulaString","toSplit","pCount","paramStr","chr","s","resParts","part","index","c","str","lastChar","act","state","expressions","char","tmp","funcName","pStringDelimiter","args","exprCopy","idx","varName","valueObj","v","res","key","formula","prop"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"fparser.js","sources":["../src/fparser.ts"],"sourcesContent":["/**\n * JS Formula Parser\n * -------------------\n * (c) 2012-2023 Alexander Schenkel, alex@alexi.ch\n *\n * JS Formula Parser takes a string, parses its mathmatical formula\n * and creates an evaluatable Formula object of it.\n *\n * Example input:\n *\n * var fObj = new Formula('sin(PI*x)/(2*PI)');\n * var result = fObj.evaluate({x: 2});\n * var results = fObj.evaluate([\n * {x: 2},\n * {x: 4},\n * {x: 8}\n * ]);\n *\n * LICENSE:\n * -------------\n * MIT license, see LICENSE file\n */\nconst MATH_CONSTANTS = {\n PI: Math.PI,\n E: Math.E,\n LN2: Math.LN2,\n LN10: Math.LN10,\n LOG2E: Math.LOG2E,\n LOG10E: Math.LOG10E,\n SQRT1_2: Math.SQRT1_2,\n SQRT2: Math.SQRT2\n};\n\ndeclare global {\n interface Math {\n [key: string]: number | Function;\n }\n}\n\ntype FormulaOptions = {\n memoization?: boolean;\n};\n\ntype ValueObject = {\n [key: string]: number | string | Function | ValueObject;\n};\n\nclass MathOperatorHelper {\n static throwIfNotNumber(value: number | string) {\n const valueType = typeof value;\n if (valueType === 'string') {\n throw new Error('Strings are not allowed in math operations');\n }\n }\n}\n\nclass MathFunctionHelper {\n static throwIfNotNumber(value: number | string) {\n const valueType = typeof value;\n if (valueType === 'string') {\n throw new Error('Strings are not allowed in math operations');\n }\n }\n}\n\nclass Expression {\n static createOperatorExpression(operator: string, left: Expression, right: Expression) {\n if (operator === '^') {\n return new PowerExpression(left, right);\n }\n if ( [ '*', '/' ].includes( operator ) ) {\n return new MultDivExpression(operator, left, right);\n }\n if ( [ '+', '-' ].includes( operator ) ) {\n return new PlusMinusExpression(operator, left, right);\n }\n if ( [ '<', '>', '<=', '>=', '=', '!=' ].includes( operator ) ) {\n return new LogicalExpression(operator, left, right);\n }\n throw new Error(`Unknown operator: ${operator}`);\n }\n\n evaluate(params: ValueObject = {}): number | string {\n throw new Error('Empty Expression - Must be defined in child classes');\n }\n\n toString() {\n return '';\n }\n}\n\nclass BracketExpression extends Expression {\n innerExpression: Expression;\n\n constructor(expr: Expression) {\n super();\n this.innerExpression = expr;\n if (!(this.innerExpression instanceof Expression)) {\n throw new Error('No inner expression given for bracket expression');\n }\n }\n evaluate(params = {}): number | string {\n return this.innerExpression.evaluate(params);\n }\n toString() {\n return `(${this.innerExpression.toString()})`;\n }\n}\n\nclass ValueExpression extends Expression {\n value: number | string;\n type: string;\n\n constructor(value: number | string, type: string = 'number') {\n super();\n this.value = Number(value);\n switch (type) {\n case 'number':\n this.value = Number(value);\n if (isNaN(this.value)) {\n throw new Error('Cannot parse number: ' + value);\n }\n break;\n case 'string':\n this.value = String(value);\n break;\n default:\n throw new Error('Invalid value type: ' + type);\n }\n this.type = type;\n }\n evaluate(): number | string {\n return this.value;\n }\n toString() {\n switch (this.type) {\n case 'number':\n return String(this.value);\n case 'string':\n return String('\"' + this.value + '\"');\n default:\n throw new Error('Invalid type');\n }\n }\n}\n\nclass PlusMinusExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n\n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if (!['+', '-'].includes(operator)) {\n throw new Error(`Operator not allowed in Plus/Minus expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(leftValue);\n MathOperatorHelper.throwIfNotNumber(rightValue);\n if (this.operator === '+') {\n return Number(leftValue) + Number(rightValue);\n }\n if (this.operator === '-') {\n return Number(leftValue) - Number(rightValue);\n }\n throw new Error('Unknown operator for PlusMinus expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\n\nclass MultDivExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n\n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if (!['*', '/'].includes(operator)) {\n throw new Error(`Operator not allowed in Multiply/Division expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(leftValue);\n MathOperatorHelper.throwIfNotNumber(rightValue);\n if (this.operator === '*') {\n return Number(leftValue) * Number(rightValue);\n }\n if (this.operator === '/') {\n return Number(leftValue) / Number(rightValue);\n }\n throw new Error('Unknown operator for MultDiv expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\n\nclass PowerExpression extends Expression {\n base: Expression;\n exponent: Expression;\n\n constructor(base: Expression, exponent: Expression) {\n super();\n this.base = base;\n this.exponent = exponent;\n }\n\n evaluate(params: ValueObject = {}): number {\n const baseValue = this.base.evaluate(params);\n const exponentValue = this.exponent.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(baseValue);\n MathOperatorHelper.throwIfNotNumber(exponentValue);\n\n return Math.pow(Number(baseValue), Number(exponentValue));\n }\n\n toString() {\n return `${this.base.toString()}^${this.exponent.toString()}`;\n }\n}\n\nclass LogicalExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n \n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if ( ! [ '<', '>', '<=', '>=', '=', '!=' ].includes( operator ) ) {\n throw new Error(`Operator not allowed in Logical expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n switch ( this.operator ) {\n case '<':\n return leftValue < rightValue ? 1 : 0;\n case '>':\n return leftValue > rightValue ? 1 : 0;\n case '<=':\n return leftValue <= rightValue ? 1 : 0;\n case '>=':\n return leftValue >= rightValue ? 1 : 0;\n case '=':\n return leftValue === rightValue ? 1 : 0;\n case '!=':\n return leftValue !== rightValue ? 1 : 0;\n }\n throw new Error('Unknown operator for Logical expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\nclass FunctionExpression extends Expression {\n fn: string;\n varPath: string[];\n argumentExpressions: Expression[];\n formulaObject: Formula | null;\n blacklisted: boolean | undefined;\n\n constructor(fn: string | null, argumentExpressions: Expression[], formulaObject: Formula | null = null) {\n super();\n this.fn = fn ?? '';\n this.varPath = this.fn.split('.');\n this.argumentExpressions = argumentExpressions || [];\n this.formulaObject = formulaObject;\n this.blacklisted = undefined;\n }\n\n evaluate(params: ValueObject = {}): number | string {\n params = params || {};\n const paramValues = this.argumentExpressions.map((a) => a.evaluate(params));\n\n // If the params object itself has a function definition with\n // the function name, call this one:\n // let fn = params[this.fn];\n try {\n let fn = getProperty(params, this.varPath, this.fn);\n if (fn instanceof Function) {\n return fn.apply(this, paramValues);\n }\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n\n let objFn;\n try {\n // perhaps the Formula object has the function? so call it:\n objFn = getProperty(this.formulaObject ?? {}, this.varPath, this.fn);\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n if (this.formulaObject && objFn instanceof Function) {\n // Don't, if it is blacklisted:\n if (this.isBlacklisted()) {\n throw new Error('Blacklisted function called: ' + this.fn);\n }\n return objFn.apply(this.formulaObject, paramValues);\n }\n\n try {\n // Has the JS Math object a function as requested? Call it:\n const mathFn = getProperty(Math, this.varPath, this.fn);\n if (mathFn instanceof Function) {\n paramValues.forEach((paramValue) => {\n MathFunctionHelper.throwIfNotNumber(paramValue);\n });\n\n return mathFn.apply(this, paramValues);\n }\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n // No more options left: sorry!\n throw new Error('Function not found: ' + this.fn);\n }\n\n toString() {\n return `${this.fn}(${this.argumentExpressions.map((a) => a.toString()).join(', ')})`;\n }\n\n isBlacklisted() {\n // cache evaluation of blacklisted function, to save call time:\n if (this.blacklisted === undefined) {\n this.blacklisted = Formula.functionBlacklist.includes(\n this.formulaObject ? this.formulaObject[this.fn] : null\n );\n }\n return this.blacklisted;\n }\n}\n\nfunction getProperty(object: ValueObject, path: string[], fullPath: string) {\n let curr: number | string | Function | ValueObject = object;\n for (let propName of path) {\n if (typeof curr !== 'object') {\n throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`);\n }\n if (curr[propName] === undefined) {\n throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`);\n }\n curr = curr[propName];\n }\n\n if (typeof curr === 'object') {\n throw new Error('Invalid value');\n }\n\n return curr;\n}\n\nclass VariableExpression extends Expression {\n fullPath: string;\n varPath: string[];\n formulaObject: Formula | null;\n\n constructor(fullPath: string, formulaObj: Formula | null = null) {\n super();\n this.formulaObject = formulaObj;\n this.fullPath = fullPath;\n this.varPath = fullPath.split('.');\n }\n\n evaluate(params = {}): number | string {\n // params contain variable / value pairs: If this object's variable matches\n // a varname found in the params, return the value.\n // eg: params = {x: 5,y:3}, varname = x, return 5\n // Objects and arrays are also supported:\n // e.g. params = {x: {y: 5}}, varname = x.y, return 5\n // or params = {x: [2,4,6]}, varname = x.2, return 6\n\n // Let's look in the value object first:\n let value = undefined;\n try {\n value = getProperty(params, this.varPath, this.fullPath);\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n if (value === undefined) {\n // Now have a look at the formula object:\n // This will throw an error if the property is not found:\n value = getProperty(this.formulaObject ?? {}, this.varPath, this.fullPath);\n }\n if (typeof value === 'function' || typeof value === 'object') {\n throw new Error(`Cannot use ${this.fullPath} as value: It contains a non-numerical value.`);\n }\n\n return value;\n }\n toString() {\n return `${this.varPath.join('.')}`;\n }\n}\n\nexport default class Formula {\n [key: string]: any;\n static Expression = Expression;\n static BracketExpression = BracketExpression;\n static PowerExpression = PowerExpression;\n static MultDivExpression = MultDivExpression;\n static PlusMinusExpression = PlusMinusExpression;\n static LogicalExpression = LogicalExpression;\n static ValueExpression = ValueExpression;\n static VariableExpression = VariableExpression;\n static FunctionExpression = FunctionExpression;\n static MATH_CONSTANTS = MATH_CONSTANTS;\n\n // Create a function blacklist:\n static functionBlacklist = Object.getOwnPropertyNames(Formula.prototype)\n .filter((prop) => Formula.prototype[prop] instanceof Function)\n .map((prop) => Formula.prototype[prop]);\n\n public formulaExpression: Expression | null;\n public options: FormulaOptions;\n public formulaStr: string;\n private _variables: string[];\n private _memory: { [key: string]: number | string };\n\n /**\n * Creates a new Formula instance\n *\n * Optional configuration can be set in the options object:\n *\n * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters\n *\n * @param {String} fStr The formula string, e.g. 'sin(x)/cos(y)'\n * @param {Object} options An options object. Supported options:\n * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters\n * @param {Formula} parentFormula Internally used to build a Formula AST\n */\n constructor(fStr: string, options: FormulaOptions | null = {}) {\n this.formulaExpression = null;\n this.options = { ...{ memoization: false }, ...options };\n this.formulaStr = '';\n this._variables = [];\n this._memory = {};\n this.setFormula(fStr);\n }\n\n /**\n * Re-sets the given String and parses it to a formula expression. Can be used after initialization,\n * to re-use the Formula object.\n *\n * @param {String} formulaString The formula string to set/parse\n * @return {this} The Formula object (this)\n */\n setFormula(formulaString: string) {\n if (formulaString) {\n this.formulaExpression = null;\n this._variables = [];\n this._memory = {};\n this.formulaStr = formulaString;\n this.formulaExpression = this.parse(formulaString);\n }\n return this;\n }\n\n /**\n * Enable memoization: An expression is only evaluated once for the same input.\n * Further evaluations with the same input will return the in-memory stored result.\n */\n enableMemoization() {\n this.options.memoization = true;\n }\n\n /**\n * Disable in-memory memoization: each call to evaluate() is executed from scratch.\n */\n disableMemoization() {\n this.options.memoization = false;\n this._memory = {};\n }\n\n /**\n * Splits the given string by ',', makes sure the ',' is not within\n * a sub-expression\n * e.g.: str = \"x,pow(3,4)\" returns 2 elements: x and pow(3,4).\n */\n splitFunctionParams(toSplit: string) {\n // do not split on ',' within matching brackets.\n let pCount = 0,\n paramStr = '';\n const params = [];\n for (let chr of toSplit.split('')) {\n if (chr === ',' && pCount === 0) {\n // Found function param, save 'em\n params.push(paramStr);\n paramStr = '';\n } else if (chr === '(') {\n pCount++;\n paramStr += chr;\n } else if (chr === ')') {\n pCount--;\n paramStr += chr;\n if (pCount < 0) {\n throw new Error('ERROR: Too many closing parentheses!');\n }\n } else {\n paramStr += chr;\n }\n }\n if (pCount !== 0) {\n throw new Error('ERROR: Too many opening parentheses!');\n }\n if (paramStr.length > 0) {\n params.push(paramStr);\n }\n return params;\n }\n\n /**\n * Cleans the input string from unnecessary whitespace,\n * and replaces some known constants:\n */\n cleanupInputFormula(s: string) {\n const resParts: string[] = [];\n const srcParts = s.split('\"');\n srcParts.forEach((part, index) => {\n // skip parts marked as string\n if (index % 2 === 0) {\n part = part.replace(/[\\s]+/g, '');\n // surround known math constants with [], to parse them as named variables [xxx]:\n Object.keys(MATH_CONSTANTS).forEach((c) => {\n part = part.replace(new RegExp(`\\\\b${c}\\\\b`, 'g'), `[${c}]`);\n });\n }\n resParts.push(part);\n });\n return resParts.join('\"');\n }\n\n /**\n * Parses the given formula string by using a state machine into a single Expression object,\n * which represents an expression tree (aka AST).\n *\n * First, we split the string into 'expression': An expression can be:\n * - a number, e.g. '3.45'\n * - an unknown variable, e.g. 'x'\n * - a single char operator, such as '*','+' etc...\n * - a named variable, in [], e.g. [myvar]\n * - a function, such as sin(x)\n * - a parenthessed expression, containing other expressions\n *\n * We want to create an expression tree out of the string. This is done in 2 stages:\n * 1. form single expressions from the string: parse the string into known expression objects:\n * - numbers/[variables]/\"strings\"\n * - operators\n * - braces (with a sub-expression)\n * - functions (with sub-expressions (aka argument expressions))\n * This will lead to an array of expressions.\n * As an example:\n * \"2 + 3 * (4 + 3 ^ 5) * sin(PI * x)\" forms an array of the following expressions:\n * `[2, +, 3, *, bracketExpr(4,+,3,^,5), * , functionExpr(PI,*,x)]`\n * 2. From the raw expression array we form an expression tree by evaluating the expressions in the correct order:\n * e.g.:\n * the expression array `[2, +, 3, *, bracketExpr(4,+,3,^,5), * , functionExpr(PI,*,x)]` will be transformed into the expression tree:\n * ```\n * root expr: (+)\n * / \\\n * 2 (*)\n * / \\\n * (*) functionExpr(...)\n * / \\\n * 3 (bracket(..))\n * ```\n *\n * In the end, we have a single root expression node, which then can be evaluated in the evaluate() function.\n *\n * @param {String} str The formula string, e.g. '3*sin(PI/x)'\n * @returns {Expression} An expression object, representing the expression tree\n */\n parse(str: string) {\n // clean the input string first. spaces, math constant replacements etc.:\n str = this.cleanupInputFormula(str);\n // start recursive call to parse:\n return this._do_parse(str);\n }\n\n /**\n * @see parse(): this is the recursive parse function, without the clean string part.\n * @param {String} str\n * @returns {Expression} An expression object, representing the expression tree\n */\n _do_parse(str: string): Expression {\n let lastChar = str.length - 1,\n act = 0,\n state:\n | 'initial'\n | 'within-nr'\n | 'within-parentheses'\n | 'within-func-parentheses'\n | 'within-named-var'\n | 'within-string'\n | 'within-expr'\n | 'within-bracket'\n | 'within-func'\n | 'within-logical-operator'\n | 'invalid' = 'initial',\n expressions = [],\n char = '',\n tmp = '',\n funcName = null,\n pCount = 0,\n pStringDelimiter = '';\n\n while (act <= lastChar) {\n switch (state) {\n case 'initial':\n // None state, the beginning. Read a char and see what happens.\n char = str.charAt(act);\n if (char.match(/[0-9.]/)) {\n // found the beginning of a number, change state to \"within-number\"\n state = 'within-nr';\n tmp = '';\n act--;\n } else if (this.isOperator(char)) {\n // Simple operators. Note: '-' must be treaten specially,\n // it could be part of a number.\n // it MUST be part of a number if the last found expression\n // was an operator (or the beginning):\n if (char === '-') {\n if (expressions.length === 0 || this.isOperatorExpr(expressions[expressions.length - 1])) {\n state = 'within-nr';\n tmp = '-';\n break;\n }\n }\n\n // Found a simple operator, store as expression:\n if (act === lastChar || this.isOperatorExpr(expressions[expressions.length - 1])) {\n state = 'invalid'; // invalid to end with an operator, or have 2 operators in conjunction\n break;\n } else {\n expressions.push(\n Expression.createOperatorExpression(char, new Expression(), new Expression())\n );\n state = 'initial';\n }\n } else if ( [ '>', '<', '=', '!' ].includes(char)) {\n // found the beginning of a logical operator, change state to \"within-logical-operator\"\n if (act === lastChar) {\n state = 'invalid'; // invalid to end with a logical operator\n break;\n } else {\n state = 'within-logical-operator';\n tmp = char;\n }\n } else if (char === '(') {\n // left parenthes found, seems to be the beginning of a new sub-expression:\n state = 'within-parentheses';\n tmp = '';\n pCount = 0;\n } else if (char === '[') {\n // left named var separator char found, seems to be the beginning of a named var:\n state = 'within-named-var';\n tmp = '';\n } else if (char.match(/[\"']/)) {\n // left string separator char found\n state = 'within-string';\n pStringDelimiter = char;\n tmp = '';\n } else if (char.match(/[a-zA-Z]/)) {\n // multiple chars means it may be a function, else its a var which counts as own expression:\n if (act < lastChar && str.charAt(act + 1).match(/[a-zA-Z0-9_.]/)) {\n tmp = char;\n state = 'within-func';\n } else {\n // Single variable found:\n // We need to check some special considerations:\n // - If the last char was a number (e.g. 3x), we need to create a multiplication out of it (3*x)\n if (\n expressions.length > 0 &&\n expressions[expressions.length - 1] instanceof ValueExpression\n ) {\n expressions.push(\n Expression.createOperatorExpression('*', new Expression(), new Expression())\n );\n }\n expressions.push(new VariableExpression(char, this));\n this.registerVariable(char);\n state = 'initial';\n tmp = '';\n }\n }\n break;\n case 'within-nr':\n char = str.charAt(act);\n if (char.match(/[0-9.]/)) {\n //Still within number, store and continue\n tmp += char;\n if (act === lastChar) {\n expressions.push(new ValueExpression(tmp));\n state = 'initial';\n }\n } else {\n // Number finished on last round, so add as expression:\n if (tmp === '-') {\n // just a single '-' means: a variable could follow (e.g. like in 3*-x), we convert it to -1: (3*-1x)\n tmp = '-1';\n }\n expressions.push(new ValueExpression(tmp));\n tmp = '';\n state = 'initial';\n act--;\n }\n break;\n\n case 'within-func':\n char = str.charAt(act);\n if (char.match(/[a-zA-Z0-9_.]/)) {\n tmp += char;\n } else if (char === '(') {\n funcName = tmp;\n tmp = '';\n pCount = 0;\n state = 'within-func-parentheses';\n } else {\n throw new Error('Wrong character for function at position ' + act);\n }\n\n break;\n\n case 'within-named-var':\n char = str.charAt(act);\n if (char === ']') {\n // end of named var, create expression:\n expressions.push(new VariableExpression(tmp, this));\n this.registerVariable(tmp);\n tmp = '';\n state = 'initial';\n } else if (char.match(/[a-zA-Z0-9_.]/)) {\n tmp += char;\n } else {\n throw new Error('Character not allowed within named variable: ' + char);\n }\n break;\n\n case 'within-string':\n char = str.charAt(act);\n if (char === pStringDelimiter) {\n // end of string, create expression:\n expressions.push(new ValueExpression(tmp, 'string'));\n tmp = '';\n state = 'initial';\n pStringDelimiter = '';\n } else {\n tmp += char;\n }\n break;\n\n case 'within-parentheses':\n case 'within-func-parentheses':\n char = str.charAt(act);\n if (pStringDelimiter) {\n // If string is opened, then:\n if (char === pStringDelimiter) {\n // end of string\n pStringDelimiter = '';\n }\n // accumulate string chars\n tmp += char;\n } else if (char === ')') {\n //Check if this is the matching closing parenthesis.If not, just read ahead.\n if (pCount <= 0) {\n // Yes, we found the closing parenthesis, create new sub-expression:\n if (state === 'within-parentheses') {\n expressions.push(new BracketExpression(this._do_parse(tmp)));\n } else if (state === 'within-func-parentheses') {\n // Function found: create expressions from the inner argument\n // string, and create a function expression with it:\n let args = this.splitFunctionParams(tmp).map((a) => this._do_parse(a));\n expressions.push(new FunctionExpression(funcName, args, this));\n funcName = null;\n }\n state = 'initial';\n } else {\n pCount--;\n tmp += char;\n }\n } else if (char === '(') {\n // begin of a new sub-parenthesis, increase counter:\n pCount++;\n tmp += char;\n } else if (char.match(/[\"']/)) {\n // start of string\n pStringDelimiter = char;\n tmp += char;\n } else {\n // all other things are just added to the sub-expression:\n tmp += char;\n }\n break;\n \n case 'within-logical-operator':\n char = str.charAt(act);\n if (char === '=') {\n // the second char of a logical operator\n // can only be an equal sign\n tmp += char;\n act++;\n }\n // logical operator finished, create expression:\n expressions.push(Expression.createOperatorExpression(tmp, new Expression(), new Expression()));\n tmp = '';\n state = 'initial';\n act--;\n break;\n }\n act++;\n }\n\n if (state !== 'initial') {\n throw new Error('Could not parse formula: Syntax error.');\n }\n\n return this.buildExpressionTree(expressions);\n }\n\n /**\n * @see parse(): Builds an expression tree from the given expression array.\n * Builds a tree with a single root expression in the correct order of operator precedence.\n *\n * Note that the given expression objects are modified and linked.\n *\n * @param {*} expressions\n * @return {Expression} The root Expression of the built expression tree\n */\n buildExpressionTree(expressions: Expression[]): Expression {\n if (expressions.length < 1) {\n throw new Error('No expression given!');\n }\n const exprCopy = [...expressions];\n let idx = 0;\n let expr = null;\n // Replace all Power expressions with a partial tree:\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof PowerExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.base = exprCopy[idx - 1];\n expr.exponent = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Mult/Div expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof MultDivExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Plus/Minus expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof PlusMinusExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Logical expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof LogicalExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n if (exprCopy.length !== 1) {\n throw new Error('Could not parse formula: incorrect syntax?');\n }\n return exprCopy[0];\n }\n\n isOperator(char: string | null) {\n return typeof char === 'string' && char.match(/[+\\-*/^]/);\n }\n\n isOperatorExpr(expr: Expression) {\n return (\n expr instanceof PlusMinusExpression || expr instanceof MultDivExpression || expr instanceof PowerExpression || expr instanceof LogicalExpression\n );\n }\n\n registerVariable(varName: string) {\n if (this._variables.indexOf(varName) < 0) {\n this._variables.push(varName);\n }\n }\n\n getVariables() {\n return this._variables;\n }\n\n /**\n * Evaluates a Formula by delivering values for the Formula's variables.\n * E.g. if the formula is '3*x^2 + 2*x + 4', you should call `evaulate` as follows:\n *\n * evaluate({x:2}) --> Result: 20\n *\n * @param {ValueObject|Array} valueObj An object containing values for variables and (unknown) functions,\n * or an array of such objects: If an array is given, all objects are evaluated and the results\n * also returned as array.\n * @return {Number|String|(Number|String)[]} The evaluated result, or an array with results\n */\n evaluate(valueObj: ValueObject | ValueObject[]): number | string | (number | string)[] {\n // resolve multiple value objects recursively:\n if (valueObj instanceof Array) {\n return valueObj.map((v) => this.evaluate(v)) as (number | string)[];\n }\n let expr = this.getExpression();\n if (!(expr instanceof Expression)) {\n throw new Error('No expression set: Did you init the object with a Formula?');\n }\n if (this.options.memoization) {\n let res = this.resultFromMemory(valueObj);\n if (res !== null) {\n return res;\n } else {\n res = expr.evaluate({ ...MATH_CONSTANTS, ...valueObj });\n this.storeInMemory(valueObj, res);\n return res;\n }\n }\n return expr.evaluate({ ...MATH_CONSTANTS, ...valueObj });\n }\n\n hashValues(valueObj: ValueObject) {\n return JSON.stringify(valueObj);\n }\n\n resultFromMemory(valueObj: ValueObject): number | string | null {\n let key = this.hashValues(valueObj);\n let res = this._memory[key];\n if (res !== undefined) {\n return res;\n } else {\n return null;\n }\n }\n\n storeInMemory(valueObj: ValueObject, value: number | string) {\n this._memory[this.hashValues(valueObj)] = value;\n }\n\n getExpression() {\n return this.formulaExpression;\n }\n\n getExpressionString() {\n return this.formulaExpression ? this.formulaExpression.toString() : '';\n }\n\n static calc(formula: string, valueObj: ValueObject | null = null, options = {}) {\n valueObj = valueObj ?? {};\n return new Formula(formula, options).evaluate(valueObj);\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/fparser.umd.cjs b/dist/fparser.umd.cjs index b704f6c..4c82898 100644 --- a/dist/fparser.umd.cjs +++ b/dist/fparser.umd.cjs @@ -1,2 +1,826 @@ -(function(c,l){typeof exports=="object"&&typeof module!="undefined"?module.exports=l():typeof define=="function"&&define.amd?define(l):(c=typeof globalThis!="undefined"?globalThis:c||self,c.Formula=l())})(this,function(){"use strict";var O=Object.defineProperty;var T=(c,l,f)=>l in c?O(c,l,{enumerable:!0,configurable:!0,writable:!0,value:f}):c[l]=f;var n=(c,l,f)=>(T(c,typeof l!="symbol"?l+"":l,f),f);const c={PI:Math.PI,E:Math.E,LN2:Math.LN2,LN10:Math.LN10,LOG2E:Math.LOG2E,LOG10E:Math.LOG10E,SQRT1_2:Math.SQRT1_2,SQRT2:Math.SQRT2};class l{static throwIfNotNumber(i){if(typeof i==="string")throw new Error("Strings are not allowed in math operations")}}class f{static throwIfNotNumber(i){if(typeof i==="string")throw new Error("Strings are not allowed in math operations")}}class h{static createOperatorExpression(i,e,t){if(i==="^")return new d(e,t);if(["*","/"].includes(i))return new b(i,e,t);if(["+","-"].includes(i))return new x(i,e,t);if(["<",">","<=",">=","=","!="].includes(i))return new v(i,e,t);throw new Error(`Unknown operator: ${i}`)}evaluate(i={}){throw new Error("Empty Expression - Must be defined in child classes")}toString(){return""}}class k extends h{constructor(e){super();n(this,"innerExpression");if(this.innerExpression=e,!(this.innerExpression instanceof h))throw new Error("No inner expression given for bracket expression")}evaluate(e={}){return this.innerExpression.evaluate(e)}toString(){return`(${this.innerExpression.toString()})`}}class m extends h{constructor(e,t="number"){super();n(this,"value");n(this,"type");switch(this.value=Number(e),t){case"number":if(this.value=Number(e),isNaN(this.value))throw new Error("Cannot parse number: "+e);break;case"string":this.value=String(e);break;default:throw new Error("Invalid value type: "+t)}this.type=t}evaluate(){return this.value}toString(){switch(this.type){case"number":return String(this.value);case"string":return'"'+this.value+'"';default:throw new Error("Invalid type")}}}class x extends h{constructor(e,t,r){super();n(this,"operator");n(this,"left");n(this,"right");if(!["+","-"].includes(e))throw new Error(`Operator not allowed in Plus/Minus expression: ${e}`);this.operator=e,this.left=t,this.right=r}evaluate(e={}){const t=this.left.evaluate(e),r=this.right.evaluate(e);if(l.throwIfNotNumber(t),l.throwIfNotNumber(r),this.operator==="+")return Number(t)+Number(r);if(this.operator==="-")return Number(t)-Number(r);throw new Error("Unknown operator for PlusMinus expression")}toString(){return`${this.left.toString()} ${this.operator} ${this.right.toString()}`}}class b extends h{constructor(e,t,r){super();n(this,"operator");n(this,"left");n(this,"right");if(!["*","/"].includes(e))throw new Error(`Operator not allowed in Multiply/Division expression: ${e}`);this.operator=e,this.left=t,this.right=r}evaluate(e={}){const t=this.left.evaluate(e),r=this.right.evaluate(e);if(l.throwIfNotNumber(t),l.throwIfNotNumber(r),this.operator==="*")return Number(t)*Number(r);if(this.operator==="/")return Number(t)/Number(r);throw new Error("Unknown operator for MultDiv expression")}toString(){return`${this.left.toString()} ${this.operator} ${this.right.toString()}`}}class d extends h{constructor(e,t){super();n(this,"base");n(this,"exponent");this.base=e,this.exponent=t}evaluate(e={}){const t=this.base.evaluate(e),r=this.exponent.evaluate(e);return l.throwIfNotNumber(t),l.throwIfNotNumber(r),Math.pow(Number(t),Number(r))}toString(){return`${this.base.toString()}^${this.exponent.toString()}`}}class v extends h{constructor(e,t,r){super();n(this,"operator");n(this,"left");n(this,"right");if(!["<",">","<=",">=","=","!="].includes(e))throw new Error(`Operator not allowed in Logical expression: ${e}`);this.operator=e,this.left=t,this.right=r}evaluate(e={}){const t=this.left.evaluate(e),r=this.right.evaluate(e);switch(this.operator){case"<":return t":return t>r?1:0;case"<=":return t<=r?1:0;case">=":return t>=r?1:0;case"=":return t===r?1:0;case"!=":return t!==r?1:0}throw new Error("Unknown operator for Logical expression")}toString(){return`${this.left.toString()} ${this.operator} ${this.right.toString()}`}}class M extends h{constructor(e,t,r=null){super();n(this,"fn");n(this,"varPath");n(this,"argumentExpressions");n(this,"formulaObject");n(this,"blacklisted");this.fn=e!=null?e:"",this.varPath=this.fn.split("."),this.argumentExpressions=t||[],this.formulaObject=r,this.blacklisted=void 0}evaluate(e={}){var a;e=e||{};const t=this.argumentExpressions.map(s=>s.evaluate(e));try{let s=g(e,this.varPath,this.fn);if(s instanceof Function)return s.apply(this,t)}catch(s){}let r;try{r=g((a=this.formulaObject)!=null?a:{},this.varPath,this.fn)}catch(s){}if(this.formulaObject&&r instanceof Function){if(this.isBlacklisted())throw new Error("Blacklisted function called: "+this.fn);return r.apply(this.formulaObject,t)}try{const s=g(Math,this.varPath,this.fn);if(s instanceof Function)return t.forEach(o=>{f.throwIfNotNumber(o)}),s.apply(this,t)}catch(s){}throw new Error("Function not found: "+this.fn)}toString(){return`${this.fn}(${this.argumentExpressions.map(e=>e.toString()).join(", ")})`}isBlacklisted(){return this.blacklisted===void 0&&(this.blacklisted=y.functionBlacklist.includes(this.formulaObject?this.formulaObject[this.fn]:null)),this.blacklisted}}function g(p,i,e){let t=p;for(let r of i){if(typeof t!="object")throw new Error(`Cannot evaluate ${r}, property not found (from path ${e})`);if(t[r]===void 0)throw new Error(`Cannot evaluate ${r}, property not found (from path ${e})`);t=t[r]}if(typeof t=="object")throw new Error("Invalid value");return t}class N extends h{constructor(e,t=null){super();n(this,"fullPath");n(this,"varPath");n(this,"formulaObject");this.formulaObject=t,this.fullPath=e,this.varPath=e.split(".")}evaluate(e={}){var r;let t;try{t=g(e,this.varPath,this.fullPath)}catch(a){}if(t===void 0&&(t=g((r=this.formulaObject)!=null?r:{},this.varPath,this.fullPath)),typeof t=="function"||typeof t=="object")throw new Error(`Cannot use ${this.fullPath} as value: It contains a non-numerical value.`);return t}toString(){return`${this.varPath.join(".")}`}}const u=class u{constructor(i,e={}){n(this,"formulaExpression");n(this,"options");n(this,"formulaStr");n(this,"_variables");n(this,"_memory");this.formulaExpression=null,this.options={memoization:!1,...e},this.formulaStr="",this._variables=[],this._memory={},this.setFormula(i)}setFormula(i){return i&&(this.formulaExpression=null,this._variables=[],this._memory={},this.formulaStr=i,this.formulaExpression=this.parse(i)),this}enableMemoization(){this.options.memoization=!0}disableMemoization(){this.options.memoization=!1,this._memory={}}splitFunctionParams(i){let e=0,t="";const r=[];for(let a of i.split(""))if(a===","&&e===0)r.push(t),t="";else if(a==="(")e++,t+=a;else if(a===")"){if(e--,t+=a,e<0)throw new Error("ERROR: Too many closing parentheses!")}else t+=a;if(e!==0)throw new Error("ERROR: Too many opening parentheses!");return t.length>0&&r.push(t),r}cleanupInputFormula(i){const e=[];return i.split('"').forEach((r,a)=>{a%2===0&&(r=r.replace(/[\s]+/g,""),Object.keys(c).forEach(s=>{r=r.replace(new RegExp(`\\b${s}\\b`,"g"),`[${s}]`)})),e.push(r)}),e.join('"')}parse(i){return i=this.cleanupInputFormula(i),this._do_parse(i)}_do_parse(i){let e=i.length-1,t=0,r="initial",a=[],s="",o="",S=null,E=0,w="";for(;t<=e;){switch(r){case"initial":if(s=i.charAt(t),s.match(/[0-9.]/))r="within-nr",o="",t--;else if(this.isOperator(s)){if(s==="-"&&(a.length===0||this.isOperatorExpr(a[a.length-1]))){r="within-nr",o="-";break}if(t===e||this.isOperatorExpr(a[a.length-1])){r="invalid";break}else a.push(h.createOperatorExpression(s,new h,new h)),r="initial"}else if([">","<","=","!"].includes(s))if(t===e){r="invalid";break}else r="within-logical-operator",o=s;else s==="("?(r="within-parentheses",o="",E=0):s==="["?(r="within-named-var",o=""):s.match(/["']/)?(r="within-string",w=s,o=""):s.match(/[a-zA-Z]/)&&(t0&&a[a.length-1]instanceof m&&a.push(h.createOperatorExpression("*",new h,new h)),a.push(new N(s,this)),this.registerVariable(s),r="initial",o=""));break;case"within-nr":s=i.charAt(t),s.match(/[0-9.]/)?(o+=s,t===e&&(a.push(new m(o)),r="initial")):(o==="-"&&(o="-1"),a.push(new m(o)),o="",r="initial",t--);break;case"within-func":if(s=i.charAt(t),s.match(/[a-zA-Z0-9_.]/))o+=s;else if(s==="(")S=o,o="",E=0,r="within-func-parentheses";else throw new Error("Wrong character for function at position "+t);break;case"within-named-var":if(s=i.charAt(t),s==="]")a.push(new N(o,this)),this.registerVariable(o),o="",r="initial";else if(s.match(/[a-zA-Z0-9_.]/))o+=s;else throw new Error("Character not allowed within named variable: "+s);break;case"within-string":s=i.charAt(t),s===w?(a.push(new m(o,"string")),o="",r="initial",w=""):o+=s;break;case"within-parentheses":case"within-func-parentheses":if(s=i.charAt(t),w)s===w&&(w=""),o+=s;else if(s===")")if(E<=0){if(r==="within-parentheses")a.push(new k(this._do_parse(o)));else if(r==="within-func-parentheses"){let P=this.splitFunctionParams(o).map($=>this._do_parse($));a.push(new M(S,P,this)),S=null}r="initial"}else E--,o+=s;else s==="("?(E++,o+=s):(s.match(/["']/)&&(w=s),o+=s);break;case"within-logical-operator":s=i.charAt(t),s==="="&&(o+=s,t++),a.push(h.createOperatorExpression(o,new h,new h)),o="",r="initial",t--;break}t++}if(r!=="initial")throw new Error("Could not parse formula: Syntax error.");return this.buildExpressionTree(a)}buildExpressionTree(i){if(i.length<1)throw new Error("No expression given!");const e=[...i];let t=0,r=null;for(;tthis.evaluate(t));let e=this.getExpression();if(!(e instanceof h))throw new Error("No expression set: Did you init the object with a Formula?");if(this.options.memoization){let t=this.resultFromMemory(i);return t!==null||(t=e.evaluate({...c,...i}),this.storeInMemory(i,t)),t}return e.evaluate({...c,...i})}hashValues(i){return JSON.stringify(i)}resultFromMemory(i){let e=this.hashValues(i),t=this._memory[e];return t!==void 0?t:null}storeInMemory(i,e){this._memory[this.hashValues(i)]=e}getExpression(){return this.formulaExpression}getExpressionString(){return this.formulaExpression?this.formulaExpression.toString():""}static calc(i,e=null,t={}){return e=e!=null?e:{},new u(i,t).evaluate(e)}};n(u,"Expression",h),n(u,"BracketExpression",k),n(u,"PowerExpression",d),n(u,"MultDivExpression",b),n(u,"PlusMinusExpression",x),n(u,"LogicalExpression",v),n(u,"ValueExpression",m),n(u,"VariableExpression",N),n(u,"FunctionExpression",M),n(u,"MATH_CONSTANTS",c),n(u,"functionBlacklist",Object.getOwnPropertyNames(u.prototype).filter(i=>u.prototype[i]instanceof Function).map(i=>u.prototype[i]));let y=u;return y}); +(function(global, factory) { + typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, global.Formula = factory()); +})(this, function() { + "use strict";var __defProp = Object.defineProperty; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __publicField = (obj, key, value) => { + __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); + return value; +}; + + const MATH_CONSTANTS = { + PI: Math.PI, + E: Math.E, + LN2: Math.LN2, + LN10: Math.LN10, + LOG2E: Math.LOG2E, + LOG10E: Math.LOG10E, + SQRT1_2: Math.SQRT1_2, + SQRT2: Math.SQRT2 + }; + class MathOperatorHelper { + static throwIfNotNumber(value) { + const valueType = typeof value; + if (valueType === "string") { + throw new Error("Strings are not allowed in math operations"); + } + } + } + class MathFunctionHelper { + static throwIfNotNumber(value) { + const valueType = typeof value; + if (valueType === "string") { + throw new Error("Strings are not allowed in math operations"); + } + } + } + class Expression { + static createOperatorExpression(operator, left, right) { + if (operator === "^") { + return new PowerExpression(left, right); + } + if (["*", "/"].includes(operator)) { + return new MultDivExpression(operator, left, right); + } + if (["+", "-"].includes(operator)) { + return new PlusMinusExpression(operator, left, right); + } + if (["<", ">", "<=", ">=", "=", "!="].includes(operator)) { + return new LogicalExpression(operator, left, right); + } + throw new Error(`Unknown operator: ${operator}`); + } + evaluate(params = {}) { + throw new Error("Empty Expression - Must be defined in child classes"); + } + toString() { + return ""; + } + } + class BracketExpression extends Expression { + constructor(expr) { + super(); + __publicField(this, "innerExpression"); + this.innerExpression = expr; + if (!(this.innerExpression instanceof Expression)) { + throw new Error("No inner expression given for bracket expression"); + } + } + evaluate(params = {}) { + return this.innerExpression.evaluate(params); + } + toString() { + return `(${this.innerExpression.toString()})`; + } + } + class ValueExpression extends Expression { + constructor(value, type = "number") { + super(); + __publicField(this, "value"); + __publicField(this, "type"); + this.value = Number(value); + switch (type) { + case "number": + this.value = Number(value); + if (isNaN(this.value)) { + throw new Error("Cannot parse number: " + value); + } + break; + case "string": + this.value = String(value); + break; + default: + throw new Error("Invalid value type: " + type); + } + this.type = type; + } + evaluate() { + return this.value; + } + toString() { + switch (this.type) { + case "number": + return String(this.value); + case "string": + return String('"' + this.value + '"'); + default: + throw new Error("Invalid type"); + } + } + } + class PlusMinusExpression extends Expression { + constructor(operator, left, right) { + super(); + __publicField(this, "operator"); + __publicField(this, "left"); + __publicField(this, "right"); + if (!["+", "-"].includes(operator)) { + throw new Error(`Operator not allowed in Plus/Minus expression: ${operator}`); + } + this.operator = operator; + this.left = left; + this.right = right; + } + evaluate(params = {}) { + const leftValue = this.left.evaluate(params); + const rightValue = this.right.evaluate(params); + MathOperatorHelper.throwIfNotNumber(leftValue); + MathOperatorHelper.throwIfNotNumber(rightValue); + if (this.operator === "+") { + return Number(leftValue) + Number(rightValue); + } + if (this.operator === "-") { + return Number(leftValue) - Number(rightValue); + } + throw new Error("Unknown operator for PlusMinus expression"); + } + toString() { + return `${this.left.toString()} ${this.operator} ${this.right.toString()}`; + } + } + class MultDivExpression extends Expression { + constructor(operator, left, right) { + super(); + __publicField(this, "operator"); + __publicField(this, "left"); + __publicField(this, "right"); + if (!["*", "/"].includes(operator)) { + throw new Error(`Operator not allowed in Multiply/Division expression: ${operator}`); + } + this.operator = operator; + this.left = left; + this.right = right; + } + evaluate(params = {}) { + const leftValue = this.left.evaluate(params); + const rightValue = this.right.evaluate(params); + MathOperatorHelper.throwIfNotNumber(leftValue); + MathOperatorHelper.throwIfNotNumber(rightValue); + if (this.operator === "*") { + return Number(leftValue) * Number(rightValue); + } + if (this.operator === "/") { + return Number(leftValue) / Number(rightValue); + } + throw new Error("Unknown operator for MultDiv expression"); + } + toString() { + return `${this.left.toString()} ${this.operator} ${this.right.toString()}`; + } + } + class PowerExpression extends Expression { + constructor(base, exponent) { + super(); + __publicField(this, "base"); + __publicField(this, "exponent"); + this.base = base; + this.exponent = exponent; + } + evaluate(params = {}) { + const baseValue = this.base.evaluate(params); + const exponentValue = this.exponent.evaluate(params); + MathOperatorHelper.throwIfNotNumber(baseValue); + MathOperatorHelper.throwIfNotNumber(exponentValue); + return Math.pow(Number(baseValue), Number(exponentValue)); + } + toString() { + return `${this.base.toString()}^${this.exponent.toString()}`; + } + } + class LogicalExpression extends Expression { + constructor(operator, left, right) { + super(); + __publicField(this, "operator"); + __publicField(this, "left"); + __publicField(this, "right"); + if (!["<", ">", "<=", ">=", "=", "!="].includes(operator)) { + throw new Error(`Operator not allowed in Logical expression: ${operator}`); + } + this.operator = operator; + this.left = left; + this.right = right; + } + evaluate(params = {}) { + const leftValue = this.left.evaluate(params); + const rightValue = this.right.evaluate(params); + switch (this.operator) { + case "<": + return leftValue < rightValue ? 1 : 0; + case ">": + return leftValue > rightValue ? 1 : 0; + case "<=": + return leftValue <= rightValue ? 1 : 0; + case ">=": + return leftValue >= rightValue ? 1 : 0; + case "=": + return leftValue === rightValue ? 1 : 0; + case "!=": + return leftValue !== rightValue ? 1 : 0; + } + throw new Error("Unknown operator for Logical expression"); + } + toString() { + return `${this.left.toString()} ${this.operator} ${this.right.toString()}`; + } + } + class FunctionExpression extends Expression { + constructor(fn, argumentExpressions, formulaObject = null) { + super(); + __publicField(this, "fn"); + __publicField(this, "varPath"); + __publicField(this, "argumentExpressions"); + __publicField(this, "formulaObject"); + __publicField(this, "blacklisted"); + this.fn = fn != null ? fn : ""; + this.varPath = this.fn.split("."); + this.argumentExpressions = argumentExpressions || []; + this.formulaObject = formulaObject; + this.blacklisted = void 0; + } + evaluate(params = {}) { + var _a; + params = params || {}; + const paramValues = this.argumentExpressions.map((a) => a.evaluate(params)); + try { + let fn = getProperty(params, this.varPath, this.fn); + if (fn instanceof Function) { + return fn.apply(this, paramValues); + } + } catch (e) { + } + let objFn; + try { + objFn = getProperty((_a = this.formulaObject) != null ? _a : {}, this.varPath, this.fn); + } catch (e) { + } + if (this.formulaObject && objFn instanceof Function) { + if (this.isBlacklisted()) { + throw new Error("Blacklisted function called: " + this.fn); + } + return objFn.apply(this.formulaObject, paramValues); + } + try { + const mathFn = getProperty(Math, this.varPath, this.fn); + if (mathFn instanceof Function) { + paramValues.forEach((paramValue) => { + MathFunctionHelper.throwIfNotNumber(paramValue); + }); + return mathFn.apply(this, paramValues); + } + } catch (e) { + } + throw new Error("Function not found: " + this.fn); + } + toString() { + return `${this.fn}(${this.argumentExpressions.map((a) => a.toString()).join(", ")})`; + } + isBlacklisted() { + if (this.blacklisted === void 0) { + this.blacklisted = Formula.functionBlacklist.includes( + this.formulaObject ? this.formulaObject[this.fn] : null + ); + } + return this.blacklisted; + } + } + function getProperty(object, path, fullPath) { + let curr = object; + for (let propName of path) { + if (typeof curr !== "object") { + throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`); + } + if (curr[propName] === void 0) { + throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`); + } + curr = curr[propName]; + } + if (typeof curr === "object") { + throw new Error("Invalid value"); + } + return curr; + } + class VariableExpression extends Expression { + constructor(fullPath, formulaObj = null) { + super(); + __publicField(this, "fullPath"); + __publicField(this, "varPath"); + __publicField(this, "formulaObject"); + this.formulaObject = formulaObj; + this.fullPath = fullPath; + this.varPath = fullPath.split("."); + } + evaluate(params = {}) { + var _a; + let value = void 0; + try { + value = getProperty(params, this.varPath, this.fullPath); + } catch (e) { + } + if (value === void 0) { + value = getProperty((_a = this.formulaObject) != null ? _a : {}, this.varPath, this.fullPath); + } + if (typeof value === "function" || typeof value === "object") { + throw new Error(`Cannot use ${this.fullPath} as value: It contains a non-numerical value.`); + } + return value; + } + toString() { + return `${this.varPath.join(".")}`; + } + } + const _Formula = class _Formula { + /** + * Creates a new Formula instance + * + * Optional configuration can be set in the options object: + * + * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters + * + * @param {String} fStr The formula string, e.g. 'sin(x)/cos(y)' + * @param {Object} options An options object. Supported options: + * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters + * @param {Formula} parentFormula Internally used to build a Formula AST + */ + constructor(fStr, options = {}) { + __publicField(this, "formulaExpression"); + __publicField(this, "options"); + __publicField(this, "formulaStr"); + __publicField(this, "_variables"); + __publicField(this, "_memory"); + this.formulaExpression = null; + this.options = { ...{ memoization: false }, ...options }; + this.formulaStr = ""; + this._variables = []; + this._memory = {}; + this.setFormula(fStr); + } + /** + * Re-sets the given String and parses it to a formula expression. Can be used after initialization, + * to re-use the Formula object. + * + * @param {String} formulaString The formula string to set/parse + * @return {this} The Formula object (this) + */ + setFormula(formulaString) { + if (formulaString) { + this.formulaExpression = null; + this._variables = []; + this._memory = {}; + this.formulaStr = formulaString; + this.formulaExpression = this.parse(formulaString); + } + return this; + } + /** + * Enable memoization: An expression is only evaluated once for the same input. + * Further evaluations with the same input will return the in-memory stored result. + */ + enableMemoization() { + this.options.memoization = true; + } + /** + * Disable in-memory memoization: each call to evaluate() is executed from scratch. + */ + disableMemoization() { + this.options.memoization = false; + this._memory = {}; + } + /** + * Splits the given string by ',', makes sure the ',' is not within + * a sub-expression + * e.g.: str = "x,pow(3,4)" returns 2 elements: x and pow(3,4). + */ + splitFunctionParams(toSplit) { + let pCount = 0, paramStr = ""; + const params = []; + for (let chr of toSplit.split("")) { + if (chr === "," && pCount === 0) { + params.push(paramStr); + paramStr = ""; + } else if (chr === "(") { + pCount++; + paramStr += chr; + } else if (chr === ")") { + pCount--; + paramStr += chr; + if (pCount < 0) { + throw new Error("ERROR: Too many closing parentheses!"); + } + } else { + paramStr += chr; + } + } + if (pCount !== 0) { + throw new Error("ERROR: Too many opening parentheses!"); + } + if (paramStr.length > 0) { + params.push(paramStr); + } + return params; + } + /** + * Cleans the input string from unnecessary whitespace, + * and replaces some known constants: + */ + cleanupInputFormula(s) { + const resParts = []; + const srcParts = s.split('"'); + srcParts.forEach((part, index) => { + if (index % 2 === 0) { + part = part.replace(/[\s]+/g, ""); + Object.keys(MATH_CONSTANTS).forEach((c) => { + part = part.replace(new RegExp(`\\b${c}\\b`, "g"), `[${c}]`); + }); + } + resParts.push(part); + }); + return resParts.join('"'); + } + /** + * Parses the given formula string by using a state machine into a single Expression object, + * which represents an expression tree (aka AST). + * + * First, we split the string into 'expression': An expression can be: + * - a number, e.g. '3.45' + * - an unknown variable, e.g. 'x' + * - a single char operator, such as '*','+' etc... + * - a named variable, in [], e.g. [myvar] + * - a function, such as sin(x) + * - a parenthessed expression, containing other expressions + * + * We want to create an expression tree out of the string. This is done in 2 stages: + * 1. form single expressions from the string: parse the string into known expression objects: + * - numbers/[variables]/"strings" + * - operators + * - braces (with a sub-expression) + * - functions (with sub-expressions (aka argument expressions)) + * This will lead to an array of expressions. + * As an example: + * "2 + 3 * (4 + 3 ^ 5) * sin(PI * x)" forms an array of the following expressions: + * `[2, +, 3, *, bracketExpr(4,+,3,^,5), * , functionExpr(PI,*,x)]` + * 2. From the raw expression array we form an expression tree by evaluating the expressions in the correct order: + * e.g.: + * the expression array `[2, +, 3, *, bracketExpr(4,+,3,^,5), * , functionExpr(PI,*,x)]` will be transformed into the expression tree: + * ``` + * root expr: (+) + * / \ + * 2 (*) + * / \ + * (*) functionExpr(...) + * / \ + * 3 (bracket(..)) + * ``` + * + * In the end, we have a single root expression node, which then can be evaluated in the evaluate() function. + * + * @param {String} str The formula string, e.g. '3*sin(PI/x)' + * @returns {Expression} An expression object, representing the expression tree + */ + parse(str) { + str = this.cleanupInputFormula(str); + return this._do_parse(str); + } + /** + * @see parse(): this is the recursive parse function, without the clean string part. + * @param {String} str + * @returns {Expression} An expression object, representing the expression tree + */ + _do_parse(str) { + let lastChar = str.length - 1, act = 0, state = "initial", expressions = [], char = "", tmp = "", funcName = null, pCount = 0, pStringDelimiter = ""; + while (act <= lastChar) { + switch (state) { + case "initial": + char = str.charAt(act); + if (char.match(/[0-9.]/)) { + state = "within-nr"; + tmp = ""; + act--; + } else if (this.isOperator(char)) { + if (char === "-") { + if (expressions.length === 0 || this.isOperatorExpr(expressions[expressions.length - 1])) { + state = "within-nr"; + tmp = "-"; + break; + } + } + if (act === lastChar || this.isOperatorExpr(expressions[expressions.length - 1])) { + state = "invalid"; + break; + } else { + expressions.push( + Expression.createOperatorExpression(char, new Expression(), new Expression()) + ); + state = "initial"; + } + } else if ([">", "<", "=", "!"].includes(char)) { + if (act === lastChar) { + state = "invalid"; + break; + } else { + state = "within-logical-operator"; + tmp = char; + } + } else if (char === "(") { + state = "within-parentheses"; + tmp = ""; + pCount = 0; + } else if (char === "[") { + state = "within-named-var"; + tmp = ""; + } else if (char.match(/["']/)) { + state = "within-string"; + pStringDelimiter = char; + tmp = ""; + } else if (char.match(/[a-zA-Z]/)) { + if (act < lastChar && str.charAt(act + 1).match(/[a-zA-Z0-9_.]/)) { + tmp = char; + state = "within-func"; + } else { + if (expressions.length > 0 && expressions[expressions.length - 1] instanceof ValueExpression) { + expressions.push( + Expression.createOperatorExpression("*", new Expression(), new Expression()) + ); + } + expressions.push(new VariableExpression(char, this)); + this.registerVariable(char); + state = "initial"; + tmp = ""; + } + } + break; + case "within-nr": + char = str.charAt(act); + if (char.match(/[0-9.]/)) { + tmp += char; + if (act === lastChar) { + expressions.push(new ValueExpression(tmp)); + state = "initial"; + } + } else { + if (tmp === "-") { + tmp = "-1"; + } + expressions.push(new ValueExpression(tmp)); + tmp = ""; + state = "initial"; + act--; + } + break; + case "within-func": + char = str.charAt(act); + if (char.match(/[a-zA-Z0-9_.]/)) { + tmp += char; + } else if (char === "(") { + funcName = tmp; + tmp = ""; + pCount = 0; + state = "within-func-parentheses"; + } else { + throw new Error("Wrong character for function at position " + act); + } + break; + case "within-named-var": + char = str.charAt(act); + if (char === "]") { + expressions.push(new VariableExpression(tmp, this)); + this.registerVariable(tmp); + tmp = ""; + state = "initial"; + } else if (char.match(/[a-zA-Z0-9_.]/)) { + tmp += char; + } else { + throw new Error("Character not allowed within named variable: " + char); + } + break; + case "within-string": + char = str.charAt(act); + if (char === pStringDelimiter) { + expressions.push(new ValueExpression(tmp, "string")); + tmp = ""; + state = "initial"; + pStringDelimiter = ""; + } else { + tmp += char; + } + break; + case "within-parentheses": + case "within-func-parentheses": + char = str.charAt(act); + if (pStringDelimiter) { + if (char === pStringDelimiter) { + pStringDelimiter = ""; + } + tmp += char; + } else if (char === ")") { + if (pCount <= 0) { + if (state === "within-parentheses") { + expressions.push(new BracketExpression(this._do_parse(tmp))); + } else if (state === "within-func-parentheses") { + let args = this.splitFunctionParams(tmp).map((a) => this._do_parse(a)); + expressions.push(new FunctionExpression(funcName, args, this)); + funcName = null; + } + state = "initial"; + } else { + pCount--; + tmp += char; + } + } else if (char === "(") { + pCount++; + tmp += char; + } else if (char.match(/["']/)) { + pStringDelimiter = char; + tmp += char; + } else { + tmp += char; + } + break; + case "within-logical-operator": + char = str.charAt(act); + if (char === "=") { + tmp += char; + act++; + } + expressions.push(Expression.createOperatorExpression(tmp, new Expression(), new Expression())); + tmp = ""; + state = "initial"; + act--; + break; + } + act++; + } + if (state !== "initial") { + throw new Error("Could not parse formula: Syntax error."); + } + return this.buildExpressionTree(expressions); + } + /** + * @see parse(): Builds an expression tree from the given expression array. + * Builds a tree with a single root expression in the correct order of operator precedence. + * + * Note that the given expression objects are modified and linked. + * + * @param {*} expressions + * @return {Expression} The root Expression of the built expression tree + */ + buildExpressionTree(expressions) { + if (expressions.length < 1) { + throw new Error("No expression given!"); + } + const exprCopy = [...expressions]; + let idx = 0; + let expr = null; + while (idx < exprCopy.length) { + expr = exprCopy[idx]; + if (expr instanceof PowerExpression) { + if (idx === 0 || idx === exprCopy.length - 1) { + throw new Error("Wrong operator position!"); + } + expr.base = exprCopy[idx - 1]; + expr.exponent = exprCopy[idx + 1]; + exprCopy[idx - 1] = expr; + exprCopy.splice(idx, 2); + } else { + idx++; + } + } + idx = 0; + expr = null; + while (idx < exprCopy.length) { + expr = exprCopy[idx]; + if (expr instanceof MultDivExpression) { + if (idx === 0 || idx === exprCopy.length - 1) { + throw new Error("Wrong operator position!"); + } + expr.left = exprCopy[idx - 1]; + expr.right = exprCopy[idx + 1]; + exprCopy[idx - 1] = expr; + exprCopy.splice(idx, 2); + } else { + idx++; + } + } + idx = 0; + expr = null; + while (idx < exprCopy.length) { + expr = exprCopy[idx]; + if (expr instanceof PlusMinusExpression) { + if (idx === 0 || idx === exprCopy.length - 1) { + throw new Error("Wrong operator position!"); + } + expr.left = exprCopy[idx - 1]; + expr.right = exprCopy[idx + 1]; + exprCopy[idx - 1] = expr; + exprCopy.splice(idx, 2); + } else { + idx++; + } + } + idx = 0; + expr = null; + while (idx < exprCopy.length) { + expr = exprCopy[idx]; + if (expr instanceof LogicalExpression) { + if (idx === 0 || idx === exprCopy.length - 1) { + throw new Error("Wrong operator position!"); + } + expr.left = exprCopy[idx - 1]; + expr.right = exprCopy[idx + 1]; + exprCopy[idx - 1] = expr; + exprCopy.splice(idx, 2); + } else { + idx++; + } + } + if (exprCopy.length !== 1) { + throw new Error("Could not parse formula: incorrect syntax?"); + } + return exprCopy[0]; + } + isOperator(char) { + return typeof char === "string" && char.match(/[+\-*/^]/); + } + isOperatorExpr(expr) { + return expr instanceof PlusMinusExpression || expr instanceof MultDivExpression || expr instanceof PowerExpression || expr instanceof LogicalExpression; + } + registerVariable(varName) { + if (this._variables.indexOf(varName) < 0) { + this._variables.push(varName); + } + } + getVariables() { + return this._variables; + } + /** + * Evaluates a Formula by delivering values for the Formula's variables. + * E.g. if the formula is '3*x^2 + 2*x + 4', you should call `evaulate` as follows: + * + * evaluate({x:2}) --> Result: 20 + * + * @param {ValueObject|Array} valueObj An object containing values for variables and (unknown) functions, + * or an array of such objects: If an array is given, all objects are evaluated and the results + * also returned as array. + * @return {Number|String|(Number|String)[]} The evaluated result, or an array with results + */ + evaluate(valueObj) { + if (valueObj instanceof Array) { + return valueObj.map((v) => this.evaluate(v)); + } + let expr = this.getExpression(); + if (!(expr instanceof Expression)) { + throw new Error("No expression set: Did you init the object with a Formula?"); + } + if (this.options.memoization) { + let res = this.resultFromMemory(valueObj); + if (res !== null) { + return res; + } else { + res = expr.evaluate({ ...MATH_CONSTANTS, ...valueObj }); + this.storeInMemory(valueObj, res); + return res; + } + } + return expr.evaluate({ ...MATH_CONSTANTS, ...valueObj }); + } + hashValues(valueObj) { + return JSON.stringify(valueObj); + } + resultFromMemory(valueObj) { + let key = this.hashValues(valueObj); + let res = this._memory[key]; + if (res !== void 0) { + return res; + } else { + return null; + } + } + storeInMemory(valueObj, value) { + this._memory[this.hashValues(valueObj)] = value; + } + getExpression() { + return this.formulaExpression; + } + getExpressionString() { + return this.formulaExpression ? this.formulaExpression.toString() : ""; + } + static calc(formula, valueObj = null, options = {}) { + valueObj = valueObj != null ? valueObj : {}; + return new _Formula(formula, options).evaluate(valueObj); + } + }; + __publicField(_Formula, "Expression", Expression); + __publicField(_Formula, "BracketExpression", BracketExpression); + __publicField(_Formula, "PowerExpression", PowerExpression); + __publicField(_Formula, "MultDivExpression", MultDivExpression); + __publicField(_Formula, "PlusMinusExpression", PlusMinusExpression); + __publicField(_Formula, "LogicalExpression", LogicalExpression); + __publicField(_Formula, "ValueExpression", ValueExpression); + __publicField(_Formula, "VariableExpression", VariableExpression); + __publicField(_Formula, "FunctionExpression", FunctionExpression); + __publicField(_Formula, "MATH_CONSTANTS", MATH_CONSTANTS); + // Create a function blacklist: + __publicField(_Formula, "functionBlacklist", Object.getOwnPropertyNames(_Formula.prototype).filter((prop) => _Formula.prototype[prop] instanceof Function).map((prop) => _Formula.prototype[prop])); + let Formula = _Formula; + return Formula; +}); //# sourceMappingURL=fparser.umd.cjs.map diff --git a/dist/fparser.umd.cjs.map b/dist/fparser.umd.cjs.map index ba2cf18..4f79255 100644 --- a/dist/fparser.umd.cjs.map +++ b/dist/fparser.umd.cjs.map @@ -1 +1 @@ -{"version":3,"file":"fparser.umd.cjs","sources":["../src/fparser.ts"],"sourcesContent":["/**\n * JS Formula Parser\n * -------------------\n * (c) 2012-2023 Alexander Schenkel, alex@alexi.ch\n *\n * JS Formula Parser takes a string, parses its mathmatical formula\n * and creates an evaluatable Formula object of it.\n *\n * Example input:\n *\n * var fObj = new Formula('sin(PI*x)/(2*PI)');\n * var result = fObj.evaluate({x: 2});\n * var results = fObj.evaluate([\n * {x: 2},\n * {x: 4},\n * {x: 8}\n * ]);\n *\n * LICENSE:\n * -------------\n * MIT license, see LICENSE file\n */\nconst MATH_CONSTANTS = {\n PI: Math.PI,\n E: Math.E,\n LN2: Math.LN2,\n LN10: Math.LN10,\n LOG2E: Math.LOG2E,\n LOG10E: Math.LOG10E,\n SQRT1_2: Math.SQRT1_2,\n SQRT2: Math.SQRT2\n};\n\ndeclare global {\n interface Math {\n [key: string]: number | Function;\n }\n}\n\ntype FormulaOptions = {\n memoization?: boolean;\n};\n\ntype ValueObject = {\n [key: string]: number | string | Function | ValueObject;\n};\n\nclass MathOperatorHelper {\n static throwIfNotNumber(value: number | string) {\n const valueType = typeof value;\n if (valueType === 'string') {\n throw new Error('Strings are not allowed in math operations');\n }\n }\n}\n\nclass MathFunctionHelper {\n static throwIfNotNumber(value: number | string) {\n const valueType = typeof value;\n if (valueType === 'string') {\n throw new Error('Strings are not allowed in math operations');\n }\n }\n}\n\nclass Expression {\n static createOperatorExpression(operator: string, left: Expression, right: Expression) {\n if (operator === '^') {\n return new PowerExpression(left, right);\n }\n if ( [ '*', '/' ].includes( operator ) ) {\n return new MultDivExpression(operator, left, right);\n }\n if ( [ '+', '-' ].includes( operator ) ) {\n return new PlusMinusExpression(operator, left, right);\n }\n if ( [ '<', '>', '<=', '>=', '=', '!=' ].includes( operator ) ) {\n return new LogicalExpression(operator, left, right);\n }\n throw new Error(`Unknown operator: ${operator}`);\n }\n\n evaluate(params: ValueObject = {}): number | string {\n throw new Error('Empty Expression - Must be defined in child classes');\n }\n\n toString() {\n return '';\n }\n}\n\nclass BracketExpression extends Expression {\n innerExpression: Expression;\n\n constructor(expr: Expression) {\n super();\n this.innerExpression = expr;\n if (!(this.innerExpression instanceof Expression)) {\n throw new Error('No inner expression given for bracket expression');\n }\n }\n evaluate(params = {}): number | string {\n return this.innerExpression.evaluate(params);\n }\n toString() {\n return `(${this.innerExpression.toString()})`;\n }\n}\n\nclass ValueExpression extends Expression {\n value: number | string;\n type: string;\n\n constructor(value: number | string, type: string = 'number') {\n super();\n this.value = Number(value);\n switch (type) {\n case 'number':\n this.value = Number(value);\n if (isNaN(this.value)) {\n throw new Error('Cannot parse number: ' + value);\n }\n break;\n case 'string':\n this.value = String(value);\n break;\n default:\n throw new Error('Invalid value type: ' + type);\n }\n this.type = type;\n }\n evaluate(): number | string {\n return this.value;\n }\n toString() {\n switch (this.type) {\n case 'number':\n return String(this.value);\n case 'string':\n return String('\"' + this.value + '\"');\n default:\n throw new Error('Invalid type');\n }\n }\n}\n\nclass PlusMinusExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n\n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if (!['+', '-'].includes(operator)) {\n throw new Error(`Operator not allowed in Plus/Minus expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(leftValue);\n MathOperatorHelper.throwIfNotNumber(rightValue);\n if (this.operator === '+') {\n return Number(leftValue) + Number(rightValue);\n }\n if (this.operator === '-') {\n return Number(leftValue) - Number(rightValue);\n }\n throw new Error('Unknown operator for PlusMinus expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\n\nclass MultDivExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n\n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if (!['*', '/'].includes(operator)) {\n throw new Error(`Operator not allowed in Multiply/Division expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(leftValue);\n MathOperatorHelper.throwIfNotNumber(rightValue);\n if (this.operator === '*') {\n return Number(leftValue) * Number(rightValue);\n }\n if (this.operator === '/') {\n return Number(leftValue) / Number(rightValue);\n }\n throw new Error('Unknown operator for MultDiv expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\n\nclass PowerExpression extends Expression {\n base: Expression;\n exponent: Expression;\n\n constructor(base: Expression, exponent: Expression) {\n super();\n this.base = base;\n this.exponent = exponent;\n }\n\n evaluate(params: ValueObject = {}): number {\n const baseValue = this.base.evaluate(params);\n const exponentValue = this.exponent.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(baseValue);\n MathOperatorHelper.throwIfNotNumber(exponentValue);\n\n return Math.pow(Number(baseValue), Number(exponentValue));\n }\n\n toString() {\n return `${this.base.toString()}^${this.exponent.toString()}`;\n }\n}\n\nclass LogicalExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n \n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if ( ! [ '<', '>', '<=', '>=', '=', '!=' ].includes( operator ) ) {\n throw new Error(`Operator not allowed in Logical expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n switch ( this.operator ) {\n case '<':\n return leftValue < rightValue ? 1 : 0;\n case '>':\n return leftValue > rightValue ? 1 : 0;\n case '<=':\n return leftValue <= rightValue ? 1 : 0;\n case '>=':\n return leftValue >= rightValue ? 1 : 0;\n case '=':\n return leftValue === rightValue ? 1 : 0;\n case '!=':\n return leftValue !== rightValue ? 1 : 0;\n }\n throw new Error('Unknown operator for Logical expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\nclass FunctionExpression extends Expression {\n fn: string;\n varPath: string[];\n argumentExpressions: Expression[];\n formulaObject: Formula | null;\n blacklisted: boolean | undefined;\n\n constructor(fn: string | null, argumentExpressions: Expression[], formulaObject: Formula | null = null) {\n super();\n this.fn = fn ?? '';\n this.varPath = this.fn.split('.');\n this.argumentExpressions = argumentExpressions || [];\n this.formulaObject = formulaObject;\n this.blacklisted = undefined;\n }\n\n evaluate(params: ValueObject = {}): number | string {\n params = params || {};\n const paramValues = this.argumentExpressions.map((a) => a.evaluate(params));\n\n // If the params object itself has a function definition with\n // the function name, call this one:\n // let fn = params[this.fn];\n try {\n let fn = getProperty(params, this.varPath, this.fn);\n if (fn instanceof Function) {\n return fn.apply(this, paramValues);\n }\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n\n let objFn;\n try {\n // perhaps the Formula object has the function? so call it:\n objFn = getProperty(this.formulaObject ?? {}, this.varPath, this.fn);\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n if (this.formulaObject && objFn instanceof Function) {\n // Don't, if it is blacklisted:\n if (this.isBlacklisted()) {\n throw new Error('Blacklisted function called: ' + this.fn);\n }\n return objFn.apply(this.formulaObject, paramValues);\n }\n\n try {\n // Has the JS Math object a function as requested? Call it:\n const mathFn = getProperty(Math, this.varPath, this.fn);\n if (mathFn instanceof Function) {\n paramValues.forEach((paramValue) => {\n MathFunctionHelper.throwIfNotNumber(paramValue);\n });\n\n return mathFn.apply(this, paramValues);\n }\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n // No more options left: sorry!\n throw new Error('Function not found: ' + this.fn);\n }\n\n toString() {\n return `${this.fn}(${this.argumentExpressions.map((a) => a.toString()).join(', ')})`;\n }\n\n isBlacklisted() {\n // cache evaluation of blacklisted function, to save call time:\n if (this.blacklisted === undefined) {\n this.blacklisted = Formula.functionBlacklist.includes(\n this.formulaObject ? this.formulaObject[this.fn] : null\n );\n }\n return this.blacklisted;\n }\n}\n\nfunction getProperty(object: ValueObject, path: string[], fullPath: string) {\n let curr: number | string | Function | ValueObject = object;\n for (let propName of path) {\n if (typeof curr !== 'object') {\n throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`);\n }\n if (curr[propName] === undefined) {\n throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`);\n }\n curr = curr[propName];\n }\n\n if (typeof curr === 'object') {\n throw new Error('Invalid value');\n }\n\n return curr;\n}\n\nclass VariableExpression extends Expression {\n fullPath: string;\n varPath: string[];\n formulaObject: Formula | null;\n\n constructor(fullPath: string, formulaObj: Formula | null = null) {\n super();\n this.formulaObject = formulaObj;\n this.fullPath = fullPath;\n this.varPath = fullPath.split('.');\n }\n\n evaluate(params = {}): number | string {\n // params contain variable / value pairs: If this object's variable matches\n // a varname found in the params, return the value.\n // eg: params = {x: 5,y:3}, varname = x, return 5\n // Objects and arrays are also supported:\n // e.g. params = {x: {y: 5}}, varname = x.y, return 5\n // or params = {x: [2,4,6]}, varname = x.2, return 6\n\n // Let's look in the value object first:\n let value = undefined;\n try {\n value = getProperty(params, this.varPath, this.fullPath);\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n if (value === undefined) {\n // Now have a look at the formula object:\n // This will throw an error if the property is not found:\n value = getProperty(this.formulaObject ?? {}, this.varPath, this.fullPath);\n }\n if (typeof value === 'function' || typeof value === 'object') {\n throw new Error(`Cannot use ${this.fullPath} as value: It contains a non-numerical value.`);\n }\n\n return value;\n }\n toString() {\n return `${this.varPath.join('.')}`;\n }\n}\n\nexport default class Formula {\n [key: string]: any;\n static Expression = Expression;\n static BracketExpression = BracketExpression;\n static PowerExpression = PowerExpression;\n static MultDivExpression = MultDivExpression;\n static PlusMinusExpression = PlusMinusExpression;\n static LogicalExpression = LogicalExpression;\n static ValueExpression = ValueExpression;\n static VariableExpression = VariableExpression;\n static FunctionExpression = FunctionExpression;\n static MATH_CONSTANTS = MATH_CONSTANTS;\n\n // Create a function blacklist:\n static functionBlacklist = Object.getOwnPropertyNames(Formula.prototype)\n .filter((prop) => Formula.prototype[prop] instanceof Function)\n .map((prop) => Formula.prototype[prop]);\n\n public formulaExpression: Expression | null;\n public options: FormulaOptions;\n public formulaStr: string;\n private _variables: string[];\n private _memory: { [key: string]: number | string };\n\n /**\n * Creates a new Formula instance\n *\n * Optional configuration can be set in the options object:\n *\n * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters\n *\n * @param {String} fStr The formula string, e.g. 'sin(x)/cos(y)'\n * @param {Object} options An options object. Supported options:\n * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters\n * @param {Formula} parentFormula Internally used to build a Formula AST\n */\n constructor(fStr: string, options: FormulaOptions | null = {}) {\n this.formulaExpression = null;\n this.options = { ...{ memoization: false }, ...options };\n this.formulaStr = '';\n this._variables = [];\n this._memory = {};\n this.setFormula(fStr);\n }\n\n /**\n * Re-sets the given String and parses it to a formula expression. Can be used after initialization,\n * to re-use the Formula object.\n *\n * @param {String} formulaString The formula string to set/parse\n * @return {this} The Formula object (this)\n */\n setFormula(formulaString: string) {\n if (formulaString) {\n this.formulaExpression = null;\n this._variables = [];\n this._memory = {};\n this.formulaStr = formulaString;\n this.formulaExpression = this.parse(formulaString);\n }\n return this;\n }\n\n /**\n * Enable memoization: An expression is only evaluated once for the same input.\n * Further evaluations with the same input will return the in-memory stored result.\n */\n enableMemoization() {\n this.options.memoization = true;\n }\n\n /**\n * Disable in-memory memoization: each call to evaluate() is executed from scratch.\n */\n disableMemoization() {\n this.options.memoization = false;\n this._memory = {};\n }\n\n /**\n * Splits the given string by ',', makes sure the ',' is not within\n * a sub-expression\n * e.g.: str = \"x,pow(3,4)\" returns 2 elements: x and pow(3,4).\n */\n splitFunctionParams(toSplit: string) {\n // do not split on ',' within matching brackets.\n let pCount = 0,\n paramStr = '';\n const params = [];\n for (let chr of toSplit.split('')) {\n if (chr === ',' && pCount === 0) {\n // Found function param, save 'em\n params.push(paramStr);\n paramStr = '';\n } else if (chr === '(') {\n pCount++;\n paramStr += chr;\n } else if (chr === ')') {\n pCount--;\n paramStr += chr;\n if (pCount < 0) {\n throw new Error('ERROR: Too many closing parentheses!');\n }\n } else {\n paramStr += chr;\n }\n }\n if (pCount !== 0) {\n throw new Error('ERROR: Too many opening parentheses!');\n }\n if (paramStr.length > 0) {\n params.push(paramStr);\n }\n return params;\n }\n\n /**\n * Cleans the input string from unnecessary whitespace,\n * and replaces some known constants:\n */\n cleanupInputFormula(s: string) {\n const resParts: string[] = [];\n const srcParts = s.split('\"');\n srcParts.forEach((part, index) => {\n // skip parts marked as string\n if (index % 2 === 0) {\n part = part.replace(/[\\s]+/g, '');\n // surround known math constants with [], to parse them as named variables [xxx]:\n Object.keys(MATH_CONSTANTS).forEach((c) => {\n part = part.replace(new RegExp(`\\\\b${c}\\\\b`, 'g'), `[${c}]`);\n });\n }\n resParts.push(part);\n });\n return resParts.join('\"');\n }\n\n /**\n * Parses the given formula string by using a state machine into a single Expression object,\n * which represents an expression tree (aka AST).\n *\n * First, we split the string into 'expression': An expression can be:\n * - a number, e.g. '3.45'\n * - an unknown variable, e.g. 'x'\n * - a single char operator, such as '*','+' etc...\n * - a named variable, in [], e.g. [myvar]\n * - a function, such as sin(x)\n * - a parenthessed expression, containing other expressions\n *\n * We want to create an expression tree out of the string. This is done in 2 stages:\n * 1. form single expressions from the string: parse the string into known expression objects:\n * - numbers/[variables]/\"strings\"\n * - operators\n * - braces (with a sub-expression)\n * - functions (with sub-expressions (aka argument expressions))\n * This will lead to an array of expressions.\n * As an example:\n * \"2 + 3 * (4 + 3 ^ 5) * sin(PI * x)\" forms an array of the following expressions:\n * `[2, +, 3, *, bracketExpr(4,+,3,^,5), * , functionExpr(PI,*,x)]`\n * 2. From the raw expression array we form an expression tree by evaluating the expressions in the correct order:\n * e.g.:\n * the expression array `[2, +, 3, *, bracketExpr(4,+,3,^,5), * , functionExpr(PI,*,x)]` will be transformed into the expression tree:\n * ```\n * root expr: (+)\n * / \\\n * 2 (*)\n * / \\\n * (*) functionExpr(...)\n * / \\\n * 3 (bracket(..))\n * ```\n *\n * In the end, we have a single root expression node, which then can be evaluated in the evaluate() function.\n *\n * @param {String} str The formula string, e.g. '3*sin(PI/x)'\n * @returns {Expression} An expression object, representing the expression tree\n */\n parse(str: string) {\n // clean the input string first. spaces, math constant replacements etc.:\n str = this.cleanupInputFormula(str);\n // start recursive call to parse:\n return this._do_parse(str);\n }\n\n /**\n * @see parse(): this is the recursive parse function, without the clean string part.\n * @param {String} str\n * @returns {Expression} An expression object, representing the expression tree\n */\n _do_parse(str: string): Expression {\n let lastChar = str.length - 1,\n act = 0,\n state:\n | 'initial'\n | 'within-nr'\n | 'within-parentheses'\n | 'within-func-parentheses'\n | 'within-named-var'\n | 'within-string'\n | 'within-expr'\n | 'within-bracket'\n | 'within-func'\n | 'within-logical-operator'\n | 'invalid' = 'initial',\n expressions = [],\n char = '',\n tmp = '',\n funcName = null,\n pCount = 0,\n pStringDelimiter = '';\n\n while (act <= lastChar) {\n switch (state) {\n case 'initial':\n // None state, the beginning. Read a char and see what happens.\n char = str.charAt(act);\n if (char.match(/[0-9.]/)) {\n // found the beginning of a number, change state to \"within-number\"\n state = 'within-nr';\n tmp = '';\n act--;\n } else if (this.isOperator(char)) {\n // Simple operators. Note: '-' must be treaten specially,\n // it could be part of a number.\n // it MUST be part of a number if the last found expression\n // was an operator (or the beginning):\n if (char === '-') {\n if (expressions.length === 0 || this.isOperatorExpr(expressions[expressions.length - 1])) {\n state = 'within-nr';\n tmp = '-';\n break;\n }\n }\n\n // Found a simple operator, store as expression:\n if (act === lastChar || this.isOperatorExpr(expressions[expressions.length - 1])) {\n state = 'invalid'; // invalid to end with an operator, or have 2 operators in conjunction\n break;\n } else {\n expressions.push(\n Expression.createOperatorExpression(char, new Expression(), new Expression())\n );\n state = 'initial';\n }\n } else if ( [ '>', '<', '=', '!' ].includes(char)) {\n // found the beginning of a logical operator, change state to \"within-logical-operator\"\n if (act === lastChar) {\n state = 'invalid'; // invalid to end with a logical operator\n break;\n } else {\n state = 'within-logical-operator';\n tmp = char;\n }\n } else if (char === '(') {\n // left parenthes found, seems to be the beginning of a new sub-expression:\n state = 'within-parentheses';\n tmp = '';\n pCount = 0;\n } else if (char === '[') {\n // left named var separator char found, seems to be the beginning of a named var:\n state = 'within-named-var';\n tmp = '';\n } else if (char.match(/[\"']/)) {\n // left string separator char found\n state = 'within-string';\n pStringDelimiter = char;\n tmp = '';\n } else if (char.match(/[a-zA-Z]/)) {\n // multiple chars means it may be a function, else its a var which counts as own expression:\n if (act < lastChar && str.charAt(act + 1).match(/[a-zA-Z0-9_.]/)) {\n tmp = char;\n state = 'within-func';\n } else {\n // Single variable found:\n // We need to check some special considerations:\n // - If the last char was a number (e.g. 3x), we need to create a multiplication out of it (3*x)\n if (\n expressions.length > 0 &&\n expressions[expressions.length - 1] instanceof ValueExpression\n ) {\n expressions.push(\n Expression.createOperatorExpression('*', new Expression(), new Expression())\n );\n }\n expressions.push(new VariableExpression(char, this));\n this.registerVariable(char);\n state = 'initial';\n tmp = '';\n }\n }\n break;\n case 'within-nr':\n char = str.charAt(act);\n if (char.match(/[0-9.]/)) {\n //Still within number, store and continue\n tmp += char;\n if (act === lastChar) {\n expressions.push(new ValueExpression(tmp));\n state = 'initial';\n }\n } else {\n // Number finished on last round, so add as expression:\n if (tmp === '-') {\n // just a single '-' means: a variable could follow (e.g. like in 3*-x), we convert it to -1: (3*-1x)\n tmp = '-1';\n }\n expressions.push(new ValueExpression(tmp));\n tmp = '';\n state = 'initial';\n act--;\n }\n break;\n\n case 'within-func':\n char = str.charAt(act);\n if (char.match(/[a-zA-Z0-9_.]/)) {\n tmp += char;\n } else if (char === '(') {\n funcName = tmp;\n tmp = '';\n pCount = 0;\n state = 'within-func-parentheses';\n } else {\n throw new Error('Wrong character for function at position ' + act);\n }\n\n break;\n\n case 'within-named-var':\n char = str.charAt(act);\n if (char === ']') {\n // end of named var, create expression:\n expressions.push(new VariableExpression(tmp, this));\n this.registerVariable(tmp);\n tmp = '';\n state = 'initial';\n } else if (char.match(/[a-zA-Z0-9_.]/)) {\n tmp += char;\n } else {\n throw new Error('Character not allowed within named variable: ' + char);\n }\n break;\n\n case 'within-string':\n char = str.charAt(act);\n if (char === pStringDelimiter) {\n // end of string, create expression:\n expressions.push(new ValueExpression(tmp, 'string'));\n tmp = '';\n state = 'initial';\n pStringDelimiter = '';\n } else {\n tmp += char;\n }\n break;\n\n case 'within-parentheses':\n case 'within-func-parentheses':\n char = str.charAt(act);\n if (pStringDelimiter) {\n // If string is opened, then:\n if (char === pStringDelimiter) {\n // end of string\n pStringDelimiter = '';\n }\n // accumulate string chars\n tmp += char;\n } else if (char === ')') {\n //Check if this is the matching closing parenthesis.If not, just read ahead.\n if (pCount <= 0) {\n // Yes, we found the closing parenthesis, create new sub-expression:\n if (state === 'within-parentheses') {\n expressions.push(new BracketExpression(this._do_parse(tmp)));\n } else if (state === 'within-func-parentheses') {\n // Function found: create expressions from the inner argument\n // string, and create a function expression with it:\n let args = this.splitFunctionParams(tmp).map((a) => this._do_parse(a));\n expressions.push(new FunctionExpression(funcName, args, this));\n funcName = null;\n }\n state = 'initial';\n } else {\n pCount--;\n tmp += char;\n }\n } else if (char === '(') {\n // begin of a new sub-parenthesis, increase counter:\n pCount++;\n tmp += char;\n } else if (char.match(/[\"']/)) {\n // start of string\n pStringDelimiter = char;\n tmp += char;\n } else {\n // all other things are just added to the sub-expression:\n tmp += char;\n }\n break;\n \n case 'within-logical-operator':\n char = str.charAt(act);\n if (char === '=') {\n // the second char of a logical operator\n // can only be an equal sign\n tmp += char;\n act++;\n }\n // logical operator finished, create expression:\n expressions.push(Expression.createOperatorExpression(tmp, new Expression(), new Expression()));\n tmp = '';\n state = 'initial';\n act--;\n break;\n }\n act++;\n }\n\n if (state !== 'initial') {\n throw new Error('Could not parse formula: Syntax error.');\n }\n\n return this.buildExpressionTree(expressions);\n }\n\n /**\n * @see parse(): Builds an expression tree from the given expression array.\n * Builds a tree with a single root expression in the correct order of operator precedence.\n *\n * Note that the given expression objects are modified and linked.\n *\n * @param {*} expressions\n * @return {Expression} The root Expression of the built expression tree\n */\n buildExpressionTree(expressions: Expression[]): Expression {\n if (expressions.length < 1) {\n throw new Error('No expression given!');\n }\n const exprCopy = [...expressions];\n let idx = 0;\n let expr = null;\n // Replace all Power expressions with a partial tree:\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof PowerExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.base = exprCopy[idx - 1];\n expr.exponent = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Mult/Div expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof MultDivExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Plus/Minus expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof PlusMinusExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Logical expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof LogicalExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n if (exprCopy.length !== 1) {\n throw new Error('Could not parse formula: incorrect syntax?');\n }\n return exprCopy[0];\n }\n\n isOperator(char: string | null) {\n return typeof char === 'string' && char.match(/[+\\-*/^]/);\n }\n\n isOperatorExpr(expr: Expression) {\n return (\n expr instanceof PlusMinusExpression || expr instanceof MultDivExpression || expr instanceof PowerExpression || expr instanceof LogicalExpression\n );\n }\n\n registerVariable(varName: string) {\n if (this._variables.indexOf(varName) < 0) {\n this._variables.push(varName);\n }\n }\n\n getVariables() {\n return this._variables;\n }\n\n /**\n * Evaluates a Formula by delivering values for the Formula's variables.\n * E.g. if the formula is '3*x^2 + 2*x + 4', you should call `evaulate` as follows:\n *\n * evaluate({x:2}) --> Result: 20\n *\n * @param {ValueObject|Array} valueObj An object containing values for variables and (unknown) functions,\n * or an array of such objects: If an array is given, all objects are evaluated and the results\n * also returned as array.\n * @return {Number|String|(Number|String)[]} The evaluated result, or an array with results\n */\n evaluate(valueObj: ValueObject | ValueObject[]): number | string | (number | string)[] {\n // resolve multiple value objects recursively:\n if (valueObj instanceof Array) {\n return valueObj.map((v) => this.evaluate(v)) as (number | string)[];\n }\n let expr = this.getExpression();\n if (!(expr instanceof Expression)) {\n throw new Error('No expression set: Did you init the object with a Formula?');\n }\n if (this.options.memoization) {\n let res = this.resultFromMemory(valueObj);\n if (res !== null) {\n return res;\n } else {\n res = expr.evaluate({ ...MATH_CONSTANTS, ...valueObj });\n this.storeInMemory(valueObj, res);\n return res;\n }\n }\n return expr.evaluate({ ...MATH_CONSTANTS, ...valueObj });\n }\n\n hashValues(valueObj: ValueObject) {\n return JSON.stringify(valueObj);\n }\n\n resultFromMemory(valueObj: ValueObject): number | string | null {\n let key = this.hashValues(valueObj);\n let res = this._memory[key];\n if (res !== undefined) {\n return res;\n } else {\n return null;\n }\n }\n\n storeInMemory(valueObj: ValueObject, value: number | string) {\n this._memory[this.hashValues(valueObj)] = value;\n }\n\n getExpression() {\n return this.formulaExpression;\n }\n\n getExpressionString() {\n return this.formulaExpression ? this.formulaExpression.toString() : '';\n }\n\n static calc(formula: string, valueObj: ValueObject | null = null, options = {}) {\n valueObj = valueObj ?? {};\n return new Formula(formula, options).evaluate(valueObj);\n }\n}\n"],"names":["MATH_CONSTANTS","MathOperatorHelper","value","MathFunctionHelper","Expression","operator","left","right","PowerExpression","MultDivExpression","PlusMinusExpression","LogicalExpression","params","BracketExpression","expr","__publicField","ValueExpression","type","leftValue","rightValue","base","exponent","baseValue","exponentValue","FunctionExpression","fn","argumentExpressions","formulaObject","paramValues","a","getProperty","e","objFn","_a","mathFn","paramValue","Formula","object","path","fullPath","curr","propName","VariableExpression","formulaObj","_Formula","fStr","options","formulaString","toSplit","pCount","paramStr","chr","s","resParts","part","index","c","str","lastChar","act","state","expressions","char","tmp","funcName","pStringDelimiter","args","exprCopy","idx","varName","valueObj","v","res","key","formula","prop"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"fparser.umd.cjs","sources":["../src/fparser.ts"],"sourcesContent":["/**\n * JS Formula Parser\n * -------------------\n * (c) 2012-2023 Alexander Schenkel, alex@alexi.ch\n *\n * JS Formula Parser takes a string, parses its mathmatical formula\n * and creates an evaluatable Formula object of it.\n *\n * Example input:\n *\n * var fObj = new Formula('sin(PI*x)/(2*PI)');\n * var result = fObj.evaluate({x: 2});\n * var results = fObj.evaluate([\n * {x: 2},\n * {x: 4},\n * {x: 8}\n * ]);\n *\n * LICENSE:\n * -------------\n * MIT license, see LICENSE file\n */\nconst MATH_CONSTANTS = {\n PI: Math.PI,\n E: Math.E,\n LN2: Math.LN2,\n LN10: Math.LN10,\n LOG2E: Math.LOG2E,\n LOG10E: Math.LOG10E,\n SQRT1_2: Math.SQRT1_2,\n SQRT2: Math.SQRT2\n};\n\ndeclare global {\n interface Math {\n [key: string]: number | Function;\n }\n}\n\ntype FormulaOptions = {\n memoization?: boolean;\n};\n\ntype ValueObject = {\n [key: string]: number | string | Function | ValueObject;\n};\n\nclass MathOperatorHelper {\n static throwIfNotNumber(value: number | string) {\n const valueType = typeof value;\n if (valueType === 'string') {\n throw new Error('Strings are not allowed in math operations');\n }\n }\n}\n\nclass MathFunctionHelper {\n static throwIfNotNumber(value: number | string) {\n const valueType = typeof value;\n if (valueType === 'string') {\n throw new Error('Strings are not allowed in math operations');\n }\n }\n}\n\nclass Expression {\n static createOperatorExpression(operator: string, left: Expression, right: Expression) {\n if (operator === '^') {\n return new PowerExpression(left, right);\n }\n if ( [ '*', '/' ].includes( operator ) ) {\n return new MultDivExpression(operator, left, right);\n }\n if ( [ '+', '-' ].includes( operator ) ) {\n return new PlusMinusExpression(operator, left, right);\n }\n if ( [ '<', '>', '<=', '>=', '=', '!=' ].includes( operator ) ) {\n return new LogicalExpression(operator, left, right);\n }\n throw new Error(`Unknown operator: ${operator}`);\n }\n\n evaluate(params: ValueObject = {}): number | string {\n throw new Error('Empty Expression - Must be defined in child classes');\n }\n\n toString() {\n return '';\n }\n}\n\nclass BracketExpression extends Expression {\n innerExpression: Expression;\n\n constructor(expr: Expression) {\n super();\n this.innerExpression = expr;\n if (!(this.innerExpression instanceof Expression)) {\n throw new Error('No inner expression given for bracket expression');\n }\n }\n evaluate(params = {}): number | string {\n return this.innerExpression.evaluate(params);\n }\n toString() {\n return `(${this.innerExpression.toString()})`;\n }\n}\n\nclass ValueExpression extends Expression {\n value: number | string;\n type: string;\n\n constructor(value: number | string, type: string = 'number') {\n super();\n this.value = Number(value);\n switch (type) {\n case 'number':\n this.value = Number(value);\n if (isNaN(this.value)) {\n throw new Error('Cannot parse number: ' + value);\n }\n break;\n case 'string':\n this.value = String(value);\n break;\n default:\n throw new Error('Invalid value type: ' + type);\n }\n this.type = type;\n }\n evaluate(): number | string {\n return this.value;\n }\n toString() {\n switch (this.type) {\n case 'number':\n return String(this.value);\n case 'string':\n return String('\"' + this.value + '\"');\n default:\n throw new Error('Invalid type');\n }\n }\n}\n\nclass PlusMinusExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n\n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if (!['+', '-'].includes(operator)) {\n throw new Error(`Operator not allowed in Plus/Minus expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(leftValue);\n MathOperatorHelper.throwIfNotNumber(rightValue);\n if (this.operator === '+') {\n return Number(leftValue) + Number(rightValue);\n }\n if (this.operator === '-') {\n return Number(leftValue) - Number(rightValue);\n }\n throw new Error('Unknown operator for PlusMinus expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\n\nclass MultDivExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n\n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if (!['*', '/'].includes(operator)) {\n throw new Error(`Operator not allowed in Multiply/Division expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(leftValue);\n MathOperatorHelper.throwIfNotNumber(rightValue);\n if (this.operator === '*') {\n return Number(leftValue) * Number(rightValue);\n }\n if (this.operator === '/') {\n return Number(leftValue) / Number(rightValue);\n }\n throw new Error('Unknown operator for MultDiv expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\n\nclass PowerExpression extends Expression {\n base: Expression;\n exponent: Expression;\n\n constructor(base: Expression, exponent: Expression) {\n super();\n this.base = base;\n this.exponent = exponent;\n }\n\n evaluate(params: ValueObject = {}): number {\n const baseValue = this.base.evaluate(params);\n const exponentValue = this.exponent.evaluate(params);\n MathOperatorHelper.throwIfNotNumber(baseValue);\n MathOperatorHelper.throwIfNotNumber(exponentValue);\n\n return Math.pow(Number(baseValue), Number(exponentValue));\n }\n\n toString() {\n return `${this.base.toString()}^${this.exponent.toString()}`;\n }\n}\n\nclass LogicalExpression extends Expression {\n operator: string;\n left: Expression;\n right: Expression;\n \n constructor(operator: string, left: Expression, right: Expression) {\n super();\n if ( ! [ '<', '>', '<=', '>=', '=', '!=' ].includes( operator ) ) {\n throw new Error(`Operator not allowed in Logical expression: ${operator}`);\n }\n this.operator = operator;\n this.left = left;\n this.right = right;\n }\n\n evaluate(params: ValueObject = {}): number {\n const leftValue = this.left.evaluate(params);\n const rightValue = this.right.evaluate(params);\n switch ( this.operator ) {\n case '<':\n return leftValue < rightValue ? 1 : 0;\n case '>':\n return leftValue > rightValue ? 1 : 0;\n case '<=':\n return leftValue <= rightValue ? 1 : 0;\n case '>=':\n return leftValue >= rightValue ? 1 : 0;\n case '=':\n return leftValue === rightValue ? 1 : 0;\n case '!=':\n return leftValue !== rightValue ? 1 : 0;\n }\n throw new Error('Unknown operator for Logical expression');\n }\n\n toString() {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\nclass FunctionExpression extends Expression {\n fn: string;\n varPath: string[];\n argumentExpressions: Expression[];\n formulaObject: Formula | null;\n blacklisted: boolean | undefined;\n\n constructor(fn: string | null, argumentExpressions: Expression[], formulaObject: Formula | null = null) {\n super();\n this.fn = fn ?? '';\n this.varPath = this.fn.split('.');\n this.argumentExpressions = argumentExpressions || [];\n this.formulaObject = formulaObject;\n this.blacklisted = undefined;\n }\n\n evaluate(params: ValueObject = {}): number | string {\n params = params || {};\n const paramValues = this.argumentExpressions.map((a) => a.evaluate(params));\n\n // If the params object itself has a function definition with\n // the function name, call this one:\n // let fn = params[this.fn];\n try {\n let fn = getProperty(params, this.varPath, this.fn);\n if (fn instanceof Function) {\n return fn.apply(this, paramValues);\n }\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n\n let objFn;\n try {\n // perhaps the Formula object has the function? so call it:\n objFn = getProperty(this.formulaObject ?? {}, this.varPath, this.fn);\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n if (this.formulaObject && objFn instanceof Function) {\n // Don't, if it is blacklisted:\n if (this.isBlacklisted()) {\n throw new Error('Blacklisted function called: ' + this.fn);\n }\n return objFn.apply(this.formulaObject, paramValues);\n }\n\n try {\n // Has the JS Math object a function as requested? Call it:\n const mathFn = getProperty(Math, this.varPath, this.fn);\n if (mathFn instanceof Function) {\n paramValues.forEach((paramValue) => {\n MathFunctionHelper.throwIfNotNumber(paramValue);\n });\n\n return mathFn.apply(this, paramValues);\n }\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n // No more options left: sorry!\n throw new Error('Function not found: ' + this.fn);\n }\n\n toString() {\n return `${this.fn}(${this.argumentExpressions.map((a) => a.toString()).join(', ')})`;\n }\n\n isBlacklisted() {\n // cache evaluation of blacklisted function, to save call time:\n if (this.blacklisted === undefined) {\n this.blacklisted = Formula.functionBlacklist.includes(\n this.formulaObject ? this.formulaObject[this.fn] : null\n );\n }\n return this.blacklisted;\n }\n}\n\nfunction getProperty(object: ValueObject, path: string[], fullPath: string) {\n let curr: number | string | Function | ValueObject = object;\n for (let propName of path) {\n if (typeof curr !== 'object') {\n throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`);\n }\n if (curr[propName] === undefined) {\n throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`);\n }\n curr = curr[propName];\n }\n\n if (typeof curr === 'object') {\n throw new Error('Invalid value');\n }\n\n return curr;\n}\n\nclass VariableExpression extends Expression {\n fullPath: string;\n varPath: string[];\n formulaObject: Formula | null;\n\n constructor(fullPath: string, formulaObj: Formula | null = null) {\n super();\n this.formulaObject = formulaObj;\n this.fullPath = fullPath;\n this.varPath = fullPath.split('.');\n }\n\n evaluate(params = {}): number | string {\n // params contain variable / value pairs: If this object's variable matches\n // a varname found in the params, return the value.\n // eg: params = {x: 5,y:3}, varname = x, return 5\n // Objects and arrays are also supported:\n // e.g. params = {x: {y: 5}}, varname = x.y, return 5\n // or params = {x: [2,4,6]}, varname = x.2, return 6\n\n // Let's look in the value object first:\n let value = undefined;\n try {\n value = getProperty(params, this.varPath, this.fullPath);\n } catch (e) {\n // pass: getProperty has found nothing, which throws an error, but\n // we need to continue\n }\n if (value === undefined) {\n // Now have a look at the formula object:\n // This will throw an error if the property is not found:\n value = getProperty(this.formulaObject ?? {}, this.varPath, this.fullPath);\n }\n if (typeof value === 'function' || typeof value === 'object') {\n throw new Error(`Cannot use ${this.fullPath} as value: It contains a non-numerical value.`);\n }\n\n return value;\n }\n toString() {\n return `${this.varPath.join('.')}`;\n }\n}\n\nexport default class Formula {\n [key: string]: any;\n static Expression = Expression;\n static BracketExpression = BracketExpression;\n static PowerExpression = PowerExpression;\n static MultDivExpression = MultDivExpression;\n static PlusMinusExpression = PlusMinusExpression;\n static LogicalExpression = LogicalExpression;\n static ValueExpression = ValueExpression;\n static VariableExpression = VariableExpression;\n static FunctionExpression = FunctionExpression;\n static MATH_CONSTANTS = MATH_CONSTANTS;\n\n // Create a function blacklist:\n static functionBlacklist = Object.getOwnPropertyNames(Formula.prototype)\n .filter((prop) => Formula.prototype[prop] instanceof Function)\n .map((prop) => Formula.prototype[prop]);\n\n public formulaExpression: Expression | null;\n public options: FormulaOptions;\n public formulaStr: string;\n private _variables: string[];\n private _memory: { [key: string]: number | string };\n\n /**\n * Creates a new Formula instance\n *\n * Optional configuration can be set in the options object:\n *\n * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters\n *\n * @param {String} fStr The formula string, e.g. 'sin(x)/cos(y)'\n * @param {Object} options An options object. Supported options:\n * - memoization (bool): If true, results are stored and re-used when evaluate() is called with the same parameters\n * @param {Formula} parentFormula Internally used to build a Formula AST\n */\n constructor(fStr: string, options: FormulaOptions | null = {}) {\n this.formulaExpression = null;\n this.options = { ...{ memoization: false }, ...options };\n this.formulaStr = '';\n this._variables = [];\n this._memory = {};\n this.setFormula(fStr);\n }\n\n /**\n * Re-sets the given String and parses it to a formula expression. Can be used after initialization,\n * to re-use the Formula object.\n *\n * @param {String} formulaString The formula string to set/parse\n * @return {this} The Formula object (this)\n */\n setFormula(formulaString: string) {\n if (formulaString) {\n this.formulaExpression = null;\n this._variables = [];\n this._memory = {};\n this.formulaStr = formulaString;\n this.formulaExpression = this.parse(formulaString);\n }\n return this;\n }\n\n /**\n * Enable memoization: An expression is only evaluated once for the same input.\n * Further evaluations with the same input will return the in-memory stored result.\n */\n enableMemoization() {\n this.options.memoization = true;\n }\n\n /**\n * Disable in-memory memoization: each call to evaluate() is executed from scratch.\n */\n disableMemoization() {\n this.options.memoization = false;\n this._memory = {};\n }\n\n /**\n * Splits the given string by ',', makes sure the ',' is not within\n * a sub-expression\n * e.g.: str = \"x,pow(3,4)\" returns 2 elements: x and pow(3,4).\n */\n splitFunctionParams(toSplit: string) {\n // do not split on ',' within matching brackets.\n let pCount = 0,\n paramStr = '';\n const params = [];\n for (let chr of toSplit.split('')) {\n if (chr === ',' && pCount === 0) {\n // Found function param, save 'em\n params.push(paramStr);\n paramStr = '';\n } else if (chr === '(') {\n pCount++;\n paramStr += chr;\n } else if (chr === ')') {\n pCount--;\n paramStr += chr;\n if (pCount < 0) {\n throw new Error('ERROR: Too many closing parentheses!');\n }\n } else {\n paramStr += chr;\n }\n }\n if (pCount !== 0) {\n throw new Error('ERROR: Too many opening parentheses!');\n }\n if (paramStr.length > 0) {\n params.push(paramStr);\n }\n return params;\n }\n\n /**\n * Cleans the input string from unnecessary whitespace,\n * and replaces some known constants:\n */\n cleanupInputFormula(s: string) {\n const resParts: string[] = [];\n const srcParts = s.split('\"');\n srcParts.forEach((part, index) => {\n // skip parts marked as string\n if (index % 2 === 0) {\n part = part.replace(/[\\s]+/g, '');\n // surround known math constants with [], to parse them as named variables [xxx]:\n Object.keys(MATH_CONSTANTS).forEach((c) => {\n part = part.replace(new RegExp(`\\\\b${c}\\\\b`, 'g'), `[${c}]`);\n });\n }\n resParts.push(part);\n });\n return resParts.join('\"');\n }\n\n /**\n * Parses the given formula string by using a state machine into a single Expression object,\n * which represents an expression tree (aka AST).\n *\n * First, we split the string into 'expression': An expression can be:\n * - a number, e.g. '3.45'\n * - an unknown variable, e.g. 'x'\n * - a single char operator, such as '*','+' etc...\n * - a named variable, in [], e.g. [myvar]\n * - a function, such as sin(x)\n * - a parenthessed expression, containing other expressions\n *\n * We want to create an expression tree out of the string. This is done in 2 stages:\n * 1. form single expressions from the string: parse the string into known expression objects:\n * - numbers/[variables]/\"strings\"\n * - operators\n * - braces (with a sub-expression)\n * - functions (with sub-expressions (aka argument expressions))\n * This will lead to an array of expressions.\n * As an example:\n * \"2 + 3 * (4 + 3 ^ 5) * sin(PI * x)\" forms an array of the following expressions:\n * `[2, +, 3, *, bracketExpr(4,+,3,^,5), * , functionExpr(PI,*,x)]`\n * 2. From the raw expression array we form an expression tree by evaluating the expressions in the correct order:\n * e.g.:\n * the expression array `[2, +, 3, *, bracketExpr(4,+,3,^,5), * , functionExpr(PI,*,x)]` will be transformed into the expression tree:\n * ```\n * root expr: (+)\n * / \\\n * 2 (*)\n * / \\\n * (*) functionExpr(...)\n * / \\\n * 3 (bracket(..))\n * ```\n *\n * In the end, we have a single root expression node, which then can be evaluated in the evaluate() function.\n *\n * @param {String} str The formula string, e.g. '3*sin(PI/x)'\n * @returns {Expression} An expression object, representing the expression tree\n */\n parse(str: string) {\n // clean the input string first. spaces, math constant replacements etc.:\n str = this.cleanupInputFormula(str);\n // start recursive call to parse:\n return this._do_parse(str);\n }\n\n /**\n * @see parse(): this is the recursive parse function, without the clean string part.\n * @param {String} str\n * @returns {Expression} An expression object, representing the expression tree\n */\n _do_parse(str: string): Expression {\n let lastChar = str.length - 1,\n act = 0,\n state:\n | 'initial'\n | 'within-nr'\n | 'within-parentheses'\n | 'within-func-parentheses'\n | 'within-named-var'\n | 'within-string'\n | 'within-expr'\n | 'within-bracket'\n | 'within-func'\n | 'within-logical-operator'\n | 'invalid' = 'initial',\n expressions = [],\n char = '',\n tmp = '',\n funcName = null,\n pCount = 0,\n pStringDelimiter = '';\n\n while (act <= lastChar) {\n switch (state) {\n case 'initial':\n // None state, the beginning. Read a char and see what happens.\n char = str.charAt(act);\n if (char.match(/[0-9.]/)) {\n // found the beginning of a number, change state to \"within-number\"\n state = 'within-nr';\n tmp = '';\n act--;\n } else if (this.isOperator(char)) {\n // Simple operators. Note: '-' must be treaten specially,\n // it could be part of a number.\n // it MUST be part of a number if the last found expression\n // was an operator (or the beginning):\n if (char === '-') {\n if (expressions.length === 0 || this.isOperatorExpr(expressions[expressions.length - 1])) {\n state = 'within-nr';\n tmp = '-';\n break;\n }\n }\n\n // Found a simple operator, store as expression:\n if (act === lastChar || this.isOperatorExpr(expressions[expressions.length - 1])) {\n state = 'invalid'; // invalid to end with an operator, or have 2 operators in conjunction\n break;\n } else {\n expressions.push(\n Expression.createOperatorExpression(char, new Expression(), new Expression())\n );\n state = 'initial';\n }\n } else if ( [ '>', '<', '=', '!' ].includes(char)) {\n // found the beginning of a logical operator, change state to \"within-logical-operator\"\n if (act === lastChar) {\n state = 'invalid'; // invalid to end with a logical operator\n break;\n } else {\n state = 'within-logical-operator';\n tmp = char;\n }\n } else if (char === '(') {\n // left parenthes found, seems to be the beginning of a new sub-expression:\n state = 'within-parentheses';\n tmp = '';\n pCount = 0;\n } else if (char === '[') {\n // left named var separator char found, seems to be the beginning of a named var:\n state = 'within-named-var';\n tmp = '';\n } else if (char.match(/[\"']/)) {\n // left string separator char found\n state = 'within-string';\n pStringDelimiter = char;\n tmp = '';\n } else if (char.match(/[a-zA-Z]/)) {\n // multiple chars means it may be a function, else its a var which counts as own expression:\n if (act < lastChar && str.charAt(act + 1).match(/[a-zA-Z0-9_.]/)) {\n tmp = char;\n state = 'within-func';\n } else {\n // Single variable found:\n // We need to check some special considerations:\n // - If the last char was a number (e.g. 3x), we need to create a multiplication out of it (3*x)\n if (\n expressions.length > 0 &&\n expressions[expressions.length - 1] instanceof ValueExpression\n ) {\n expressions.push(\n Expression.createOperatorExpression('*', new Expression(), new Expression())\n );\n }\n expressions.push(new VariableExpression(char, this));\n this.registerVariable(char);\n state = 'initial';\n tmp = '';\n }\n }\n break;\n case 'within-nr':\n char = str.charAt(act);\n if (char.match(/[0-9.]/)) {\n //Still within number, store and continue\n tmp += char;\n if (act === lastChar) {\n expressions.push(new ValueExpression(tmp));\n state = 'initial';\n }\n } else {\n // Number finished on last round, so add as expression:\n if (tmp === '-') {\n // just a single '-' means: a variable could follow (e.g. like in 3*-x), we convert it to -1: (3*-1x)\n tmp = '-1';\n }\n expressions.push(new ValueExpression(tmp));\n tmp = '';\n state = 'initial';\n act--;\n }\n break;\n\n case 'within-func':\n char = str.charAt(act);\n if (char.match(/[a-zA-Z0-9_.]/)) {\n tmp += char;\n } else if (char === '(') {\n funcName = tmp;\n tmp = '';\n pCount = 0;\n state = 'within-func-parentheses';\n } else {\n throw new Error('Wrong character for function at position ' + act);\n }\n\n break;\n\n case 'within-named-var':\n char = str.charAt(act);\n if (char === ']') {\n // end of named var, create expression:\n expressions.push(new VariableExpression(tmp, this));\n this.registerVariable(tmp);\n tmp = '';\n state = 'initial';\n } else if (char.match(/[a-zA-Z0-9_.]/)) {\n tmp += char;\n } else {\n throw new Error('Character not allowed within named variable: ' + char);\n }\n break;\n\n case 'within-string':\n char = str.charAt(act);\n if (char === pStringDelimiter) {\n // end of string, create expression:\n expressions.push(new ValueExpression(tmp, 'string'));\n tmp = '';\n state = 'initial';\n pStringDelimiter = '';\n } else {\n tmp += char;\n }\n break;\n\n case 'within-parentheses':\n case 'within-func-parentheses':\n char = str.charAt(act);\n if (pStringDelimiter) {\n // If string is opened, then:\n if (char === pStringDelimiter) {\n // end of string\n pStringDelimiter = '';\n }\n // accumulate string chars\n tmp += char;\n } else if (char === ')') {\n //Check if this is the matching closing parenthesis.If not, just read ahead.\n if (pCount <= 0) {\n // Yes, we found the closing parenthesis, create new sub-expression:\n if (state === 'within-parentheses') {\n expressions.push(new BracketExpression(this._do_parse(tmp)));\n } else if (state === 'within-func-parentheses') {\n // Function found: create expressions from the inner argument\n // string, and create a function expression with it:\n let args = this.splitFunctionParams(tmp).map((a) => this._do_parse(a));\n expressions.push(new FunctionExpression(funcName, args, this));\n funcName = null;\n }\n state = 'initial';\n } else {\n pCount--;\n tmp += char;\n }\n } else if (char === '(') {\n // begin of a new sub-parenthesis, increase counter:\n pCount++;\n tmp += char;\n } else if (char.match(/[\"']/)) {\n // start of string\n pStringDelimiter = char;\n tmp += char;\n } else {\n // all other things are just added to the sub-expression:\n tmp += char;\n }\n break;\n \n case 'within-logical-operator':\n char = str.charAt(act);\n if (char === '=') {\n // the second char of a logical operator\n // can only be an equal sign\n tmp += char;\n act++;\n }\n // logical operator finished, create expression:\n expressions.push(Expression.createOperatorExpression(tmp, new Expression(), new Expression()));\n tmp = '';\n state = 'initial';\n act--;\n break;\n }\n act++;\n }\n\n if (state !== 'initial') {\n throw new Error('Could not parse formula: Syntax error.');\n }\n\n return this.buildExpressionTree(expressions);\n }\n\n /**\n * @see parse(): Builds an expression tree from the given expression array.\n * Builds a tree with a single root expression in the correct order of operator precedence.\n *\n * Note that the given expression objects are modified and linked.\n *\n * @param {*} expressions\n * @return {Expression} The root Expression of the built expression tree\n */\n buildExpressionTree(expressions: Expression[]): Expression {\n if (expressions.length < 1) {\n throw new Error('No expression given!');\n }\n const exprCopy = [...expressions];\n let idx = 0;\n let expr = null;\n // Replace all Power expressions with a partial tree:\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof PowerExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.base = exprCopy[idx - 1];\n expr.exponent = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Mult/Div expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof MultDivExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Plus/Minus expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof PlusMinusExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n // Replace all Logical expressions with a partial tree:\n idx = 0;\n expr = null;\n while (idx < exprCopy.length) {\n expr = exprCopy[idx];\n if (expr instanceof LogicalExpression) {\n if (idx === 0 || idx === exprCopy.length - 1) {\n throw new Error('Wrong operator position!');\n }\n expr.left = exprCopy[idx - 1];\n expr.right = exprCopy[idx + 1];\n exprCopy[idx - 1] = expr;\n exprCopy.splice(idx, 2);\n } else {\n idx++;\n }\n }\n\n if (exprCopy.length !== 1) {\n throw new Error('Could not parse formula: incorrect syntax?');\n }\n return exprCopy[0];\n }\n\n isOperator(char: string | null) {\n return typeof char === 'string' && char.match(/[+\\-*/^]/);\n }\n\n isOperatorExpr(expr: Expression) {\n return (\n expr instanceof PlusMinusExpression || expr instanceof MultDivExpression || expr instanceof PowerExpression || expr instanceof LogicalExpression\n );\n }\n\n registerVariable(varName: string) {\n if (this._variables.indexOf(varName) < 0) {\n this._variables.push(varName);\n }\n }\n\n getVariables() {\n return this._variables;\n }\n\n /**\n * Evaluates a Formula by delivering values for the Formula's variables.\n * E.g. if the formula is '3*x^2 + 2*x + 4', you should call `evaulate` as follows:\n *\n * evaluate({x:2}) --> Result: 20\n *\n * @param {ValueObject|Array} valueObj An object containing values for variables and (unknown) functions,\n * or an array of such objects: If an array is given, all objects are evaluated and the results\n * also returned as array.\n * @return {Number|String|(Number|String)[]} The evaluated result, or an array with results\n */\n evaluate(valueObj: ValueObject | ValueObject[]): number | string | (number | string)[] {\n // resolve multiple value objects recursively:\n if (valueObj instanceof Array) {\n return valueObj.map((v) => this.evaluate(v)) as (number | string)[];\n }\n let expr = this.getExpression();\n if (!(expr instanceof Expression)) {\n throw new Error('No expression set: Did you init the object with a Formula?');\n }\n if (this.options.memoization) {\n let res = this.resultFromMemory(valueObj);\n if (res !== null) {\n return res;\n } else {\n res = expr.evaluate({ ...MATH_CONSTANTS, ...valueObj });\n this.storeInMemory(valueObj, res);\n return res;\n }\n }\n return expr.evaluate({ ...MATH_CONSTANTS, ...valueObj });\n }\n\n hashValues(valueObj: ValueObject) {\n return JSON.stringify(valueObj);\n }\n\n resultFromMemory(valueObj: ValueObject): number | string | null {\n let key = this.hashValues(valueObj);\n let res = this._memory[key];\n if (res !== undefined) {\n return res;\n } else {\n return null;\n }\n }\n\n storeInMemory(valueObj: ValueObject, value: number | string) {\n this._memory[this.hashValues(valueObj)] = value;\n }\n\n getExpression() {\n return this.formulaExpression;\n }\n\n getExpressionString() {\n return this.formulaExpression ? this.formulaExpression.toString() : '';\n }\n\n static calc(formula: string, valueObj: ValueObject | null = null, options = {}) {\n valueObj = valueObj ?? {};\n return new Formula(formula, options).evaluate(valueObj);\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/spec/specs/exprTreeSpec.js b/spec/specs/exprTreeSpec.js index 010b32f..b679939 100644 --- a/spec/specs/exprTreeSpec.js +++ b/spec/specs/exprTreeSpec.js @@ -92,6 +92,12 @@ describe('Expression Tree tests', function () { expect(ret.evaluate()).toBe(1); }); + it('evaluates a logical expresstion with multiple operands correctly', () => { + const fObj = new Fparser('(x >= 0) * (x <= 1) * x * 100'); + let result = fObj.evaluate([{ x: 0.5 }, { x: 0.7 }, { x: 1.5 }, { x: -0.5 }, { x: -1.7 }]); + expect(result).toEqual([50, 70, 0, -0, -0]); + }); + it('parses parentheses correctly', () => { const formulaStr = '2*(3+4)/4x*(3-y)'; const f = new Fparser(); diff --git a/src/fparser.ts b/src/fparser.ts index 7b4d792..9a8ab0d 100644 --- a/src/fparser.ts +++ b/src/fparser.ts @@ -1,7 +1,7 @@ /** * JS Formula Parser * ------------------- - * (c) 2012-2023 Alexander Schenkel, alex@alexi.ch + * (c) 2012-2024 Alexander Schenkel, alex@alexi.ch * * JS Formula Parser takes a string, parses its mathmatical formula * and creates an evaluatable Formula object of it.