Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/lesterlyu/fast-formula-parser
Parse and evaluate MS Excel formula in javascript.
https://github.com/lesterlyu/fast-formula-parser
evaluator excel excel-formula excel-formulas formula formula-parser interpreter javascript js parser spreadsheet
Last synced: 2 days ago
JSON representation
Parse and evaluate MS Excel formula in javascript.
- Host: GitHub
- URL: https://github.com/lesterlyu/fast-formula-parser
- Owner: LesterLyu
- License: mit
- Created: 2019-03-22T13:24:34.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2024-06-07T15:19:37.000Z (9 months ago)
- Last Synced: 2025-02-14T08:46:30.926Z (7 days ago)
- Topics: evaluator, excel, excel-formula, excel-formulas, formula, formula-parser, interpreter, javascript, js, parser, spreadsheet
- Language: JavaScript
- Homepage: https://www.npmjs.com/package/fast-formula-parser
- Size: 2.94 MB
- Stars: 473
- Watchers: 10
- Forks: 66
- Open Issues: 17
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
### Announcement
This very excellent project is now being managed as part of
[](https://www.sheetxl.com)
---

[](https://www.npmjs.com/package/fast-formula-parser)
[](https://www.npmjs.com/package/fast-formula-parser)
[](https://coveralls.io/github/LesterLyu/fast-formula-parser?branch=master)
## [A Fast Excel Formula Parser & Evaluator](https://github.com/LesterLyu/fast-formula-parser)A fast and reliable excel formula parser in javascript. Using **LL(1)** parser.
### [Demo](https://lesterlyu.github.io/#/demo/fast-formula-parser)
### [Documentation](https://lesterlyu.github.io/fast-formula-parser/index.html)
### [Grammar Diagram](https://lesterlyu.github.io/fast-formula-parser/generated_diagrams.html)### [SheetXL Home Page](https://www.sheetxl.com)
### Supports 280 Formulas
```
ABS, 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,
BITRSHIFT, 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,
COVARIANCE.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,
FISHER, 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,
IMCONJUGATE, 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,
KURT, 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,
PHI, 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,
SUMX2PY2, 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
```
### Size: 291KB Minified, 81KB Gzipped+Minified### Background
Inspired by [XLParser](https://github.com/spreadsheetlab/XLParser/blob/master/src/XLParser/ExcelFormulaGrammar.cs)
and 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).Note: The grammar in my implementation is different from theirs. My implementation gets rid of ambiguities to boost the performance.
### What is not supported:
- [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)
- Anything with `[` and `]`
- Ambiguous old styles
- Sheet name contains `:`, e.g. `SUM('1003:1856'!D6)`
- Sheet name with space that is not quoted, e.g. `I am a sheet!A1`
- `SUM(Sheet2:Sheet3!A1:C3)`
- You tell me### Performance
- The expected performance is at least 3x faster than the optimized [formula-parser](https://github.com/LesterLyu/formula-parser).### Dependency
- [Chevrotain](https://github.com/SAP/chevrotain) , thanks to this great parser building toolkit.### [Examples](https://github.com/LesterLyu/fast-formula-parser/blob/master/examples/example.js)
- Install
```sh
npm i fast-formula-parser
# or using yarn
yarn add fast-formula-parser
```
- Import
```js
const FormulaParser = require('fast-formula-parser');
const {FormulaHelpers, Types, FormulaError, MAX_ROW, MAX_COLUMN} = FormulaParser;
// or
import FormulaParser, {FormulaHelpers, Types, FormulaError, MAX_ROW, MAX_COLUMN} from 'fast-formula-parser';
```
UMD minified build is also provides:
```html
```
- Basic Usage
```js
const data = [
// A B C
[1, 2, 3], // row 1
[4, 5, 6] // row 2
];const parser = new FormulaParser({
// External functions, this will override internal functions with same name
functions: {
CHAR: (number) => {
number = FormulaHelpers.accept(number, Types.NUMBER);
if (number > 255 || number < 1)
throw FormulaError.VALUE;
return String.fromCharCode(number);
}
},// Variable used in formulas (defined name)
// Should only return range reference or cell reference
onVariable: (name, sheetName) => {
// If it is a range reference (A1:B2)
return {
sheet: 'sheet name',
from: {
row: 1,
col: 1,
},
to: {
row: 2,
col: 2,
}
};
// If it is a cell reference (A1)
return {
sheet: 'sheet name',
row: 1,
col: 1
}
},// retrieve cell value
onCell: ({sheet, row, col}) => {
// using 1-based index
// return the cell value, see possible types in next section.
return data[row - 1][col - 1];
},// retrieve range values
onRange: (ref) => {
// using 1-based index
// Be careful when ref.to.col is MAX_COLUMN or ref.to.row is MAX_ROW, this will result in
// unnecessary loops in this approach.
const arr = [];
for (let row = ref.from.row; row <= ref.to.row; row++) {
const innerArr = [];
if (data[row - 1]) {
for (let col = ref.from.col; col <= ref.to.col; col++) {
innerArr.push(data[row - 1][col - 1]);
}
}
arr.push(innerArr);
}
return arr;
}
});// position is required for evaluating certain formulas, e.g. ROW()
const position = {row: 1, col: 1, sheet: 'Sheet1'};// parse the formula, the position of where the formula is located is required
// for some functions.
console.log(parser.parse('SUM(A:C)', position));
// print 21// you can specify if the return value can be an array, this is helpful when dealing
// with an array formula
console.log(parser.parse('MMULT({1,5;2,3},{1,2;2,3})', position, true));
// print [ [ 11, 17 ], [ 8, 13 ] ]
```- Custom Async functions
> Remember to use `await parser.parseAsync(...)` instead of `parser.parse(...)`
```js
const position = {row: 1, col: 1, sheet: 'Sheet1'};
const parser = new FormulaParser({
onCell: ref => {
return 1;
},
functions: {
DEMO_FUNC: async () => {
return [[1,2,3],[4,5,6]];
}
},
});
console.log(await parser.parseAsync('A1 + IMPORT_CSV())', position));
// print 2
console.log(await parser.parseAsync('SUM(DEMO_FUNC(), 1))', position));
// print 22
```
- Custom function requires parser context (e.g. location of the formula)
```js
const position = {row: 1, col: 1, sheet: 'Sheet1'};
const parser = new FormulaParser({
functionsNeedContext: {
// the first argument is the context
// the followings are the arguments passed to the function
ROW_PLUS_COL: (context, ...args) => {
return context.position.row + context.position.col;
}
},
});
console.log(await parser.parseAsync('SUM(ROW_PLUS_COL(), 1)', position));
// print 3
```- Parse Formula Dependency
> This is helpful for building `dependency graph/tree`.
```js
import {DepParser} from 'fast-formula-parser';
const depParser = new DepParser({
// onVariable is the only thing you need provide if the formula contains variables
onVariable: variable => {
return 'VAR1' === variable ? {from: {row: 1, col: 1}, to: {row: 2, col: 2}} : {row: 1, col: 1};
}
});// position of the formula should be provided
const position = {row: 1, col: 1, sheet: 'Sheet1'};// Return an array of references (range reference or cell reference)
// This gives [{row: 1, col: 1, sheet: 'Sheet1'}]
depParser.parse('A1+1', position);// This gives [{sheet: 'Sheet1', from: {row: 1, col: 1}, to: {row: 3, col: 3}}]
depParser.parse('A1:C3', position);// This gives [{from: {row: 1, col: 1}, to: {row: 2, col: 2}}]
depParser.parse('VAR1 + 1', position);// Complex formula
depParser.parse('IF(MONTH($K$1)<>MONTH($K$1-(WEEKDAY($K$1,1)-(start_day-1))-IF((WEEKDAY($K$1,1)-(start_day-1))<=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))<=0,7,0)+(ROW(O5)-ROW($K$3))*7+(COLUMN(O5)-COLUMN($K$3)+1))', position);
// This gives the following result
const result = [
{
"col": 11,
"row": 1,
"sheet": "Sheet1",
},
{
"col": 1,
"row": 1,
"sheet": "Sheet1",
},
{
"col": 15,
"row": 5,
"sheet": "Sheet1",
},
{
"col": 11,
"row": 3,
"sheet": "Sheet1",
},
];
```### Formula data types in JavaScript
> The following data types are used in excel formulas and these are the only valid data types a formula or a function can return.
- Number (date uses number): `1234`
- String: `'some string'`
- Boolean: `true`, `false`
- Array: `[[1, 2, true, 'str']]`
- Range Reference: (1-based index)
```js
const ref = {
sheet: String,
from: {
row: Number,
col: Number,
},
to: {
row: Number,
col: Number,
},
}
```
- Cell Reference: (1-based index)
```js
const ref = {
sheet: String,
row: Number,
col: Number,
}
```
- [Union (e.g. (A1:C3, E1:G6))](https://github.com/LesterLyu/fast-formula-parser/blob/master/grammar/type/collection.js)
- [FormulaError](https://lesterlyu.github.io/fast-formula-parser/FormulaError.html)
- `FormulaError.DIV0`: `#DIV/0!`
- `FormulaError.NA`: `#N/A`
- `FormulaError.NAME`: `#NAME?`
- `FormulaError.NULL`: `#NULL!`
- `FormulaError.NUM`: `#NUM!`
- `FormulaError.REF`: `#REF!`
- `FormulaError.VALUE`: `#VALUE!`### Types Definition
> Comming soon### Error handling
- Lexing/Parsing Error
> Error location is available at `error.details.errorLocation`
```js
try {
parser.parse('SUM(1))', position);
} catch (e) {
console.log(e);
// #ERROR!:
// SUM(1))
// ^
// Error at position 1:7
// Redundant input, expecting EOF but found: )expect(e).to.be.instanceof(FormulaError);
expect(e.details.errorLocation.line).to.eq(1);
expect(e.details.errorLocation.column).to.eq(7);
expect(e.name).to.eq('#ERROR!');
expect(e.details.name).to.eq('NotAllInputParsedException');
}
```
- Error from internal/external functions or unexpected error from the parser
> The error will be wrapped into `FormulaError`. The exact error is in `error.details`.
```js
const parser = new FormulaParser({
functions: {
BAD_FN: () => {
throw new SyntaxError();
}
}
});try {
parser.parse('SUM(1))', position);
} catch (e) {
expect(e).to.be.instanceof(FormulaError);
expect(e.name).to.eq('#ERROR!');
expect(e.details.name).to.eq('SyntaxError');
}
```### Thanks
- [](https://www.jetbrains.com/?from=fast-formula-parser)