{"id":13991731,"url":"https://github.com/LesterLyu/fast-formula-parser","last_synced_at":"2025-07-22T14:31:55.459Z","repository":{"id":41081594,"uuid":"177144438","full_name":"LesterLyu/fast-formula-parser","owner":"LesterLyu","description":"Parse and evaluate MS Excel formula in javascript.","archived":false,"fork":false,"pushed_at":"2024-06-07T15:19:37.000Z","size":3078,"stargazers_count":464,"open_issues_count":17,"forks_count":64,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-11-29T02:14:41.130Z","etag":null,"topics":["evaluator","excel","excel-formula","excel-formulas","formula","formula-parser","interpreter","javascript","js","parser","spreadsheet"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/fast-formula-parser","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/LesterLyu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2019-03-22T13:24:34.000Z","updated_at":"2024-11-28T04:01:34.000Z","dependencies_parsed_at":"2022-07-21T11:32:57.039Z","dependency_job_id":"0bf10dec-1094-457f-8036-4cfda0f08d6e","html_url":"https://github.com/LesterLyu/fast-formula-parser","commit_stats":{"total_commits":291,"total_committers":7,"mean_commits":41.57142857142857,"dds":0.4467353951890034,"last_synced_commit":"5ef104f96be3d00e3c67130c0d1c44a272b9c363"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LesterLyu%2Ffast-formula-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LesterLyu%2Ffast-formula-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LesterLyu%2Ffast-formula-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LesterLyu%2Ffast-formula-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LesterLyu","download_url":"https://codeload.github.com/LesterLyu/fast-formula-parser/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227025065,"owners_count":17719214,"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":["evaluator","excel","excel-formula","excel-formulas","formula","formula-parser","interpreter","javascript","js","parser","spreadsheet"],"created_at":"2024-08-09T14:01:33.531Z","updated_at":"2024-11-29T12:30:39.461Z","avatar_url":"https://github.com/LesterLyu.png","language":"JavaScript","readme":"### Announcement\n\nThis very excellent project is now being managed as part of\n\n[![Logo](https://www.sheetxl.com/img/logo-color-text-135.png)](https://www.sheetxl.com)\n\n\n---\n![GitHub](https://img.shields.io/github/license/lesterlyu/fast-formula-parser)\n[![npm (tag)](https://img.shields.io/npm/v/fast-formula-parser/latest)](https://www.npmjs.com/package/fast-formula-parser)\n[![npm](https://img.shields.io/npm/dt/fast-formula-parser)](https://www.npmjs.com/package/fast-formula-parser)\n[![Coverage Status](https://coveralls.io/repos/github/LesterLyu/fast-formula-parser/badge.svg?branch=master)](https://coveralls.io/github/LesterLyu/fast-formula-parser?branch=master)\n## [A Fast Excel Formula Parser \u0026 Evaluator](https://github.com/LesterLyu/fast-formula-parser)\n\nA fast and reliable excel formula parser in javascript. Using **LL(1)** parser.\n\n### [Demo](https://lesterlyu.github.io/#/demo/fast-formula-parser)\n### [Documentation](https://lesterlyu.github.io/fast-formula-parser/index.html)\n### [Grammar Diagram](https://lesterlyu.github.io/fast-formula-parser/generated_diagrams.html)\n\n### [SheetXL Home Page](https://www.sheetxl.com)\n### Supports 280 Formulas\n```\nABS, ACOS, ACOSH, ACOT, ACOTH, ADDRESS, AND, ARABIC, AREAS, ASC, ASIN, ASINH, ATAN, ATAN2, ATANH, AVEDEV, AVERAGE, AVERAGEA, AVERAGEIF, BAHTTEXT, BASE, BESSELI, BESSELJ, BESSELK, BESSELY, BETA.DIST, BETA.INV, BIN2DEC, BIN2HEX, BIN2OCT, BINOM.DIST, BINOM.DIST.RANGE, BINOM.INV, BITAND, BITLSHIFT, BITOR,\nBITRSHIFT, BITXOR, CEILING, CEILING.MATH, CEILING.PRECISE, CHAR, CHISQ.DIST, CHISQ.DIST.RT, CHISQ.INV, CHISQ.INV.RT, CHISQ.TEST, CLEAN, CODE, COLUMN, COLUMNS, COMBIN, COMBINA, COMPLEX, CONCAT, CONCATENATE, CONFIDENCE.NORM, CONFIDENCE.T, CORREL, COS, COSH, COT, COTH, COUNT, COUNTIF, COVARIANCE.P,\nCOVARIANCE.S, CSC, CSCH, DATE, DATEDIF, DATEVALUE, DAY, DAYS, DAYS360, DBCS, DEC2BIN, DEC2HEX, DEC2OCT, DECIMAL, DEGREES, DELTA, DEVSQ, DOLLAR, EDATE, ENCODEURL, EOMONTH, ERF, ERFC, ERROR.TYPE, EVEN, EXACT, EXP, EXPON.DIST, F.DIST, F.DIST.RT, F.INV, F.INV.RT, F.TEST, FACT, FACTDOUBLE, FALSE, FIND, FINDB,\nFISHER, FISHERINV, FIXED, FLOOR, FLOOR.MATH, FLOOR.PRECISE, FORECAST, FORECAST.LINEAR, FREQUENCY, GAMMA, GAMMA.DIST, GAMMA.INV, GAMMALN, GAMMALN.PRECISE, GAUSS, GCD, GEOMEAN, GESTEP, GROWTH, HARMEAN, HEX2BIN, HEX2DEC, HEX2OCT, HLOOKUP, HOUR, HYPGEOM.DIST, IF, IFERROR, IFNA, IFS, IMABS, IMAGINARY, IMARGUMENT,\nIMCONJUGATE, IMCOS, IMCOSH, IMCOT, IMCSC, IMCSCH, IMDIV, IMEXP, IMLN, IMLOG10, IMLOG2, IMPOWER, IMPRODUCT, IMREAL, IMSEC, IMSECH, IMSIN, IMSINH, IMSQRT, IMSUB, IMSUM, IMTAN, INDEX, INT, INTERCEPT, ISBLANK, ISERR, ISERROR, ISEVEN, ISLOGICAL, ISNA, ISNONTEXT, ISNUMBER, ISO.CEILING, ISOWEEKNUM, ISREF, ISTEXT,\nKURT, LCM, LEFT, LEFTB, LN, LOG, LOG10, LOGNORM.DIST, LOGNORM.INV, LOWER, MDETERM, MID, MIDB, MINUTE, MMULT, MOD, MONTH, MROUND, MULTINOMIAL, MUNIT, N, NA, NEGBINOM.DIST, NETWORKDAYS, NETWORKDAYS.INTL, NORM.DIST, NORM.INV, NORM.S.DIST, NORM.S.INV, NOT, NOW, NUMBERVALUE, OCT2BIN, OCT2DEC, OCT2HEX, ODD, OR,\nPHI, PI, POISSON.DIST, POWER, PRODUCT, PROPER, QUOTIENT, RADIANS, RAND, RANDBETWEEN, REPLACE, REPLACEB, REPT, RIGHT, RIGHTB, ROMAN, ROUND, ROUNDDOWN, ROUNDUP, ROW, ROWS, SEARCH, SEARCHB, SEC, SECH, SECOND, SERIESSUM, SIGN, SIN, SINH, SQRT, SQRTPI, STANDARDIZE, SUM, SUMIF, SUMPRODUCT, SUMSQ, SUMX2MY2,\nSUMX2PY2, SUMXMY2, T, T.DIST, T.DIST.2T, T.DIST.RT, T.INV, T.INV.2T, TAN, TANH, TEXT, TIME, TIMEVALUE, TODAY, TRANSPOSE, TRIM, TRUE, TRUNC, TYPE, UNICHAR, UNICODE, UPPER, VLOOKUP, WEBSERVICE, WEEKDAY, WEEKNUM, WEIBULL.DIST, WORKDAY, WORKDAY.INTL, XOR, YEAR, YEARFRAC\n```\n### Size: 291KB Minified, 81KB Gzipped+Minified\n\n### Background\n\nInspired by [XLParser](https://github.com/spreadsheetlab/XLParser/blob/master/src/XLParser/ExcelFormulaGrammar.cs)\nand the paper [\"A Grammar for Spreadsheet Formulas Evaluated on Two Large Datasets\" by Efthimia Aivaloglou, David Hoepelman and Felienne Hermans](https://fenia266781730.files.wordpress.com/2019/01/07335408.pdf).\n\nNote: The grammar in my implementation is different from theirs. My implementation gets rid of ambiguities to boost the performance.\n\n### What is not supported:\n - [External reference](https://support.office.com/en-ie/article/create-an-external-reference-link-to-a-cell-range-in-another-workbook-c98d1803-dd75-4668-ac6a-d7cca2a9b95f)\n    - Anything with `[` and `]`\n - Ambiguous old styles\n    - Sheet name contains `:`, e.g. `SUM('1003:1856'!D6)`\n    - Sheet name with space that is not quoted, e.g. `I am a sheet!A1`\n - `SUM(Sheet2:Sheet3!A1:C3)`\n - You tell me\n\n### Performance\n  - The expected performance is at least 3x faster than the optimized [formula-parser](https://github.com/LesterLyu/formula-parser).\n\n### Dependency\n  - [Chevrotain](https://github.com/SAP/chevrotain) , thanks to this great parser building toolkit.\n\n### [Examples](https://github.com/LesterLyu/fast-formula-parser/blob/master/examples/example.js)\n - Install\n    ```sh\n    npm i fast-formula-parser\n    # or using yarn\n    yarn add fast-formula-parser\n    ```\n - Import\n    ```js\n    const FormulaParser = require('fast-formula-parser');\n    const {FormulaHelpers, Types, FormulaError, MAX_ROW, MAX_COLUMN} = FormulaParser;\n    // or\n    import FormulaParser, {FormulaHelpers, Types, FormulaError, MAX_ROW, MAX_COLUMN} from 'fast-formula-parser';\n    ```\n    UMD minified build is also provides:\n    ```html\n    \u003cscript src=\"/node_modules/fast-formula-parser/build/parser.min.js\"\u003e \u003c/script\u003e\n    ```\n  - Basic Usage\n    ```js\n    const data = [\n      // A  B  C\n        [1, 2, 3], // row 1\n        [4, 5, 6]  // row 2\n    ];\n\n    const parser = new FormulaParser({\n\n        // External functions, this will override internal functions with same name\n        functions: {\n            CHAR: (number) =\u003e {\n                number = FormulaHelpers.accept(number, Types.NUMBER);\n                if (number \u003e 255 || number \u003c 1)\n                    throw FormulaError.VALUE;\n                return String.fromCharCode(number);\n            }\n        },\n\n        // Variable used in formulas (defined name)\n        // Should only return range reference or cell reference\n        onVariable: (name, sheetName) =\u003e {\n            // If it is a range reference (A1:B2)\n            return {\n                sheet: 'sheet name',\n                from: {\n                    row: 1,\n                    col: 1,\n                },\n                to: {\n                    row: 2,\n                    col: 2,\n                }\n            };\n            // If it is a cell reference (A1)\n            return {\n                sheet: 'sheet name',\n                row: 1,\n                col: 1\n            }\n        },\n\n        // retrieve cell value\n        onCell: ({sheet, row, col}) =\u003e {\n            // using 1-based index\n            // return the cell value, see possible types in next section.\n            return data[row - 1][col - 1];\n        },\n\n        // retrieve range values\n        onRange: (ref) =\u003e {\n            // using 1-based index\n            // Be careful when ref.to.col is MAX_COLUMN or ref.to.row is MAX_ROW, this will result in\n            // unnecessary loops in this approach.\n            const arr = [];\n            for (let row = ref.from.row; row \u003c= ref.to.row; row++) {\n                const innerArr = [];\n                if (data[row - 1]) {\n                    for (let col = ref.from.col; col \u003c= ref.to.col; col++) {\n                        innerArr.push(data[row - 1][col - 1]);\n                    }\n                }\n                arr.push(innerArr);\n            }\n            return arr;\n        }\n    });\n\n    // position is required for evaluating certain formulas, e.g. ROW()\n    const position = {row: 1, col: 1, sheet: 'Sheet1'};\n\n    // parse the formula, the position of where the formula is located is required\n    // for some functions.\n    console.log(parser.parse('SUM(A:C)', position));\n    // print 21\n\n    // you can specify if the return value can be an array, this is helpful when dealing\n    // with an array formula\n    console.log(parser.parse('MMULT({1,5;2,3},{1,2;2,3})', position, true));\n    // print [ [ 11, 17 ], [ 8, 13 ] ]\n    ```\n\n  - Custom Async functions\n    \u003e Remember to use `await parser.parseAsync(...)` instead of `parser.parse(...)`\n    ```js\n    const position = {row: 1, col: 1, sheet: 'Sheet1'};\n    const parser = new FormulaParser({\n        onCell: ref =\u003e {\n            return 1;\n        },\n        functions: {\n            DEMO_FUNC: async () =\u003e {\n                return [[1,2,3],[4,5,6]];\n            }\n        },\n    });\n    console.log(await parser.parseAsync('A1 + IMPORT_CSV())', position));\n    // print 2\n    console.log(await parser.parseAsync('SUM(DEMO_FUNC(), 1))', position));\n    // print 22\n    ```\n  - Custom function requires parser context (e.g. location of the formula)\n    ```js\n    const position = {row: 1, col: 1, sheet: 'Sheet1'};\n    const parser = new FormulaParser({\n        functionsNeedContext: {\n            // the first argument is the context\n            // the followings are the arguments passed to the function\n            ROW_PLUS_COL: (context, ...args) =\u003e {\n                 return context.position.row + context.position.col;\n            }\n        },\n    });\n    console.log(await parser.parseAsync('SUM(ROW_PLUS_COL(), 1)', position));\n    // print 3\n    ```\n\n  - Parse Formula Dependency\n    \u003e This is helpful for building `dependency graph/tree`.\n    ```js\n    import {DepParser} from 'fast-formula-parser';\n    const depParser = new DepParser({\n        // onVariable is the only thing you need provide if the formula contains variables\n        onVariable: variable =\u003e {\n            return 'VAR1' === variable ? {from: {row: 1, col: 1}, to: {row: 2, col: 2}} : {row: 1, col: 1};\n        }\n    });\n\n    // position of the formula should be provided\n    const position = {row: 1, col: 1, sheet: 'Sheet1'};\n\n    // Return an array of references (range reference or cell reference)\n    // This gives [{row: 1, col: 1, sheet: 'Sheet1'}]\n    depParser.parse('A1+1', position);\n\n    // This gives [{sheet: 'Sheet1', from: {row: 1, col: 1}, to: {row: 3, col: 3}}]\n    depParser.parse('A1:C3', position);\n\n    // This gives [{from: {row: 1, col: 1}, to: {row: 2, col: 2}}]\n    depParser.parse('VAR1 + 1', position);\n\n    // Complex formula\n    depParser.parse('IF(MONTH($K$1)\u003c\u003eMONTH($K$1-(WEEKDAY($K$1,1)-(start_day-1))-IF((WEEKDAY($K$1,1)-(start_day-1))\u003c=0,7,0)+(ROW(O5)-ROW($K$3))*7+(COLUMN(O5)-COLUMN($K$3)+1)),\"\",$K$1-(WEEKDAY($K$1,1)-(start_day-1))-IF((WEEKDAY($K$1,1)-(start_day-1))\u003c=0,7,0)+(ROW(O5)-ROW($K$3))*7+(COLUMN(O5)-COLUMN($K$3)+1))', position);\n    // This gives the following result\n    const result = [\n        {\n            \"col\": 11,\n            \"row\": 1,\n            \"sheet\": \"Sheet1\",\n        },\n        {\n            \"col\": 1,\n            \"row\": 1,\n            \"sheet\": \"Sheet1\",\n        },\n        {\n            \"col\": 15,\n            \"row\": 5,\n            \"sheet\": \"Sheet1\",\n        },\n        {\n            \"col\": 11,\n            \"row\": 3,\n            \"sheet\": \"Sheet1\",\n        },\n    ];\n    ```\n\n### Formula data types in JavaScript\n\u003e The following data types are used in excel formulas and these are the only valid data types a formula or a function can return.\n   - Number (date uses number): `1234`\n   - String: `'some string'`\n   - Boolean: `true`, `false`\n   - Array: `[[1, 2, true, 'str']]`\n   - Range Reference: (1-based index)\n       ```js\n       const ref = {\n           sheet: String,\n           from: {\n               row: Number,\n               col: Number,\n           },\n           to: {\n               row: Number,\n               col: Number,\n           },\n       }\n       ```\n   - Cell Reference: (1-based index)\n       ```js\n       const ref = {\n           sheet: String,\n           row: Number,\n           col: Number,\n       }\n       ```\n   - [Union (e.g. (A1:C3, E1:G6))](https://github.com/LesterLyu/fast-formula-parser/blob/master/grammar/type/collection.js)\n   - [FormulaError](https://lesterlyu.github.io/fast-formula-parser/FormulaError.html)\n     - `FormulaError.DIV0`: `#DIV/0!`\n     - `FormulaError.NA`: `#N/A`\n     - `FormulaError.NAME`: `#NAME?`\n     - `FormulaError.NULL`: `#NULL!`\n     - `FormulaError.NUM`: `#NUM!`\n     - `FormulaError.REF`: `#REF!`\n     - `FormulaError.VALUE`: `#VALUE!`\n\n### Types Definition\n\u003e Comming soon\n\n### Error handling\n\n - Lexing/Parsing Error\n    \u003e Error location is available at `error.details.errorLocation`\n    ```js\n    try {\n        parser.parse('SUM(1))', position);\n    } catch (e) {\n        console.log(e);\n        // #ERROR!:\n        // SUM(1))\n        //       ^\n        // Error at position 1:7\n        // Redundant input, expecting EOF but found: )\n\n        expect(e).to.be.instanceof(FormulaError);\n        expect(e.details.errorLocation.line).to.eq(1);\n        expect(e.details.errorLocation.column).to.eq(7);\n        expect(e.name).to.eq('#ERROR!');\n        expect(e.details.name).to.eq('NotAllInputParsedException');\n    }\n    ```\n - Error from internal/external functions or unexpected error from the parser\n    \u003e The error will be wrapped into `FormulaError`. The exact error is in `error.details`.\n    ```js\n    const parser = new FormulaParser({\n        functions: {\n            BAD_FN: () =\u003e {\n                throw new SyntaxError();\n            }\n        }\n    });\n\n    try {\n        parser.parse('SUM(1))', position);\n    } catch (e) {\n        expect(e).to.be.instanceof(FormulaError);\n        expect(e.name).to.eq('#ERROR!');\n        expect(e.details.name).to.eq('SyntaxError');\n    }\n    ```\n\n### Thanks\n- [![JetBrains](https://raw.githubusercontent.com/LesterLyu/fast-formula-parser/master/logos/jetbrains-variant-4.svg)](https://www.jetbrains.com/?from=fast-formula-parser)\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLesterLyu%2Ffast-formula-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FLesterLyu%2Ffast-formula-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLesterLyu%2Ffast-formula-parser/lists"}