spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
[Parser, Errors] work-in-progress on getting error-catching formulas working
author
Ben Vogt <[email protected]>
date
2017-09-04 18:54:13
stats
7 file(s) changed, 71 insertions(+), 37 deletions(-)
files
src/Errors.ts
src/Formulas.ts
src/Formulas/AllFormulas.ts
src/Parser/Parser.ts
src/Sheet.ts
tests/Formulas/InfoTest.ts
tests/SheetFormulaTest.ts
  1diff --git a/src/Errors.ts b/src/Errors.ts
  2index d84bb72..891b7c4 100644
  3--- a/src/Errors.ts
  4+++ b/src/Errors.ts
  5@@ -5,6 +5,7 @@ let REF_ERROR = "#REF!";
  6 let NAME_ERROR = "#NAME!";
  7 let NUM_ERROR = "#NUM!";
  8 let NA_ERROR = "#N/A";
  9+let PARSE_ERROR = "#ERROR";
 10 
 11 class NullError extends Error {
 12   constructor(message: string) {
 13@@ -55,6 +56,13 @@ class NAError extends Error {
 14   }
 15 }
 16 
 17+class ParseError extends Error {
 18+  constructor(message: string) {
 19+    super(message);
 20+    this.name = PARSE_ERROR;
 21+  }
 22+}
 23+
 24 export {
 25   DIV_ZERO_ERROR,
 26   NULL_ERROR,
 27@@ -63,11 +71,13 @@ export {
 28   NAME_ERROR,
 29   NUM_ERROR,
 30   NA_ERROR,
 31+  PARSE_ERROR,
 32   DivZeroError,
 33   NullError,
 34   ValueError,
 35   RefError,
 36   NameError,
 37   NumError,
 38-  NAError
 39+  NAError,
 40+  ParseError
 41 }
 42\ No newline at end of file
 43diff --git a/src/Formulas.ts b/src/Formulas.ts
 44index ea0cdb9..79fd940 100644
 45--- a/src/Formulas.ts
 46+++ b/src/Formulas.ts
 47@@ -1,4 +1,7 @@
 48 import * as AllFormulas from "./Formulas/AllFormulas";
 49+import {
 50+  __TRY_CATCH_FORMULAS
 51+} from "./Formulas/AllFormulas";
 52 
 53 let Formulas = {
 54   exists: function(fn: string) {
 55@@ -11,6 +14,9 @@ let Formulas = {
 56     if (fn in AllFormulas.__COMPLEX) {
 57       return AllFormulas.__COMPLEX[fn];
 58     }
 59+  },
 60+  isTryCatchFormula: function (fn: string) : boolean {
 61+    return __TRY_CATCH_FORMULAS[fn] !== undefined;
 62   }
 63 };
 64 
 65diff --git a/src/Formulas/AllFormulas.ts b/src/Formulas/AllFormulas.ts
 66index 34966f8..017f049 100644
 67--- a/src/Formulas/AllFormulas.ts
 68+++ b/src/Formulas/AllFormulas.ts
 69@@ -251,7 +251,7 @@ import {
 70 } from "./Date"
 71 
 72 // Using alias to bind dot-notation function names.
 73-let __COMPLEX = {
 74+const __COMPLEX = {
 75   "F.DIST": FDIST$LEFTTAILED,
 76   "NETWORKDAYS.INTL": NETWORKDAYS$INTL,
 77   "WORKDAY.INTL": WORKDAY$INTL,
 78@@ -263,8 +263,14 @@ let __COMPLEX = {
 79   "RANK.EQ": RANK$EQ
 80 };
 81 
 82+const __TRY_CATCH_FORMULAS : Object = {
 83+  "ERROR.TYPE": ERRORTYPE,
 84+  "ERRORTYPE": ERRORTYPE,
 85+};
 86+
 87 export {
 88   __COMPLEX,
 89+  __TRY_CATCH_FORMULAS,
 90 
 91   ABS,
 92   ACOS,
 93diff --git a/src/Parser/Parser.ts b/src/Parser/Parser.ts
 94index 03b3e29..a2e9121 100644
 95--- a/src/Parser/Parser.ts
 96+++ b/src/Parser/Parser.ts
 97@@ -1,6 +1,7 @@
 98 import {
 99   ObjectFromPairs
100 } from "../Utilities/ObjectFromPairs";
101+import {ParseError} from "../Errors";
102 
103 // Rules represent the Regular Expressions that will be used in sequence to match a given input to the Parser.
104 const WHITE_SPACE_RULE = /^(?:\s+)/; // rule 0
105@@ -1272,7 +1273,8 @@ let Parser = (function () {
106         },
107         p,
108         newState,
109-        expected;
110+        expected,
111+        catchFailuresOn = false;
112       while (true) {
113         // retrieve state number from top of stack
114         state = stack[stack.length - 1];
115@@ -1353,7 +1355,7 @@ let Parser = (function () {
116           // just recovered from another error
117           if (recovering == 3) {
118             if (symbol === EOF || preErrorSymbol === EOF) {
119-              throw new Error(errStr || 'Parsing halted while starting to recover from another error.');
120+              throw new ParseError(errStr || 'Parsing halted while starting to recover from another error.');
121             }
122 
123             // discard current lookahead and grab another
124@@ -1366,7 +1368,7 @@ let Parser = (function () {
125 
126           // try to recover from error
127           if (error_rule_depth === false) {
128-            throw new Error(errStr || 'Parsing halted. No suitable error recovery rule available.');
129+            throw new ParseError(errStr || 'Parsing halted. No suitable error recovery rule available.');
130           }
131           popStack(error_rule_depth);
132 
133@@ -1379,7 +1381,7 @@ let Parser = (function () {
134 
135         // this shouldn't happen, unless resolve defaults are off
136         if (action[0] instanceof Array && action.length > 1) {
137-          throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
138+          throw new ParseError('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
139         }
140 
141         // LexActions are always:
142@@ -1463,7 +1465,7 @@ let Parser = (function () {
143         if (this.yy.parser) {
144           this.yy.parser.parseError(str, hash);
145         } else {
146-          throw new Error(str);
147+          throw new ParseError(str);
148         }
149       },
150 
151diff --git a/src/Sheet.ts b/src/Sheet.ts
152index 8e48590..b79db5b 100644
153--- a/src/Sheet.ts
154+++ b/src/Sheet.ts
155@@ -7,7 +7,7 @@ import {
156 import {
157   DivZeroError,
158   RefError,
159-  NameError
160+  NameError, ParseError
161 } from "./Errors";
162 import {
163   Formulas
164@@ -55,11 +55,11 @@ let Sheet = (function () {
165     };
166 
167     newParser.yy.parseError = function (str, hash) {
168-      throw {
169+      throw new ParseError(JSON.stringify({
170         name: 'Parser error',
171         message: str,
172         prop: hash
173-      }
174+      }));
175     };
176 
177     newParser.yy.handler = handler;
178@@ -463,12 +463,9 @@ let Sheet = (function () {
179     },
180 
181     cellValue: function (cellId) {
182-      let value,
183-        origin = this,
184+      let origin = this,
185         cell = instance.matrix.getCell(cellId);
186 
187-      // get value, defaulting to undefined
188-      value = cell.isBlank() ? undefined : cell.getValue();
189       //update dependencies
190       instance.matrix.getCell(origin).updateDependencies([cellId]);
191       // check references error
192@@ -479,7 +476,7 @@ let Sheet = (function () {
193       }
194       // check if any error occurs
195       if (!cell.isBlank() && cell.getError()) {
196-        throw cell.getError()
197+        throw cell.getError();
198       }
199       return cell;
200     },
201@@ -533,7 +530,7 @@ let Sheet = (function () {
202           instance.matrix.getCell(id).setError(new RefError("Reference does not exist"));
203           instance.matrix.getCell(id).clearValue();
204         });
205-        throw new RefError("Reference does not exist.");
206+        error = new RefError("Reference does not exist.");
207       }
208     } catch (ex) {
209       error = ex;
210diff --git a/tests/Formulas/InfoTest.ts b/tests/Formulas/InfoTest.ts
211index ebeb050..b0e5c41 100644
212--- a/tests/Formulas/InfoTest.ts
213+++ b/tests/Formulas/InfoTest.ts
214@@ -141,7 +141,7 @@ test("ISREF", function(){
215 });
216 
217 test("ERRORTYPE", function(){
218-  var errorCell = new Cell("A1");
219+  let errorCell = new Cell("A1");
220   errorCell.setError(new NAError("error"));
221   assertEquals(ERRORTYPE(new NullError("error")), 1);
222   assertEquals(ERRORTYPE(new DivZeroError("error")), 2);
223@@ -173,7 +173,7 @@ test("ISBLANK", function(){
224 
225 
226 test("ISERR", function(){
227-  var errorCell = new Cell("A1");
228+  let errorCell = new Cell("A1");
229   errorCell.setError(new DivZeroError("err"));
230   assertEquals(ISERR(errorCell), true);
231   assertEquals(ISERR(Cell.BuildFrom("A1", 10)), false);
232@@ -190,7 +190,7 @@ test("ISERR", function(){
233 
234 
235 test("ISERROR", function(){
236-  var errorCell = new Cell("A1");
237+  let errorCell = new Cell("A1");
238   errorCell.setError(new DivZeroError("err"));
239   assertEquals(ISERROR(errorCell), true);
240   assertEquals(ISERROR(Cell.BuildFrom("A1", 10)), false);
241@@ -209,7 +209,7 @@ test("ISERROR", function(){
242 
243 
244 test("ISNA", function(){
245-  var errorCell = new Cell("A1");
246+  let errorCell = new Cell("A1");
247   errorCell.setError(new NAError("err"));
248   assertEquals(ISNA(errorCell), true);
249   assertEquals(ISNA(Cell.BuildFrom("A1", 10)), false);
250@@ -228,7 +228,7 @@ test("ISNA", function(){
251 
252 
253 test("IFERROR", function(){
254-  var errorCell = new Cell("A1");
255+  let errorCell = new Cell("A1");
256   errorCell.setError(new NAError("err"));
257   assertEquals(IFERROR(errorCell, 10), 10);
258   assertEquals(IFERROR(10), 10);
259@@ -246,7 +246,7 @@ test("TYPE", function(){
260   assertEquals(TYPE(false), 4);
261   assertEquals(TYPE(new NAError("err")), 16);
262   assertEquals(TYPE([1, 2, 3]), 64);
263-  var errorCell = new Cell("A1");
264+  let errorCell = new Cell("A1");
265   errorCell.setError(new NAError("err"));
266   assertEquals(TYPE(errorCell), 16);
267   assertEquals(TYPE(Cell.BuildFrom("A1", 1)), 1);
268diff --git a/tests/SheetFormulaTest.ts b/tests/SheetFormulaTest.ts
269index 010ba10..8f5ccd9 100644
270--- a/tests/SheetFormulaTest.ts
271+++ b/tests/SheetFormulaTest.ts
272@@ -8,49 +8,49 @@ import {
273 import {
274   DIV_ZERO_ERROR,
275   VALUE_ERROR,
276-  NA_ERROR
277+  NA_ERROR, PARSE_ERROR
278 } from "../src/Errors";
279 
280 function assertFormulaEqualsError(formula: string, errorString: string) {
281-  var sheet  = new Sheet();
282+  let sheet  = new Sheet();
283   sheet.setCell("A1", formula);
284-  var cell = sheet.getCell("A1");
285+  let cell = sheet.getCell("A1");
286   assertEquals(cell.getError().name, errorString);
287   assertEquals(cell.getValue(), null);
288 }
289 
290 function assertFormulaEquals(formula: string, expectation: any) {
291-  var sheet  = new Sheet();
292+  let sheet  = new Sheet();
293   sheet.setCell("A1", formula);
294-  var cell = sheet.getCell("A1");
295+  let cell = sheet.getCell("A1");
296   assertEquals(cell.getError(), null);
297   assertEquals(cell.getValue(), expectation);
298 }
299 
300 function assertFormulaEqualsDependsOnReference(refId: string, value: any, formula: string, expectation: any) {
301-  var sheet  = new Sheet();
302+  let sheet  = new Sheet();
303   sheet.setCell(refId, value);
304   sheet.setCell("A1", formula);
305-  var cell = sheet.getCell("A1");
306+  let cell = sheet.getCell("A1");
307   assertEquals(cell.getError(), null);
308   assertEquals(cell.getValue(), expectation);
309 }
310 
311 function assertFormulaResultsInType(formula: string, type: string) {
312-  var sheet  = new Sheet();
313+  let sheet  = new Sheet();
314   sheet.setCell("A1", formula);
315-  var cell = sheet.getCell("A1");
316+  let cell = sheet.getCell("A1");
317   assertEquals(cell.getError(), null);
318   assertEquals(typeof cell.getValue(), type);
319 }
320 
321 function assertFormulaEqualsArray(formula: string, expectation: any) {
322-  var sheet  = new Sheet();
323+  let sheet  = new Sheet();
324   sheet.setCell("A1", formula);
325-  var cell = sheet.getCell("A1");
326+  let cell = sheet.getCell("A1");
327   assertEquals(null, cell.getError());
328-  var values = cell.getValue();
329-  for (var index in values) {
330+  let values = cell.getValue();
331+  for (let index in values) {
332     assertEquals(values[index], expectation[index]);
333   }
334 }
335@@ -967,6 +967,17 @@ test("Sheet TO_TEXT", function(){
336   assertFormulaEquals('=TO_TEXT(false)', "FALSE");
337 });
338 
339+test("Sheet ERROR.TYPE", function(){
340+  let sheet = new Sheet();
341+  sheet.setCell("M1", "= 1/0");
342+  sheet.setCell("A1", "=ERROR.TYPE(M1)");
343+  assertEquals(sheet.getCell("A1").getValue(), 2);
344+});
345+
346+test("Sheet parsing error", function(){
347+  assertFormulaEqualsError('= 10e', PARSE_ERROR);
348+});
349+
350 test("Sheet *", function(){
351   assertFormulaEquals('= 10 * 10', 100);
352   assertFormulaEquals('= 10 * 0', 0);