commit
message
[ParseEngine] simple number version working
author
Ben Vogt <[email protected]>
date
2017-12-09 20:35:17
stats
2 file(s) changed,
327 insertions(+),
94 deletions(-)
files
src/Parser/ParseEngine.ts
tests/Parser/ParseEngineTest.ts
1diff --git a/src/Parser/ParseEngine.ts b/src/Parser/ParseEngine.ts
2index 7b73fd2..42f5c22 100644
3--- a/src/Parser/ParseEngine.ts
4+++ b/src/Parser/ParseEngine.ts
5@@ -327,7 +327,8 @@ const enum ReduceActions {
6 START_ARRAY = 28,
7 INVERT_NUMBER = 29,
8 EXPRESSION = 30,
9- AS_ARRAY = 31
10+ AS_ARRAY = 31,
11+ REFLEXIVE_REDUCE = 31
12 };
13
14 /**
15@@ -360,6 +361,9 @@ class ReductionPair {
16
17 const enum Tree {
18 START = 0,
19+ NUMBER = 1,
20+ STRING = 2,
21+ BOOLEAN = 3,
22 VARIABLE = 4,
23 ERROR = 5,
24 FORMULA = 6,
25@@ -396,15 +400,15 @@ const enum Tree {
26 */
27 let productions : Array<ReductionPair> = [];
28 productions[ReduceActions.NO_ACTION] = null;
29-productions[ReduceActions.RETURN_LAST] = new ReductionPair(3, 2);
30+productions[ReduceActions.RETURN_LAST] = new ReductionPair(Tree.NUMBER, 2);
31 productions[ReduceActions.CALL_VARIABLE] = new ReductionPair(Tree.VARIABLE, 1);
32-productions[ReduceActions.AS_NUMBER] = new ReductionPair(Tree.VARIABLE, 1);
33+productions[ReduceActions.AS_NUMBER] = new ReductionPair(Tree.NUMBER, 1);
34 productions[ReduceActions.INVERT_NUMBER] = new ReductionPair(Tree.VARIABLE, 1);
35 productions[ReduceActions.AS_STRING] = new ReductionPair(Tree.VARIABLE, 1);
36 productions[ReduceActions.AMPERSAND] = new ReductionPair(Tree.AMPERSAND, 3);
37 productions[ReduceActions.EQUALS] = new ReductionPair(Tree.EQUALS, 3);
38 productions[ReduceActions.PLUS] = new ReductionPair(Tree.PLUS, 3);
39-productions[ReduceActions.LAST_NUMBER] = new ReductionPair(Tree.VARIABLE, 3);
40+productions[ReduceActions.LAST_NUMBER] = new ReductionPair(Tree.NUMBER, 3);
41 productions[ReduceActions.LTE] = new ReductionPair(Tree.VARIABLE, 4);
42 productions[ReduceActions.GTE] = new ReductionPair(Tree.VARIABLE, 4);
43 productions[ReduceActions.NOT_EQ] = new ReductionPair(Tree.VARIABLE, 4);
44@@ -422,6 +426,7 @@ productions[ReduceActions.CELL_RANGE_VALUE] = new ReductionPair(Tree.VARIABLE, 3
45 productions[ReduceActions.PERCENT] = new ReductionPair(Tree.VARIABLE, 3);
46 productions[ReduceActions.AS_ERROR] = new ReductionPair(Tree.ERROR, 1);
47 productions[ReduceActions.AS_ARRAY] = new ReductionPair(Tree.VARIABLE, 1);
48+productions[ReduceActions.REFLEXIVE_REDUCE] = new ReductionPair(Tree.VARIABLE, 1);
49 const PRODUCTIONS = productions;
50
51
52@@ -434,85 +439,16 @@ let table = [];
53 // All functions in the spreadsheet start with a 0-token.
54 // `=`
55 table[Tree.START] = ObjectBuilder
56- .add(Symbol.NUMBER, Tree.VARIABLE)
57+ .add(Symbol.NUMBER, Tree.NUMBER)
58 .add(Symbol.WHITE_SPACE, Tree.START)
59 .add(Symbol.END, Tree.TERMINATE)
60 .build();
61-table[Tree.VARIABLE] = ObjectBuilder
62- .add(Symbol.PLUS, [SHIFT, ReduceActions.PLUS])
63- .add(Symbol.MINUS, [SHIFT, ReduceActions.MINUS])
64- .add(Symbol.ASTERISK, [SHIFT, ReduceActions.MULTIPLY])
65- .add(Symbol.DIVIDE, [SHIFT, ReduceActions.DIVIDE])
66- .add(Symbol.CARROT, [SHIFT, ReduceActions.TO_POWER])
67- .add(Symbol.PERCENT, [REDUCE, ReduceActions.PERCENT])
68- .add(Symbol.AMPERSAND, [SHIFT, ReduceActions.AMPERSAND])
69- .add(Symbol.WHITE_SPACE, Tree.TERMINATE)
70- .add(Symbol.END, Tree.TERMINATE)
71- .build();
72-table[Tree.PLUS] = ObjectBuilder
73- .add(Symbol.VARIABLE, null)
74- .add(Symbol.CELL_REF, null)
75- .add(Symbol.FIXED_CELL_REF, null)
76- .add(Symbol.POUND, null)
77- .add(Symbol.FORMULA, null)
78- .add(Symbol.OPEN_PAREN, null)
79- .add(Symbol.OPEN_ARRAY, null)
80- .add(Symbol.WHITE_SPACE, null)
81- .build();
82-table[Tree.MINUS] = ObjectBuilder
83- .add(Symbol.VARIABLE, null)
84- .add(Symbol.CELL_REF, null)
85- .add(Symbol.FIXED_CELL_REF, null)
86- .add(Symbol.POUND, null)
87- .add(Symbol.FORMULA, null)
88- .add(Symbol.OPEN_PAREN, null)
89- .add(Symbol.OPEN_ARRAY, null)
90- .add(Symbol.WHITE_SPACE, null)
91- .build();
92-table[Tree.ASTERISK] = ObjectBuilder
93- .add(Symbol.VARIABLE, null)
94- .add(Symbol.CELL_REF, null)
95- .add(Symbol.FIXED_CELL_REF, null)
96- .add(Symbol.POUND, null)
97- .add(Symbol.FORMULA, null)
98- .add(Symbol.OPEN_PAREN, null)
99- .add(Symbol.OPEN_ARRAY, null)
100- .add(Symbol.WHITE_SPACE, null)
101- .build();
102-table[Tree.SLASH] = ObjectBuilder
103- .add(Symbol.VARIABLE, null)
104- .add(Symbol.CELL_REF, null)
105- .add(Symbol.FIXED_CELL_REF, null)
106- .add(Symbol.POUND, null)
107- .add(Symbol.FORMULA, null)
108- .add(Symbol.OPEN_PAREN, null)
109- .add(Symbol.OPEN_ARRAY, null)
110- .add(Symbol.WHITE_SPACE, null)
111- .build();
112-table[Tree.CARROT] = ObjectBuilder
113- .add(Symbol.VARIABLE, null)
114- .add(Symbol.CELL_REF, null)
115- .add(Symbol.FIXED_CELL_REF, null)
116- .add(Symbol.POUND, null)
117- .add(Symbol.FORMULA, null)
118- .add(Symbol.OPEN_PAREN, null)
119- .add(Symbol.OPEN_ARRAY, null)
120- .add(Symbol.WHITE_SPACE, null)
121+table[Tree.NUMBER] = ObjectBuilder
122+ .add(Symbol.WHITE_SPACE, Tree.NUMBER)
123+ .add(Symbol.END, [REDUCE, ReduceActions.AS_NUMBER])
124 .build();
125-table[Tree.AMPERSAND] = ObjectBuilder
126- .add(Symbol.VARIABLE, null)
127- .add(Symbol.CELL_REF, null)
128- .add(Symbol.FIXED_CELL_REF, null)
129- .add(Symbol.POUND, null)
130- .add(Symbol.FORMULA, null)
131- .add(Symbol.OPEN_PAREN, null)
132- .add(Symbol.OPEN_ARRAY, null)
133- .add(Symbol.WHITE_SPACE, null)
134- .build();
135-table[Tree.EXPRESSION] = null;
136-table[Tree.INVERT_NEXT] = null;
137 table[Tree.TERMINATE] = ObjectBuilder
138- .add(Symbol.END, [ACCEPT, ReduceActions.RETURN_LAST])
139+ .add(Symbol.END, [REDUCE, ReduceActions.RETURN_LAST])
140 .build();
141 const ACTION_TABLE = table;
142
143@@ -621,6 +557,9 @@ let Parser = (function () {
144 case ReduceActions.AS_ERROR:
145 this.$ = constructErrorByName(virtualStack[vsl]);
146 break;
147+ case ReduceActions.REFLEXIVE_REDUCE:
148+ this.$ = virtualStack[vsl];
149+ break;
150 }
151 } catch (e) {
152 if (catchOnFailure) {
153@@ -767,11 +706,30 @@ let Parser = (function () {
154 }
155 // read action for current state and first input
156 action = ACTION_TABLE[state] && ACTION_TABLE[state][symbol];
157- console.log(action, state, symbol);
158 }
159
160- // handle parse error
161- if (typeof action === 'undefined' || !action.length || !action[0]) {
162+ console.log("STEP");
163+ console.log({
164+ text: lexer.match,
165+ token: SYMBOL_INDEX_TO_NAME[symbol] || symbol,
166+ symbol: symbol,
167+ tokenIndex: symbol,
168+ line: lexer.yylineno,
169+ loc: yyloc,
170+ state: state,
171+ stack: stack,
172+ semanticValueStack: semanticValueStack
173+ });
174+
175+ if (typeof action == "number") {
176+ stack.push(symbol);
177+ semanticValueStack.push(lexer.yytext);
178+ locationStack.push(lexer.yylloc);
179+ stack.push(action);
180+ symbol = null;
181+ continue;
182+ // handle parse error
183+ } else if (typeof action === 'undefined' || !action.length || !action[0]) {
184 let error_rule_depth;
185 let errStr = '';
186
187@@ -1109,7 +1067,6 @@ let Parser = (function () {
188 }
189 if (match) {
190 token = this.test_match(match, rules[index]);
191- console.log("match", match, "index", index, "token", token);
192 if (token !== false) {
193 return token;
194 }
195@@ -1166,7 +1123,7 @@ let Parser = (function () {
196 case 8:
197 return ReduceActions.CALL_FUNCTION_LAST_BLANK;
198 case 9:
199- return ReduceActions.AS_NUMBER;
200+ return Symbol.NUMBER;
201 case 12:
202 return ReduceActions.FIXED_CELL_RANGE_VAL;
203 case 13:
204diff --git a/tests/Parser/ParseEngineTest.ts b/tests/Parser/ParseEngineTest.ts
205index a00d64c..6ff8809 100644
206--- a/tests/Parser/ParseEngineTest.ts
207+++ b/tests/Parser/ParseEngineTest.ts
208@@ -1,6 +1,10 @@
209 import {
210 Parser
211 } from "../../src/Parser/ParseEngine";
212+import {TypeConverter} from "../../src/Utilities/TypeConverter";
213+import {DivZeroError, NameError} from "../../src/Errors";
214+import {Formulas} from "../../src/Formulas";
215+import {assertEquals, test} from "../Utils/Asserts";
216
217
218 let FormulaParser = function(handler) {
219@@ -31,7 +35,286 @@ let FormulaParser = function(handler) {
220 return newParser;
221 };
222
223-let parser = FormulaParser({});
224+let utils = {
225+ isArray: function (value) {
226+ return value instanceof Array;
227+ },
228+
229+ isFunction: function (value) {
230+ return value instanceof Function;
231+ },
232+
233+ toNum: function (chr) {
234+ chr = utils.clearFormula(chr);
235+ let base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', i, j, result = 0;
236+
237+ for (i = 0, j = chr.length - 1; i < chr.length; i += 1, j -= 1) {
238+ result += Math.pow(base.length, j) * (base.indexOf(chr[i]) + 1);
239+ }
240+
241+ if (result) {
242+ --result;
243+ }
244+
245+ return result;
246+ },
247+
248+ toChar: function (num) {
249+ let s = '';
250+
251+ while (num >= 0) {
252+ s = String.fromCharCode(num % 26 + 97) + s;
253+ num = Math.floor(num / 26) - 1;
254+ }
255+
256+ return s.toUpperCase();
257+ },
258+ XYtoA1: function (x, y) {
259+ function numberToLetters(num) {
260+ let mod = num % 26,
261+ pow = num / 26 | 0,
262+ out = mod ? String.fromCharCode(64 + mod) : (--pow, 'Z');
263+ return pow ? numberToLetters(pow) + out : out;
264+ }
265+ return numberToLetters(x+1) + (y+1).toString();
266+ },
267+ cellCoords: function (cell) {
268+ let num = cell.match(/\d+$/),
269+ alpha = cell.replace(num, '');
270+
271+ return {
272+ row: parseInt(num[0], 10) - 1,
273+ col: utils.toNum(alpha)
274+ };
275+ },
276+
277+ clearFormula: function (formula) {
278+ return formula.replace(/\$/g, '');
279+ },
280+
281+ iterateCells: function (startCell, endCell, callback) {
282+ let result = {
283+ index: [], // list of cell index: A1, A2, A3
284+ value: [] // list of cell value
285+ };
286+
287+ let cols = {
288+ start: 0,
289+ end: 0
290+ };
291+
292+ if (endCell.col >= startCell.col) {
293+ cols = {
294+ start: startCell.col,
295+ end: endCell.col
296+ };
297+ } else {
298+ cols = {
299+ start: endCell.col,
300+ end: startCell.col
301+ };
302+ }
303+
304+ let rows = {
305+ start: 0,
306+ end: 0
307+ };
308+
309+ if (endCell.row >= startCell.row) {
310+ rows = {
311+ start: startCell.row,
312+ end: endCell.row
313+ };
314+ } else {
315+ rows = {
316+ start: endCell.row,
317+ end: startCell.row
318+ };
319+ }
320+
321+ for (let column = cols.start; column <= cols.end; column++) {
322+ for (let row = rows.start; row <= rows.end; row++) {
323+ let cellIndex = utils.toChar(column) + (row + 1),
324+ cellValue = helper.cellValue.call(this, cellIndex);
325+
326+ result.index.push(cellIndex);
327+ result.value.push(cellValue);
328+ }
329+ }
330+
331+ if (utils.isFunction(callback)) {
332+ return callback.apply(callback, [result]);
333+ } else {
334+ return result;
335+ }
336+ },
337+
338+ sort: function (rev) {
339+ return function (a, b) {
340+ return ((a < b) ? -1 : ((a > b) ? 1 : 0)) * (rev ? -1 : 1);
341+ }
342+ }
343+};
344+
345+let helper = {
346+ /**
347+ * Is the value a number or can the value be interpreted as a number
348+ */
349+ number: function (x) {
350+ return TypeConverter.valueToNumber(x);
351+ },
352+
353+ string: function (str) {
354+ return str.substring(1, str.length - 1);
355+ },
356+
357+ numberInverted: function (num) {
358+ return this.number(num) * (-1);
359+ },
360+
361+ specialMatch: function (type, exp1, exp2) {
362+ let result;
363+
364+ switch (type) {
365+ case '&':
366+ result = exp1.toString() + exp2.toString();
367+ break;
368+ }
369+ return result;
370+ },
371+
372+ logicMatch: function (type, exp1, exp2) {
373+ let result;
374+
375+ switch (type) {
376+ case '=':
377+ result = (exp1 === exp2);
378+ break;
379+
380+ case '>':
381+ result = (exp1 > exp2);
382+ break;
383+
384+ case '<':
385+ result = (exp1 < exp2);
386+ break;
387+
388+ case '>=':
389+ result = (exp1 >= exp2);
390+ break;
391+
392+ case '<=':
393+ result = (exp1 === exp2);
394+ break;
395+
396+ case '<>':
397+ result = (exp1 != exp2);
398+ break;
399+
400+ case 'NOT':
401+ result = (exp1 != exp2);
402+ break;
403+ }
404+
405+ return result;
406+ },
407+
408+ mathMatch: function (type, number1, number2) {
409+ let result;
410+
411+ number1 = helper.number(number1);
412+ number2 = helper.number(number2);
413+
414+ switch (type) {
415+ case '+':
416+ result = number1 + number2;
417+ break;
418+ case '-':
419+ result = number1 - number2;
420+ break;
421+ case '/':
422+ if (number2 === 0) {
423+ throw new DivZeroError("Evaluation caused divide by zero error.");
424+ }
425+ if (number2 !== 0 && number1 === 0) {
426+ result = 0;
427+ }
428+ result = number1 / number2;
429+ if (result == Infinity) {
430+ throw new DivZeroError("Evaluation caused divide by zero error.");
431+ } else if (isNaN(result)) {
432+ throw new DivZeroError("Evaluation caused divide by zero error.");
433+ }
434+ break;
435+ case '*':
436+ result = number1 * number2;
437+ break;
438+ case '^':
439+ result = Math.pow(number1, number2);
440+ break;
441+ }
442+
443+ return result;
444+ },
445+
446+ callFunction: function (fn, args) {
447+ fn = fn.toUpperCase();
448+ args = args || [];
449+ let formulas = {
450+ "SUM": function(...args) {
451+ return 10;
452+ }
453+ };
454+ if (fn in formulas) {
455+ return formulas[fn].apply(this, args);
456+ }
457+
458+ throw new NameError("Unknown function: '" + fn + "'.");
459+ },
460+
461+ callVariable: function (args) {
462+ args = args || [];
463+ let str = args.shift(); // the first in args is the name of the function to call.
464+
465+ if (str) {
466+ str = str.toUpperCase();
467+ if (Formulas.exists(str)) {
468+ return Formulas.get(str).apply(this, args);
469+ }
470+ }
471+
472+ throw new NameError("Unknown variable: '" + str + "'.");
473+ },
474+
475+ cellValue: function (cellId) {
476+
477+ },
478+
479+ cellRangeValue: function (start: string, end: string) {
480+
481+ },
482+
483+ fixedCellValue: function (id) {
484+ id = id.replace(/\$/g, '');
485+ return helper.cellValue.call(this, id);
486+ },
487+
488+ fixedCellRangeValue: function (start, end) {
489+ start = start.replace(/\$/g, '');
490+ end = end.replace(/\$/g, '');
491+
492+ return helper.cellRangeValue.call(this, start, end);
493+ }
494+};
495+
496+
497+let parser = FormulaParser({
498+ helper: helper,
499+ utils: utils
500+});
501 parser.setObj("A1");
502
503-console.log(parser.parse('5'));
504\ No newline at end of file
505+
506+test("Declare number", function () {
507+ assertEquals(parser.parse('5'), 5);
508+});
509\ No newline at end of file