{"id":18486827,"url":"https://github.com/bylexus/fparse","last_synced_at":"2025-10-22T20:07:43.737Z","repository":{"id":15994565,"uuid":"18737786","full_name":"bylexus/fparse","owner":"bylexus","description":"A JavaScript Formula Parser","archived":false,"fork":false,"pushed_at":"2024-08-31T09:17:46.000Z","size":1039,"stargazers_count":89,"open_issues_count":4,"forks_count":15,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-14T06:32:09.927Z","etag":null,"topics":["formula","formulaparser","javascript-formula-parser","javascript-math","mathematical-formulas","parser"],"latest_commit_sha":null,"homepage":"http://fparser.alexi.ch/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bylexus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-04-13T19:30:08.000Z","updated_at":"2024-09-25T05:40:24.000Z","dependencies_parsed_at":"2024-09-27T03:36:36.078Z","dependency_job_id":"561b49ce-f2e1-4797-ae8a-96f06256279a","html_url":"https://github.com/bylexus/fparse","commit_stats":{"total_commits":52,"total_committers":4,"mean_commits":13.0,"dds":0.5192307692307692,"last_synced_commit":"f9c058c65da6d836f81488e6fe687696fd25defd"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bylexus%2Ffparse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bylexus%2Ffparse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bylexus%2Ffparse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bylexus%2Ffparse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bylexus","download_url":"https://codeload.github.com/bylexus/fparse/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247912806,"owners_count":21017049,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["formula","formulaparser","javascript-formula-parser","javascript-math","mathematical-formulas","parser"],"created_at":"2024-11-06T12:49:52.155Z","updated_at":"2025-10-22T20:07:43.659Z","avatar_url":"https://github.com/bylexus.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# fparser\n\n\u003e A JavaScript Formula Parser\n\n![Master Build](https://github.com/bylexus/fparse/actions/workflows/build.yml/badge.svg)\n![Develop Build](https://github.com/bylexus/fparse/actions/workflows/build.yml/badge.svg?branch=develop)\n\nfparser provides a Formula class that parses strings containing mathematical formulas (e.g. `x*sin(PI*x/2)`) into an evaluationable object.\nOne can then provide values for all unknown variables / functions and evaluate a numeric value from the formula.\n\nFor an example application, see https://fparser.alexi.ch/.\n\n## Features\n\nParses a mathematical formula from a string. Known expressions:\n\n-   _Numbers_ in the form [-]digits[.digits], e.g. \"-133.2945\"\n-   _simple operators_: '+','-','\\*','/', '^' expanded in correct order\n-   _logical operators_: '\u003c','\u003c=','\u003e','\u003e=', '=', '!=', which evaluate to 1 or 0. Useful for implementing conditional logic\n-   _parentheses_ '(', ')' for grouping (e.g. \"5\\*(3+2)\")\n-   all _JavaScript Math object functions_ (e.g. \"sin(3.14)\")\n-   all _JavaScript Math constants_ like PI, E\n-   the use of _own functions_\n-   the use of single-char _variables_ (like '2x')\n-   the use of named variables (like '2\\*[myVar]')\n-   the use of strings as function arguments (like 'concat(\"Size: \", 2, \" mm\")')\n-   the use of strings as variables (like 'concat(\"Size: \", 2, \" \", [unit])')\n-   the use of path named variables and functions (like '2\\*[myVar.property.innerProperty]')\n-   _memoization_: store already evaluated results for faster re-calcs\n-   use it in Web pages, as ES6 module or as NodeJS module\n-   Example:\u003cbr /\u003e \u003ccode\u003e-1*(sin(2^x)/(PI*x))\\*cos(x)\u003c/code\u003e\n\n## Usage\n\nInclude directly in your web page:\n\n```html\n\u003c!-- Within a web page: Load the fparser library: --\u003e\n\u003cscript src=\"dist/fparser.js\"\u003e\u003c/script\u003e\n\u003cscript\u003econst f = new Formula('x+3');\u003c/script\u003e\n```\n\nInstall it from npmjs.org:\n\n```shell\n# Install it using npm:\n$ npm install --save fparser\n```\n\nThen use as ES6 module (recommended):\n\n```javascript\nimport Formula from 'fparser';\n```\n\nor use it as UMD module:\n\n```javascript\nconst Formula = require('fparser');\n```\n\n... and finally use it:\n\n```javascript\n// 1. Create a Formula object instance by passing a formula string:\nconst fObj = new Formula('2^x');\n\n// 2. evaluate the formula, delivering a value object for each unknown entity:\nlet result = fObj.evaluate({ x: 3 }); // result = 8\n\n// or deliver multiple value objects to return multiple results:\nlet results = fObj.evaluate([{ x: 2 }, { x: 4 }, { x: 8 }]); // results = [4,16,256]\n\n// You can also directly evaluate a value if you only need a one-shot result:\nlet result = Formula.calc('2^x', { x: 3 }); // result = 8\nlet results = Formula.calc('2^x', [{ x: 2 }, { x: 4 }, { x: 8 }]); // results = [4,16,256]\n```\n\n## More options\n\n### Using multiple variables\n\n```javascript\nconst fObj = new Formula('a*x^2 + b*x + c');\n\n// Just pass a value object containing a value for each unknown variable:\nlet result = fObj.evaluate({ a: 2, b: -1, c: 3, x: 3 }); // result = 18\n```\n\n### Using named variables\n\nInstead of single-char variables (like `2x+y`), you can also use named variables in brackets:\n\n```javascript\nconst fObj = new Formula('2*[var1] + sin([var2]+PI)');\n\n// Just pass a value object containing a value for each named variable:\nlet result = fObj.evaluate({ var1: 5, var2: 0.7 });\n```\n\nThe 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\nbracket syntax.\n\n### Using named object path variables\n\nNamed variables in brackets can also describe an object property path:\n\n```javascript\nconst fObj = new Formula('2*[var1.propertyA] + 3*[var2.propertyB.propertyC]');\n\n// Just pass a value object containing a value for each named variable:\nlet result = fObj.evaluate({ var1: { propertyA: 3 }, var2: { propertyB: { propertyC: 9 } } });\n```\n\nThis even works for array values: Instead of the property name, use a 0-based index in an array:\n\n```javascript\n// var2.propertyB is an array, so we can use an index for the 3rd entry of propertyB:\nconst fObj = new Formula('2*[var1.propertyA] + 3*[var2.propertyB.2]');\nlet result = fObj.evaluate({ var1: { propertyA: 3 }, var2: { propertyB: [2, 4, 6] } });\n```\n\n### Using user-defined functions\n\n```javascript\nconst fObj = new Formula('sin(inverse(x))');\n\n//Define the function(s) on the Formula object, then use it multiple times:\nfObj.inverse = (value) =\u003e 1/value;\nlet results = fObj.evaluate({x: 1,x:2,x:3});\n\n// Or pass it in the value object, and OVERRIDE an existing function:\nlet result = fObj.evaluate({\n\tx: 2/Math.PI,\n\tinverse: (value) =\u003e  (-1*value)\n});\n\nIf defined in the value object AND on the formula object, the Value object has the precedence\n```\n\nFunctions also support the object path syntax:\n\n```javascript\n// in an evaluate() value object:\nconst fObj = new Formula('sin(lib.inverse(x))');\nconst res = fObj.evaluate({\n\tlib: { inverse: (value) =\u003e 1/value }\n});\n\n// or set it on the Formula instance:\nconst fObj2 = new Formula('sin(lib.inverse(x))');\nfObj2.lib = { inverse: (value) =\u003e 1/value };\nconst res2 = fObj.evaluate();\n```\n\n### Using strings\n\nYou can also pass strings as values or variable values (not only numbers): It is then in your responsibility to\nprovide a function that can make sense of the string:\n\nE.g. you can create a function that concats 2 values:\n\n```javascript\nconst fObj = new Formula('concat([var1], \"Bar\")');\nlet result = fObj.evaluate({ var1: 'Foo', concat: (s1, s2) =\u003e s1 + s2 });\n```\n\nHere, the result of the evaluation is again a string.\n\nOf course you can use strings to make decisions: Here, we provide a function `longer` that\nreturns the length of the longer of two strings, and calculates the remaining length:\n\n```javascript\nconst fObj = new Formula('20 - longer([var1], \"Bar\")');\nlet result = fObj.evaluate({ var1: 'FooBar', longer: (s1, s2) =\u003e s1.length \u003e s2.length ? s1.length : s2.length });\n// --\u003e 14\n```\n\n### Using of logical operators\n\nLogical operators allow for conditional logic. The result of the evaluation is always `0` (expression is false) or `1` (expression is true).\n\nExample:\n\nCalculate a percentage value based on a variable `x`, but only if `x` is between 0 and 1:\n\n```javascript\nconst fObj = new Formula('x \u003e= 0 * x \u003c= 1 * x * 100');\nlet result = fObj.evaluate([{ x: 0.5 }, { x: 0.7 }, { x: 1.5 },  { x: -0.5 }, { x: -1.7 }]);\n// --\u003e [50, 70, 0, 0, 0]\n```\n\nThis could be used to simulate or \"shortcut\" comparison functions. The same could be achieved with a user-definded function:\n\n```javascript\nconst fObj = new Formula('withinOne(x) * 100');\nfObj.withinOne = (x) =\u003e (x \u003e= 0 \u0026\u0026 x \u003c= 1 ? x : 0);\nlet result = fObj.evaluate([{ x: 0.5 }, { x: 0.7 }, { x: 1.5 },  { x: -0.5 }, { x: -1.7 }]);\n// --\u003e [50, 70, 0, 0, 0]\n```\n\n### Conditional evaluation\n\nThe previous chapter introduced logical operators. This can be used to implement a conditional function, or `if` function:\n\nExample: Kids get a 50% discount on a price if they are under 18:\n\n```javascript\nconst fObj = new Formula('ifElse([age] \u003c 18, [price]*0.5, [price])');\nfObj.ifElse = (predicate, trueValue, falseValue) =\u003e (predicate ? trueValue : falseValue);\nconst res = fObj.evaluate([{ price: 100, age: 17 }, { price: 100, age: 20 }]);\n// --\u003e res = [50, 100]\n```\n\n\n### Re-use a Formula object\n\nYou can instantiate a Formula object without formula, and set it later, and even re-use the existing object:\n\n```javascript\nconst fObj = new Formula();\n// ...\nfObj.setFormula('2*x^2 + 5*x + 3');\nlet res = fObj.evaluate({ x: 3 });\n// ...\nfObj.setFormula('x*y');\nres = fObj.evaluate({ x: 2, y: 4 });\n```\n\n### Memoization\n\nTo avoid re-calculation of already evaluated results, the formula parser object supports **memoization**:\nit stores already evaluated results for given expression parameters.\n\nExample:\n\n```javascript\nconst fObj = new Formula('x * y', { memoization: true });\nlet res1 = fObj.evaluate({ x: 2, y: 3 }); // 6, evaluated by calculating x*y\nlet res2 = fObj.evaluate({ x: 2, y: 3 }); // 6, from memory\n```\n\nYou can enable / disable memoization on the object:\n\n```javascript\nconst fObj = new Formula('x * y');\nlet res1 = fObj.evaluate({ x: 2, y: 3 }); // 6, evaluated by calculating x*y\nfObj.enableMemoization();\nlet res2 = fObj.evaluate({ x: 2, y: 3 }); // 6, evaluated by calculating x*y\nlet res3 = fObj.evaluate({ x: 2, y: 3 }); // 6, from memory\n```\n\n### Blacklisted functions\n\nThe `Formula` class blacklists its internal functions like `evaluate`, `parse` etc. You cannot create a formula that calls an internal `Formula`\nfunction:\n\n```javascript\n// Internal functions cannot be used in formulas:\nconst fObj = new Formula('evaluate(x)');\nfObj.evaluate({ x: 5 }); // throws an Error\n\n// This also counts for function aliases / references to internal functions:\nconst fObj = new Formula('ex(x)');\nfObj.ex = fObj.evaluate;\nfObj.evaluate({ x: 5 }); // still throws an Error: ex is an alias of evaluate\n```\n\nThe `Formula` object keeps a function reference of all blacklisted functions in the `Formula.functionBlacklist` array.\n\nYou can get a list of all blacklisted functions:\n\n```javascript\nlet blacklistNames = Formula.functionBlacklist.map((f) =\u003e f.name));\n```\n\nOr you can check if a specific function is in the blacklist:\n\n```javascript\nfObj = new Formula('x * y');\n// fObj.evaluate is a Function pointer to an internal, blacklisted function:\nFormula.functionBlacklist.includes(fObj.evaluate);\n```\n\nIf you want to provide your own function for a blacklisted internal function,\nprovide it with the `evaluate` function:\n\n```javascript\nfObj = new Formula('evaluate(x * y)');\nfObj.evaluate({ x: 1, y: 2, evaluate: (x, y) =\u003e x + y });\n```\n\nNow, `evaluate` in your formula uses your own version of this function.\n\n### Get all used variables\n\n```javascript\n// Get all used variables in the order of their appereance:\nconst f4 = new Formula('x*sin(PI*y) + y / (2-x*[var1]) + [var2]');\nconsole.log(f4.getVariables()); // ['x','y','var1','var2']\n```\n\n### Get the parsed formula string\n\nAfter parsing, get the formula string as parsed:\n\n```javascript\n// Get all used variables in the order of their appereance:\nconst f = new Formula('x      * (  y  +    9 )');\nconsole.log(f.getExpressionString()); // 'x * (y + 9)'\n```\n\n## Changelog\n\n### 3.1.0\n\n- [Feature] Adding Logical Operators `\u003c`, `\u003e`, `\u003c=`, `\u003e=`, `=`, `!=`\n\n### 3.0.1\n\n- [Bugfix] Fixing `main` entry in `package.json`: The 3.0.0 build could not be used as ES 6 module import with the non-existing main entry.\n\n### 3.0.0\n\nThis is a long-wanted \"migrate to typescript and modernize build infrastrucure\" release. \nIt introduces some *few* breaking changes, which hopefully are simple to adapt in existing code, or does not affect end users at all (I hope).\n\n- [Breaking]: new build system (vitejs instead of webpack)\n- [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`.\n- [Breaking]: An empty formula now throws an Error when parsed.\n- [Breaking]: `VariableExpression` class now needs Formula instance in constructor. This should not affect any end-user, but I did not test all edge cases.\n- [Change]: Migrating source code to TypeScript. This should not affect end-users.\n- [Feature]: Variables and functions now both support object paths (e.g. `obj.fn(3*[obj.value])`)\n\n### 2.1.0\n\n- [Breaking]: Blacklisting internal functions: You cannot use internal functions as formula function anymore.\n- [Feature]: Supporting object paths as variable values (e.g. `3*[obj1.property1.innerProperty]`), thanks to [SamStonehouse](https://github.com/SamStonehouse)\n- [Change]: Updated build infrastructure: upped versions of build tools\n\n### 2.0.2\n\n-   Fixing Issue #22: If the formula started with a single negate variable (e.g. `-z*t`), the parser got confused.\n\n### 2.0.0\n\nThis release is a complete re-vamp, see below. it **should** be completely backward compatible to the 1.x versions, but I did not test all\nedge cases.\n\n-   Switched to MIT license\n-   complete refactoring of the parsing and evaluating part: The parser now creates an Expression Tree (AST) that saves extra time while evaluating - Evaluation now only traverses the AST, which is much faster.\n-   added `getExpressionString()` function to get a formatted string from the formula\n-   adding support for memoization: store already evaluated results\n-   Switched bundler to webpack\n-   fixed some parser bugs\n\n### 1.4.0\n\n-   Adding support for named variables (`2x + [var1]`)\n-   switched testing to chromium runner instead of PhantomJS\n\n### 1.3.0\n\n-   modernized library: The source is now ES6 code, and transpiled in a dist ES5+ library.\n-   Make sure you include dist/fparser.js if you are using it as a browser library.\n-   Drop support for Bower, as there are more modern approaches (npm) for package dependency nowadays\n\n## Contributors\n\nThanks to all the additional contributors:\n\n- [LuigiPulcini](https://github.com/LuigiPulcini) for:\n  - the Strings support\n  - the Logical Operator support\n\n## TODOs, Whishlist\n\n* [ ] support for double- and single quote strings (now: only double quotes)\n* [ ] make parser state names via enum, instead of error-prone strings\n* [ ] Implement standard logic functions:\n\t* [ ] `and(...args)`: if all given arguments are trueish (\u003e 0), then the last arg is returned as value\n\t* [ ] `or(...args)`: the first trueish (\u003e 0) arg is returned as value\n\t* [ ] `ifElse(predicate, trueValue, falseValue)`: returns the trueValue if the predicate is trueish (\u003e 0), else the falseValue is returned\n* [ ] Refactor / rebuild parser:\n  * separate tokenize step\n  * then use Djikstra's Shunting Yard algorithm to convert the Inifix notation to Postfix, which is\n    way simpler to execute (See https://en.wikipedia.org/wiki/Shunting_yard_algorithm)\n\n## License\n\nLicensed under the MIT license, see LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbylexus%2Ffparse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbylexus%2Ffparse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbylexus%2Ffparse/lists"}