spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← All files
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}