@rowsncolumns/fast-formula-parser 6.2.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Dishu(Lester) Lyu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,319 @@
1
+ ![GitHub](https://img.shields.io/github/license/lesterlyu/fast-formula-parser)
2
+ [![npm (tag)](https://img.shields.io/npm/v/fast-formula-parser/latest)](https://www.npmjs.com/package/fast-formula-parser)
3
+ [![npm](https://img.shields.io/npm/dt/fast-formula-parser)](https://www.npmjs.com/package/fast-formula-parser)
4
+ [![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)
5
+ [![Build Status](https://travis-ci.com/LesterLyu/fast-formula-parser.svg?branch=master)](https://travis-ci.com/LesterLyu/fast-formula-parser)
6
+ ## [A Fast Excel Formula Parser & Evaluator](https://github.com/LesterLyu/fast-formula-parser)
7
+
8
+ A fast and reliable excel formula parser in javascript. Using **LL(1)** parser.
9
+
10
+ ### [Demo](https://lesterlyu.github.io/#/demo/fast-formula-parser)
11
+ ### [Documentation](https://lesterlyu.github.io/fast-formula-parser/index.html)
12
+ ### [Grammar Diagram](https://lesterlyu.github.io/fast-formula-parser/generated_diagrams.html)
13
+ ### Supports 280 Formulas
14
+ ```
15
+ 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,
16
+ 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,
17
+ 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,
18
+ 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,
19
+ 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,
20
+ 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, NORMINV, NORM.S.DIST, NORM.S.INV, NOT, NOW, NUMBERVALUE, OCT2BIN, OCT2DEC, OCT2HEX, ODD, OR,
21
+ 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,
22
+ 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, VLOOKUP, WEBSERVICE, WEEKDAY, WEEKNUM, WEIBULL.DIST, WORKDAY, WORKDAY.INTL, XOR, YEAR, YEARFRAC
23
+ ```
24
+ ### Size: 291KB Minified, 81KB Gzipped+Minified
25
+
26
+ ### Background
27
+
28
+ Inspired by [XLParser](https://github.com/spreadsheetlab/XLParser/blob/master/src/XLParser/ExcelFormulaGrammar.cs)
29
+ 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).
30
+
31
+ Note: The grammar in my implementation is different from theirs. My implementation gets rid of ambiguities to boost the performance.
32
+
33
+ ### What is not supported:
34
+ - [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)
35
+ - Anything with `[` and `]`
36
+ - Ambiguous old styles
37
+ - Sheet name contains `:`, e.g. `SUM('1003:1856'!D6)`
38
+ - Sheet name with space that is not quoted, e.g. `I am a sheet!A1`
39
+ - `SUM(Sheet2:Sheet3!A1:C3)`
40
+ - You tell me
41
+
42
+ ### Performance
43
+ - The expected performance is at least 3x faster than the optimized [formula-parser](https://github.com/LesterLyu/formula-parser).
44
+
45
+ ### Dependency
46
+ - [Chevrotain](https://github.com/SAP/chevrotain) , thanks to this great parser building toolkit.
47
+
48
+ ### [Examples](https://github.com/LesterLyu/fast-formula-parser/blob/master/examples/example.js)
49
+ - Install
50
+ ```sh
51
+ npm i fast-formula-parser
52
+ # or using yarn
53
+ yarn add fast-formula-parser
54
+ ```
55
+ - Import
56
+ ```js
57
+ const FormulaParser = require('fast-formula-parser');
58
+ const {FormulaHelpers, Types, FormulaError, MAX_ROW, MAX_COLUMN} = FormulaParser;
59
+ // or
60
+ import FormulaParser, {FormulaHelpers, Types, FormulaError, MAX_ROW, MAX_COLUMN} from 'fast-formula-parser';
61
+ ```
62
+ UMD minified build is also provides:
63
+ ```html
64
+ <script src="/node_modules/fast-formula-parser/build/parser.min.js"> </script>
65
+ ```
66
+ - Basic Usage
67
+ ```js
68
+ const data = [
69
+ // A B C
70
+ [1, 2, 3], // row 1
71
+ [4, 5, 6] // row 2
72
+ ];
73
+
74
+ const parser = new FormulaParser({
75
+
76
+ // External functions, this will override internal functions with same name
77
+ functions: {
78
+ CHAR: (number) => {
79
+ number = FormulaHelpers.accept(number, Types.NUMBER);
80
+ if (number > 255 || number < 1)
81
+ throw FormulaError.VALUE;
82
+ return String.fromCharCode(number);
83
+ }
84
+ },
85
+
86
+ // Variable used in formulas (defined name)
87
+ // Should only return range reference or cell reference
88
+ onVariable: (name, sheetName) => {
89
+ // If it is a range reference (A1:B2)
90
+ return {
91
+ sheet: 'sheet name',
92
+ from: {
93
+ row: 1,
94
+ col: 1,
95
+ },
96
+ to: {
97
+ row: 2,
98
+ col: 2,
99
+ }
100
+ };
101
+ // If it is a cell reference (A1)
102
+ return {
103
+ sheet: 'sheet name',
104
+ row: 1,
105
+ col: 1
106
+ }
107
+ },
108
+
109
+ // retrieve cell value
110
+ onCell: ({sheet, row, col}) => {
111
+ // using 1-based index
112
+ // return the cell value, see possible types in next section.
113
+ return data[row - 1][col - 1];
114
+ },
115
+
116
+ // retrieve range values
117
+ onRange: (ref) => {
118
+ // using 1-based index
119
+ // Be careful when ref.to.col is MAX_COLUMN or ref.to.row is MAX_ROW, this will result in
120
+ // unnecessary loops in this approach.
121
+ const arr = [];
122
+ for (let row = ref.from.row; row <= ref.to.row; row++) {
123
+ const innerArr = [];
124
+ if (data[row - 1]) {
125
+ for (let col = ref.from.col; col <= ref.to.col; col++) {
126
+ innerArr.push(data[row - 1][col - 1]);
127
+ }
128
+ }
129
+ arr.push(innerArr);
130
+ }
131
+ return arr;
132
+ }
133
+ });
134
+
135
+ // position is required for evaluating certain formulas, e.g. ROW()
136
+ const position = {row: 1, col: 1, sheet: 'Sheet1'};
137
+
138
+ // parse the formula, the position of where the formula is located is required
139
+ // for some functions.
140
+ console.log(parser.parse('SUM(A:C)', position));
141
+ // print 21
142
+
143
+ // you can specify if the return value can be an array, this is helpful when dealing
144
+ // with an array formula
145
+ console.log(parser.parse('MMULT({1,5;2,3},{1,2;2,3})', position, true));
146
+ // print [ [ 11, 17 ], [ 8, 13 ] ]
147
+ ```
148
+
149
+ - Custom Async functions
150
+ > Remember to use `await parser.parseAsync(...)` instead of `parser.parse(...)`
151
+ ```js
152
+ const position = {row: 1, col: 1, sheet: 'Sheet1'};
153
+ const parser = new FormulaParser({
154
+ onCell: ref => {
155
+ return 1;
156
+ },
157
+ functions: {
158
+ DEMO_FUNC: async () => {
159
+ return [[1,2,3],[4,5,6]];
160
+ }
161
+ },
162
+ });
163
+ console.log(await parser.parseAsync('A1 + IMPORT_CSV())', position));
164
+ // print 2
165
+ console.log(await parser.parseAsync('SUM(DEMO_FUNC(), 1))', position));
166
+ // print 22
167
+ ```
168
+ - Custom function requires parser context (e.g. location of the formula)
169
+ ```js
170
+ const position = {row: 1, col: 1, sheet: 'Sheet1'};
171
+ const parser = new FormulaParser({
172
+ functionsNeedContext: {
173
+ // the first argument is the context
174
+ // the followings are the arguments passed to the function
175
+ ROW_PLUS_COL: (context, ...args) => {
176
+ return context.position.row + context.position.col;
177
+ }
178
+ },
179
+ });
180
+ console.log(await parser.parseAsync('SUM(ROW_PLUS_COL(), 1)', position));
181
+ // print 3
182
+ ```
183
+
184
+ - Parse Formula Dependency
185
+ > This is helpful for building `dependency graph/tree`.
186
+ ```js
187
+ import {DepParser} from 'fast-formula-parser';
188
+ const depParser = new DepParser({
189
+ // onVariable is the only thing you need provide if the formula contains variables
190
+ onVariable: variable => {
191
+ return 'VAR1' === variable ? {from: {row: 1, col: 1}, to: {row: 2, col: 2}} : {row: 1, col: 1};
192
+ }
193
+ });
194
+
195
+ // position of the formula should be provided
196
+ const position = {row: 1, col: 1, sheet: 'Sheet1'};
197
+
198
+ // Return an array of references (range reference or cell reference)
199
+ // This gives [{row: 1, col: 1, sheet: 'Sheet1'}]
200
+ depParser.parse('A1+1', position);
201
+
202
+ // This gives [{sheet: 'Sheet1', from: {row: 1, col: 1}, to: {row: 3, col: 3}}]
203
+ depParser.parse('A1:C3', position);
204
+
205
+ // This gives [{from: {row: 1, col: 1}, to: {row: 2, col: 2}}]
206
+ depParser.parse('VAR1 + 1', position);
207
+
208
+ // Complex formula
209
+ 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);
210
+ // This gives the following result
211
+ const result = [
212
+ {
213
+ "col": 11,
214
+ "row": 1,
215
+ "sheet": "Sheet1",
216
+ },
217
+ {
218
+ "col": 1,
219
+ "row": 1,
220
+ "sheet": "Sheet1",
221
+ },
222
+ {
223
+ "col": 15,
224
+ "row": 5,
225
+ "sheet": "Sheet1",
226
+ },
227
+ {
228
+ "col": 11,
229
+ "row": 3,
230
+ "sheet": "Sheet1",
231
+ },
232
+ ];
233
+ ```
234
+
235
+ ### Formula data types in JavaScript
236
+ > The following data types are used in excel formulas and these are the only valid data types a formula or a function can return.
237
+ - Number (date uses number): `1234`
238
+ - String: `'some string'`
239
+ - Boolean: `true`, `false`
240
+ - Array: `[[1, 2, true, 'str']]`
241
+ - Range Reference: (1-based index)
242
+ ```js
243
+ const ref = {
244
+ sheet: String,
245
+ from: {
246
+ row: Number,
247
+ col: Number,
248
+ },
249
+ to: {
250
+ row: Number,
251
+ col: Number,
252
+ },
253
+ }
254
+ ```
255
+ - Cell Reference: (1-based index)
256
+ ```js
257
+ const ref = {
258
+ sheet: String,
259
+ row: Number,
260
+ col: Number,
261
+ }
262
+ ```
263
+ - [Union (e.g. (A1:C3, E1:G6))](https://github.com/LesterLyu/fast-formula-parser/blob/master/grammar/type/collection.js)
264
+ - [FormulaError](https://lesterlyu.github.io/fast-formula-parser/FormulaError.html)
265
+ - `FormulaError.DIV0`: `#DIV/0!`
266
+ - `FormulaError.NA`: `#N/A`
267
+ - `FormulaError.NAME`: `#NAME?`
268
+ - `FormulaError.NULL`: `#NULL!`
269
+ - `FormulaError.NUM`: `#NUM!`
270
+ - `FormulaError.REF`: `#REF!`
271
+ - `FormulaError.VALUE`: `#VALUE!`
272
+
273
+ ### Types Definition
274
+ > Comming soon
275
+
276
+ ### Error handling
277
+
278
+ - Lexing/Parsing Error
279
+ > Error location is available at `error.details.errorLocation`
280
+ ```js
281
+ try {
282
+ parser.parse('SUM(1))', position);
283
+ } catch (e) {
284
+ console.log(e);
285
+ // #ERROR!:
286
+ // SUM(1))
287
+ // ^
288
+ // Error at position 1:7
289
+ // Redundant input, expecting EOF but found: )
290
+
291
+ expect(e).to.be.instanceof(FormulaError);
292
+ expect(e.details.errorLocation.line).to.eq(1);
293
+ expect(e.details.errorLocation.column).to.eq(7);
294
+ expect(e.name).to.eq('#ERROR!');
295
+ expect(e.details.name).to.eq('NotAllInputParsedException');
296
+ }
297
+ ```
298
+ - Error from internal/external functions or unexpected error from the parser
299
+ > The error will be wrapped into `FormulaError`. The exact error is in `error.details`.
300
+ ```js
301
+ const parser = new FormulaParser({
302
+ functions: {
303
+ BAD_FN: () => {
304
+ throw new SyntaxError();
305
+ }
306
+ }
307
+ });
308
+
309
+ try {
310
+ parser.parse('SUM(1))', position);
311
+ } catch (e) {
312
+ expect(e).to.be.instanceof(FormulaError);
313
+ expect(e.name).to.eq('#ERROR!');
314
+ expect(e.details.name).to.eq('SyntaxError');
315
+ }
316
+ ```
317
+
318
+ ### Thanks
319
+ - [![JetBrains](https://raw.githubusercontent.com/LesterLyu/fast-formula-parser/master/logos/jetbrains-variant-4.svg)](https://www.jetbrains.com/?from=fast-formula-parser)