commit
message
[Sheet] adding documentation
author
Ben Vogt <[email protected]>
date
2017-12-20 02:21:45
stats
4 file(s) changed,
105 insertions(+),
314 deletions(-)
files
src/Parser/HelperUtils.ts
src/Parser/Parser.ts
src/Sheet.ts
src/Utilities/MoreUtils.ts
1diff --git a/src/Parser/HelperUtils.ts b/src/Parser/HelperUtils.ts
2deleted file mode 100644
3index 54f83cd..0000000
4--- a/src/Parser/HelperUtils.ts
5+++ /dev/null
6@@ -1,283 +0,0 @@
7-import {TypeConverter} from "../Utilities/TypeConverter";
8-import {DivZeroError, NameError, RefError} from "../Errors";
9-import {Formulas} from "../Formulas";
10-
11-
12-class HelperUtils {
13- public dataStore: any; // TODO: make this not any.
14-
15- constructor(dataStore: any) {
16- this.dataStore = dataStore;
17- }
18-
19- /**
20- * Is the value a number or can the value be interpreted as a number
21- */
22- number(x) {
23- return TypeConverter.valueToNumber(x);
24- }
25-
26- string(str) {
27- return str.substring(1, str.length - 1);
28- }
29-
30- numberInverted(num) {
31- return this.number(num) * (-1);
32- }
33-
34- specialMatch(type, exp1, exp2) {
35- let result;
36- switch (type) {
37- case '&':
38- result = exp1.toString() + exp2.toString();
39- break;
40- }
41- return result;
42- }
43-
44- logicMatch(type, exp1, exp2) {
45- let result;
46- switch (type) {
47- case '=':
48- result = (exp1 === exp2);
49- break;
50- case '>':
51- result = (exp1 > exp2);
52- break;
53- case '<':
54- result = (exp1 < exp2);
55- break;
56- case '>=':
57- result = (exp1 >= exp2);
58- break;
59- case '<=':
60- result = (exp1 <= exp2);
61- break;
62- case '<>':
63- result = (exp1 != exp2);
64- break;
65- }
66- return result;
67- }
68-
69- mathMatch(type, number1, number2) {
70- let result;
71- number1 = this.number(number1);
72- number2 = this.number(number2);
73- switch (type) {
74- case '+':
75- result = number1 + number2;
76- break;
77- case '-':
78- result = number1 - number2;
79- break;
80- case '/':
81- if (number2 === 0) {
82- throw new DivZeroError("Evaluation caused divide by zero error.");
83- }
84- if (number2 !== 0 && number1 === 0) {
85- result = 0;
86- }
87- result = number1 / number2;
88- if (result == Infinity) {
89- throw new DivZeroError("Evaluation caused divide by zero error.");
90- } else if (isNaN(result)) {
91- throw new DivZeroError("Evaluation caused divide by zero error.");
92- }
93- break;
94- case '*':
95- result = number1 * number2;
96- break;
97- case '^':
98- result = Math.pow(number1, number2);
99- break;
100- }
101- return result;
102- }
103-
104- callFunction (fn, args) {
105- fn = fn.toUpperCase();
106- args = args || [];
107- if (Formulas.exists(fn)) {
108- return Formulas.get(fn).apply(this, args);
109- }
110- throw new NameError("Unknown function: '" + fn + "'.");
111- }
112-
113- callVariable (args) {
114- args = args || [];
115- let str = args.shift(); // the first in args is the name of the function to call.
116- if (str) {
117- str = str.toUpperCase();
118- if (Formulas.exists(str)) {
119- return Formulas.get(str).apply(this, args);
120- }
121- }
122- throw new NameError("Unknown variable: '" + str + "'.");
123- }
124-
125- cellValue (origin, cellId) {
126- let cell = this.dataStore.getCell(cellId);
127-
128- //update dependencies
129- this.dataStore.getCell(origin).updateDependencies([cellId]);
130- // check references error
131- if (cell && cell.getDependencies()) {
132- if (cell.getDependencies().indexOf(cellId) !== -1) {
133- throw new RefError("Reference does not exist.");
134- }
135- }
136- return cell;
137- }
138-
139- cellRangeValue (origin, start: string, end: string) {
140- let coordsStart = this.cellCoords(start);
141- let coordsEnd = this.cellCoords(end);
142-
143- // iterate cells to get values and indexes
144- let cells = this.iterateCells(origin, coordsStart, coordsEnd),
145- result = [];
146- //update dependencies
147- this.dataStore.getCell(origin).updateDependencies(cells.index);
148-
149- result.push(cells.value);
150- return result;
151- }
152-
153- fixedCellValue (origin, id) {
154- id = id.replace(/\$/g, '');
155- return this.cellValue(origin, id);
156- }
157-
158- fixedCellRangeValue (origin, start, end) {
159- start = start.replace(/\$/g, '');
160- end = end.replace(/\$/g, '');
161-
162- return this.cellRangeValue(origin, start, end);
163- }
164-
165- static isArray(value) {
166- return value instanceof Array;
167- }
168-
169- static isFunction(value) {
170- return value instanceof Function;
171- }
172-
173- static toNum(chr) {
174- chr = HelperUtils.clearFormula(chr);
175- let base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', i, j, result = 0;
176-
177- for (i = 0, j = chr.length - 1; i < chr.length; i += 1, j -= 1) {
178- result += Math.pow(base.length, j) * (base.indexOf(chr[i]) + 1);
179- }
180-
181- if (result) {
182- --result;
183- }
184-
185- return result;
186- }
187-
188- toChar(num) {
189- let s = '';
190-
191- while (num >= 0) {
192- s = String.fromCharCode(num % 26 + 97) + s;
193- num = Math.floor(num / 26) - 1;
194- }
195-
196- return s.toUpperCase();
197- }
198-
199- XYtoA1(x, y) {
200- function numberToLetters(num) {
201- let mod = num % 26,
202- pow = num / 26 | 0,
203- out = mod ? String.fromCharCode(64 + mod) : (--pow, 'Z');
204- return pow ? numberToLetters(pow) + out : out;
205- }
206- return numberToLetters(x+1) + (y+1).toString();
207- }
208-
209- cellCoords(cell) {
210- let num = cell.match(/\d+$/),
211- alpha = cell.replace(num, '');
212-
213- return {
214- row: parseInt(num[0], 10) - 1,
215- col: HelperUtils.toNum(alpha)
216- };
217- }
218-
219- static clearFormula(formula) {
220- return formula.replace(/\$/g, '');
221- }
222-
223- iterateCells(origin, startCell, endCell, callback?) {
224- let result = {
225- index: [], // list of cell index: A1, A2, A3
226- value: [] // list of cell value
227- };
228-
229- let cols = {
230- start: 0,
231- end: 0
232- };
233-
234- if (endCell.col >= startCell.col) {
235- cols = {
236- start: startCell.col,
237- end: endCell.col
238- };
239- } else {
240- cols = {
241- start: endCell.col,
242- end: startCell.col
243- };
244- }
245-
246- let rows = {
247- start: 0,
248- end: 0
249- };
250-
251- if (endCell.row >= startCell.row) {
252- rows = {
253- start: startCell.row,
254- end: endCell.row
255- };
256- } else {
257- rows = {
258- start: endCell.row,
259- end: startCell.row
260- };
261- }
262-
263- for (let column = cols.start; column <= cols.end; column++) {
264- for (let row = rows.start; row <= rows.end; row++) {
265- let cellIndex = this.toChar(column) + (row + 1),
266- cellValue = this.cellValue(origin, cellIndex);
267-
268- result.index.push(cellIndex);
269- result.value.push(cellValue);
270- }
271- }
272-
273- if (HelperUtils.isFunction(callback)) {
274- return callback.apply(callback, [result]);
275- } else {
276- return result;
277- }
278- }
279-
280- static sort(rev?) {
281- return function (a, b) {
282- return ((a < b) ? -1 : ((a > b) ? 1 : 0)) * (rev ? -1 : 1);
283- }
284- }
285-}
286-
287-export {
288- HelperUtils
289-}
290\ No newline at end of file
291diff --git a/src/Parser/Parser.ts b/src/Parser/Parser.ts
292index 577d6cb..bc0aefc 100644
293--- a/src/Parser/Parser.ts
294+++ b/src/Parser/Parser.ts
295@@ -25,8 +25,18 @@ import {
296 string
297 } from "../Utilities/MoreUtils";
298 import {TypeConverter} from "../Utilities/TypeConverter";
299-import {DIVIDE, EQ, GT, GTE, LT, LTE, MINUS, MULTIPLY, POWER, SUM} from "../Formulas/Math";
300-
301+import {
302+ DIVIDE,
303+ EQ,
304+ GT,
305+ GTE,
306+ LT,
307+ LTE,
308+ MINUS,
309+ MULTIPLY,
310+ POWER,
311+ SUM
312+} from "../Formulas/Math";
313
314 let Parser = (function () {
315 let parser = {
316diff --git a/src/Sheet.ts b/src/Sheet.ts
317index 98168dc..8d592d8 100644
318--- a/src/Sheet.ts
319+++ b/src/Sheet.ts
320@@ -4,19 +4,16 @@ import {DataStore} from "./Parser/DataStore";
321 import {FormulaParser} from "./Parser/Parser";
322 import {Formulas} from "./Formulas";
323 import {
324- isFunction, characterToNumber, numberToCharacter, convertXYtoA1Coordinates,
325+ numberToCharacter,
326+ convertXYtoA1Coordinates,
327 A1toRowColCoordinates
328 } from "./Utilities/MoreUtils";
329
330-// TODO: Document.
331+/**
332+ * Represents a spreadsheet parser and data-store that act together as a functional spreadsheet.
333+ */
334 class Sheet {
335- private parser = {
336- yy:{
337- obj: undefined
338- },
339- setObj: function (obj: string) {},
340- parse: function (formula: string) {}
341- };
342+ private parser;
343 private dataStore : DataStore;
344
345 constructor() {
346@@ -24,17 +21,22 @@ class Sheet {
347 this.dataStore = new DataStore();
348 }
349
350- iterateCells (origin, startCell, endCell, callback?) {
351+ /**
352+ * Iterate through cells in the data-store, returning the collected cells in the range.
353+ * @param origin
354+ * @param startCell
355+ * @param endCell
356+ * @returns {{index: Array; value: Array}}
357+ */
358+ iterateCells (origin, startCell, endCell) {
359 let result = {
360 index: [], // list of cell index: A1, A2, A3
361 value: [] // list of cell value
362 };
363-
364 let cols = {
365 start: 0,
366 end: 0
367 };
368-
369 if (endCell.col >= startCell.col) {
370 cols = {
371 start: startCell.col,
372@@ -46,12 +48,10 @@ class Sheet {
373 end: startCell.col
374 };
375 }
376-
377 let rows = {
378 start: 0,
379 end: 0
380 };
381-
382 if (endCell.row >= startCell.row) {
383 rows = {
384 start: startCell.row,
385@@ -63,7 +63,6 @@ class Sheet {
386 end: startCell.row
387 };
388 }
389-
390 for (let column = cols.start; column <= cols.end; column++) {
391 for (let row = rows.start; row <= rows.end; row++) {
392 let cellIndex = numberToCharacter(column) + (row + 1),
393@@ -73,14 +72,15 @@ class Sheet {
394 result.value.push(cellValue);
395 }
396 }
397-
398- if (isFunction(callback)) {
399- return callback.apply(callback, [result]);
400- } else {
401- return result;
402- }
403+ return result;
404 }
405
406+ /**
407+ * Call function with given arguments. Used for calling formulas.
408+ * @param fn
409+ * @param args
410+ * @returns {any}
411+ */
412 callFunction(fn, args) {
413 fn = fn.toUpperCase();
414 args = args || [];
415@@ -91,6 +91,11 @@ class Sheet {
416 throw new NameError("Unknown function: '" + fn + "'.");
417 }
418
419+ /**
420+ * Call variable, which could include calling a function.
421+ * @param args
422+ * @returns {any}
423+ */
424 callVariable(args) {
425 args = args || [];
426 let str = args.shift(); // the first in args is the name of the function to call.
427@@ -105,6 +110,12 @@ class Sheet {
428 throw new NameError("Unknown variable: '" + str + "'.");
429 };
430
431+ /**
432+ * Fetch cell, updating dependencies in process.
433+ * @param origin
434+ * @param cellId
435+ * @returns {Cell}
436+ */
437 cellValue(origin, cellId) {
438 let cell = this.dataStore.getCell(cellId);
439
440@@ -119,6 +130,13 @@ class Sheet {
441 return cell;
442 }
443
444+ /**
445+ * Get a range of cells.
446+ * @param origin - the cell id in A1 notation from which this range is being referenced.
447+ * @param {string} start - first cell coordinate (in A1 notation) in iteration
448+ * @param {string} end - final cell coordinate (in A1 notation) in iteration
449+ * @returns {Array}
450+ */
451 cellRangeValue(origin, start: string, end: string) {
452 let coordsStart = A1toRowColCoordinates(start),
453 coordsEnd = A1toRowColCoordinates(end);
454@@ -133,11 +151,24 @@ class Sheet {
455 return result;
456 }
457
458+ /**
459+ * Get a fixed cell value.
460+ * @param origin
461+ * @param id
462+ * @returns {Cell}
463+ */
464 fixedCellValue (origin, id) {
465 id = id.replace(/\$/g, '');
466 return this.cellValue(origin, id);
467 };
468
469+ /**
470+ * Get a fixed cell value range.
471+ * @param origin
472+ * @param start
473+ * @param end
474+ * @returns {Array}
475+ */
476 fixedCellRangeValue(origin, start, end) {
477 start = start.replace(/\$/g, '');
478 end = end.replace(/\$/g, '');
479@@ -145,6 +176,10 @@ class Sheet {
480 return this.cellRangeValue(origin, start, end);
481 };
482
483+ /**
484+ * Recalculate dependencies for a cell.
485+ * @param {Cell} cell
486+ */
487 private recalculateCellDependencies(cell: Cell) {
488 let allDependencies = this.dataStore.getDependencies(cell.getId());
489
490@@ -156,6 +191,11 @@ class Sheet {
491 }
492 }
493
494+ /**
495+ * Executes the formula in a cell.
496+ * @param {Cell} cell
497+ * @returns {{error: Error; result: any} | {error: any; result: any}}
498+ */
499 private calculateCellFormula(cell: Cell) {
500 // to avoid double translate formulas, update cell data in parser
501 let parsed = this.parse(cell.getFormula(), cell.getId());
502@@ -166,6 +206,10 @@ class Sheet {
503 return parsed;
504 }
505
506+ /**
507+ * Add a cell to the data-store, recording and updating dependencies if necessary.
508+ * @param {Cell} cell
509+ */
510 private registerCellInDataStore(cell: Cell) {
511 this.dataStore.addCell(cell);
512 if (cell.hasFormula()) {
513@@ -173,6 +217,12 @@ class Sheet {
514 }
515 }
516
517+ /**
518+ * Parse a formula for a given cellId. This involves all calculations and look-ups.
519+ * @param formula
520+ * @param cellId
521+ * @returns {any}
522+ */
523 public parse(formula, cellId) {
524 let result = null;
525 let error = null;
526@@ -206,6 +256,11 @@ class Sheet {
527 }
528 }
529
530+ /**
531+ * Set a cell's value, by id.
532+ * @param {string} id
533+ * @param {string} value
534+ */
535 public setCell(id: string, value: string) {
536 let cell = new Cell(id);
537 cell.setValue(value.toString());
538@@ -213,6 +268,11 @@ class Sheet {
539 this.recalculateCellDependencies(cell);
540 }
541
542+ /**
543+ * Get a cell from the data-store, returning null if a cell is undefined.
544+ * @param {string} id
545+ * @returns {Cell}
546+ */
547 public getCell(id: string) : Cell {
548 let cell = this.dataStore.getCell(id);
549 if (cell === undefined) {
550@@ -221,6 +281,10 @@ class Sheet {
551 return cell;
552 }
553
554+ /**
555+ * Load an a matrix of cells into the data-store.
556+ * @param {Array<Array<any>>} input
557+ */
558 public load(input: Array<Array<any>>) {
559 for (let y = 0; y < input.length; y++) {
560 for (let x = 0; x < input[0].length; x++) {
561diff --git a/src/Utilities/MoreUtils.ts b/src/Utilities/MoreUtils.ts
562index a5bb31f..71c4b96 100644
563--- a/src/Utilities/MoreUtils.ts
564+++ b/src/Utilities/MoreUtils.ts
565@@ -25,15 +25,6 @@ function isArray(value) : boolean {
566 return value instanceof Array;
567 }
568
569-/**
570- * Returns true if value is instance of a Function.
571- * @param value
572- * @returns {boolean}
573- */
574-function isFunction(value) : boolean {
575- return value instanceof Function;
576-}
577-
578 /**
579 * Alphabetical character to number.
580 * @param chr
581@@ -305,7 +296,6 @@ export {
582 isDefined,
583 isUndefined,
584 isArray,
585- isFunction,
586 string,
587 numberToCharacter,
588 convertXYtoA1Coordinates,