UNPKG

fast-formula-parser

Version:

fast excel formula parser

155 lines (140 loc) 6.23 kB
![GitHub](https://img.shields.io/github/license/lesterlyu/fast-formula-parser) [![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) [![Build Status](https://travis-ci.com/LesterLyu/fast-formula-parser.svg?branch=master)](https://travis-ci.com/LesterLyu/fast-formula-parser) ## [A Fast Excel Formula Parser](https://github.com/LesterLyu/fast-formula-parser) > **This project is under developments and not ready to publish.** Aim to be the fastest and the most reliable excel formula parser in javascript. Using **LL(1)** parser. ### [Documentation](https://lesterlyu.github.io/fast-formula-parser/) ### [Grammar Diagram](https://lesterlyu.github.io/fast-formula-parser/generated_diagrams.html) ### 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 my optimized [formula-parser](https://github.com/LesterLyu/formula-parser) with *more syntax support*. ### 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) - 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'; ``` - 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) onVariable: (name, sheetName) => { // range reference (A1:B2) return { sheet: 'sheet name', from: { row: 1, col: 1, }, to: { row: 2, col: 2, } }; // 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; } }); // parse the formula, the position of where the formula is located is required // for some functions. console.log(parser.parse('SUM(A:C)', {sheet: 'Sheet 1', row: 1, col: 1})); // 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})', {sheet: 'Sheet 1', row: 1, col: 1}, true)); // print [ [ 11, 17 ], [ 8, 13 ] ] ``` - Formula data types in JavaScript - 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](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!`