Announcement
As part of the SheetXL project, we have integrated the excellent fast-formula-parser into the larger SheetXL ecosystem.
The new package, @sheetxl/formulas, powers the @sheetxl/sdk and brings updated capabilities:
Highlights
- ✅ All existing formulas plus 100 new ones
- ✅ Written in TypeScript for strong typing and modern tooling
- ✅ Runs in both Node.js and the browser (via the SheetXL runtime)
- ✅ High performance — up to 10× faster than Excel for most operations
- ✅ Integrated with the
@sheetxl/sdk for full spreadsheet capabilities
- ✅ Roadmap: Full Excel parity expected in the next 6–12 months
- ✅ MIT License: All formulas remain MIT licensed
We recommend migrating to @sheetxl/formulas to benefit from ongoing improvements.
🙏 Thanks
Special thanks to @LesterLyu (original creator) and all contributors who helped create this most excellent library. Your work has made it possible for hundreds of projects to build spreadsheet-like functionality in JavaScript.

A fast and reliable excel formula parser in javascript. Using LL(1) parser.
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
and the paper “A Grammar for Spreadsheet Formulas Evaluated on Two Large Datasets” by Efthimia Aivaloglou, David Hoepelman and Felienne Hermans.
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
- 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
- The expected performance is at least 3x faster than the optimized formula-parser.
Dependency
- Chevrotain , thanks to this great parser building toolkit.
-
Install
npm i fast-formula-parser
# or using yarn
yarn add fast-formula-parser
-
Import
const FormulaParser = require('fast-formula-parser');
const {FormulaHelpers, Types, FormulaError, MAX_ROW, MAX_COLUMN} = FormulaParser;
import FormulaParser, {FormulaHelpers, Types, FormulaError, MAX_ROW, MAX_COLUMN} from 'fast-formula-parser';
UMD minified build is also provides:
<script src="/node_modules/fast-formula-parser/build/parser.min.js"> </script>
-
Basic Usage
const data = [
[1, 2, 3],
[4, 5, 6]
];
const parser = new FormulaParser({
functions: {
CHAR: (number) => {
number = FormulaHelpers.accept(number, Types.NUMBER);
if (number > 255 || number < 1)
throw FormulaError.VALUE;
return String.fromCharCode(number);
}
},
onVariable: (name, sheetName) => {
return {
sheet: 'sheet name',
from: {
row: 1,
col: 1,
},
to: {
row: 2,
col: 2,
}
};
return {
sheet: 'sheet name',
row: 1,
col: 1
}
},
onCell: ({sheet, row, col}) => {
return data[row - 1][col - 1];
},
onRange: (ref) => {
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;
}
});
const position = {row: 1, col: 1, sheet: 'Sheet1'};
console.log(parser.parse('SUM(A:C)', position));
console.log(parser.parse('MMULT({1,5;2,3},{1,2;2,3})', position, true));
-
Custom Async functions
Remember to use await parser.parseAsync(...) instead of parser.parse(...)
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));
console.log(await parser.parseAsync('SUM(DEMO_FUNC(), 1))', position));
-
Custom function requires parser context (e.g. location of the formula)
const position = {row: 1, col: 1, sheet: 'Sheet1'};
const parser = new FormulaParser({
functionsNeedContext: {
ROW_PLUS_COL: (context, ...args) => {
return context.position.row + context.position.col;
}
},
});
console.log(await parser.parseAsync('SUM(ROW_PLUS_COL(), 1)', position));
-
Parse Formula Dependency
This is helpful for building dependency graph/tree.
import {DepParser} from 'fast-formula-parser';
const depParser = new DepParser({
onVariable: variable => {
return 'VAR1' === variable ? {from: {row: 1, col: 1}, to: {row: 2, col: 2}} : {row: 1, col: 1};
}
});
const position = {row: 1, col: 1, sheet: 'Sheet1'};
depParser.parse('A1+1', position);
depParser.parse('A1:C3', position);
depParser.parse('VAR1 + 1', position);
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);
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",
},
];
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)
const ref = {
sheet: String,
from: {
row: Number,
col: Number,
},
to: {
row: Number,
col: Number,
},
}
- Cell Reference: (1-based index)
const ref = {
sheet: String,
row: Number,
col: Number,
}
- Union (e.g. (A1:C3, E1:G6))
- FormulaError
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
try {
parser.parse('SUM(1))', position);
} catch (e) {
console.log(e);
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.
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
评论