name:
src/Parser/Parser.ts
-rw-r--r--
35488
1import {
2 constructErrorByName,
3 ParseError
4} from "../Errors";
5import {
6 Formulas
7} from "../Formulas";
8import {
9 Symbol
10} from "./Symbols";
11import {
12 ReduceActions
13} from "./ReduceActions";
14import {
15 ReductionPair
16} from "./ReductionPair";
17import {
18 RuleIndex
19} from "./RuleIndex";
20import {
21 ACTION_TABLE,
22 RULES,
23 REDUCE,
24 ACCEPT,
25 SHIFT,
26 SYMBOL_INDEX_TO_NAME,
27 SYMBOL_NAME_TO_INDEX,
28 PRODUCTIONS
29} from "./ParserConstants"
30import {
31 isArray,
32 isUndefined,
33 string
34} from "../Utilities/MoreUtils";
35import {TypeConverter} from "../Utilities/TypeConverter";
36import {
37 DIVIDE,
38 EQ,
39 GT,
40 GTE,
41 LT,
42 LTE,
43 MINUS,
44 MULTIPLY,
45 POWER,
46 SUM
47} from "../Formulas/Math";
48
49let Parser = (function () {
50 let parser = {
51 lexer: undefined,
52 Parser: undefined,
53 trace: function trace() {},
54 yy: {},
55 /**
56 * Perform a reduce action on the given virtual stack. Basically, fetching, deriving, or calculating a value.
57 * @param rawValueOfReduceOriginToken - Some actions require the origin token to perform a reduce action. For
58 * example, when reducing the cell reference A1 to it's actual value this value would be "A1".
59 * @param sharedStateYY - the shared state that has all helpers, and current working object.
60 * @param reduceActionToPerform - the ReduceAction to perform with the current virtual stack. Since this function
61 * is only called in one place, this should always be action[1] in that context.
62 * @param virtualStack - Array of values to use in action.
63 * @param catchOnFailure - If we are performing an action that could result in a failure, and we cant to catch and
64 * assign the error thrown, this should be set to true.
65 * @returns {number|boolean|string}
66 */
67 performAction: function (rawValueOfReduceOriginToken, sharedStateYY, reduceActionToPerform, virtualStack : Array<any>, catchOnFailure : boolean) {
68 // For context, this function is only called with `apply`, so `this` is `yyval`.
69
70 const vsl = virtualStack.length - 1;
71 try {
72 switch (reduceActionToPerform) {
73 case ReduceActions.ReturnLast:
74 return virtualStack[vsl - 1];
75 case ReduceActions.CallVariable:
76 this.$ = sharedStateYY.handler.callVariable.call(this, virtualStack[vsl]);
77 break;
78 case ReduceActions.AsNumber:
79 this.$ = TypeConverter.valueToNumber(virtualStack[vsl]);
80 break;
81 case ReduceActions.AsString:
82 this.$ = string(virtualStack[vsl]);
83 break;
84 case ReduceActions.Ampersand:
85 this.$ = TypeConverter.valueToString(virtualStack[vsl - 2]) + TypeConverter.valueToString(virtualStack[vsl]);
86 break;
87 case ReduceActions.Equals:
88 this.$ = EQ(virtualStack[vsl - 2], virtualStack[vsl]);
89 break;
90 case ReduceActions.Plus:
91 this.$ = SUM(virtualStack[vsl - 2], virtualStack[vsl]);
92 break;
93 case ReduceActions.LastExpression:
94 this.$ = virtualStack[vsl - 1];
95 break;
96 case ReduceActions.LTE:
97 this.$ = LTE(virtualStack[vsl - 3], virtualStack[vsl]);
98 break;
99 case ReduceActions.GTE:
100 this.$ = GTE(virtualStack[vsl - 3], virtualStack[vsl]);
101 break;
102 case ReduceActions.NotEqual:
103 this.$ = !EQ(virtualStack[vsl - 3], virtualStack[vsl]);
104 break;
105 case ReduceActions.GT:
106 this.$ = GT(virtualStack[vsl - 2], virtualStack[vsl]);
107 break;
108 case ReduceActions.LT:
109 this.$ = LT(virtualStack[vsl - 2], virtualStack[vsl]);
110 break;
111 case ReduceActions.Minus:
112 this.$ = MINUS(virtualStack[vsl - 2], virtualStack[vsl]);
113 break;
114 case ReduceActions.Multiply:
115 this.$ = MULTIPLY(virtualStack[vsl - 2], virtualStack[vsl]);
116 break;
117 case ReduceActions.Divide:
118 this.$ = DIVIDE(virtualStack[vsl - 2], virtualStack[vsl]);
119 break;
120 case ReduceActions.ToPower:
121 this.$ = POWER(virtualStack[vsl - 2], virtualStack[vsl]);
122 break;
123 case ReduceActions.InvertNumber:
124 this.$ = TypeConverter.valueToInvertedNumber(virtualStack[vsl]);
125 if (isNaN(this.$)) {
126 this.$ = 0;
127 }
128 break;
129 case ReduceActions.ToNumberNANAsZero:
130 this.$ = TypeConverter.valueToNumber(virtualStack[vsl]);
131 if (isNaN(this.$)) {
132 this.$ = 0;
133 }
134 break;
135 case ReduceActions.CallFunctionLastBlank:
136 this.$ = sharedStateYY.handler.callFunction.call(this, virtualStack[vsl - 2], '');
137 break;
138 case ReduceActions.CallFunctionLastTwoInStack:
139 this.$ = sharedStateYY.handler.callFunction.call(this, virtualStack[vsl - 3], virtualStack[vsl - 1]);
140 break;
141 case ReduceActions.FixedCellValue:
142 this.$ = sharedStateYY.handler.fixedCellValue(sharedStateYY.originCellId, virtualStack[vsl]);
143 break;
144 case ReduceActions.FixedCellRangeValue:
145 this.$ = sharedStateYY.handler.fixedCellRangeValue(sharedStateYY.originCellId, virtualStack[vsl - 2], virtualStack[vsl]);
146 break;
147 case ReduceActions.CellValue:
148 this.$ = sharedStateYY.handler.cellValue(sharedStateYY.originCellId, virtualStack[vsl]);
149 break;
150 case ReduceActions.CellRangeValue:
151 this.$ = sharedStateYY.handler.cellRangeValue(sharedStateYY.originCellId, virtualStack[vsl - 2], virtualStack[vsl]);
152 break;
153 case ReduceActions.EnsureIsArray:
154 if (isArray(virtualStack[vsl])) {
155 this.$ = virtualStack[vsl];
156 } else {
157 this.$ = [virtualStack[vsl]];
158 }
159 break;
160 case ReduceActions.EnsureYYTextIsArray:
161 let result = [],
162 arr = eval("[" + rawValueOfReduceOriginToken + "]");
163 arr.forEach(function (item) {
164 result.push(item);
165 });
166 this.$ = result;
167 break;
168 case ReduceActions.ReduceInt:
169 case ReduceActions.ReducePercent:
170 virtualStack[vsl - 2].push(virtualStack[vsl]);
171 this.$ = virtualStack[vsl - 2];
172 break;
173 case ReduceActions.WrapCurrentTokenAsArray:
174 this.$ = [virtualStack[vsl]];
175 break;
176 case ReduceActions.EnsureLastTwoINArrayAndPush:
177 this.$ = (isArray(virtualStack[vsl - 2]) ? virtualStack[vsl - 2] : [virtualStack[vsl - 2]]);
178 this.$.push(virtualStack[vsl]);
179 break;
180 case ReduceActions.ReflexiveReduce:
181 this.$ = virtualStack[vsl];
182 break;
183 case ReduceActions.ReduceFloat:
184 this.$ = TypeConverter.valueToNumber(virtualStack[vsl - 2] + '.' + virtualStack[vsl]);
185 break;
186 case ReduceActions.ReducePrevAsPercent:
187 this.$ = virtualStack[vsl - 1] * 0.01;
188 break;
189 case ReduceActions.ReduceLastThreeA:
190 case ReduceActions.ReduceLastThreeB:
191 this.$ = virtualStack[vsl - 2] + virtualStack[vsl - 1] + virtualStack[vsl];
192 break;
193 case ReduceActions.AsError:
194 this.$ = constructErrorByName(virtualStack[vsl]);
195 break;
196 }
197 } catch (e) {
198 if (catchOnFailure) {
199 // NOTE: I'm not sure if some of these ReduceAction map correctly in the case of an error.
200 switch (reduceActionToPerform) {
201 case ReduceActions.ReturnLast:
202 return virtualStack[vsl - 1];
203 case ReduceActions.CallVariable:
204 case ReduceActions.AsNumber:
205 case ReduceActions.AsString:
206 case ReduceActions.Ampersand:
207 case ReduceActions.Equals:
208 case ReduceActions.Plus:
209 case ReduceActions.LastExpression:
210 case ReduceActions.LTE:
211 case ReduceActions.GTE:
212 case ReduceActions.NotEqual:
213 case ReduceActions.GT:
214 case ReduceActions.LT:
215 case ReduceActions.Minus:
216 case ReduceActions.Multiply:
217 case ReduceActions.Divide:
218 case ReduceActions.ToPower:
219 case ReduceActions.CallFunctionLastBlank:
220 case ReduceActions.CallFunctionLastTwoInStack:
221 case ReduceActions.FixedCellValue:
222 case ReduceActions.FixedCellRangeValue:
223 case ReduceActions.CellValue:
224 case ReduceActions.CellRangeValue:
225 this.$ = e;
226 break;
227 case ReduceActions.InvertNumber:
228 this.$ = e;
229 if (isNaN(this.$)) {
230 this.$ = 0;
231 }
232 break;
233 case ReduceActions.ToNumberNANAsZero:
234 this.$ = e;
235 if (isNaN(this.$)) {
236 this.$ = 0;
237 }
238 break;
239 case ReduceActions.EnsureIsArray:
240 if (isArray(virtualStack[vsl])) {
241 this.$ = virtualStack[vsl];
242 } else {
243 this.$ = [virtualStack[vsl]];
244 }
245 break;
246 case ReduceActions.EnsureYYTextIsArray:
247 let result = [],
248 arr = eval("[" + rawValueOfReduceOriginToken + "]");
249 arr.forEach(function (item) {
250 result.push(item);
251 });
252 this.$ = result;
253 break;
254 case ReduceActions.ReduceInt:
255 case ReduceActions.ReducePercent:
256 virtualStack[vsl - 2].push(virtualStack[vsl]);
257 this.$ = virtualStack[vsl - 2];
258 break;
259 case ReduceActions.WrapCurrentTokenAsArray:
260 this.$ = [virtualStack[vsl]];
261 break;
262 case ReduceActions.EnsureLastTwoINArrayAndPush:
263 this.$ = (isArray(virtualStack[vsl - 2]) ? virtualStack[vsl - 2] : [virtualStack[vsl - 2]]);
264 this.$.push(virtualStack[vsl]);
265 break;
266 case ReduceActions.ReflexiveReduce:
267 this.$ = virtualStack[vsl];
268 break;
269 case ReduceActions.ReduceFloat:
270 this.$ = parseFloat(virtualStack[vsl - 2] + '.' + virtualStack[vsl]);
271 break;
272 case ReduceActions.ReducePrevAsPercent:
273 this.$ = virtualStack[vsl - 1] * 0.01;
274 break;
275 case ReduceActions.ReduceLastThreeA:
276 case ReduceActions.ReduceLastThreeB:
277 this.$ = virtualStack[vsl - 2] + virtualStack[vsl - 1] + virtualStack[vsl];
278 break;
279 }
280 } else {
281 throw e;
282 }
283 }
284 },
285 defaultActions: {19: [REDUCE, ReduceActions.ReturnLast]},
286 parseError: function parseError(str, hash) {
287 if (hash.recoverable) {
288 this.trace(str);
289 } else {
290 throw new ParseError(str);
291 }
292 },
293 parse: function parse(input) {
294 let stack = [0],
295 semanticValueStack = [null],
296 locationStack = [],
297 yytext = '',
298 yylineno = 0,
299 yyleng = 0,
300 recovering = 0,
301 TERROR = 2,
302 EOF = 1;
303
304 let args = locationStack.slice.call(arguments, 1);
305 let lexer = Object.create(this.lexer);
306 let sharedState = {
307 yy: {
308 parseError: undefined,
309 lexer: {
310 parseError: undefined
311 },
312 parser: {
313 parseError: undefined
314 }
315 }
316 };
317 // copy state
318 for (let k in this.yy) {
319 if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
320 sharedState.yy[k] = this.yy[k];
321 }
322 }
323
324 lexer.setInput(input, sharedState.yy);
325 sharedState.yy.lexer = lexer;
326 sharedState.yy.parser = this;
327 if (typeof lexer.yylloc == 'undefined') {
328 lexer.yylloc = {};
329 }
330 let yyloc = lexer.yylloc;
331 locationStack.push(yyloc);
332
333 let ranges = lexer.options && lexer.options.ranges;
334
335 if (typeof sharedState.yy.parseError === 'function') {
336 this.parseError = sharedState.yy.parseError;
337 } else {
338 this.parseError = Object.getPrototypeOf(this).parseError;
339 }
340
341 function popStack(n) {
342 stack.length = stack.length - 2 * n;
343 semanticValueStack.length = semanticValueStack.length - n;
344 locationStack.length = locationStack.length - n;
345 }
346
347 function lex() {
348 let token = lexer.lex() || EOF;
349 // if token isn't its numeric value, convert
350 if (typeof token !== 'number') {
351 token = SYMBOL_NAME_TO_INDEX[token] || token;
352 }
353 return token;
354 }
355
356 let symbol,
357 preErrorSymbol,
358 state,
359 action,
360 result,
361 yyval = {
362 $: undefined,
363 _$: undefined
364 },
365 p,
366 newState,
367 expected,
368 catchFailuresOn = false;
369 while (true) {
370 // retrieve state number from top of stack
371 state = stack[stack.length - 1];
372
373 // use default actions if available
374 if (this.defaultActions[state]) {
375 action = this.defaultActions[state];
376 } else {
377 if (typeof symbol == 'undefined'|| symbol === null) {
378 symbol = lex();
379 }
380 // read action for current state and first input
381 action = ACTION_TABLE[state] && ACTION_TABLE[state][symbol];
382 }
383
384 // console.log({
385 // text: lexer.match,
386 // token: SYMBOL_INDEX_TO_NAME[symbol] || symbol,
387 // tokenIndex: symbol,
388 // line: lexer.yylineno,
389 // loc: yyloc,
390 // state: state,
391 // stack: stack,
392 // semanticValueStack: semanticValueStack
393 // });
394
395 // handle parse error
396 if (typeof action === 'undefined' || !action.length || !action[0]) {
397 let error_rule_depth;
398 let errStr = '';
399
400 // Return the rule stack depth where the nearest error rule can be found.
401 // Return FALSE when no error recovery rule was found.
402 this.locateNearestErrorRecoveryRule = function(state) {
403 let stack_probe = stack.length - 1;
404 let depth = 0;
405
406 // try to recover from error
407 for (; ;) {
408 if (isUndefined(state)) {
409 return false;
410 }
411 // check for error recovery rule in this state
412 if ((TERROR.toString()) in ACTION_TABLE[state]) {
413 return depth;
414 }
415 if (state === 0 || stack_probe < 2) {
416 return false; // No suitable error recovery rule available.
417 }
418 stack_probe -= 2; // popStack(1): [symbol, action]
419 state = stack[stack_probe];
420 ++depth;
421 }
422 };
423
424 if (!recovering) {
425 // first see if there's any chance at hitting an error recovery rule:
426 error_rule_depth = this.locateNearestErrorRecoveryRule(state);
427
428 // Report error
429 expected = [];
430 let expectedIndexes = [];
431 let tableState = ACTION_TABLE[state];
432 for (p in ACTION_TABLE[state]) {
433 if (SYMBOL_INDEX_TO_NAME[p] && p > TERROR) {
434 expected.push(SYMBOL_INDEX_TO_NAME[p]);
435 expectedIndexes.push(p);
436 }
437 }
438 if (lexer.showPosition) {
439 errStr = 'Parse error on line ' + (yylineno + 1) + ": " + lexer.showPosition() + " Expecting " + expected.join(', ') + ", got '" + (SYMBOL_INDEX_TO_NAME[symbol] || symbol) + "'";
440 } else {
441 errStr = 'Parse error on line ' + (yylineno + 1) + ": Unexpected " +
442 (symbol == EOF ? "end of input" :
443 ("'" + (SYMBOL_INDEX_TO_NAME[symbol] || symbol) + "'"));
444 }
445 this.parseError(errStr, {
446 text: lexer.match,
447 token: SYMBOL_INDEX_TO_NAME[symbol] || symbol,
448 tokenIndex: symbol,
449 line: lexer.yylineno,
450 loc: yyloc,
451 expected: expected,
452 expectedIndexes: expectedIndexes,
453 state: state,
454 tableState: tableState,
455 stack: stack,
456 semanticValueStack: semanticValueStack,
457 recoverable: (error_rule_depth !== false)
458 });
459 } else if (preErrorSymbol !== EOF) {
460 error_rule_depth = this.locateNearestErrorRecoveryRule(state);
461 }
462
463 // just recovered from another error
464 if (recovering == 3) {
465 if (symbol === EOF || preErrorSymbol === EOF) {
466 throw new ParseError(errStr || 'Parsing halted while starting to recover from another error.');
467 }
468
469 // discard current lookahead and grab another
470 yyleng = lexer.yyleng;
471 yytext = lexer.yytext;
472 yylineno = lexer.yylineno;
473 yyloc = lexer.yylloc;
474 symbol = lex();
475 }
476
477 // try to recover from error
478 if (error_rule_depth === false) {
479 throw new ParseError(errStr || 'Parsing halted. No suitable error recovery rule available.');
480 }
481 popStack(error_rule_depth);
482
483 preErrorSymbol = (symbol == TERROR ? null : symbol); // save the lookahead token
484 symbol = TERROR; // insert generic error symbol as new lookahead
485 state = stack[stack.length - 1];
486 action = ACTION_TABLE[state] && ACTION_TABLE[state][TERROR];
487 recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
488 }
489
490 // this shouldn't happen, unless resolve defaults are off
491 if (action[0] instanceof Array && action.length > 1) {
492 throw new ParseError('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
493 }
494
495 // Available actions:
496 // Shift: continue to process tokens.
497 // Reduce: enough tokens have been gathered to reduce input through evaluation.
498 // Accept: return.
499 switch (action[0]) {
500 case SHIFT: // Shift
501 stack.push(symbol);
502 semanticValueStack.push(lexer.yytext);
503 locationStack.push(lexer.yylloc);
504 stack.push(action[1]); // push state
505 // console.log("SHIFT", "literal", lexer.yytext, " symbol", symbol, " symbol name", SYMBOL_INDEX_TO_NAME[symbol], " action", action,
506 // " stack", stack, " semanticValueStack", semanticValueStack);
507 symbol = null;
508
509 if (Formulas.isTryCatchFormula(lexer.yytext)) {
510 catchFailuresOn = true;
511 }
512
513 if (!preErrorSymbol) { // normal execution/no error
514 yyleng = lexer.yyleng;
515 yytext = lexer.yytext;
516 yylineno = lexer.yylineno;
517 yyloc = lexer.yylloc;
518 if (recovering > 0) {
519 recovering--;
520 }
521 } else {
522 // error just occurred, resume old lookahead f/ before error
523 symbol = preErrorSymbol;
524 preErrorSymbol = null;
525 }
526 break;
527
528 case REDUCE: // Reduce
529 // console.log("REDUCE", "literal", lexer.yytext, " symbol", symbol, " symbol name", SYMBOL_INDEX_TO_NAME[symbol], " action", action,
530 // " stack", stack, " semanticValueStack", semanticValueStack);
531 let currentProduction : ReductionPair = PRODUCTIONS[action[1]];
532
533 let lengthToReduceStackBy = currentProduction.getLengthToReduceStackBy();
534
535 // perform semantic action
536 yyval.$ = semanticValueStack[semanticValueStack.length - lengthToReduceStackBy]; // default to $$ = $1
537 // default location, uses first token for firsts, last for lasts
538 yyval._$ = {
539 first_line: locationStack[locationStack.length - (lengthToReduceStackBy || 1)].first_line,
540 last_line: locationStack[locationStack.length - 1].last_line,
541 first_column: locationStack[locationStack.length - (lengthToReduceStackBy || 1)].first_column,
542 last_column: locationStack[locationStack.length - 1].last_column
543 };
544 if (ranges) {
545 yyval._$.range = [locationStack[locationStack.length - (lengthToReduceStackBy || 1)].range[0], locationStack[locationStack.length - 1].range[1]];
546 }
547 // If we are inside of a formula that should catch errors, then catch and return them.
548 result = this.performAction.apply(yyval, [yytext, sharedState.yy, action[1], semanticValueStack, catchFailuresOn].concat(args));
549
550 if (typeof result !== 'undefined') {
551 return result;
552 }
553
554 // pop off stack
555 if (lengthToReduceStackBy) {
556 stack = stack.slice(0, -1 * lengthToReduceStackBy * 2);
557 semanticValueStack = semanticValueStack.slice(0, -1 * lengthToReduceStackBy);
558 locationStack = locationStack.slice(0, -1 * lengthToReduceStackBy);
559 }
560
561 // push non-terminal (reduce)
562 stack.push(currentProduction.getReplacementSymbol());
563 semanticValueStack.push(yyval.$);
564 locationStack.push(yyval._$);
565 newState = ACTION_TABLE[stack[stack.length - 2]][stack[stack.length - 1]];
566 stack.push(newState);
567 break;
568
569 case ACCEPT:
570 // Accept
571 return true;
572 }
573
574 }
575 }
576 };
577
578 parser.lexer = (function () {
579 return ({
580 EOF: 1,
581
582 parseError: function parseError(str, hash) {
583 if (this.yy.parser) {
584 this.yy.parser.parseError(str, hash);
585 } else {
586 throw new ParseError(str);
587 }
588 },
589
590 // resets the lexer, sets new input
591 setInput: function (input, yy) {
592 this.yy = yy || this.yy || {};
593 this.yy.parseError = function (str, hash) {
594 throw new ParseError(JSON.stringify({
595 name: 'Parser error',
596 message: str,
597 prop: hash
598 }));
599 };
600 this._input = input;
601 this._more = this._backtrack = this.done = false;
602 this.yylineno = this.yyleng = 0;
603 this.yytext = this.matched = this.match = '';
604 this.conditionStack = ['INITIAL'];
605 this.yylloc = {
606 first_line: 1,
607 first_column: 0,
608 last_line: 1,
609 last_column: 0
610 };
611 if (this.options.ranges) {
612 this.yylloc.range = [0, 0];
613 }
614 this.offset = 0;
615 return this;
616 },
617
618 // consumes and returns one char from the input
619 input: function () {
620 let ch = this._input[0];
621 this.yytext += ch;
622 this.yyleng++;
623 this.offset++;
624 this.match += ch;
625 this.matched += ch;
626 let lines = ch.match(/(?:\r\n?|\n).*/g);
627 if (lines) {
628 this.yylineno++;
629 this.yylloc.last_line++;
630 } else {
631 this.yylloc.last_column++;
632 }
633 if (this.options.ranges) {
634 this.yylloc.range[1]++;
635 }
636
637 this._input = this._input.slice(1);
638 return ch;
639 },
640
641 // unshifts one char (or a string) into the input
642 unput: function (ch) {
643 let len = ch.length;
644 let lines = ch.split(/(?:\r\n?|\n)/g);
645
646 this._input = ch + this._input;
647 this.yytext = this.yytext.substr(0, this.yytext.length - len);
648 //this.yyleng -= len;
649 this.offset -= len;
650 let oldLines = this.match.split(/(?:\r\n?|\n)/g);
651 this.match = this.match.substr(0, this.match.length - 1);
652 this.matched = this.matched.substr(0, this.matched.length - 1);
653
654 if (lines.length - 1) {
655 this.yylineno -= lines.length - 1;
656 }
657 let r = this.yylloc.range;
658
659 this.yylloc = {
660 first_line: this.yylloc.first_line,
661 last_line: this.yylineno + 1,
662 first_column: this.yylloc.first_column,
663 last_column: lines ?
664 (lines.length === oldLines.length ? this.yylloc.first_column : 0)
665 + oldLines[oldLines.length - lines.length].length - lines[0].length :
666 this.yylloc.first_column - len
667 };
668
669 if (this.options.ranges) {
670 this.yylloc.range = [r[0], r[0] + this.yyleng - len];
671 }
672 this.yyleng = this.yytext.length;
673 return this;
674 },
675
676 // When called from action, caches matched text and appends it on next action
677 more: function () {
678 this._more = true;
679 return this;
680 },
681
682 // displays already matched input, i.e. for error messages
683 pastInput: function () {
684 let past = this.matched.substr(0, this.matched.length - this.match.length);
685 return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, "");
686 },
687
688 // displays upcoming input, i.e. for error messages
689 upcomingInput: function () {
690 let next = this.match;
691 if (next.length < 20) {
692 next += this._input.substr(0, 20 - next.length);
693 }
694 return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
695 },
696
697 // displays the character position where the lexing error occurred, i.e. for error messages
698 showPosition: function () {
699 let pre = this.pastInput();
700 let c = new Array(pre.length + 1).join("-");
701 return pre + this.upcomingInput() + "\n" + c + "^";
702 },
703
704 // test the lexed token: return FALSE when not a match, otherwise return token
705 testMatch: function (match, indexed_rule) {
706 let token,
707 lines,
708 backup;
709
710 if (this.options.backtrack_lexer) {
711 // save context
712 backup = {
713 yylineno: this.yylineno,
714 yylloc: {
715 first_line: this.yylloc.first_line,
716 last_line: this.last_line,
717 first_column: this.yylloc.first_column,
718 last_column: this.yylloc.last_column
719 },
720 yytext: this.yytext,
721 match: this.match,
722 matches: this.matches,
723 matched: this.matched,
724 yyleng: this.yyleng,
725 offset: this.offset,
726 _more: this._more,
727 _input: this._input,
728 yy: this.yy,
729 conditionStack: this.conditionStack.slice(0),
730 done: this.done
731 };
732 if (this.options.ranges) {
733 backup.yylloc.range = this.yylloc.range.slice(0);
734 }
735 }
736
737 lines = match[0].match(/(?:\r\n?|\n).*/g);
738 if (lines) {
739 this.yylineno += lines.length;
740 }
741 this.yylloc = {
742 first_line: this.yylloc.last_line,
743 last_line: this.yylineno + 1,
744 first_column: this.yylloc.last_column,
745 last_column: lines ?
746 lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
747 this.yylloc.last_column + match[0].length
748 };
749 this.yytext += match[0];
750 this.match += match[0];
751 this.matches = match;
752 this.yyleng = this.yytext.length;
753 if (this.options.ranges) {
754 this.yylloc.range = [this.offset, this.offset += this.yyleng];
755 }
756 this._more = false;
757 this._backtrack = false;
758 this._input = this._input.slice(match[0].length);
759 this.matched += match[0];
760 token = this.mapRuleIndexToSymbolEnumeration(indexed_rule);
761 if (this.done && this._input) {
762 this.done = false;
763 }
764 if (token) {
765 return token;
766 } else if (this._backtrack) {
767 // recover context
768 for (let k in backup) {
769 this[k] = backup[k];
770 }
771 return false; // rule action called reject() implying the next rule should be tested instead.
772 }
773 return false;
774 },
775
776 // return next match in input
777 next: function () {
778 if (this.done) {
779 return this.EOF;
780 }
781 if (!this._input) {
782 this.done = true;
783 }
784
785 let token,
786 match,
787 tempMatch,
788 index;
789 if (!this._more) {
790 this.yytext = '';
791 this.match = '';
792 }
793 let rules = this._currentRules();
794 for (let i = 0; i < rules.length; i++) {
795 tempMatch = this._input.match(RULES[rules[i]]);
796 if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
797 match = tempMatch;
798 index = i;
799 if (this.options.backtrack_lexer) {
800 token = this.testMatch(tempMatch, rules[i]);
801 if (token !== false) {
802 return token;
803 } else if (this._backtrack) {
804 match = false;
805 // rule action called reject() implying a rule mis-match.
806 // implied `continue`
807 } else {
808 // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
809 return false;
810 }
811 } else if (!this.options.flex) {
812 break;
813 }
814 }
815 }
816 if (match) {
817 token = this.testMatch(match, rules[index]);
818 if (token !== false) {
819 return token;
820 }
821 // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
822 return false;
823 }
824 if (this._input === "") {
825 return this.EOF;
826 } else {
827 return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
828 text: "",
829 token: null,
830 line: this.yylineno
831 });
832 }
833 },
834
835 // return next match that has a token
836 lex: function lex() {
837 let r = this.next();
838 if (r) {
839 return r;
840 } else {
841 return this.lex();
842 }
843 },
844
845 // produce the lexer rule set which is active for the currently active lexer condition state
846 _currentRules: function _currentRules() {
847 if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
848 return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
849 } else {
850 return this.conditions.INITIAL.rules;
851 }
852 },
853
854 options: {
855 // backtrack_lexer?
856 // ranges?
857 // flex?
858 },
859
860 mapRuleIndexToSymbolEnumeration: function (ruleIndex) {
861 switch (ruleIndex) {
862 case RuleIndex.WhiteSpace:
863 // skip whitespace
864 break;
865 case RuleIndex.DoubleQuotes:
866 return Symbol.String;
867 case RuleIndex.SingleQuotes:
868 return Symbol.String;
869 case RuleIndex.FormulaName:
870 return Symbol.Function;
871 case RuleIndex.$A1Cell:
872 return Symbol.FixedCell;
873 case RuleIndex.A1Cell:
874 return Symbol.CellUpper;
875 case RuleIndex.FormulaNameSimple:
876 return Symbol.Function;
877 case RuleIndex.Variable:
878 return Symbol.Variable;
879 case RuleIndex.SimpleVariable:
880 return Symbol.Variable;
881 case RuleIndex.Integer:
882 return Symbol.NumberUpper;
883 case RuleIndex.SelfContainedArray:
884 return Symbol.Array;
885 case RuleIndex.DollarSign:
886 // skip whitespace??
887 break;
888 case RuleIndex.Ampersand:
889 return Symbol.Ampersand;
890 case RuleIndex.SingleWhitespace:
891 return ' ';
892 case RuleIndex.Period:
893 return Symbol.Decimal;
894 case RuleIndex.Colon:
895 return Symbol.Colon;
896 case RuleIndex.Semicolon:
897 return Symbol.Semicolon;
898 case RuleIndex.Comma:
899 return Symbol.Comma;
900 case RuleIndex.Asterisk:
901 return Symbol.Asterisk;
902 case RuleIndex.ForwardSlash:
903 return Symbol.Divide;
904 case RuleIndex.Minus:
905 return Symbol.Minus;
906 case RuleIndex.Plus:
907 return Symbol.Plus;
908 case RuleIndex.Caret:
909 return Symbol.Carrot;
910 case RuleIndex.OpenParen:
911 return Symbol.LeftParen;
912 case RuleIndex.CloseParen:
913 return Symbol.RightParen;
914 case RuleIndex.GreaterThan:
915 return Symbol.GreaterThan;
916 case RuleIndex.LessThanSign:
917 return Symbol.LessThan;
918 case RuleIndex.OpenDoubleQuote:
919 return '"';
920 case RuleIndex.OpenSingleQuote:
921 return "'";
922 case RuleIndex.ExclamationPoint:
923 return "!";
924 case RuleIndex.Equals:
925 return Symbol.Equals;
926 case RuleIndex.Percent:
927 return Symbol.Percent;
928 case RuleIndex.FullError:
929 return Symbol.FullError;
930 case RuleIndex.EndOfString:
931 return Symbol.EOF;
932 }
933 },
934 conditions: {
935 INITIAL: {
936 rules: [
937 RuleIndex.WhiteSpace,
938 RuleIndex.DoubleQuotes,
939 RuleIndex.SingleQuotes,
940 RuleIndex.FormulaName,
941 RuleIndex.$A1Cell,
942 RuleIndex.A1Cell,
943 RuleIndex.FormulaNameSimple,
944 RuleIndex.Variable,
945 RuleIndex.SimpleVariable ,
946 RuleIndex.Integer,
947 RuleIndex.SelfContainedArray,
948 RuleIndex.DollarSign,
949 RuleIndex.Ampersand ,
950 RuleIndex.SingleWhitespace,
951 RuleIndex.Period,
952 RuleIndex.Colon,
953 RuleIndex.Semicolon,
954 RuleIndex.Comma,
955 RuleIndex.Asterisk,
956 RuleIndex.ForwardSlash,
957 RuleIndex.Minus,
958 RuleIndex.Plus,
959 RuleIndex.Caret,
960 RuleIndex.OpenParen,
961 RuleIndex.CloseParen,
962 RuleIndex.GreaterThan,
963 RuleIndex.LessThanSign,
964 RuleIndex.OpenDoubleQuote,
965 RuleIndex.OpenSingleQuote,
966 RuleIndex.ExclamationPoint,
967 RuleIndex.Equals,
968 RuleIndex.Percent,
969 RuleIndex.FullError,
970 RuleIndex.EndOfString,
971 37
972 ],
973 "inclusive": true
974 }
975 }
976 });
977 })();
978 function Parser() {
979 this.yy = {};
980 }
981
982 Parser.prototype = parser;
983 parser.Parser = Parser;
984 return new Parser;
985})();
986
987/**
988 * Creates a new FormulaParser, which parses formulas, and does minimal error handling.
989 *
990 * @param handler should be a Sheet, since the parser needs access to fixedCellValue, cellValue, cellRangeValue, and
991 * fixedCellRangeValue
992 * @returns formula parser instance for use with parser.js
993 * @constructor
994 */
995let FormulaParser = function(handler) {
996 let formulaLexer = function () {};
997 formulaLexer.prototype = Parser.lexer;
998
999 let formulaParser = function () {
1000 this.lexer = new formulaLexer();
1001 this.yy = {};
1002 };
1003
1004 formulaParser.prototype = Parser;
1005 let newParser = new formulaParser;
1006 newParser.yy.handler = handler;
1007 return newParser;
1008};
1009
1010export {
1011 FormulaParser
1012}