diff --git a/.github/workflows/develop-build.yml b/.github/workflows/develop-build.yml index b719591..a82c556 100644 --- a/.github/workflows/develop-build.yml +++ b/.github/workflows/develop-build.yml @@ -1,7 +1,7 @@ # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions -name: Develp Build CI +name: Develop Build CI on: push: diff --git a/README.md b/README.md index 04779d4..cc9dbd9 100644 --- a/README.md +++ b/README.md @@ -154,9 +154,25 @@ const res2 = fObj.evaluate(); ### Using strings +You can also pass strings as values or variable values (not only numbers): It is then in your responsibility to +provide a function that can make sense of the string: + +E.g. you can create a function that concats 2 values: + +```javascript +const fObj = new Formula('concat([var1], "Bar")'); +let result = fObj.evaluate({ var1: 'Foo', concat: (s1, s2) => s1 + s2 }); +``` + +Here, the result of the evaluation is again a string. + +Of course you can use strings to make decisions: Here, we provide a function `longer` that +returns the length of the longer of two strings, and calculates the remaining length: + ```javascript -const fObj = new Formula('compare( [p1], [p2], "<" )', { supportStrings: true }); -let result = fObj.evaluate({ x: 1.5 }); +const fObj = new Formula('20 - longer([var1], "Bar")'); +let result = fObj.evaluate({ var1: 'FooBar', longer: (s1, s2) => s1.length > s2.length ? s1.length : s2.length }); +// --> 14 ``` ### Re-use a Formula object @@ -307,6 +323,12 @@ edge cases. - Make sure you include dist/fparser.js if you are using it as a browser library. - Drop support for Bower, as there are more modern approaches (npm) for package dependency nowadays +## Contributors + +Thanks to all the additional contributors: + +- [LuigiPulcini](https://github.com/LuigiPulcini) for the Strings support + ## License Licensed under the MIT license, see LICENSE file. diff --git a/demopage/package-lock.json b/demopage/package-lock.json index cc23469..4b1362f 100644 --- a/demopage/package-lock.json +++ b/demopage/package-lock.json @@ -1,12 +1,12 @@ { "name": "fparser-demopage", - "version": "0.0.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fparser-demopage", - "version": "0.0.0", + "version": "0.2.0", "dependencies": { "bootstrap": "^5.2.3", "chart.js": "^4.2.1", diff --git a/dist/fparser.d.ts b/dist/fparser.d.ts index 044a425..e0be9ff 100644 --- a/dist/fparser.d.ts +++ b/dist/fparser.d.ts @@ -7,7 +7,7 @@ type FormulaOptions = { memoization?: boolean; }; type ValueObject = { - [key: string]: number | Function | ValueObject; + [key: string]: number | string | Function | ValueObject; }; declare class Expression { static createOperatorExpression(operator: string, left: Expression, right: Expression): PowerExpression | MultDivExpression | PlusMinusExpression; @@ -57,7 +57,7 @@ declare class FunctionExpression extends Expression { formulaObject: Formula | null; blacklisted: boolean | undefined; constructor(fn: string | null, argumentExpressions: Expression[], formulaObject?: Formula | null); - evaluate(params?: ValueObject): number; + evaluate(params?: ValueObject): number | string; toString(): string; isBlacklisted(): boolean; } @@ -66,7 +66,7 @@ declare class VariableExpression extends Expression { varPath: string[]; formulaObject: Formula | null; constructor(fullPath: string, formulaObj?: Formula | null); - evaluate(params?: {}): number; + evaluate(params?: {}): number | string; toString(): string; } export default class Formula { @@ -150,7 +150,7 @@ export default class Formula { * * 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 + * - numbers/[variables]/"strings" * - operators * - braces (with a sub-expression) * - functions (with sub-expressions (aka argument expressions)) @@ -206,14 +206,14 @@ export default class Formula { * @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|Array} The evaluated result, or an array with results + * @return {Number|String|(Number|String)[]} The evaluated result, or an array with results */ - evaluate(valueObj: ValueObject | ValueObject[]): number | number[] | string | string[]; + evaluate(valueObj: ValueObject | ValueObject[]): number | string | (number | string)[]; hashValues(valueObj: ValueObject): string; resultFromMemory(valueObj: ValueObject): number | string | null; storeInMemory(valueObj: ValueObject, value: number | string): void; getExpression(): Expression | null; getExpressionString(): string; - static calc(formula: string, valueObj?: ValueObject | null, options?: {}): string | number | string[] | number[]; + static calc(formula: string, valueObj?: ValueObject | null, options?: {}): string | number | (string | number)[]; } export {}; diff --git a/dist/fparser.js b/dist/fparser.js index 9055a53..83474ac 100644 --- a/dist/fparser.js +++ b/dist/fparser.js @@ -1,10 +1,7 @@ -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 = { +var P = Object.defineProperty; +var O = (h, r, e) => r in h ? P(h, r, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[r] = e; +var n = (h, r, e) => (O(h, typeof r != "symbol" ? r + "" : r, e), e); +const E = { PI: Math.PI, E: Math.E, LN2: Math.LN2, @@ -14,78 +11,66 @@ const MATH_CONSTANTS = { SQRT1_2: Math.SQRT1_2, SQRT2: Math.SQRT2 }; -class MathOperatorHelper { - static throwIfNotNumber(value) { - const valueType = typeof value; - if (valueType === "string") { +class p { + static throwIfNotNumber(r) { + if (typeof r === "string") throw new Error("Strings are not allowed in math operations"); - } } } -class MathFunctionHelper { - static throwIfNotNumber(value) { - const valueType = typeof value; - if (valueType === "string") { +class $ { + static throwIfNotNumber(r) { + if (typeof r === "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 (operator === "*" || operator === "/") { - return new MultDivExpression(operator, left, right); - } - if (operator === "+" || operator === "-") { - return new PlusMinusExpression(operator, left, right); - } - throw new Error(`Unknown operator: ${operator}`); - } - evaluate(params = {}) { +class u { + static createOperatorExpression(r, e, t) { + if (r === "^") + return new x(e, t); + if (r === "*" || r === "/") + return new b(r, e, t); + if (r === "+" || r === "-") + return new g(r, e, t); + throw new Error(`Unknown operator: ${r}`); + } + evaluate(r = {}) { throw new Error("Empty Expression - Must be defined in child classes"); } toString() { return ""; } } -class BracketExpression extends Expression { - constructor(expr) { +class N extends u { + constructor(e) { super(); - __publicField(this, "innerExpression"); - this.innerExpression = expr; - if (!(this.innerExpression instanceof Expression)) { + n(this, "innerExpression"); + if (this.innerExpression = e, !(this.innerExpression instanceof u)) throw new Error("No inner expression given for bracket expression"); - } } - evaluate(params = {}) { - return this.innerExpression.evaluate(params); + evaluate(e = {}) { + return this.innerExpression.evaluate(e); } toString() { return `(${this.innerExpression.toString()})`; } } -class ValueExpression extends Expression { - constructor(value, type = "number") { +class w extends u { + constructor(e, t = "number") { super(); - __publicField(this, "value"); - __publicField(this, "type"); - this.value = Number(value); - switch (type) { + n(this, "value"); + n(this, "type"); + switch (this.value = Number(e), t) { case "number": - this.value = Number(value); - if (isNaN(this.value)) { - throw new Error("Cannot parse number: " + value); - } + if (this.value = Number(e), isNaN(this.value)) + throw new Error("Cannot parse number: " + e); break; case "string": - this.value = String(value); + this.value = String(e); break; default: - throw new Error("Invalid value type: " + type); + throw new Error("Invalid value type: " + t); } - this.type = type; + this.type = t; } evaluate() { return this.value; @@ -95,197 +80,157 @@ class ValueExpression extends Expression { case "number": return String(this.value); case "string": - return String('"' + this.value + '"'); + return '"' + this.value + '"'; default: throw new Error("Invalid type"); } } } -class PlusMinusExpression extends Expression { - constructor(operator, left, right) { +class g extends u { + constructor(e, t, i) { 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); - } + 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 = i; + } + evaluate(e = {}) { + const t = this.left.evaluate(e), i = this.right.evaluate(e); + if (p.throwIfNotNumber(t), p.throwIfNotNumber(i), this.operator === "+") + return Number(t) + Number(i); + if (this.operator === "-") + return Number(t) - Number(i); 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) { +class b extends u { + constructor(e, t, i) { 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); - } + 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 = i; + } + evaluate(e = {}) { + const t = this.left.evaluate(e), i = this.right.evaluate(e); + if (p.throwIfNotNumber(t), p.throwIfNotNumber(i), this.operator === "*") + return Number(t) * Number(i); + if (this.operator === "/") + return Number(t) / Number(i); 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) { +class x extends u { + constructor(e, t) { 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)); + n(this, "base"); + n(this, "exponent"); + this.base = e, this.exponent = t; + } + evaluate(e = {}) { + const t = this.base.evaluate(e), i = this.exponent.evaluate(e); + return p.throwIfNotNumber(t), p.throwIfNotNumber(i), Math.pow(Number(t), Number(i)); } toString() { return `${this.base.toString()}^${this.exponent.toString()}`; } } -class FunctionExpression extends Expression { - constructor(fn, argumentExpressions, formulaObject = null) { +class S extends u { + constructor(e, t, i = 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)); + 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 = i, this.blacklisted = void 0; + } + evaluate(e = {}) { + var a; + e = e || {}; + const t = this.argumentExpressions.map((s) => s.evaluate(e)); try { - let fn = getProperty(params, this.varPath, this.fn); - if (fn instanceof Function) { - return fn.apply(this, paramValues); - } - } catch (e) { + let s = m(e, this.varPath, this.fn); + if (s instanceof Function) + return s.apply(this, t); + } catch (s) { } - let objFn; + let i; try { - objFn = getProperty((_a = this.formulaObject) != null ? _a : {}, this.varPath, this.fn); - } catch (e) { + i = m((a = this.formulaObject) != null ? a : {}, this.varPath, this.fn); + } catch (s) { } - if (this.formulaObject && objFn instanceof Function) { - if (this.isBlacklisted()) { + if (this.formulaObject && i instanceof Function) { + if (this.isBlacklisted()) throw new Error("Blacklisted function called: " + this.fn); - } - return objFn.apply(this.formulaObject, paramValues); + return i.apply(this.formulaObject, t); } 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) { + const s = m(Math, this.varPath, this.fn); + if (s instanceof Function) + return t.forEach((o) => { + $.throwIfNotNumber(o); + }), s.apply(this, t); + } catch (s) { } throw new Error("Function not found: " + this.fn); } toString() { - return `${this.fn}(${this.argumentExpressions.map((a) => a.toString()).join(", ")})`; + return `${this.fn}(${this.argumentExpressions.map((e) => e.toString()).join(", ")})`; } isBlacklisted() { - if (this.blacklisted === void 0) { - this.blacklisted = Formula.functionBlacklist.includes( - this.formulaObject ? this.formulaObject[this.fn] : null - ); - } - return this.blacklisted; + return this.blacklisted === void 0 && (this.blacklisted = y.functionBlacklist.includes( + this.formulaObject ? this.formulaObject[this.fn] : null + )), 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") { +function m(h, r, e) { + let t = h; + for (let i of r) { + if (typeof t != "object") + throw new Error(`Cannot evaluate ${i}, property not found (from path ${e})`); + if (t[i] === void 0) + throw new Error(`Cannot evaluate ${i}, property not found (from path ${e})`); + t = t[i]; + } + if (typeof t == "object") throw new Error("Invalid value"); - } - return curr; + return t; } -class VariableExpression extends Expression { - constructor(fullPath, formulaObj = null) { +class d extends u { + constructor(e, t = 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; + n(this, "fullPath"); + n(this, "varPath"); + n(this, "formulaObject"); + this.formulaObject = t, this.fullPath = e, this.varPath = e.split("."); + } + evaluate(e = {}) { + var i; + let t; try { - value = getProperty(params, this.varPath, this.fullPath); - } catch (e) { + t = m(e, this.varPath, this.fullPath); + } catch (a) { } - if (value === void 0) { - value = getProperty((_a = this.formulaObject) != null ? _a : {}, this.varPath, this.fullPath); - } - if (typeof value === "function" || typeof value === "object") { + if (t === void 0 && (t = m((i = this.formulaObject) != null ? i : {}, 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 value; + return t; } toString() { return `${this.varPath.join(".")}`; } } -const _Formula = class _Formula { +const l = class l { /** * Creates a new Formula instance * @@ -298,18 +243,13 @@ const _Formula = class _Formula { * - 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); + constructor(r, 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(r); } /** * Re-sets the given String and parses it to a formula expression. Can be used after initialization, @@ -318,80 +258,55 @@ const _Formula = class _Formula { * @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; + setFormula(r) { + return r && (this.formulaExpression = null, this._variables = [], this._memory = {}, this.formulaStr = r, this.formulaExpression = this.parse(r)), 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; + this.options.memoization = !0; } /** * Disable in-memory memoization: each call to evaluate() is executed from scratch. */ disableMemoization() { - this.options.memoization = false; - this._memory = {}; + this.options.memoization = !1, 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) { + splitFunctionParams(r) { + let e = 0, t = ""; + const i = []; + for (let a of r.split("")) + if (a === "," && e === 0) + i.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 { - paramStr += chr; - } - } - if (pCount !== 0) { + } else + t += a; + if (e !== 0) throw new Error("ERROR: Too many opening parentheses!"); - } - if (paramStr.length > 0) { - params.push(paramStr); - } - return params; + return t.length > 0 && i.push(t), i; } /** * 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('"'); + cleanupInputFormula(r) { + const e = []; + return r.split('"').forEach((i, a) => { + a % 2 === 0 && (i = i.replace(/[\s]+/g, ""), Object.keys(E).forEach((s) => { + i = i.replace(new RegExp(`\\b${s}\\b`, "g"), `[${s}]`); + })), e.push(i); + }), e.join('"'); } /** * Parses the given formula string by using a state machine into a single Expression object, @@ -407,7 +322,7 @@ const _Formula = class _Formula { * * 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 + * - numbers/[variables]/"strings" * - operators * - braces (with a sub-expression) * - functions (with sub-expressions (aka argument expressions)) @@ -433,164 +348,84 @@ const _Formula = class _Formula { * @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); + parse(r) { + return r = this.cleanupInputFormula(r), this._do_parse(r); } /** * @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) { + _do_parse(r) { + let e = r.length - 1, t = 0, i = "initial", a = [], s = "", o = "", v = null, f = 0, c = ""; + for (; t <= e; ) { + switch (i) { 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"; + if (s = r.charAt(t), s.match(/[0-9.]/)) + i = "within-nr", o = "", t--; + else if (this.isOperator(s)) { + if (s === "-" && (a.length === 0 || this.isOperatorExpr(a[a.length - 1]))) { + i = "within-nr", o = "-"; break; - } else { - expressions.push( - Expression.createOperatorExpression(char, new Expression(), new Expression()) - ); - state = "initial"; - } - } 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 = ""; } - } + if (t === e || this.isOperatorExpr(a[a.length - 1])) { + i = "invalid"; + break; + } else + a.push( + u.createOperatorExpression(s, new u(), new u()) + ), i = "initial"; + } else + s === "(" ? (i = "within-parentheses", o = "", f = 0) : s === "[" ? (i = "within-named-var", o = "") : s.match(/["']/) ? (i = "within-string", c = s, o = "") : s.match(/[a-zA-Z]/) && (t < e && r.charAt(t + 1).match(/[a-zA-Z0-9_.]/) ? (o = s, i = "within-func") : (a.length > 0 && a[a.length - 1] instanceof w && a.push( + u.createOperatorExpression("*", new u(), new u()) + ), a.push(new d(s, this)), this.registerVariable(s), i = "initial", o = "")); 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--; - } + s = r.charAt(t), s.match(/[0-9.]/) ? (o += s, t === e && (a.push(new w(o)), i = "initial")) : (o === "-" && (o = "-1"), a.push(new w(o)), o = "", i = "initial", t--); 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); - } + if (s = r.charAt(t), s.match(/[a-zA-Z0-9_.]/)) + o += s; + else if (s === "(") + v = o, o = "", f = 0, i = "within-func-parentheses"; + else + throw new Error("Wrong character for function at position " + t); 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); - } + if (s = r.charAt(t), s === "]") + a.push(new d(o, this)), this.registerVariable(o), o = "", i = "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": - char = str.charAt(act); - if (char === pStringDelimiter) { - expressions.push(new ValueExpression(tmp, "string")); - tmp = ""; - state = "initial"; - pStringDelimiter = ""; - } else { - tmp += char; - } + s = r.charAt(t), s === c ? (a.push(new w(o, "string")), o = "", i = "initial", c = "") : o += s; 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; + if (s = r.charAt(t), c) + s === c && (c = ""), o += s; + else if (s === ")") + if (f <= 0) { + if (i === "within-parentheses") + a.push(new N(this._do_parse(o))); + else if (i === "within-func-parentheses") { + let M = this.splitFunctionParams(o).map((k) => this._do_parse(k)); + a.push(new S(v, M, this)), v = null; } - state = "initial"; - } else { - pCount--; - tmp += char; - } - } else if (char === "(") { - pCount++; - tmp += char; - } else if (char.match(/["']/)) { - pStringDelimiter = char; - tmp += char; - } else { - tmp += char; - } + i = "initial"; + } else + f--, o += s; + else + s === "(" ? (f++, o += s) : (s.match(/["']/) && (c = s), o += s); break; } - act++; + t++; } - if (state !== "initial") { + if (i !== "initial") throw new Error("Could not parse formula: Syntax error."); - } - return this.buildExpressionTree(expressions); + return this.buildExpressionTree(a); } /** * @see parse(): Builds an expression tree from the given expression array. @@ -601,74 +436,44 @@ const _Formula = class _Formula { * @param {*} expressions * @return {Expression} The root Expression of the built expression tree */ - buildExpressionTree(expressions) { - if (expressions.length < 1) { + buildExpressionTree(r) { + if (r.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) { + const e = [...r]; + let t = 0, i = null; + for (; t < e.length; ) + if (i = e[t], i instanceof x) { + if (t === 0 || t === e.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) { + i.base = e[t - 1], i.exponent = e[t + 1], e[t - 1] = i, e.splice(t, 2); + } else + t++; + for (t = 0, i = null; t < e.length; ) + if (i = e[t], i instanceof b) { + if (t === 0 || t === e.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) { + i.left = e[t - 1], i.right = e[t + 1], e[t - 1] = i, e.splice(t, 2); + } else + t++; + for (t = 0, i = null; t < e.length; ) + if (i = e[t], i instanceof g) { + if (t === 0 || t === e.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) { + i.left = e[t - 1], i.right = e[t + 1], e[t - 1] = i, e.splice(t, 2); + } else + t++; + if (e.length !== 1) throw new Error("Could not parse formula: incorrect syntax?"); - } - return exprCopy[0]; + return e[0]; } - isOperator(char) { - return typeof char === "string" && char.match(/[+\-*/^]/); + isOperator(r) { + return typeof r == "string" && r.match(/[+\-*/^]/); } - isOperatorExpr(expr) { - return expr instanceof PlusMinusExpression || expr instanceof MultDivExpression || expr instanceof PowerExpression; + isOperatorExpr(r) { + return r instanceof g || r instanceof b || r instanceof x; } - registerVariable(varName) { - if (this._variables.indexOf(varName) < 0) { - this._variables.push(varName); - } + registerVariable(r) { + this._variables.indexOf(r) < 0 && this._variables.push(r); } getVariables() { return this._variables; @@ -682,42 +487,29 @@ const _Formula = class _Formula { * @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|Array} The evaluated result, or an array with results + * @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)) { + evaluate(r) { + if (r instanceof Array) + return r.map((t) => this.evaluate(t)); + let e = this.getExpression(); + if (!(e instanceof u)) 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; + let t = this.resultFromMemory(r); + return t !== null || (t = e.evaluate({ ...E, ...r }), this.storeInMemory(r, t)), t; } + return e.evaluate({ ...E, ...r }); + } + hashValues(r) { + return JSON.stringify(r); + } + resultFromMemory(r) { + let e = this.hashValues(r), t = this._memory[e]; + return t !== void 0 ? t : null; } - storeInMemory(valueObj, value) { - this._memory[this.hashValues(valueObj)] = value; + storeInMemory(r, e) { + this._memory[this.hashValues(r)] = e; } getExpression() { return this.formulaExpression; @@ -725,24 +517,14 @@ const _Formula = class _Formula { getExpressionString() { return this.formulaExpression ? this.formulaExpression.toString() : ""; } - static calc(formula, valueObj = null, options = {}) { - valueObj = valueObj != null ? valueObj : {}; - return new _Formula(formula, options).evaluate(valueObj); + static calc(r, e = null, t = {}) { + return e = e != null ? e : {}, new l(r, t).evaluate(e); } }; -__publicField(_Formula, "Expression", Expression); -__publicField(_Formula, "BracketExpression", BracketExpression); -__publicField(_Formula, "PowerExpression", PowerExpression); -__publicField(_Formula, "MultDivExpression", MultDivExpression); -__publicField(_Formula, "PlusMinusExpression", PlusMinusExpression); -__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; +n(l, "Expression", u), n(l, "BracketExpression", N), n(l, "PowerExpression", x), n(l, "MultDivExpression", b), n(l, "PlusMinusExpression", g), n(l, "ValueExpression", w), n(l, "VariableExpression", d), n(l, "FunctionExpression", S), n(l, "MATH_CONSTANTS", E), // Create a function blacklist: +n(l, "functionBlacklist", Object.getOwnPropertyNames(l.prototype).filter((r) => l.prototype[r] instanceof Function).map((r) => l.prototype[r])); +let y = l; export { - Formula as default + y as default }; //# sourceMappingURL=fparser.js.map diff --git a/dist/fparser.js.map b/dist/fparser.js.map index ce11faa..598edda 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 | 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 (operator === '*' || operator === '/') {\n return new MultDivExpression(operator, left, right);\n }\n if (operator === '+' || operator === '-') {\n return new PlusMinusExpression(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}\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 {\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 | 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 = {}) {\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 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\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 | '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 (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 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 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\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|Array} The evaluated result, or an array with results\n */\n evaluate(valueObj: ValueObject | ValueObject[]): number | number[] | string | 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 +{"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 (operator === '*' || operator === '/') {\n return new MultDivExpression(operator, left, right);\n }\n if (operator === '+' || operator === '-') {\n return new PlusMinusExpression(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}\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 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 | '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 (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 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 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\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","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 diff --git a/dist/fparser.umd.cjs b/dist/fparser.umd.cjs index a7be1ce..4da1ea3 100644 --- a/dist/fparser.umd.cjs +++ b/dist/fparser.umd.cjs @@ -1,751 +1,2 @@ -(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 (operator === "*" || operator === "/") { - return new MultDivExpression(operator, left, right); - } - if (operator === "+" || operator === "-") { - return new PlusMinusExpression(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 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 - * - 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 (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; - } - 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++; - } - } - 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; - } - 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|Array} 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, "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; -}); +(function(c,h){typeof exports=="object"&&typeof module!="undefined"?module.exports=h():typeof define=="function"&&define.amd?define(h):(c=typeof globalThis!="undefined"?globalThis:c||self,c.Formula=h())})(this,function(){"use strict";var O=Object.defineProperty;var $=(c,h,f)=>h in c?O(c,h,{enumerable:!0,configurable:!0,writable:!0,value:f}):c[h]=f;var n=(c,h,f)=>($(c,typeof h!="symbol"?h+"":h,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 h{static throwIfNotNumber(r){if(typeof r==="string")throw new Error("Strings are not allowed in math operations")}}class f{static throwIfNotNumber(r){if(typeof r==="string")throw new Error("Strings are not allowed in math operations")}}class l{static createOperatorExpression(r,e,t){if(r==="^")return new d(e,t);if(r==="*"||r==="/")return new x(r,e,t);if(r==="+"||r==="-")return new b(r,e,t);throw new Error(`Unknown operator: ${r}`)}evaluate(r={}){throw new Error("Empty Expression - Must be defined in child classes")}toString(){return""}}class S extends l{constructor(e){super();n(this,"innerExpression");if(this.innerExpression=e,!(this.innerExpression instanceof l))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 l{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 b extends l{constructor(e,t,i){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=i}evaluate(e={}){const t=this.left.evaluate(e),i=this.right.evaluate(e);if(h.throwIfNotNumber(t),h.throwIfNotNumber(i),this.operator==="+")return Number(t)+Number(i);if(this.operator==="-")return Number(t)-Number(i);throw new Error("Unknown operator for PlusMinus expression")}toString(){return`${this.left.toString()} ${this.operator} ${this.right.toString()}`}}class x extends l{constructor(e,t,i){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=i}evaluate(e={}){const t=this.left.evaluate(e),i=this.right.evaluate(e);if(h.throwIfNotNumber(t),h.throwIfNotNumber(i),this.operator==="*")return Number(t)*Number(i);if(this.operator==="/")return Number(t)/Number(i);throw new Error("Unknown operator for MultDiv expression")}toString(){return`${this.left.toString()} ${this.operator} ${this.right.toString()}`}}class d extends l{constructor(e,t){super();n(this,"base");n(this,"exponent");this.base=e,this.exponent=t}evaluate(e={}){const t=this.base.evaluate(e),i=this.exponent.evaluate(e);return h.throwIfNotNumber(t),h.throwIfNotNumber(i),Math.pow(Number(t),Number(i))}toString(){return`${this.base.toString()}^${this.exponent.toString()}`}}class M extends l{constructor(e,t,i=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=i,this.blacklisted=void 0}evaluate(e={}){var a;e=e||{};const t=this.argumentExpressions.map(s=>s.evaluate(e));try{let s=E(e,this.varPath,this.fn);if(s instanceof Function)return s.apply(this,t)}catch(s){}let i;try{i=E((a=this.formulaObject)!=null?a:{},this.varPath,this.fn)}catch(s){}if(this.formulaObject&&i instanceof Function){if(this.isBlacklisted())throw new Error("Blacklisted function called: "+this.fn);return i.apply(this.formulaObject,t)}try{const s=E(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=v.functionBlacklist.includes(this.formulaObject?this.formulaObject[this.fn]:null)),this.blacklisted}}function E(p,r,e){let t=p;for(let i of r){if(typeof t!="object")throw new Error(`Cannot evaluate ${i}, property not found (from path ${e})`);if(t[i]===void 0)throw new Error(`Cannot evaluate ${i}, property not found (from path ${e})`);t=t[i]}if(typeof t=="object")throw new Error("Invalid value");return t}class y extends l{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 i;let t;try{t=E(e,this.varPath,this.fullPath)}catch(a){}if(t===void 0&&(t=E((i=this.formulaObject)!=null?i:{},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(r,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(r)}setFormula(r){return r&&(this.formulaExpression=null,this._variables=[],this._memory={},this.formulaStr=r,this.formulaExpression=this.parse(r)),this}enableMemoization(){this.options.memoization=!0}disableMemoization(){this.options.memoization=!1,this._memory={}}splitFunctionParams(r){let e=0,t="";const i=[];for(let a of r.split(""))if(a===","&&e===0)i.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&&i.push(t),i}cleanupInputFormula(r){const e=[];return r.split('"').forEach((i,a)=>{a%2===0&&(i=i.replace(/[\s]+/g,""),Object.keys(c).forEach(s=>{i=i.replace(new RegExp(`\\b${s}\\b`,"g"),`[${s}]`)})),e.push(i)}),e.join('"')}parse(r){return r=this.cleanupInputFormula(r),this._do_parse(r)}_do_parse(r){let e=r.length-1,t=0,i="initial",a=[],s="",o="",N=null,g=0,w="";for(;t<=e;){switch(i){case"initial":if(s=r.charAt(t),s.match(/[0-9.]/))i="within-nr",o="",t--;else if(this.isOperator(s)){if(s==="-"&&(a.length===0||this.isOperatorExpr(a[a.length-1]))){i="within-nr",o="-";break}if(t===e||this.isOperatorExpr(a[a.length-1])){i="invalid";break}else a.push(l.createOperatorExpression(s,new l,new l)),i="initial"}else s==="("?(i="within-parentheses",o="",g=0):s==="["?(i="within-named-var",o=""):s.match(/["']/)?(i="within-string",w=s,o=""):s.match(/[a-zA-Z]/)&&(t0&&a[a.length-1]instanceof m&&a.push(l.createOperatorExpression("*",new l,new l)),a.push(new y(s,this)),this.registerVariable(s),i="initial",o=""));break;case"within-nr":s=r.charAt(t),s.match(/[0-9.]/)?(o+=s,t===e&&(a.push(new m(o)),i="initial")):(o==="-"&&(o="-1"),a.push(new m(o)),o="",i="initial",t--);break;case"within-func":if(s=r.charAt(t),s.match(/[a-zA-Z0-9_.]/))o+=s;else if(s==="(")N=o,o="",g=0,i="within-func-parentheses";else throw new Error("Wrong character for function at position "+t);break;case"within-named-var":if(s=r.charAt(t),s==="]")a.push(new y(o,this)),this.registerVariable(o),o="",i="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=r.charAt(t),s===w?(a.push(new m(o,"string")),o="",i="initial",w=""):o+=s;break;case"within-parentheses":case"within-func-parentheses":if(s=r.charAt(t),w)s===w&&(w=""),o+=s;else if(s===")")if(g<=0){if(i==="within-parentheses")a.push(new S(this._do_parse(o)));else if(i==="within-func-parentheses"){let k=this.splitFunctionParams(o).map(P=>this._do_parse(P));a.push(new M(N,k,this)),N=null}i="initial"}else g--,o+=s;else s==="("?(g++,o+=s):(s.match(/["']/)&&(w=s),o+=s);break}t++}if(i!=="initial")throw new Error("Could not parse formula: Syntax error.");return this.buildExpressionTree(a)}buildExpressionTree(r){if(r.length<1)throw new Error("No expression given!");const e=[...r];let t=0,i=null;for(;tthis.evaluate(t));let e=this.getExpression();if(!(e instanceof l))throw new Error("No expression set: Did you init the object with a Formula?");if(this.options.memoization){let t=this.resultFromMemory(r);return t!==null||(t=e.evaluate({...c,...r}),this.storeInMemory(r,t)),t}return e.evaluate({...c,...r})}hashValues(r){return JSON.stringify(r)}resultFromMemory(r){let e=this.hashValues(r),t=this._memory[e];return t!==void 0?t:null}storeInMemory(r,e){this._memory[this.hashValues(r)]=e}getExpression(){return this.formulaExpression}getExpressionString(){return this.formulaExpression?this.formulaExpression.toString():""}static calc(r,e=null,t={}){return e=e!=null?e:{},new u(r,t).evaluate(e)}};n(u,"Expression",l),n(u,"BracketExpression",S),n(u,"PowerExpression",d),n(u,"MultDivExpression",x),n(u,"PlusMinusExpression",b),n(u,"ValueExpression",m),n(u,"VariableExpression",y),n(u,"FunctionExpression",M),n(u,"MATH_CONSTANTS",c),n(u,"functionBlacklist",Object.getOwnPropertyNames(u.prototype).filter(r=>u.prototype[r]instanceof Function).map(r=>u.prototype[r]));let v=u;return v}); //# sourceMappingURL=fparser.umd.cjs.map diff --git a/dist/fparser.umd.cjs.map b/dist/fparser.umd.cjs.map index 0e93714..d76c28b 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 | 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 (operator === '*' || operator === '/') {\n return new MultDivExpression(operator, left, right);\n }\n if (operator === '+' || operator === '-') {\n return new PlusMinusExpression(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}\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 {\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 | 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 = {}) {\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 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\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 | '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 (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 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 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\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|Array} The evaluated result, or an array with results\n */\n evaluate(valueObj: ValueObject | ValueObject[]): number | number[] | string | 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 +{"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 (operator === '*' || operator === '/') {\n return new MultDivExpression(operator, left, right);\n }\n if (operator === '+' || operator === '-') {\n return new PlusMinusExpression(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}\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 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 | '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 (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 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 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\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","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 diff --git a/spec/specs/stringBasicSpec.js b/spec/specs/stringBasicSpec.js index dc702b8..2e4f9fd 100644 --- a/spec/specs/stringBasicSpec.js +++ b/spec/specs/stringBasicSpec.js @@ -1,7 +1,7 @@ import Fparser from '../../dist/fparser.js'; describe('String basic feature', function () { - it('support parsing with single and double quotes', function () { + it('support parsing with double quotes', function () { expect(new Fparser('"foo"').evaluate()).toEqual('foo'); }); @@ -38,12 +38,6 @@ describe('String basic feature', function () { expect(new Fparser('myFnFoo([myVar])').evaluate({ myFnFoo: myFnFoo, myVar: 'bar' })).toEqual('foobar'); }); - // it('blocking math operator "*" of number and variable', function () { - // expect(new Fparser('2x').evaluate('2x', { x: 'foo' })).toThrowError( - // /Math operators required type of number: given is string/ - // ); - // }); - it('blocking math operator "+"', function () { const operations = ['"foo" + 123', '123 + "foo"', '"foo" + "bar"', '("foo") + 123', '123 + ("foo")']; @@ -83,4 +77,27 @@ describe('String basic feature', function () { expect(() => new Fparser(operation).evaluate()).toThrowError(/Strings are not allowed in math operations/); }); }); + + it('works with string parameters in a real example', function () { + const leftElseRight = (l, op, r) => { + switch (op) { + case '<': + return l < r ? l : r; + case '>': + return l > r ? l : r; + default: + throw new Error('Invalid operator: ' + op); + } + }; + let res = new Fparser('4*leftElseRight([var1], "<", [var2])').evaluate({ var1: 1, var2: 2, leftElseRight }); + expect(res).toEqual(4); + + res = new Fparser('4*leftElseRight([var1], ">", [var2])').evaluate({ var1: 1, var2: 2, leftElseRight }); + expect(res).toEqual(8); + }); + + it('evaluates a string result', function () { + let res = new Fparser('concat([var1], "Bar")').evaluate({ var1: 'Foo', concat: (s1, s2) => s1 + s2 }); + expect(res).toEqual('FooBar'); + }); }); diff --git a/src/fparser.d.ts b/src/fparser.d.ts index bf9d199..8a59438 100644 --- a/src/fparser.d.ts +++ b/src/fparser.d.ts @@ -10,14 +10,18 @@ type ValueObject = { [key: string]: number | Function | ValueObject; }; declare class Expression { - static createOperatorExpression(operator: string, left: Expression, right: Expression): PowerExpression | MultDivExpression | PlusMinusExpression; + static createOperatorExpression( + operator: string, + left: Expression, + right: Expression + ): PowerExpression | MultDivExpression | PlusMinusExpression; evaluate(params?: ValueObject): number | string; toString(): string; } declare class BracketExpression extends Expression { innerExpression: Expression; constructor(expr: Expression); - evaluate(params?: {}): number; + evaluate(params?: {}): number | string; toString(): string; } declare class ValueExpression extends Expression { @@ -55,7 +59,7 @@ declare class FunctionExpression extends Expression { formulaObject: Formula | null; blacklisted: boolean | undefined; constructor(fn: string | null, argumentExpressions: Expression[], formulaObject?: Formula | null); - evaluate(params?: ValueObject): number; + evaluate(params?: ValueObject): number | string; toString(): string; isBlacklisted(): boolean; } @@ -63,7 +67,7 @@ declare class VariableExpression extends Expression { fullPath: string; varPath: string[]; constructor(fullPath: string); - evaluate(params?: {}): number; + evaluate(params?: {}): number | string; toString(): string; } export default class Formula { @@ -203,14 +207,14 @@ export default class Formula { * @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|Array} The evaluated result, or an array with results + * @return {Number|String|(Number|String)[]} The evaluated result, or an array with results */ - evaluate(valueObj: ValueObject | ValueObject[]): number | number[]; + evaluate(valueObj: ValueObject | ValueObject[]): number | string | (number | string)[]; hashValues(valueObj: ValueObject): string; - resultFromMemory(valueObj: ValueObject): number | null; - storeInMemory(valueObj: ValueObject, value: number): void; + resultFromMemory(valueObj: ValueObject): number | string | null; + storeInMemory(valueObj: ValueObject, value: number | string): void; getExpression(): Expression | null; getExpressionString(): string; - static calc(formula: string, valueObj?: ValueObject | null, options?: {}): number | number[]; + static calc(formula: string, valueObj?: ValueObject | null, options?: {}): number | string | (number | string)[]; } export {}; diff --git a/src/fparser.ts b/src/fparser.ts index 2cd09ad..1d2d348 100644 --- a/src/fparser.ts +++ b/src/fparser.ts @@ -42,11 +42,11 @@ type FormulaOptions = { }; type ValueObject = { - [key: string]: number | Function | ValueObject; + [key: string]: number | string | Function | ValueObject; }; class MathOperatorHelper { - static throwIfNotNumber(value: number | string ) { + static throwIfNotNumber(value: number | string) { const valueType = typeof value; if (valueType === 'string') { throw new Error('Strings are not allowed in math operations'); @@ -55,7 +55,7 @@ class MathOperatorHelper { } class MathFunctionHelper { - static throwIfNotNumber(value: number | string ) { + static throwIfNotNumber(value: number | string) { const valueType = typeof value; if (valueType === 'string') { throw new Error('Strings are not allowed in math operations'); @@ -248,7 +248,7 @@ class FunctionExpression extends Expression { this.blacklisted = undefined; } - evaluate(params: ValueObject = {}): number { + evaluate(params: ValueObject = {}): number | string { params = params || {}; const paramValues = this.argumentExpressions.map((a) => a.evaluate(params)); @@ -288,7 +288,7 @@ class FunctionExpression extends Expression { paramValues.forEach((paramValue) => { MathFunctionHelper.throwIfNotNumber(paramValue); }); - + return mathFn.apply(this, paramValues); } } catch (e) { @@ -315,7 +315,7 @@ class FunctionExpression extends Expression { } function getProperty(object: ValueObject, path: string[], fullPath: string) { - let curr: number | Function | ValueObject = object; + let curr: number | string | Function | ValueObject = object; for (let propName of path) { if (typeof curr !== 'object') { throw new Error(`Cannot evaluate ${propName}, property not found (from path ${fullPath})`); @@ -345,7 +345,7 @@ class VariableExpression extends Expression { this.varPath = fullPath.split('.'); } - evaluate(params = {}) { + evaluate(params = {}): number | string { // params contain variable / value pairs: If this object's variable matches // a varname found in the params, return the value. // eg: params = {x: 5,y:3}, varname = x, return 5 @@ -527,7 +527,7 @@ export default class Formula { * * 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 + * - numbers/[variables]/"strings" * - operators * - braces (with a sub-expression) * - functions (with sub-expressions (aka argument expressions)) @@ -877,12 +877,12 @@ export default class Formula { * @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|Array} The evaluated result, or an array with results + * @return {Number|String|(Number|String)[]} The evaluated result, or an array with results */ - evaluate(valueObj: ValueObject | ValueObject[]): number | number[] | string | string[] { + evaluate(valueObj: ValueObject | ValueObject[]): number | string | (number | string)[] { // resolve multiple value objects recursively: if (valueObj instanceof Array) { - return valueObj.map((v) => this.evaluate(v)) as number[] | string[]; + return valueObj.map((v) => this.evaluate(v)) as (number | string)[]; } let expr = this.getExpression(); if (!(expr instanceof Expression)) {