spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
[TypeConverter] adding logic for converting Cells to primitives
author
Ben Vogt <[email protected]>
date
2017-07-09 15:41:29
stats
7 file(s) changed, 144 insertions(+), 4 deletions(-)
files
TODO.md
src/Cell.ts
src/Utilities/TypeConverter.ts
tests/CellTest.ts
tests/Formulas/EngineeringTest.ts
tests/Formulas/FinancialTest.ts
tests/Utilities/TypeConverterTest.ts
  1diff --git a/TODO.md b/TODO.md
  2index fb09647..c5bde3d 100644
  3--- a/TODO.md
  4+++ b/TODO.md
  5@@ -24,6 +24,10 @@ For example 64 tbs to a qt.
  6 For example `=#N/A` should force an error to be thrown inside the cell.
  7 
  8 
  9+### Formula's use of type-conversion could be standardized
 10+We could give each function a type-array that matches the arguments, and could be used to map to the TypeConverter functions. For example if a formula looks like `THING(a: number, b: string, c: array<numbers>)` it could have a property called `typeArray = ["number", "string", "array<numbers>"]` and then for each argument it uses a map to see which TypeConverter function it should use to ensure type on those arguments. This would allow us to test these type-converters more efficiently, rather than testing to be sure each formula converted the types properly.
 11+
 12+
 13 ### Fix documentation regular expression, it is butchering URLs.
 14 
 15 
 16diff --git a/src/Cell.ts b/src/Cell.ts
 17index a5358a9..ff6af9a 100644
 18--- a/src/Cell.ts
 19+++ b/src/Cell.ts
 20@@ -164,6 +164,19 @@ class Cell {
 21   toString() : string {
 22     return "id=" + this.id + ", value=" + this.typedValue + ", rawFormulaText=" + this.rawFormulaText + ", error=" + this.error;
 23   }
 24+
 25+  /**
 26+   * Build a cell with an id and value.
 27+   * @param id - A1-notation id or key.
 28+   * @param value - value of the cell.
 29+   * @returns {Cell}
 30+   * @constructor
 31+   */
 32+  static BuildFrom(id: string, value: any) : Cell {
 33+    var cell = new Cell(id);
 34+    cell.setValue(value);
 35+    return cell;
 36+  }
 37 }
 38 
 39 function toNum(chr) {
 40diff --git a/src/Utilities/TypeConverter.ts b/src/Utilities/TypeConverter.ts
 41index ea479ef..235b2b4 100644
 42--- a/src/Utilities/TypeConverter.ts
 43+++ b/src/Utilities/TypeConverter.ts
 44@@ -434,9 +434,10 @@ class TypeConverter {
 45         if (value.hasError()) {
 46           throw value.getError();
 47         }
 48-        return value.getValue();
 49+        value = value.getValue();
 50       }
 51-    } else if (typeof value === "number") {
 52+    }
 53+    if (typeof value === "number") {
 54       return value;
 55     } else if (typeof value === "string") {
 56       if (value === "") {
 57@@ -472,6 +473,16 @@ class TypeConverter {
 58    * @returns {boolean} to return.
 59    */
 60   public static valueToBoolean(value: any) {
 61+    if (value instanceof Cell) {
 62+      if (value.isBlank()) {
 63+        return false;
 64+      } else {
 65+        if (value.hasError()) {
 66+          throw value.getError();
 67+        }
 68+        value = value.getValue();
 69+      }
 70+    }
 71     if (typeof value === "number") {
 72       return value !== 0;
 73     } else if (typeof value === "string") {
 74@@ -486,7 +497,16 @@ class TypeConverter {
 75    * @returns {string} string representation of value
 76    */
 77   public static valueToString(value: any) : string {
 78-    if (typeof value === "number") {
 79+    if (value instanceof Cell) {
 80+      if (value.isBlank()) {
 81+        return "";
 82+      } else {
 83+        if (value.hasError()) {
 84+          throw value.getError();
 85+        }
 86+        return value.getValue().toString();
 87+      }
 88+    } else if (typeof value === "number") {
 89       return value.toString();
 90     } else if (typeof value === "string") {
 91       return value;
 92@@ -501,7 +521,16 @@ class TypeConverter {
 93    * @returns {number} representing a time value
 94    */
 95   public static valueToTimestampNumber(value: any) : number {
 96-    if (typeof value === "number") {
 97+    if (value instanceof Cell) {
 98+      if (value.isBlank()) {
 99+        return 0;
100+      } else {
101+        if (value.hasError()) {
102+          throw value.getError();
103+        }
104+        return value.getValue();
105+      }
106+    } else if (typeof value === "number") {
107       return value;
108     } else if (typeof value === "string") {
109       if (value == "") {
110@@ -536,7 +565,7 @@ class TypeConverter {
111    * @returns {boolean} if could be coerced to a number
112    */
113   public static canCoerceToNumber(value: any) : boolean {
114-    if (typeof value === "number" || typeof value === "boolean") {
115+    if (typeof value === "number" || typeof value === "boolean" || value instanceof Cell) {
116       return true;
117     } else if (typeof value === "string") {
118       return TypeConverter.isNumber(value);
119@@ -643,7 +672,16 @@ class TypeConverter {
120    * @returns {number} date
121    */
122   public static valueToDateNumber(value: any, coerceBoolean?: boolean) : number {
123-    if (typeof value === "number") {
124+    if (value instanceof Cell) {
125+      if (value.isBlank()) {
126+        return 0;
127+      } else {
128+        if (value.hasError()) {
129+          throw value.getError();
130+        }
131+        return value.getValue();
132+      }
133+    } else if (typeof value === "number") {
134       return value;
135     } else if (typeof value === "string") {
136       try {
137diff --git a/tests/CellTest.ts b/tests/CellTest.ts
138index 5f0a2a5..a8bab0a 100644
139--- a/tests/CellTest.ts
140+++ b/tests/CellTest.ts
141@@ -37,3 +37,10 @@ test("Cell.isBlank", function(){
142   assertIsNull(v.getError());
143   assertEquals(v.isBlank(), true);
144 });
145+
146+test("Cell.BuildFrom", function(){
147+  var v = Cell.BuildFrom("A1", 10);
148+  assertEquals(v.getValue(), 10);
149+  assertIsNull(v.getError());
150+  assertEquals(v.isBlank(), false);
151+});
152diff --git a/tests/Formulas/EngineeringTest.ts b/tests/Formulas/EngineeringTest.ts
153index b425b07..4cc7dac 100644
154--- a/tests/Formulas/EngineeringTest.ts
155+++ b/tests/Formulas/EngineeringTest.ts
156@@ -13,9 +13,13 @@ import {
157   catchAndAssertEquals,
158   test
159 } from "../Utils/Asserts";
160+import {
161+  Cell
162+} from "../../src/Cell";
163 
164 
165 test("BIN2DEC", function(){
166+  assertEquals(BIN2DEC(Cell.BuildFrom("A1", "1010101010")), -342);
167   assertEquals(BIN2DEC("1010101010"), -342);
168   assertEquals(BIN2DEC("10"), 2);
169   assertEquals(BIN2DEC(["10", "str"]), 2);
170@@ -35,6 +39,7 @@ test("BIN2DEC", function(){
171 
172 
173 test("BIN2HEX", function(){
174+  assertEquals(BIN2HEX(Cell.BuildFrom("A1", "1010101010")), "FFFFFFFEAA");
175   assertEquals(BIN2HEX("1010101010"), "FFFFFFFEAA");
176   assertEquals(BIN2HEX("10"), "2");
177   assertEquals(BIN2HEX("10101010"), "AA");
178@@ -63,6 +68,7 @@ test("BIN2HEX", function(){
179 
180 
181 test("BIN2OCT", function(){
182+  assertEquals(BIN2OCT(Cell.BuildFrom("A1", "1010101010")), "7777777252");
183   assertEquals(BIN2OCT("1010101010"), "7777777252");
184   assertEquals(BIN2OCT("10"), "2");
185   assertEquals(BIN2OCT("100"), "4");
186@@ -91,6 +97,7 @@ test("BIN2OCT", function(){
187 
188 
189 test("DEC2BIN", function(){
190+  assertEquals(DEC2BIN(Cell.BuildFrom("A1", 100)), "1100100");
191   assertEquals(DEC2BIN([100]), "1100100");
192   assertEquals(DEC2BIN(100), "1100100");
193   assertEquals(DEC2BIN(22), "10110");
194@@ -126,6 +133,7 @@ test("DEC2BIN", function(){
195 
196 
197 test("DEC2HEX", function(){
198+  assertEquals(DEC2HEX(Cell.BuildFrom("A1", 100)), "64");
199   assertEquals(DEC2HEX([100]), "64");
200   assertEquals(DEC2HEX(100), "64");
201   assertEquals(DEC2HEX(22), "16");
202@@ -164,6 +172,7 @@ test("DEC2HEX", function(){
203 
204 
205 test("DEC2OCT", function(){
206+  assertEquals(DEC2OCT(Cell.BuildFrom("A1", 100)), "144");
207   assertEquals(DEC2OCT([100]), "144");
208   assertEquals(DEC2OCT(100), "144");
209   assertEquals(DEC2OCT(22), "26");
210@@ -202,6 +211,7 @@ test("DEC2OCT", function(){
211 
212 
213 test("DELTA", function(){
214+  assertEquals(DELTA(Cell.BuildFrom("A1", 2), 2), 1);
215   assertEquals(DELTA(2, 2), 1);
216   assertEquals(DELTA(2, 1), 0);
217   assertEquals(DELTA(2), 0);
218diff --git a/tests/Formulas/FinancialTest.ts b/tests/Formulas/FinancialTest.ts
219index 6153da6..9ca7207 100644
220--- a/tests/Formulas/FinancialTest.ts
221+++ b/tests/Formulas/FinancialTest.ts
222@@ -31,9 +31,13 @@ import {
223   catchAndAssertEquals,
224   test
225 } from "../Utils/Asserts";
226+import {
227+  Cell
228+} from "../../src/Cell";
229 
230 
231 test("ACCRINT", function(){
232+  assertEquals(ACCRINT(DATE(2000, 1, 1), DATE(2000, 2, 1), DATE(2002, 12, 31), 0.05, Cell.BuildFrom("A1", 100), 4), 14.98631386861314);
233   assertEquals(ACCRINT(DATE(2000, 1, 1), DATE(2000, 2, 1), DATE(2002, 12, 31), 0.05, 100, 4), 14.98631386861314);
234   assertEquals(ACCRINT(DATE(2011, 1, 1), DATE(2011, 2, 1), DATE(2014, 7, 1), 0.1, 1000, 1, 0), 350);
235   assertEquals(ACCRINT(DATE(2001, 1, 1), DATE(2011, 2, 1), DATE(2014, 7, 1), 0.1, 1000, 2, 1), 1349.6186192059456);
236@@ -66,6 +70,7 @@ test("ACCRINT", function(){
237 
238 
239 test("CUMPRINC", function(){
240+  assertEquals(CUMPRINC(0.12, 12, 100, 1, Cell.BuildFrom("A1", 5), false), -26.324171373034403);
241   assertEquals(CUMPRINC(0.12, 12, 100, 1, 5, false), -26.324171373034403);
242   assertEquals(CUMPRINC(0.12, 12, 100, 1, 5, 0), -26.324171373034403);
243   assertEquals(CUMPRINC(0.12, 12, 100, 1, 5, true), -34.21801015449499);
244@@ -92,6 +97,7 @@ test("CUMPRINC", function(){
245 
246 
247 test("CUMIPMT", function(){
248+  assertEquals(CUMIPMT(0.12, 12, 100, 1, Cell.BuildFrom("A1", 5), 0), -54.39423242396348);
249   assertEquals(CUMIPMT(0.12, 12, 100, 1, 5, 0), -54.39423242396348);
250   assertEquals(CUMIPMT(0.12, 12, 100, 1, 5, false), -54.39423242396348);
251   assertEquals(CUMIPMT(0.12, 12, 100, 1, 5, true), -37.851993235681675);
252@@ -121,6 +127,7 @@ test("CUMIPMT", function(){
253 
254 
255 test("DB", function(){
256+  assertEquals(DB(100, 50, 10, 2, Cell.BuildFrom("A1", 12)), 6.2482428240683285);
257   assertEquals(DB(100, 50, 10, 2, 12), 6.2482428240683285);
258   assertEquals(DB("100", "50", "10", "2", "12"), 6.2482428240683285);
259   assertEquals(DB(100, 50, 10, 2, 12.9999999), 6.2482428240683285);
260@@ -137,6 +144,7 @@ test("DB", function(){
261 
262 
263 test("DDB", function(){
264+  assertEquals(DDB(100, 50, 10, 2, Cell.BuildFrom("A1", 2.25)), 17.4375);
265   assertEquals(DDB(100, 50, 10, 2, 2.25), 17.4375);
266   assertEquals(DDB(100, [50], 10, 2, "2.25"), 17.4375);
267   catchAndAssertEquals(function() {
268@@ -149,6 +157,7 @@ test("DDB", function(){
269 
270 
271 test("DOLLAR", function(){
272+  assertEquals(DOLLAR(1.2351, Cell.BuildFrom("A1", 4)), 1.2351);
273   assertEquals(DOLLAR(1.2351, 4), 1.2351);
274   assertEquals(DOLLAR(1.2351, 2), 1.23);
275   assertEquals(DOLLAR("$3.141592653589793", "2"), 3.14);
276@@ -172,6 +181,7 @@ test("DOLLAR", function(){
277 
278 
279 test("DOLLARDE", function(){
280+  assertEquals(DOLLARDE(0, Cell.BuildFrom("A1", 32)), 0);
281   assertEquals(DOLLARDE(0, 32), 0);
282   assertEquals(DOLLARDE(100.1, 32), 100.3125);
283   assertEquals(DOLLARDE(100.1, 32.9999), 100.3125);
284@@ -201,6 +211,7 @@ test("DOLLARDE", function(){
285 
286 
287 test("DOLLARFR", function(){
288+  assertEquals(DOLLARFR(100.1, Cell.BuildFrom("A1", 32)), 100.032);
289   assertEquals(DOLLARFR(100.1, 32), 100.032);
290   assertEquals(DOLLARFR(100.1, 32), 100.032);
291   assertEquals(DOLLARFR(100.1, 32.9999), 100.032);
292@@ -230,6 +241,7 @@ test("DOLLARFR", function(){
293 
294 
295 test("EFFECT", function(){
296+  assertEquals(EFFECT(0.99, Cell.BuildFrom("A1", 12)), 1.5890167507927795);
297   assertEquals(EFFECT(0.99, 12), 1.5890167507927795);
298   assertEquals(EFFECT(0.99, 12.111), 1.5890167507927795);
299   assertEquals(EFFECT(0.99, 12.999), 1.5890167507927795);
300diff --git a/tests/Utilities/TypeConverterTest.ts b/tests/Utilities/TypeConverterTest.ts
301index c133ce4..c39d10e 100644
302--- a/tests/Utilities/TypeConverterTest.ts
303+++ b/tests/Utilities/TypeConverterTest.ts
304@@ -2,12 +2,23 @@
305 import * as moment from "moment";
306 import {
307   assertEquals,
308-  test, catchAndAssertEquals
309+  test,
310+  catchAndAssertEquals
311 } from "../Utils/Asserts";
312 import {
313   TypeConverter
314 } from "../../src/Utilities/TypeConverter";
315-import {VALUE_ERROR, REF_ERROR} from "../../src/Errors";
316+import {
317+  VALUE_ERROR,
318+  REF_ERROR, ValueError
319+} from "../../src/Errors";
320+import {
321+  Cell
322+} from "../../src/Cell";
323+
324+var ERROR_CELL = new Cell("A1");
325+ERROR_CELL.setError(new ValueError("Whooops!"));
326+
327 
328 test("TypeConverter.unitsToTimeNumber", function () {
329   assertEquals(TypeConverter.unitsToTimeNumber(10, 10, 10), 0.4237268518518518);
330@@ -62,6 +73,7 @@ test("TypeConverter.momentToNumber", function () {
331 
332 
333 test("TypeConverter.valueToDateNumber", function () {
334+  assertEquals(TypeConverter.valueToDateNumber(Cell.BuildFrom("A1", 10)), 10);
335   assertEquals(TypeConverter.valueToDateNumber(10), 10);
336   assertEquals(TypeConverter.valueToDateNumber("10"), 10);
337   assertEquals(TypeConverter.valueToDateNumber("10.0"), 10);
338@@ -72,6 +84,9 @@ test("TypeConverter.valueToDateNumber", function () {
339   catchAndAssertEquals(function () {
340     TypeConverter.valueToDateNumber(false); // Do not convert boolean
341   }, VALUE_ERROR);
342+  catchAndAssertEquals(function () {
343+    TypeConverter.valueToDateNumber(ERROR_CELL);
344+  }, VALUE_ERROR);
345   catchAndAssertEquals(function () {
346     console.log(TypeConverter.valueToDateNumber("str"));
347   }, VALUE_ERROR);
348@@ -79,6 +94,7 @@ test("TypeConverter.valueToDateNumber", function () {
349 
350 
351 test("TypeConverter.firstValueAsDateNumber", function () {
352+  assertEquals(TypeConverter.firstValueAsDateNumber([Cell.BuildFrom("A1", 10)]), 10);
353   assertEquals(TypeConverter.firstValueAsDateNumber([10]), 10);
354   assertEquals(TypeConverter.firstValueAsDateNumber([[10]]), 10);
355   assertEquals(TypeConverter.firstValueAsDateNumber([[[[[10]]]]]), 10);
356@@ -88,6 +104,9 @@ test("TypeConverter.firstValueAsDateNumber", function () {
357   assertEquals(TypeConverter.firstValueAsDateNumber(["1992-1-2"]), 33605);
358   assertEquals(TypeConverter.firstValueAsDateNumber([false], true), 0);
359   assertEquals(TypeConverter.firstValueAsDateNumber([true], true), 1);
360+  catchAndAssertEquals(function () {
361+    TypeConverter.firstValueAsDateNumber([ERROR_CELL]);
362+  }, VALUE_ERROR);
363   catchAndAssertEquals(function () {
364     TypeConverter.firstValueAsDateNumber([false]); // Do not convert boolean
365   }, VALUE_ERROR);
366@@ -112,6 +131,7 @@ test("TypeConverter.firstValue", function () {
367 
368 
369 test("TypeConverter.valueToTimestampNumber", function () {
370+  assertEquals(TypeConverter.valueToTimestampNumber(Cell.BuildFrom("A1", 10)), 10);
371   assertEquals(TypeConverter.valueToTimestampNumber(10), 10);
372   assertEquals(TypeConverter.valueToTimestampNumber(""), 0);
373   assertEquals(TypeConverter.valueToTimestampNumber("12:00pm"), 0.5);
374@@ -127,10 +147,15 @@ test("TypeConverter.valueToTimestampNumber", function () {
375   catchAndAssertEquals(function () {
376     TypeConverter.valueToTimestampNumber("str");
377   }, VALUE_ERROR);
378+  catchAndAssertEquals(function () {
379+    TypeConverter.valueToTimestampNumber(ERROR_CELL);
380+  }, VALUE_ERROR);
381 });
382 
383 
384 test("TypeConverter.valueToString", function () {
385+  assertEquals(TypeConverter.valueToString(Cell.BuildFrom("A1", 10)), "10");
386+  assertEquals(TypeConverter.valueToString(new Cell("A1")), "");
387   assertEquals(TypeConverter.valueToString(10), "10");
388   assertEquals(TypeConverter.valueToString("10"), "10");
389   assertEquals(TypeConverter.valueToString("This is a string"), "This is a string");
390@@ -138,10 +163,16 @@ test("TypeConverter.valueToString", function () {
391   assertEquals(TypeConverter.valueToString(-0.33824284782334), "-0.33824284782334");
392   assertEquals(TypeConverter.valueToString(false), "FALSE");
393   assertEquals(TypeConverter.valueToString(true), "TRUE");
394+  catchAndAssertEquals(function () {
395+    TypeConverter.valueToString(ERROR_CELL);
396+  }, VALUE_ERROR);
397 });
398 
399 
400 test("TypeConverter.valueToBoolean", function () {
401+  assertEquals(TypeConverter.valueToBoolean(Cell.BuildFrom("A1", 10)), true);
402+  assertEquals(TypeConverter.valueToBoolean(Cell.BuildFrom("A1", 0)), false);
403+  assertEquals(TypeConverter.valueToBoolean(new Cell("A1")), false);
404   assertEquals(TypeConverter.valueToBoolean(10), true);
405   assertEquals(TypeConverter.valueToBoolean(-10), true);
406   assertEquals(TypeConverter.valueToBoolean(1.11111), true);
407@@ -151,10 +182,15 @@ test("TypeConverter.valueToBoolean", function () {
408   catchAndAssertEquals(function () {
409     TypeConverter.valueToBoolean("str");
410   }, VALUE_ERROR);
411+  catchAndAssertEquals(function () {
412+    TypeConverter.valueToBoolean(ERROR_CELL);
413+  }, VALUE_ERROR);
414 });
415 
416 
417 test("TypeConverter.valueToNumber", function () {
418+  assertEquals(TypeConverter.valueToNumber(Cell.BuildFrom("A1", 10)), 10);
419+  assertEquals(TypeConverter.valueToNumber(Cell.BuildFrom("A1", "10")), 10);
420   assertEquals(TypeConverter.valueToNumber(10), 10);
421   assertEquals(TypeConverter.valueToNumber(-10), -10);
422   assertEquals(TypeConverter.valueToNumber(1.11111), 1.11111);
423@@ -171,6 +207,9 @@ test("TypeConverter.valueToNumber", function () {
424   catchAndAssertEquals(function () {
425     TypeConverter.valueToNumber("str");
426   }, VALUE_ERROR);
427+  catchAndAssertEquals(function () {
428+    TypeConverter.valueToNumber(ERROR_CELL);
429+  }, VALUE_ERROR);
430 });
431 
432 
433@@ -241,6 +280,9 @@ test("TypeConverter.stringToNumber", function () {
434 
435 
436 test("TypeConverter.valueToNumberGracefully", function () {
437+  assertEquals(TypeConverter.valueToNumberGracefully(Cell.BuildFrom("A1", "10")), 10);
438+  assertEquals(TypeConverter.valueToNumberGracefully(Cell.BuildFrom("A1", "not-graceful")), 0);
439+  assertEquals(TypeConverter.valueToNumberGracefully(new Cell("A1")), 0);
440   assertEquals(TypeConverter.valueToNumberGracefully(10), 10);
441   assertEquals(TypeConverter.valueToNumberGracefully(-10), -10);
442   assertEquals(TypeConverter.valueToNumberGracefully(1.11111), 1.11111);