commit
message
[Parser, TODO.md] documenting work to allow formulas to catch errors, references, etc, by handling Cell objects
author
Ben Vogt <[email protected]>
date
2017-07-08 15:08:02
stats
3 file(s) changed,
108 insertions(+),
58 deletions(-)
files
TODO.md
dist/parser.js
src/Parser.ts
1diff --git a/TODO.md b/TODO.md
2index 3823d59..c1460f9 100644
3--- a/TODO.md
4+++ b/TODO.md
5@@ -5,7 +5,7 @@
6 Instead of having non-primitives, (i.e. Date, DateTime, Time, Dollar), cells should have formats based on the highest-order type that was used during the compilation and execution of a cell's dependency. For example, `DATE` might return a number, but the cell that called `DATE` would be aware of it calling a formula that returns an non-primitive type, and would display the returned number as a Date. If you're using `DATE` in conjunction with `DOLLAR` it would still display the returned value as a Date. The hierarchy would look like: [Date, DateTime, Time, Dollar, number, boolean, string]. Advantages to this would include not having to cast down when using primitive operators, and flexibility in display. It would also simplify the types themselves, by having types be constants and just having helpers to convert, display, and do normal operations with them.
7
8
9-### Sheet should automatically parse some values, unless told otherwises.
10+### Sheet should automatically parse some values, unless told otherwise.
11 When entering raw values into cells, if the value is a string, the Sheet should automatically attempt to convert to a number. For example, `= 10e2` should be be evaluated with a RegEx and converted to a number. See `Sheet.helper.number`.
12
13
14@@ -20,34 +20,41 @@ For example the CHOOSE formula can't be parsed: `=CHOOSE(2, [1, 2, 3])`.
15 For example 64 tbs to a qt.
16
17
18+### Raw input of errors should be allowed.
19+For example `=#N/A` should force an error to be thrown inside the cell.
20+
21+
22 ### Fix documentation regular expression, it is butchering URLs.
23
24
25-### Formulas to write
26+### Meta-Formulas to write
27+In general, many of these formulas can be written by allowing the Sheet and Parser to return Cell objects in addition to primitive types.
28
29-* ERROR.TYPE - Requires changes to Parser.ts
30-* ISBLANK - Requires changes to Parser.ts
31-* ISERR - Requires changes to Parser.ts
32-* ISERROR - Requires changes to Parser.ts
33-* ISFORMULA - Requires changes to Parser.ts
34-* ISNA - Requires changes to Parser.ts
35-* ISREF - Requires changes to Parser.ts
36-* TYPE - Requires changes to Parser.ts
37-* CELL - Requires changes to Parser.ts
38-* IFERROR - Requires changes to Parser.ts
39-* ADDRESS - Requires changes to Parser.ts
40-* COLUMN - Requires changes to Parser.ts
41-* COLUMNS - Requires changes to Parser.ts
42-* HLOOKUP - Requires changes to Parser.ts
43-* INDEX - Requires changes to Parser.ts
44-* INDIRECT - Requires changes to Parser.ts
45-* LOOKUP - Requires changes to Parser.ts
46-* MATCH - Requires changes to Parser.ts
47-* OFFSET - Requires changes to Parser.ts
48-* ROW - Requires changes to Parser.ts
49-* ROWS - Requires changes to Parser.ts
50-* VLOOKUP - Requires changes to Parser.ts
51-* COUNTBLANK - Requires changes to
52+* ISREF - Should be handled at a Parser/Sheet level. If the Parser or Sheet is able to fetch a cell -- even if it's empty -- this function should return true.
53+* ERROR.TYPE - Fetch actual cell to check for error, which requires changes in Parser and Sheet. If the returned cell doesn't have an error, then it should throw NA.
54+* ISBLANK - Requires changes to Parser/Sheet. If we fetch a cell, and it is "blank", return true. Could be implemented by adding a field to cells indicating if they're blank. Right now empty cells are causing errors because they don't exist. We should allow users to fetch empty cells, they should just be considered blank, or undefined, or null.
55+* ISERR - Requires changes to Parser/Sheet to either wrap enclosed functions in try-catch, or fetch actual cell value, and check it's error field.
56+* ISERROR - See ISERR.
57+* ISFORMULA - Requires changes to Parser/Sheet to fetch a cell, and check the formula field to see if it contains a formula.
58+* ISNA - Requires changes to Parser/Sheet for similar reasons to ISERR; check reference cell value or error field.
59+* TYPE - Requires changes to Parser/Sheet to allow for values or cells to be returned to the function. If it's a value, return the value type. If it's a cell, return the value or error inside it.
60+* CELL - Requires changes to Parser/Sheet so that the raw cell is returned to the function. The raw cell should contain all information necessary for returning specified info.
61+* IFERROR - Requires changes to Parser/Sheet for similar reasons to ISERR.
62+* ADDRESS - Requires changes to Parser/Sheet
63+* COLUMN - Requires changes to Parser/Sheet
64+* COLUMNS - Requires changes to Parser/Sheet
65+* HLOOKUP - Requires changes to Parser/Sheet
66+* INDEX - Requires changes to Parser/Sheet
67+* INDIRECT - Requires changes to Parser/Sheet
68+* LOOKUP - Requires changes to Parser/Sheet
69+* MATCH - Requires changes to Parser/Sheet
70+* OFFSET - Requires changes to Parser/Sheet
71+* ROW - Requires changes to Parser/Sheet
72+* ROWS - Requires changes to Parser/Sheet
73+* VLOOKUP - Requires changes to Parser/Sheet
74+* COUNTBLANK - Requires changes to to Parser/Sheet so when we iterate through a range to return an array, we call a special function that accumulates all values, black/null/undefined or otherwise.
75+
76+### Formulas to write
77 * SERIESSUM
78 * SUBTOTAL
79 * TO_DATE - Contingent upon cells having display formats derived from type-hierarchy
80diff --git a/dist/parser.js b/dist/parser.js
81index becf7b6..58653eb 100644
82--- a/dist/parser.js
83+++ b/dist/parser.js
84@@ -235,9 +235,11 @@ var Parser = (function () {
85 }
86 break;
87 case 23:
88+ // console.log("message from parser: 'calling function with no args': ", $$[$0 - 2]);
89 this.$ = yy.handler.helper.callFunction.call(this, $$[$0 - 2], '');
90 break;
91 case 24:
92+ // console.log("message from parser: 'calling function w/ args': ", $$[$0 - 3], $$[$0 - 1]);
93 this.$ = yy.handler.helper.callFunction.call(this, $$[$0 - 3], $$[$0 - 1]);
94 break;
95 case 28:
96@@ -1201,7 +1203,7 @@ var Parser = (function () {
97 }
98 else if (this._backtrack) {
99 match = false;
100- continue; // rule action called reject() implying a rule MISmatch.
101+ continue; // rule action called reject() implying a rule mis-match.
102 }
103 else {
104 // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
105@@ -1371,11 +1373,13 @@ var Parser = (function () {
106 rules: [/^(?:\s+)/,
107 /^(?:"(\\["]|[^"])*")/,
108 /^(?:'(\\[']|[^'])*')/,
109+ // Changed from /^(?:[A-Za-z]{1,}[A-Za-z_0-9]+(?=[(]))/
110 /^(?:[A-Za-z.]{1,}[A-Za-z_0-9]+(?=[(]))/,
111 /^(?:([0]?[1-9]|1[0-2])[:][0-5][0-9]([:][0-5][0-9])?[ ]?(AM|am|aM|Am|PM|pm|pM|Pm))/,
112 /^(?:([0]?[0-9]|1[0-9]|2[0-3])[:][0-5][0-9]([:][0-5][0-9])?)/,
113 /^(?:\$[A-Za-z]+\$[0-9]+)/,
114 /^(?:[A-Za-z]+[0-9]+)/,
115+ // Changed from /^(?:[A-Za-z.]+(?=[(]))/
116 /^(?:[A-Za-z.]+(?=[(]))/,
117 /^(?:[A-Za-z]{1,}[A-Za-z_0-9]+)/,
118 /^(?:[A-Za-z_]+)/,
119diff --git a/src/Parser.ts b/src/Parser.ts
120index 8035127..26a2e90 100644
121--- a/src/Parser.ts
122+++ b/src/Parser.ts
123@@ -234,9 +234,11 @@ var Parser = (function () {
124 }
125 break;
126 case 23:
127+ // console.log("message from parser: 'calling function with no args': ", $$[$0 - 2]);
128 this.$ = yy.handler.helper.callFunction.call(this, $$[$0 - 2], '');
129 break;
130 case 24:
131+ // console.log("message from parser: 'calling function w/ args': ", $$[$0 - 3], $$[$0 - 1]);
132 this.$ = yy.handler.helper.callFunction.call(this, $$[$0 - 3], $$[$0 - 1]);
133 break;
134 case 28:
135@@ -1027,7 +1029,7 @@ var Parser = (function () {
136 }
137 },
138
139-// resets the lexer, sets new input
140+ // resets the lexer, sets new input
141 setInput: function (input, yy) {
142 this.yy = yy || this.yy || {};
143 this._input = input;
144@@ -1048,7 +1050,7 @@ var Parser = (function () {
145 return this;
146 },
147
148-// consumes and returns one char from the input
149+ // consumes and returns one char from the input
150 input: function () {
151 var ch = this._input[0];
152 this.yytext += ch;
153@@ -1071,7 +1073,7 @@ var Parser = (function () {
154 return ch;
155 },
156
157-// unshifts one char (or a string) into the input
158+ // unshifts one char (or a string) into the input
159 unput: function (ch) {
160 var len = ch.length;
161 var lines = ch.split(/(?:\r\n?|\n)/g);
162@@ -1106,13 +1108,13 @@ var Parser = (function () {
163 return this;
164 },
165
166-// When called from action, caches matched text and appends it on next action
167+ // When called from action, caches matched text and appends it on next action
168 more: function () {
169 this._more = true;
170 return this;
171 },
172
173-// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
174+ // When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
175 reject: function () {
176 if (this.options.backtrack_lexer) {
177 this._backtrack = true;
178@@ -1127,18 +1129,18 @@ var Parser = (function () {
179 return this;
180 },
181
182-// retain first n characters of the match
183+ // retain first n characters of the match
184 less: function (n) {
185 this.unput(this.match.slice(n));
186 },
187
188-// displays already matched input, i.e. for error messages
189+ // displays already matched input, i.e. for error messages
190 pastInput: function () {
191 var past = this.matched.substr(0, this.matched.length - this.match.length);
192 return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, "");
193 },
194
195-// displays upcoming input, i.e. for error messages
196+ // displays upcoming input, i.e. for error messages
197 upcomingInput: function () {
198 var next = this.match;
199 if (next.length < 20) {
200@@ -1147,14 +1149,14 @@ var Parser = (function () {
201 return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
202 },
203
204-// displays the character position where the lexing error occurred, i.e. for error messages
205+ // displays the character position where the lexing error occurred, i.e. for error messages
206 showPosition: function () {
207 var pre = this.pastInput();
208 var c = new Array(pre.length + 1).join("-");
209 return pre + this.upcomingInput() + "\n" + c + "^";
210 },
211
212-// test the lexed token: return FALSE when not a match, otherwise return token
213+ // test the lexed token: return FALSE when not a match, otherwise return token
214 test_match: function (match, indexed_rule) {
215 var token,
216 lines,
217@@ -1226,7 +1228,7 @@ var Parser = (function () {
218 return false;
219 },
220
221-// return next match in input
222+ // return next match in input
223 next: function () {
224 if (this.done) {
225 return this.EOF;
226@@ -1255,7 +1257,7 @@ var Parser = (function () {
227 return token;
228 } else if (this._backtrack) {
229 match = false;
230- continue; // rule action called reject() implying a rule MISmatch.
231+ continue; // rule action called reject() implying a rule mis-match.
232 } else {
233 // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
234 return false;
235@@ -1284,7 +1286,7 @@ var Parser = (function () {
236 }
237 },
238
239-// return next match that has a token
240+ // return next match that has a token
241 lex: function lex() {
242 var r = this.next();
243 if (r) {
244@@ -1294,12 +1296,12 @@ var Parser = (function () {
245 }
246 },
247
248-// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
249+ // activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
250 begin: function begin(condition) {
251 this.conditionStack.push(condition);
252 },
253
254-// pop the previously active lexer condition state off the condition stack
255+ // pop the previously active lexer condition state off the condition stack
256 popState: function popState() {
257 var n = this.conditionStack.length - 1;
258 if (n > 0) {
259@@ -1309,7 +1311,7 @@ var Parser = (function () {
260 }
261 },
262
263-// produce the lexer rule set which is active for the currently active lexer condition state
264+ // produce the lexer rule set which is active for the currently active lexer condition state
265 _currentRules: function _currentRules() {
266 if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
267 return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
268@@ -1318,7 +1320,7 @@ var Parser = (function () {
269 }
270 },
271
272-// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
273+ // return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
274 topState: function topState(n) {
275 n = this.conditionStack.length - 1 - Math.abs(n || 0);
276 if (n >= 0) {
277@@ -1328,12 +1330,12 @@ var Parser = (function () {
278 }
279 },
280
281-// alias for begin(condition)
282+ // alias for begin(condition)
283 pushState: function pushState(condition) {
284 this.begin(condition);
285 },
286
287-// return the number of states currently on the stack
288+ // return the number of states currently on the stack
289 stateStackSize: function stateStackSize() {
290 return this.conditionStack.length;
291 },
292@@ -1422,43 +1424,45 @@ var Parser = (function () {
293 }
294 },
295 // NOTE: Alterations made in some regular-expressions to allow for formulas containing dot-notation. Eg: F.INV
296- rules: [/^(?:\s+)/,
297- /^(?:"(\\["]|[^"])*")/,
298- /^(?:'(\\[']|[^'])*')/,
299- /^(?:[A-Za-z.]{1,}[A-Za-z_0-9]+(?=[(]))/, // Changed from /^(?:[A-Za-z]{1,}[A-Za-z_0-9]+(?=[(]))/
300- /^(?:([0]?[1-9]|1[0-2])[:][0-5][0-9]([:][0-5][0-9])?[ ]?(AM|am|aM|Am|PM|pm|pM|Pm))/,
301- /^(?:([0]?[0-9]|1[0-9]|2[0-3])[:][0-5][0-9]([:][0-5][0-9])?)/,
302- /^(?:\$[A-Za-z]+\$[0-9]+)/,
303- /^(?:[A-Za-z]+[0-9]+)/,
304- /^(?:[A-Za-z.]+(?=[(]))/, //Changed from /^(?:[A-Za-z.]+(?=[(]))/
305- /^(?:[A-Za-z]{1,}[A-Za-z_0-9]+)/,
306- /^(?:[A-Za-z_]+)/,
307- /^(?:[0-9]+)/,
308- /^(?:\[(.*)?\])/,
309- /^(?:\$)/,
310- /^(?:&)/,
311- /^(?: )/,
312- /^(?:[.])/,
313- /^(?::)/,
314- /^(?:;)/,
315- /^(?:,)/,
316- /^(?:\*)/,
317- /^(?:\/)/,
318- /^(?:-)/,
319- /^(?:\+)/,
320- /^(?:\^)/,
321- /^(?:\()/,
322- /^(?:\))/,
323- /^(?:>)/,
324- /^(?:<)/,
325- /^(?:NOT\b)/,
326- /^(?:")/,
327- /^(?:')/,
328- /^(?:!)/,
329- /^(?:=)/,
330- /^(?:%)/,
331- /^(?:[#])/,
332- /^(?:$)/],
333+ rules: [/^(?:\s+)/, // rule 0
334+ /^(?:"(\\["]|[^"])*")/, // rule 1
335+ /^(?:'(\\[']|[^'])*')/, // rule 2
336+ // Changed from /^(?:[A-Za-z]{1,}[A-Za-z_0-9]+(?=[(]))/
337+ /^(?:[A-Za-z.]{1,}[A-Za-z_0-9]+(?=[(]))/, // rule 3
338+ /^(?:([0]?[1-9]|1[0-2])[:][0-5][0-9]([:][0-5][0-9])?[ ]?(AM|am|aM|Am|PM|pm|pM|Pm))/, // rule 4
339+ /^(?:([0]?[0-9]|1[0-9]|2[0-3])[:][0-5][0-9]([:][0-5][0-9])?)/, // rule 5
340+ /^(?:\$[A-Za-z]+\$[0-9]+)/, // rule 6
341+ /^(?:[A-Za-z]+[0-9]+)/, // rule 7
342+ // Changed from /^(?:[A-Za-z.]+(?=[(]))/
343+ /^(?:[A-Za-z.]+(?=[(]))/, // rule 8
344+ /^(?:[A-Za-z]{1,}[A-Za-z_0-9]+)/, // rule 9
345+ /^(?:[A-Za-z_]+)/, // rule 10
346+ /^(?:[0-9]+)/, // rule 11
347+ /^(?:\[(.*)?\])/, // rule 12
348+ /^(?:\$)/, // rule 13
349+ /^(?:&)/, // rule 14
350+ /^(?: )/, // rule 15
351+ /^(?:[.])/, // rule 16
352+ /^(?::)/, // rule 17
353+ /^(?:;)/, // rule 18
354+ /^(?:,)/, // rule 19
355+ /^(?:\*)/, // rule 20
356+ /^(?:\/)/, // rule 21
357+ /^(?:-)/, // rule 22
358+ /^(?:\+)/, // rule 23
359+ /^(?:\^)/, // rule 24
360+ /^(?:\()/, // rule 25
361+ /^(?:\))/, // rule 26
362+ /^(?:>)/, // rule 27
363+ /^(?:<)/, // rule 28
364+ /^(?:NOT\b)/, // rule 29
365+ /^(?:")/, // rule 30
366+ /^(?:')/, // rule 31
367+ /^(?:!)/, // rule 32
368+ /^(?:=)/, // rule 33
369+ /^(?:%)/, // rule 34
370+ /^(?:[#])/, // rule 35
371+ /^(?:$)/], // rule 36
372 conditions: {
373 "INITIAL": {
374 "rules": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36],