Comparing version 2.1.0 to 3.0.0
@@ -1,2 +0,487 @@ | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Formula=e():t.Formula=e()}(this,(()=>{return t={91:function(t,e){var r,n;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=function(r){"use strict";function n(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&o(t,e)}function o(t,e){return o=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},o(t,e)}function i(t){var e=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var r,n=a(t);if(e){var o=a(this).constructor;r=Reflect.construct(n,arguments,o)}else r=n.apply(this,arguments);return function(t,e){if(e&&("object"===u(e)||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(this,r)}}function a(t){return a=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},a(t)}function u(t){return u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},u(t)}function s(t){return function(t){if(Array.isArray(t))return c(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||l(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function l(t,e){if(t){if("string"==typeof t)return c(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?c(t,e):void 0}}function c(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);r<e;r++)n[r]=t[r];return n}function f(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function h(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?f(Object(r),!0).forEach((function(e){var n,o,i;n=t,o=e,i=r[e],(o=b(o))in n?Object.defineProperty(n,o,{value:i,enumerable:!0,configurable:!0,writable:!0}):n[o]=i})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):f(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function p(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function v(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,b(n.key),n)}}function y(t,e,r){return e&&v(t.prototype,e),r&&v(t,r),Object.defineProperty(t,"prototype",{writable:!1}),t}function b(t){var e=function(t,e){if("object"!==u(t)||null===t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var n=r.call(t,e||"default");if("object"!==u(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"===u(e)?e:String(e)}Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var m={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},g=r.default=function(){function t(e){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};p(this,t),this.formulaExpression=null,this.options=h(h({},{memoization:!1}),r),this._variables=[],this._memory={},this.setFormula(e)}return y(t,[{key:"setFormula",value:function(t){return t&&(this.formulaExpression=null,this._variables=[],this._memory={},this.formulaStr=t,this.formulaExpression=this.parse(t)),this}},{key:"enableMemoization",value:function(){this.options.memoization=!0}},{key:"disableMemoization",value:function(){this.options.memoization=!1,this._memory={}}},{key:"splitFunctionParams",value:function(t){var e,r=0,n="",o=[],i=function(t,e){var r="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!r){if(Array.isArray(t)||(r=l(t))||e&&t&&"number"==typeof t.length){r&&(t=r);var n=0,o=function(){};return{s:o,n:function(){return n>=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){r=r.call(t)},n:function(){var t=r.next();return a=t.done,t},e:function(t){u=!0,i=t},f:function(){try{a||null==r.return||r.return()}finally{if(u)throw i}}}}(t.split(""));try{for(i.s();!(e=i.n()).done;){var a=e.value;if(","===a&&0===r)o.push(n),n="";else if("("===a)r++,n+=a;else if(")"===a){if(n+=a,--r<0)throw new Error("ERROR: Too many closing parentheses!")}else n+=a}}catch(t){i.e(t)}finally{i.f()}if(0!==r)throw new Error("ERROR: Too many opening parentheses!");return n.length>0&&o.push(n),o}},{key:"cleanupInputString",value:function(t){return t=t.replace(/\s+/g,""),Object.keys(m).forEach((function(e){t=t.replace(new RegExp("\\b".concat(e,"\\b"),"g"),"[".concat(e,"]"))})),t}},{key:"parse",value:function(t){return t=this.cleanupInputString(t),this._do_parse(t)}},{key:"_do_parse",value:function(t){for(var e=this,r=t.length-1,n=0,o=0,i=[],a="",u="",s=null,l=0;n<=r;){switch(o){case 0:if((a=t.charAt(n)).match(/[0-9.]/))o="within-nr",u="",n--;else if(this.isOperator(a)){if("-"===a&&(0===i.length||this.isOperatorExpr(i[i.length-1]))){o="within-nr",u="-";break}if(n===r||this.isOperatorExpr(i[i.length-1])){o=-1;break}i.push(d.createOperatorExpression(a)),o=0}else"("===a?(o="within-parentheses",u="",l=0):"["===a?(o="within-named-var",u=""):a.match(/[a-zA-Z]/)&&(n<r&&t.charAt(n+1).match(/[a-zA-Z0-9_]/)?(u=a,o="within-func"):(i.length>0&&i[i.length-1]instanceof E&&i.push(d.createOperatorExpression("*")),i.push(new j(a)),this.registerVariable(a),o=0,u=""));break;case"within-nr":(a=t.charAt(n)).match(/[0-9.]/)?(u+=a,n===r&&(i.push(new E(u)),o=0)):("-"===u&&(u=-1),i.push(new E(u)),u="",o=0,n--);break;case"within-func":if((a=t.charAt(n)).match(/[a-zA-Z0-9_]/))u+=a;else{if("("!==a)throw new Error("Wrong character for function at position "+n);s=u,u="",l=0,o="within-func-parentheses"}break;case"within-named-var":if("]"===(a=t.charAt(n)))i.push(new j(u)),this.registerVariable(u),u="",o=0;else{if(!a.match(/[a-zA-Z0-9_.]/))throw new Error("Character not allowed within named variable: "+a);u+=a}break;case"within-parentheses":case"within-func-parentheses":if(")"===(a=t.charAt(n)))if(l<=0){if("within-parentheses"===o)i.push(new w(this._do_parse(u)));else if("within-func-parentheses"===o){var c=this.splitFunctionParams(u).map((function(t){return e._do_parse(t)}));i.push(new S(s,c,this)),s=null}o=0}else l--,u+=a;else"("===a?(l++,u+=a):u+=a}n++}if(0!==o)throw new Error("Could not parse formula: Syntax error.");return this.buildExpressionTree(i)}},{key:"buildExpressionTree",value:function(t){if(t.length<1)return null;for(var e=s(t),r=0,n=null;r<e.length;)if((n=e[r])instanceof k){if(0===r||r===e.length-1)throw new Error("Wrong operator position!");n.base=e[r-1],n.exponent=e[r+1],e[r-1]=n,e.splice(r,2)}else r++;for(r=0,n=null;r<e.length;)if((n=e[r])instanceof x){if(0===r||r===e.length-1)throw new Error("Wrong operator position!");n.left=e[r-1],n.right=e[r+1],e[r-1]=n,e.splice(r,2)}else r++;for(r=0,n=null;r<e.length;)if((n=e[r])instanceof O){if(0===r||r===e.length-1)throw new Error("Wrong operator position!");n.left=e[r-1],n.right=e[r+1],e[r-1]=n,e.splice(r,2)}else r++;if(1!==e.length)throw new Error("Could not parse formula: incorrect syntax?");return e[0]}},{key:"isOperator",value:function(t){return"string"==typeof t&&t.match(/[+\-*/^]/)}},{key:"isOperatorExpr",value:function(t){return t instanceof O||t instanceof x||t instanceof k}},{key:"registerVariable",value:function(t){this._variables.indexOf(t)<0&&this._variables.push(t)}},{key:"getVariables",value:function(){return this._variables}},{key:"evaluate",value:function(t){var e=this;if(t instanceof Array)return t.map((function(t){return e.evaluate(t)}));var r=this.getExpression();if(!(r instanceof d))throw new Error("No expression set: Did you init the object with a Formula?");if(this.options.memoization){var n=this.resultFromMemory(t);return null!==n||(n=r.evaluate(h(h({},m),t)),this.storeInMemory(t,n)),n}return r.evaluate(h(h({},m),t))}},{key:"hashValues",value:function(t){return JSON.stringify(t)}},{key:"resultFromMemory",value:function(t){var e=this.hashValues(t),r=this._memory[e];return void 0!==r?r:null}},{key:"storeInMemory",value:function(t,e){this._memory[this.hashValues(t)]=e}},{key:"getExpression",value:function(){return this.formulaExpression}},{key:"getExpressionString",value:function(){return this.formulaExpression?this.formulaExpression.toString():""}}],[{key:"calc",value:function(e,r){return r=r||{},new t(e,arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).evaluate(r)}}]),t}(),d=function(){function t(){p(this,t)}return y(t,[{key:"evaluate",value:function(){throw new Error("Must be defined in child classes")}},{key:"toString",value:function(){return""}}],[{key:"createOperatorExpression",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if("^"===t)return new k(t,e,r);if("*"===t||"/"===t)return new x(t,e,r);if("+"===t||"-"===t)return new O(t,e,r);throw new Error("Unknown operator: ".concat(t))}}]),t}(),w=function(t){n(r,t);var e=i(r);function r(t){var n;if(p(this,r),(n=e.call(this)).innerExpression=t,!(n.innerExpression instanceof d))throw new Error("No inner expression given for bracket expression");return n}return y(r,[{key:"evaluate",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return this.innerExpression.evaluate(t)}},{key:"toString",value:function(){return"(".concat(this.innerExpression.toString(),")")}}]),r}(d),E=function(t){n(r,t);var e=i(r);function r(t){var n;if(p(this,r),(n=e.call(this)).value=Number(t),isNaN(n.value))throw new Error("Cannot parse number: "+t);return n}return y(r,[{key:"evaluate",value:function(){return this.value}},{key:"toString",value:function(){return String(this.value)}}]),r}(d),O=function(t){n(r,t);var e=i(r);function r(t){var n,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(p(this,r),n=e.call(this),!["+","-"].includes(t))throw new Error("Operator not allowed in Plus/Minus expression: ".concat(t));return n.operator=t,n.left=o,n.right=i,n}return y(r,[{key:"evaluate",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if("+"===this.operator)return this.left.evaluate(t)+this.right.evaluate(t);if("-"===this.operator)return this.left.evaluate(t)-this.right.evaluate(t);throw new Error("Unknown operator for PlusMinus expression")}},{key:"toString",value:function(){return"".concat(this.left.toString()," ").concat(this.operator," ").concat(this.right.toString())}}]),r}(d),x=function(t){n(r,t);var e=i(r);function r(t){var n,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(p(this,r),n=e.call(this),!["*","/"].includes(t))throw new Error("Operator not allowed in Multiply/Division expression: ".concat(t));return n.operator=t,n.left=o,n.right=i,n}return y(r,[{key:"evaluate",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if("*"===this.operator)return this.left.evaluate(t)*this.right.evaluate(t);if("/"===this.operator)return this.left.evaluate(t)/this.right.evaluate(t);throw new Error("Unknown operator for MultDiv expression")}},{key:"toString",value:function(){return"".concat(this.left.toString()," ").concat(this.operator," ").concat(this.right.toString())}}]),r}(d),k=function(t){n(r,t);var e=i(r);function r(){var t,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return p(this,r),(t=e.call(this)).base=n,t.exponent=o,t}return y(r,[{key:"evaluate",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return Math.pow(this.base.evaluate(t),this.exponent.evaluate(t))}},{key:"toString",value:function(){return"".concat(this.base.toString(),"^").concat(this.exponent.toString())}}]),r}(d),S=function(t){n(r,t);var e=i(r);function r(t,n){var o,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;return p(this,r),(o=e.call(this)).fn=t,o.argumentExpressions=n||[],o.formulaObject=i,o.blacklisted=void 0,o}return y(r,[{key:"evaluate",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};t=t||{};var e=this.argumentExpressions.map((function(e){return e.evaluate(t)}));if(t[this.fn]instanceof Function)return t[this.fn].apply(this,e);if(this.formulaObject&&this.formulaObject[this.fn]instanceof Function){if(this.isBlacklisted())throw new Error("Blacklisted function called: "+this.fn);return this.formulaObject[this.fn].apply(this.formulaObject,e)}if(Math[this.fn]instanceof Function)return Math[this.fn].apply(this,e);throw new Error("Function not found: "+this.fn)}},{key:"toString",value:function(){return"".concat(this.fn,"(").concat(this.argumentExpressions.map((function(t){return t.toString()})).join(", "),")")}},{key:"isBlacklisted",value:function(){return void 0===this.blacklisted&&(this.blacklisted=g.functionBlacklist.includes(this.formulaObject?this.formulaObject[this.fn]:null)),this.blacklisted}}]),r}(d);var j=function(t){n(r,t);var e=i(r);function r(t){var n;return p(this,r),(n=e.call(this)).fullPath=t,n.varPath=t.split("."),n}return y(r,[{key:"evaluate",value:function(){return Number(function(t,e,r){for(var n=t,o=0;o<e.length;o++){if(void 0===n[e[o]])throw new Error("Cannot evaluate ".concat(e[o],", property not found (from path ").concat(r,")"));n=n[e[o]]}if("object"===u(n))throw new Error("Invalid value");return n}(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},this.varPath,this.fullPath))}},{key:"toString",value:function(){return"".concat(this.varPath.join("."))}}]),r}(d);g.Expression=d,g.BracketExpression=w,g.PowerExpression=k,g.MultDivExpression=x,g.PlusMinusExpression=O,g.ValueExpression=E,g.VariableExpression=j,g.FunctionExpression=S,g.MATH_CONSTANTS=m,g.functionBlacklist=Object.getOwnPropertyNames(g.prototype).filter((function(t){return g.prototype[t]instanceof Function})).map((function(t){return g.prototype[t]})),t.exports=e.default},void 0===(n=r.apply(e,[e]))||(t.exports=n)}},e={},function r(n){var o=e[n];if(void 0!==o)return o.exports;var i=e[n]={exports:{}};return t[n].call(i.exports,i,i.exports,r),i.exports}(91);var t,e})); | ||
//# sourceMappingURL=fparser.js.map | ||
var k = Object.defineProperty; | ||
var P = (u, i, t) => i in u ? k(u, i, { enumerable: !0, configurable: !0, writable: !0, value: t }) : u[i] = t; | ||
var n = (u, i, t) => (P(u, typeof i != "symbol" ? i + "" : i, t), t); | ||
const f = { | ||
PI: Math.PI, | ||
E: Math.E, | ||
LN2: Math.LN2, | ||
LN10: Math.LN10, | ||
LOG2E: Math.LOG2E, | ||
LOG10E: Math.LOG10E, | ||
SQRT1_2: Math.SQRT1_2, | ||
SQRT2: Math.SQRT2 | ||
}; | ||
class l { | ||
static createOperatorExpression(i, t, e) { | ||
if (i === "^") | ||
return new x(t, e); | ||
if (i === "*" || i === "/") | ||
return new g(i, t, e); | ||
if (i === "+" || i === "-") | ||
return new E(i, t, e); | ||
throw new Error(`Unknown operator: ${i}`); | ||
} | ||
evaluate(i = {}) { | ||
throw new Error("Empty Expression - Must be defined in child classes"); | ||
} | ||
toString() { | ||
return ""; | ||
} | ||
} | ||
class b extends l { | ||
constructor(t) { | ||
super(); | ||
n(this, "innerExpression"); | ||
if (this.innerExpression = t, !(this.innerExpression instanceof l)) | ||
throw new Error("No inner expression given for bracket expression"); | ||
} | ||
evaluate(t = {}) { | ||
return this.innerExpression.evaluate(t); | ||
} | ||
toString() { | ||
return `(${this.innerExpression.toString()})`; | ||
} | ||
} | ||
class w extends l { | ||
constructor(t) { | ||
super(); | ||
n(this, "value"); | ||
if (this.value = Number(t), isNaN(this.value)) | ||
throw new Error("Cannot parse number: " + t); | ||
} | ||
evaluate() { | ||
return this.value; | ||
} | ||
toString() { | ||
return String(this.value); | ||
} | ||
} | ||
class E extends l { | ||
constructor(t, e, r) { | ||
super(); | ||
n(this, "operator"); | ||
n(this, "left"); | ||
n(this, "right"); | ||
if (!["+", "-"].includes(t)) | ||
throw new Error(`Operator not allowed in Plus/Minus expression: ${t}`); | ||
this.operator = t, this.left = e, this.right = r; | ||
} | ||
evaluate(t = {}) { | ||
if (this.operator === "+") | ||
return this.left.evaluate(t) + this.right.evaluate(t); | ||
if (this.operator === "-") | ||
return this.left.evaluate(t) - this.right.evaluate(t); | ||
throw new Error("Unknown operator for PlusMinus expression"); | ||
} | ||
toString() { | ||
return `${this.left.toString()} ${this.operator} ${this.right.toString()}`; | ||
} | ||
} | ||
class g extends l { | ||
constructor(t, e, r) { | ||
super(); | ||
n(this, "operator"); | ||
n(this, "left"); | ||
n(this, "right"); | ||
if (!["*", "/"].includes(t)) | ||
throw new Error(`Operator not allowed in Multiply/Division expression: ${t}`); | ||
this.operator = t, this.left = e, this.right = r; | ||
} | ||
evaluate(t = {}) { | ||
if (this.operator === "*") | ||
return this.left.evaluate(t) * this.right.evaluate(t); | ||
if (this.operator === "/") | ||
return this.left.evaluate(t) / this.right.evaluate(t); | ||
throw new Error("Unknown operator for MultDiv expression"); | ||
} | ||
toString() { | ||
return `${this.left.toString()} ${this.operator} ${this.right.toString()}`; | ||
} | ||
} | ||
class x extends l { | ||
constructor(t, e) { | ||
super(); | ||
n(this, "base"); | ||
n(this, "exponent"); | ||
this.base = t, this.exponent = e; | ||
} | ||
evaluate(t = {}) { | ||
return Math.pow(this.base.evaluate(t), this.exponent.evaluate(t)); | ||
} | ||
toString() { | ||
return `${this.base.toString()}^${this.exponent.toString()}`; | ||
} | ||
} | ||
class y extends l { | ||
constructor(t, e, r = null) { | ||
super(); | ||
n(this, "fn"); | ||
n(this, "varPath"); | ||
n(this, "argumentExpressions"); | ||
n(this, "formulaObject"); | ||
n(this, "blacklisted"); | ||
this.fn = t != null ? t : "", this.varPath = this.fn.split("."), this.argumentExpressions = e || [], this.formulaObject = r, this.blacklisted = void 0; | ||
} | ||
evaluate(t = {}) { | ||
var o; | ||
t = t || {}; | ||
const e = this.argumentExpressions.map((s) => s.evaluate(t)); | ||
try { | ||
let s = c(t, this.varPath, this.fn); | ||
if (s instanceof Function) | ||
return s.apply(this, e); | ||
} catch (s) { | ||
} | ||
let r; | ||
try { | ||
r = c((o = this.formulaObject) != null ? o : {}, this.varPath, this.fn); | ||
} catch (s) { | ||
} | ||
if (this.formulaObject && r instanceof Function) { | ||
if (this.isBlacklisted()) | ||
throw new Error("Blacklisted function called: " + this.fn); | ||
return r.apply(this.formulaObject, e); | ||
} | ||
try { | ||
const s = c(Math, this.varPath, this.fn); | ||
if (s instanceof Function) | ||
return s.apply(this, e); | ||
} catch (s) { | ||
} | ||
throw new Error("Function not found: " + this.fn); | ||
} | ||
toString() { | ||
return `${this.fn}(${this.argumentExpressions.map((t) => t.toString()).join(", ")})`; | ||
} | ||
isBlacklisted() { | ||
return this.blacklisted === void 0 && (this.blacklisted = d.functionBlacklist.includes( | ||
this.formulaObject ? this.formulaObject[this.fn] : null | ||
)), this.blacklisted; | ||
} | ||
} | ||
function c(u, i, t) { | ||
let e = u; | ||
for (let r of i) { | ||
if (typeof e != "object") | ||
throw new Error(`Cannot evaluate ${r}, property not found (from path ${t})`); | ||
if (e[r] === void 0) | ||
throw new Error(`Cannot evaluate ${r}, property not found (from path ${t})`); | ||
e = e[r]; | ||
} | ||
if (typeof e == "object") | ||
throw new Error("Invalid value"); | ||
return e; | ||
} | ||
class v extends l { | ||
constructor(t, e = null) { | ||
super(); | ||
n(this, "fullPath"); | ||
n(this, "varPath"); | ||
n(this, "formulaObject"); | ||
this.formulaObject = e, this.fullPath = t, this.varPath = t.split("."); | ||
} | ||
evaluate(t = {}) { | ||
var r; | ||
let e; | ||
try { | ||
e = c(t, this.varPath, this.fullPath); | ||
} catch (o) { | ||
} | ||
if (e === void 0 && (e = c((r = this.formulaObject) != null ? r : {}, this.varPath, this.fullPath)), typeof e == "function" || typeof e == "object") | ||
throw new Error(`Cannot use ${this.fullPath} as value: It contains a non-numerical value.`); | ||
return Number(e); | ||
} | ||
toString() { | ||
return `${this.varPath.join(".")}`; | ||
} | ||
} | ||
const h = class h { | ||
/** | ||
* 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(i, t = {}) { | ||
n(this, "formulaExpression"); | ||
n(this, "options"); | ||
n(this, "formulaStr"); | ||
n(this, "_variables"); | ||
n(this, "_memory"); | ||
this.formulaExpression = null, this.options = { memoization: !1, ...t }, this.formulaStr = "", this._variables = [], this._memory = {}, this.setFormula(i); | ||
} | ||
/** | ||
* 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(i) { | ||
return i && (this.formulaExpression = null, this._variables = [], this._memory = {}, this.formulaStr = i, this.formulaExpression = this.parse(i)), this; | ||
} | ||
/** | ||
* Enable memoization: An expression is only evaluated once for the same input. | ||
* Further evaluations with the same input will return the in-memory stored result. | ||
*/ | ||
enableMemoization() { | ||
this.options.memoization = !0; | ||
} | ||
/** | ||
* Disable in-memory memoization: each call to evaluate() is executed from scratch. | ||
*/ | ||
disableMemoization() { | ||
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(i) { | ||
let t = 0, e = ""; | ||
const r = []; | ||
for (let o of i.split("")) | ||
if (o === "," && t === 0) | ||
r.push(e), e = ""; | ||
else if (o === "(") | ||
t++, e += o; | ||
else if (o === ")") { | ||
if (t--, e += o, t < 0) | ||
throw new Error("ERROR: Too many closing parentheses!"); | ||
} else | ||
e += o; | ||
if (t !== 0) | ||
throw new Error("ERROR: Too many opening parentheses!"); | ||
return e.length > 0 && r.push(e), r; | ||
} | ||
/** | ||
* Cleans the input string from unnecessary whitespace, | ||
* and replaces some known constants: | ||
*/ | ||
cleanupInputString(i) { | ||
return i = i.replace(/\s+/g, ""), Object.keys(f).forEach((t) => { | ||
i = i.replace(new RegExp(`\\b${t}\\b`, "g"), `[${t}]`); | ||
}), i; | ||
} | ||
/** | ||
* 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(i) { | ||
return i = this.cleanupInputString(i), this._do_parse(i); | ||
} | ||
/** | ||
* @see parse(): this is the recursive parse function, without the clean string part. | ||
* @param {String} str | ||
* @returns {Expression} An expression object, representing the expression tree | ||
*/ | ||
_do_parse(i) { | ||
let t = i.length - 1, e = 0, r = "initial", o = [], s = "", a = "", m = null, p = 0; | ||
for (; e <= t; ) { | ||
switch (r) { | ||
case "initial": | ||
if (s = i.charAt(e), s.match(/[0-9.]/)) | ||
r = "within-nr", a = "", e--; | ||
else if (this.isOperator(s)) { | ||
if (s === "-" && (o.length === 0 || this.isOperatorExpr(o[o.length - 1]))) { | ||
r = "within-nr", a = "-"; | ||
break; | ||
} | ||
if (e === t || this.isOperatorExpr(o[o.length - 1])) { | ||
r = "invalid"; | ||
break; | ||
} else | ||
o.push( | ||
l.createOperatorExpression(s, new l(), new l()) | ||
), r = "initial"; | ||
} else | ||
s === "(" ? (r = "within-parentheses", a = "", p = 0) : s === "[" ? (r = "within-named-var", a = "") : s.match(/[a-zA-Z]/) && (e < t && i.charAt(e + 1).match(/[a-zA-Z0-9_.]/) ? (a = s, r = "within-func") : (o.length > 0 && o[o.length - 1] instanceof w && o.push( | ||
l.createOperatorExpression("*", new l(), new l()) | ||
), o.push(new v(s, this)), this.registerVariable(s), r = "initial", a = "")); | ||
break; | ||
case "within-nr": | ||
s = i.charAt(e), s.match(/[0-9.]/) ? (a += s, e === t && (o.push(new w(a)), r = "initial")) : (a === "-" && (a = "-1"), o.push(new w(a)), a = "", r = "initial", e--); | ||
break; | ||
case "within-func": | ||
if (s = i.charAt(e), s.match(/[a-zA-Z0-9_.]/)) | ||
a += s; | ||
else if (s === "(") | ||
m = a, a = "", p = 0, r = "within-func-parentheses"; | ||
else | ||
throw new Error("Wrong character for function at position " + e); | ||
break; | ||
case "within-named-var": | ||
if (s = i.charAt(e), s === "]") | ||
o.push(new v(a, this)), this.registerVariable(a), a = "", r = "initial"; | ||
else if (s.match(/[a-zA-Z0-9_.]/)) | ||
a += s; | ||
else | ||
throw new Error("Character not allowed within named variable: " + s); | ||
break; | ||
case "within-parentheses": | ||
case "within-func-parentheses": | ||
if (s = i.charAt(e), s === ")") | ||
if (p <= 0) { | ||
if (r === "within-parentheses") | ||
o.push(new b(this._do_parse(a))); | ||
else if (r === "within-func-parentheses") { | ||
let S = this.splitFunctionParams(a).map((M) => this._do_parse(M)); | ||
o.push(new y(m, S, this)), m = null; | ||
} | ||
r = "initial"; | ||
} else | ||
p--, a += s; | ||
else | ||
s === "(" && p++, a += s; | ||
break; | ||
} | ||
e++; | ||
} | ||
if (r !== "initial") | ||
throw new Error("Could not parse formula: Syntax error."); | ||
return this.buildExpressionTree(o); | ||
} | ||
/** | ||
* @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(i) { | ||
if (i.length < 1) | ||
throw new Error("No expression given!"); | ||
const t = [...i]; | ||
let e = 0, r = null; | ||
for (; e < t.length; ) | ||
if (r = t[e], r instanceof x) { | ||
if (e === 0 || e === t.length - 1) | ||
throw new Error("Wrong operator position!"); | ||
r.base = t[e - 1], r.exponent = t[e + 1], t[e - 1] = r, t.splice(e, 2); | ||
} else | ||
e++; | ||
for (e = 0, r = null; e < t.length; ) | ||
if (r = t[e], r instanceof g) { | ||
if (e === 0 || e === t.length - 1) | ||
throw new Error("Wrong operator position!"); | ||
r.left = t[e - 1], r.right = t[e + 1], t[e - 1] = r, t.splice(e, 2); | ||
} else | ||
e++; | ||
for (e = 0, r = null; e < t.length; ) | ||
if (r = t[e], r instanceof E) { | ||
if (e === 0 || e === t.length - 1) | ||
throw new Error("Wrong operator position!"); | ||
r.left = t[e - 1], r.right = t[e + 1], t[e - 1] = r, t.splice(e, 2); | ||
} else | ||
e++; | ||
if (t.length !== 1) | ||
throw new Error("Could not parse formula: incorrect syntax?"); | ||
return t[0]; | ||
} | ||
isOperator(i) { | ||
return typeof i == "string" && i.match(/[+\-*/^]/); | ||
} | ||
isOperatorExpr(i) { | ||
return i instanceof E || i instanceof g || i instanceof x; | ||
} | ||
registerVariable(i) { | ||
this._variables.indexOf(i) < 0 && this._variables.push(i); | ||
} | ||
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<ValueObject>} 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<Number>} The evaluated result, or an array with results | ||
*/ | ||
evaluate(i) { | ||
if (i instanceof Array) | ||
return i.map((e) => this.evaluate(e)); | ||
let t = this.getExpression(); | ||
if (!(t instanceof l)) | ||
throw new Error("No expression set: Did you init the object with a Formula?"); | ||
if (this.options.memoization) { | ||
let e = this.resultFromMemory(i); | ||
return e !== null || (e = t.evaluate({ ...f, ...i }), this.storeInMemory(i, e)), e; | ||
} | ||
return t.evaluate({ ...f, ...i }); | ||
} | ||
hashValues(i) { | ||
return JSON.stringify(i); | ||
} | ||
resultFromMemory(i) { | ||
let t = this.hashValues(i), e = this._memory[t]; | ||
return e !== void 0 ? e : null; | ||
} | ||
storeInMemory(i, t) { | ||
this._memory[this.hashValues(i)] = t; | ||
} | ||
getExpression() { | ||
return this.formulaExpression; | ||
} | ||
getExpressionString() { | ||
return this.formulaExpression ? this.formulaExpression.toString() : ""; | ||
} | ||
static calc(i, t = null, e = {}) { | ||
return t = t != null ? t : {}, new h(i, e).evaluate(t); | ||
} | ||
}; | ||
n(h, "Expression", l), n(h, "BracketExpression", b), n(h, "PowerExpression", x), n(h, "MultDivExpression", g), n(h, "PlusMinusExpression", E), n(h, "ValueExpression", w), n(h, "VariableExpression", v), n(h, "FunctionExpression", y), n(h, "MATH_CONSTANTS", f), // Create a function blacklist: | ||
n(h, "functionBlacklist", Object.getOwnPropertyNames(h.prototype).filter((i) => h.prototype[i] instanceof Function).map((i) => h.prototype[i])); | ||
let d = h; | ||
export { | ||
d as default | ||
}; | ||
//# sourceMappingURL=fparser.js.map |
{ | ||
"name": "fparser", | ||
"version": "2.1.0", | ||
"version": "3.0.0", | ||
"description": "A Math Formula parser library for JavaScript", | ||
"main": "dist/fparser.js", | ||
"module": "src/fparser.js", | ||
"type": "module", | ||
"module": "src/fparser.ts", | ||
"files": [ | ||
"dist/" | ||
], | ||
"scripts": { | ||
@@ -12,5 +16,5 @@ "docker-image": "docker build --pull --target develop -t fparser .", | ||
"docker-test": "docker run --rm -ti -v \"$PWD\":/usr/src/app -w /usr/src/app fparse npm run test", | ||
"build-dev": "NODE_ENV=development webpack", | ||
"build-dev": "NODE_ENV=development tsc --noEmit && vite build --mode=development && tsc --emitDeclarationOnly --declaration", | ||
"build": "NODE_ENV=production tsc --noEmit && vite build --minify --mode=production && tsc --emitDeclarationOnly --declaration", | ||
"build-demopage-image": "docker build --pull -t fparser-demopage .", | ||
"build": "NODE_ENV=production webpack", | ||
"test": "NODE_ENV=development npm run build-dev && jasmine && karma start" | ||
@@ -39,6 +43,4 @@ }, | ||
"devDependencies": { | ||
"@babel/core": "^7.23.3", | ||
"@babel/preset-env": "^7.23.3", | ||
"babel-loader": "^9.1.3", | ||
"babel-plugin-add-module-exports": "^1.0.4", | ||
"typescript": "^5.2.2", | ||
"vite": "^5.0.0", | ||
"eslint": "^8.54.0", | ||
@@ -51,5 +53,3 @@ "eslint-config-prettier": "^9.0.0", | ||
"karma-jasmine": "^5.1.0", | ||
"prettier": "^3.1.0", | ||
"webpack": "^5.89.0", | ||
"webpack-cli": "^5.1.4" | ||
"prettier": "^3.1.0" | ||
}, | ||
@@ -56,0 +56,0 @@ "eslintConfig": { |
@@ -25,3 +25,3 @@ # fparser | ||
- the use of named variables (like '2\*[myVar]') | ||
- the use of path named variables (like '2\*[myVar.property.innerProperty]') | ||
- the use of path named variables and functions (like '2\*[myVar.property.innerProperty]') | ||
- _memoization_: store already evaluated results for faster re-calcs | ||
@@ -33,20 +33,32 @@ - use it in Web pages, as ES6 module or as NodeJS module | ||
Include directly in your web page: | ||
```html | ||
<!-- Within a web page: Load the fparser library: --> | ||
<script src="dist/fparser.js"></script> | ||
<script>const f = new Formula('x+3');</script> | ||
``` | ||
```javascript | ||
// As node module: | ||
Install: | ||
Install it from npmjs.org: | ||
```shell | ||
# Install it using npm: | ||
$ npm install --save fparser | ||
``` | ||
Use: | ||
const Formula = require('fparser'); | ||
Then use as ES6 module (recommended): | ||
or: | ||
```javascript | ||
import Formula from 'fparser'; | ||
``` | ||
or use it as UMD module: | ||
```javascript | ||
const Formula = require('fparser'); | ||
``` | ||
... and finally use it: | ||
```javascript | ||
// 1. Create a Formula object instance by passing a formula string: | ||
@@ -64,7 +76,2 @@ const fObj = new Formula('2^x'); | ||
let results = Formula.calc('2^x', [{ x: 2 }, { x: 4 }, { x: 8 }]); // results = [4,16,256] | ||
// Usage in NodeJS: | ||
const Formula = require('fparser'); | ||
const fObj = new Formula('2^x)'); | ||
// .... vice versa | ||
``` | ||
@@ -94,4 +101,7 @@ | ||
### Using named path variables | ||
The reason for the bracket syntax is the support of shortcut multiplication of single vars, e.g. `2xy` is a shorthand for `2*x*y`. As the parser cannot decide if `xy` means "the variable named `xy", or `calc x*y`, we had to introduce the | ||
bracket syntax. | ||
### Using named object path variables | ||
Named variables in brackets can also describe an object property path: | ||
@@ -106,3 +116,3 @@ | ||
This even works for array values: Instead of the property name, use an index in an array: | ||
This even works for array values: Instead of the property name, use a 0-based index in an array: | ||
@@ -133,2 +143,17 @@ ```javascript | ||
Functions also support the object path syntax: | ||
```javascript | ||
// in an evaluate() value object: | ||
const fObj = new Formula('sin(lib.inverse(x))'); | ||
const res = fObj.evaluate({ | ||
lib: { inverse: (value) => 1/value } | ||
}); | ||
// or set it on the Formula instance: | ||
const fObj2 = new Formula('sin(lib.inverse(x))'); | ||
fObj2.lib = { inverse: (value) => 1/value }; | ||
const res2 = fObj.evaluate(); | ||
``` | ||
### Re-use a Formula object | ||
@@ -233,2 +258,14 @@ | ||
### 3.0.0 | ||
This is a long-wanted "migrate to typescript and modernize build infrastrucure" release. | ||
It introduces some *few* breaking changes, which hopefully are simple to adapt in existing code, or does not affect end users at all (I hope). | ||
- [Breaking]: new build system (vitejs instead of webpack) | ||
- [Breaking]: UMD module version available as `dist/fparser.umd.js` instead of `dist/fparser.js`: If you need the UMD version, use `dist/fparser.umd.js` instead of `dist/fparser.js`. | ||
- [Breaking]: An empty formula now throws an Error when parsed. | ||
- [Breaking]: `VariableExpression` class now needs Formula instance in constructor. This should not affect any end-user, but I did not test all edge cases. | ||
- [Change]: Migrating source code to TypeScript. This should not affect end-users. | ||
- [Feature]: Variables and functions now both support object paths (e.g. `obj.fn(3*[obj.value])`) | ||
### 2.1.0 | ||
@@ -235,0 +272,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
10
300
0
Yes
136952
8
744
1