Coverage Analysis ensures systematic exercise of the parser

Note: This is not a live (in-browser) code coverage report. The analysis is performed offline (using node-cover).
Tested revision: b61b064 (dated 4 Aug 2012 ).

/*
    Copyright (C) 2012 Ariya Hidayat 
    Copyright (C) 2012 Mathias Bynens 
    Copyright (C) 2012 Joost-Wim Boekesteijn 
    Copyright (C) 2012 Kris Kowal 
    Copyright (C) 2012 Yusuke Suzuki 
    Copyright (C) 2012 Arpad Borsos 
    Copyright (C) 2011 Ariya Hidayat 
  
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
  
      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
  
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED. IN NO EVENT SHALL  BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
  
  /*jslint bitwise:true plusplus:true */
  /*global esprima:true, define:true, exports:true, window: true,
  throwError: true, createLiteral: true, generateStatement: true,
  parseAssignmentExpression: true, parseBlock: true,
  parseClassExpression: true, parseClassDeclaration: true, parseExpression: true,
  parseFunctionDeclaration: true, parseFunctionExpression: true,
  parseFunctionSourceElements: true, parseVariableIdentifier: true,
  parseImportSpecifier: true,
  parseLeftHandSideExpression: true,
  parseStatement: true, parseSourceElement: true, parseModuleBlock: true, parseConciseBody: true,
  parseYieldExpression: true
  */
  
  (function (factory) {
      'use strict';
  
      // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
      // and plain browser loading,
      if (typeof define === 'function' && define.amd) {
          define(['exports'], factory);
      } else if (typeof exports !== 'undefined') {
          factory(exports);
      } else {
          factory((window.esprima = {}));
      }
  }(function (exports) {
      'use strict';
  
      var Token,
          TokenName,
          Syntax,
          PropertyKind,
          Messages,
          Regex,
          source,
          strict,
          yieldAllowed,
          yieldFound,
          index,
          lineNumber,
          lineStart,
          length,
          buffer,
          state,
          extra;
  
      Token = {
          BooleanLiteral: 1,
          EOF: 2,
          Identifier: 3,
          Keyword: 4,
          NullLiteral: 5,
          NumericLiteral: 6,
          Punctuator: 7,
          StringLiteral: 8
      };
  
      TokenName = {};
      TokenName[Token.BooleanLiteral] = 'Boolean';
      TokenName[Token.EOF] = '';
      TokenName[Token.Identifier] = 'Identifier';
      TokenName[Token.Keyword] = 'Keyword';
      TokenName[Token.NullLiteral] = 'Null';
      TokenName[Token.NumericLiteral] = 'Numeric';
      TokenName[Token.Punctuator] = 'Punctuator';
      TokenName[Token.StringLiteral] = 'String';
  
      Syntax = {
          ArrayExpression: 'ArrayExpression',
          ArrayPattern: 'ArrayPattern',
          ArrowFunctionExpression: 'ArrowFunctionExpression',
          AssignmentExpression: 'AssignmentExpression',
          BinaryExpression: 'BinaryExpression',
          BlockStatement: 'BlockStatement',
          BreakStatement: 'BreakStatement',
          CallExpression: 'CallExpression',
          CatchClause: 'CatchClause',
          ClassBody: 'ClassBody',
          ClassDeclaration: 'ClassDeclaration',
          ClassExpression: 'ClassExpression',
          MethodDefinition: 'MethodDefinition',
          ClassHeritage: 'ClassHeritage',
          ConditionalExpression: 'ConditionalExpression',
          ContinueStatement: 'ContinueStatement',
          DebuggerStatement: 'DebuggerStatement',
          DoWhileStatement: 'DoWhileStatement',
          EmptyStatement: 'EmptyStatement',
          ExportDeclaration: 'ExportDeclaration',
          ExportSpecifier: 'ExportSpecifier',
          ExportSpecifierSet: 'ExportSpecifierSet',
          ExpressionStatement: 'ExpressionStatement',
          ForInStatement: 'ForInStatement',
          ForOfStatement: 'ForOfStatement',
          ForStatement: 'ForStatement',
          FunctionDeclaration: 'FunctionDeclaration',
          FunctionExpression: 'FunctionExpression',
          Glob: 'Glob',
          Identifier: 'Identifier',
          IfStatement: 'IfStatement',
          ImportDeclaration: 'ImportDeclaration',
          ImportSpecifier: 'ImportSpecifier',
          LabeledStatement: 'LabeledStatement',
          Literal: 'Literal',
          LogicalExpression: 'LogicalExpression',
          MemberExpression: 'MemberExpression',
          ModuleDeclaration: 'ModuleDeclaration',
          NewExpression: 'NewExpression',
          ObjectExpression: 'ObjectExpression',
          ObjectPattern: 'ObjectPattern',
          Path:  'Path',
          Program: 'Program',
          Property: 'Property',
          ReturnStatement: 'ReturnStatement',
          SequenceExpression: 'SequenceExpression',
          SwitchCase: 'SwitchCase',
          SwitchStatement: 'SwitchStatement',
          ThisExpression: 'ThisExpression',
          ThrowStatement: 'ThrowStatement',
          TryStatement: 'TryStatement',
          UnaryExpression: 'UnaryExpression',
          UpdateExpression: 'UpdateExpression',
          VariableDeclaration: 'VariableDeclaration',
          VariableDeclarator: 'VariableDeclarator',
          WhileStatement: 'WhileStatement',
          WithStatement: 'WithStatement',
          YieldExpression: 'YieldExpression'
      };
  
      PropertyKind = {
          Data: 1,
          Get: 2,
          Set: 4
      };
  
      // Error messages should be identical to V8.
      Messages = {
          UnexpectedToken:  'Unexpected token %0',
          UnexpectedNumber:  'Unexpected number',
          UnexpectedString:  'Unexpected string',
          UnexpectedIdentifier:  'Unexpected identifier',
          UnexpectedReserved:  'Unexpected reserved word',
          UnexpectedEOS:  'Unexpected end of input',
          NewlineAfterThrow:  'Illegal newline after throw',
          InvalidRegExp: 'Invalid regular expression',
          UnterminatedRegExp:  'Invalid regular expression: missing /',
          InvalidLHSInAssignment:  'Invalid left-hand side in assignment',
          InvalidLHSInForIn:  'Invalid left-hand side in for-in',
          MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
          NoCatchOrFinally:  'Missing catch or finally after try',
          UnknownLabel: 'Undefined label \'%0\'',
          Redeclaration: '%0 \'%1\' has already been declared',
          IllegalContinue: 'Illegal continue statement',
          IllegalBreak: 'Illegal break statement',
          IllegalReturn: 'Illegal return statement',
          IllegalYield: 'Illegal yield expression',
          StrictModeWith:  'Strict mode code may not include a with statement',
          StrictCatchVariable:  'Catch variable may not be eval or arguments in strict mode',
          StrictVarName:  'Variable name may not be eval or arguments in strict mode',
          StrictParamName:  'Parameter name eval or arguments is not allowed in strict mode',
          StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
          StrictFunctionName:  'Function name may not be eval or arguments in strict mode',
          StrictOctalLiteral:  'Octal literals are not allowed in strict mode.',
          StrictDelete:  'Delete of an unqualified identifier in strict mode.',
          StrictDuplicateProperty:  'Duplicate data property in object literal not allowed in strict mode',
          AccessorDataProperty:  'Object literal may not have data and accessor property with the same name',
          AccessorGetSet:  'Object literal may not have multiple get/set accessors with the same name',
          StrictLHSAssignment:  'Assignment to eval or arguments is not allowed in strict mode',
          StrictLHSPostfix:  'Postfix increment/decrement may not have eval or arguments operand in strict mode',
          StrictLHSPrefix:  'Prefix increment/decrement may not have eval or arguments operand in strict mode',
          StrictReservedWord:  'Use of future reserved word in strict mode',
          NoFromAfterImport: 'Missing from after import',
          NoYieldInGenerator: 'Missing yield in generator'
      };
  
      // See also tools/generate-unicode-regex.py.
      Regex = {
          NonAsciiIdentifierStart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'),
          NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]')
      };
  
      // Ensure the condition is true, otherwise throw an error.
      // This is only to have a better contract semantic, i.e. another safety net
      // to catch a logic error. The condition shall be fulfilled in normal case.
      // Do NOT use this to enforce a certain condition on any user input.
  
      function assert(condition, message) {
          if (!condition) {
              throw new Error('ASSERT: ' + message);
          }
      }
  
      function sliceSource(from, to) {
          return source.slice(from, to);
      }
  
      if (typeof 'esprima'[0] === 'undefined') {
          sliceSource = function sliceArraySource(from, to) {
              return source.slice(from, to).join('');
          };
      }
  
      function isDecimalDigit(ch) {
          return '0123456789'.indexOf(ch) >= 0;
      }
  
      function isHexDigit(ch) {
          return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
      }
  
      function isOctalDigit(ch) {
          return '01234567'.indexOf(ch) >= 0;
      }
  
  
      // 7.2 White Space
  
      function isWhiteSpace(ch) {
          return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') ||
              (ch === '\u000C') || (ch === '\u00A0') ||
              (ch.charCodeAt(0) >= 0x1680 &&
               '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0);
      }
  
      // 7.3 Line Terminators
  
      function isLineTerminator(ch) {
          return (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029');
      }
  
      // 7.6 Identifier Names and Identifiers
  
      function isIdentifierStart(ch) {
          return (ch === '$') || (ch === '_') || (ch === '\\') ||
              (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
              ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch));
      }
  
      function isIdentifierPart(ch) {
          return (ch === '$') || (ch === '_') || (ch === '\\') ||
              (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
              ((ch >= '0') && (ch <= '9')) ||
              ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch));
      }
  
      // 7.6.1.2 Future Reserved Words
  
      function isFutureReservedWord(id) {
          switch (id) {
  
          // Future reserved words.
          case 'class':
          case 'enum':
          case 'export':
          case 'extends':
          case 'import':
          case 'super':
              return true;
          }
  
          return false;
      }
  
      function isStrictModeReservedWord(id) {
          switch (id) {
  
          // Strict Mode reserved words.
          case 'implements':
          case 'interface':
          case 'package':
          case 'private':
          case 'protected':
          case 'public':
          case 'static':
          case 'yield':
          case 'let':
              return true;
          }
  
          return false;
      }
  
      function isRestrictedWord(id) {
          return id === 'eval' || id === 'arguments';
      }
  
      // 7.6.1.1 Keywords
  
      function isKeyword(id) {
          var keyword = false;
          switch (id.length) {
          case 2:
              keyword = (id === 'if') || (id === 'in') || (id === 'do');
              break;
          case 3:
              keyword = (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try');
              break;
          case 4:
              keyword = (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with');
              break;
          case 5:
              keyword = (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw');
              break;
          case 6:
              keyword = (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch');
              break;
          case 7:
              keyword = (id === 'default') || (id === 'finally');
              break;
          case 8:
              keyword = (id === 'function') || (id === 'continue') || (id === 'debugger');
              break;
          case 10:
              keyword = (id === 'instanceof');
              break;
          }
  
          if (keyword) {
              return true;
          }
  
          switch (id) {
          // Future reserved words.
          // 'const' is specialized as Keyword in V8.
          case 'const':
              return true;
  
          // For compatiblity to SpiderMonkey and ES.next
          case 'yield':
          case 'let':
              return true;
          }
  
          if (strict && isStrictModeReservedWord(id)) {
              return true;
          }
  
          return isFutureReservedWord(id);
      }
  
      // Return the next character and move forward.
  
      function nextChar() {
          return source[index++];
      }
  
      // 7.4 Comments
  
      function skipComment() {
          var ch, blockComment, lineComment;
  
          blockComment = false;
          lineComment = false;
  
          while (index < length) {
              ch = source[index];
  
              if (lineComment) {
                  ch = nextChar();
                  if (isLineTerminator(ch)) {
                      lineComment = false;
                      if (ch === '\r' && source[index] === '\n') {
                          ++index;
                      }
                      ++lineNumber;
                      lineStart = index;
                  }
              } else if (blockComment) {
                  if (isLineTerminator(ch)) {
                      if (ch === '\r' && source[index + 1] === '\n') {
                          ++index;
                      }
                      ++lineNumber;
                      ++index;
                      lineStart = index;
                      if (index >= length) {
                          throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                      }
                  } else {
                      ch = nextChar();
                      if (index >= length) {
                          throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                      }
                      if (ch === '*') {
                          ch = source[index];
                          if (ch === '/') {
                              ++index;
                              blockComment = false;
                          }
                      }
                  }
              } else if (ch === '/') {
                  ch = source[index + 1];
                  if (ch === '/') {
                      index += 2;
                      lineComment = true;
                  } else if (ch === '*') {
                      index += 2;
                      blockComment = true;
                      if (index >= length) {
                          throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                      }
                  } else {
                      break;
                  }
              } else if (isWhiteSpace(ch)) {
                  ++index;
              } else if (isLineTerminator(ch)) {
                  ++index;
                  if (ch ===  '\r' && source[index] === '\n') {
                      ++index;
                  }
                  ++lineNumber;
                  lineStart = index;
              } else {
                  break;
              }
          }
      }
  
      function scanHexEscape(prefix) {
          var i, len, ch, code = 0;
  
          len = (prefix === 'u') ? 4 : 2;
          for (i = 0; i < len; ++i) {
              if (index < length && isHexDigit(source[index])) {
                  ch = nextChar();
                  code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
              } else {
                  return '';
              }
          }
          return String.fromCharCode(code);
      }
  
      function scanUnicodeCodePointEscape() {
          var ch, code, cu1, cu2;
  
          ch = source[index];
          code = 0;
  
          // At least, one hex digit is required.
          if (ch === '}') {
              throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
          }
  
          while (index < length) {
              ch = nextChar();
              if (!isHexDigit(ch)) {
                  break;
              }
              code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
          }
  
          if (code > 0x10FFFF || ch !== '}') {
              throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
          }
  
          // UTF-16 Encoding
          if (code <= 0xFFFF) {
              return String.fromCharCode(code);
          }
          cu1 = ((code - 0x10000) >> 10) + 0xD800;
          cu2 = ((code - 0x10000) & 1023) + 0xDC00;
          return String.fromCharCode(cu1, cu2);
      }
  
      function scanIdentifier() {
          var ch, start, id, restore;
  
          ch = source[index];
          if (!isIdentifierStart(ch)) {
              return;
          }
  
          start = index;
          if (ch === '\\') {
              ++index;
              if (source[index] !== 'u') {
                  return;
              }
              ++index;
              restore = index;
              ch = scanHexEscape('u');
              if (ch) {
                  if (ch === '\\' || !isIdentifierStart(ch)) {
                      return;
                  }
                  id = ch;
              } else {
                  index = restore;
                  id = 'u';
              }
          } else {
              id = nextChar();
          }
  
          while (index < length) {
              ch = source[index];
              if (!isIdentifierPart(ch)) {
                  break;
              }
              if (ch === '\\') {
                  ++index;
                  if (source[index] !== 'u') {
                      return;
                  }
                  ++index;
                  restore = index;
                  ch = scanHexEscape('u');
                  if (ch) {
                      if (ch === '\\' || !isIdentifierPart(ch)) {
                          return;
                      }
                      id += ch;
                  } else {
                      index = restore;
                      id += 'u';
                  }
              } else {
                  id += nextChar();
              }
          }
  
          // There is no keyword or literal with only one character.
          // Thus, it must be an identifier.
          if (id.length === 1) {
              return {
                  type: Token.Identifier,
                  value: id,
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          if (isKeyword(id)) {
              return {
                  type: Token.Keyword,
                  value: id,
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          // 7.8.1 Null Literals
  
          if (id === 'null') {
              return {
                  type: Token.NullLiteral,
                  value: id,
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          // 7.8.2 Boolean Literals
  
          if (id === 'true' || id === 'false') {
              return {
                  type: Token.BooleanLiteral,
                  value: id,
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          return {
              type: Token.Identifier,
              value: id,
              lineNumber: lineNumber,
              lineStart: lineStart,
              range: [start, index]
          };
      }
  
      // 7.7 Punctuators
  
      function scanPunctuator() {
          var start = index,
              ch1 = source[index],
              ch2,
              ch3,
              ch4;
  
          // Check for most common single-character punctuators.
  
          if (ch1 === ';' || ch1 === '{' || ch1 === '}') {
              ++index;
              return {
                  type: Token.Punctuator,
                  value: ch1,
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          if (ch1 === ',' || ch1 === '(' || ch1 === ')') {
              ++index;
              return {
                  type: Token.Punctuator,
                  value: ch1,
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          // Dot (.) can also start a floating-point number, hence the need
          // to check the next character.
  
          ch2 = source[index + 1];
          if (ch1 === '.' && !isDecimalDigit(ch2)) {
              return {
                  type: Token.Punctuator,
                  value: nextChar(),
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          // Peek more characters.
  
          ch3 = source[index + 2];
          ch4 = source[index + 3];
  
          // 4-character punctuator: >>>=
  
          if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
              if (ch4 === '=') {
                  index += 4;
                  return {
                      type: Token.Punctuator,
                      value: '>>>=',
                      lineNumber: lineNumber,
                      lineStart: lineStart,
                      range: [start, index]
                  };
              }
          }
  
          // 3-character punctuators: === !== >>> <<= >>=
  
          if (ch1 === '=' && ch2 === '=' && ch3 === '=') {
              index += 3;
              return {
                  type: Token.Punctuator,
                  value: '===',
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          if (ch1 === '!' && ch2 === '=' && ch3 === '=') {
              index += 3;
              return {
                  type: Token.Punctuator,
                  value: '!==',
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
              index += 3;
              return {
                  type: Token.Punctuator,
                  value: '>>>',
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          if (ch1 === '<' && ch2 === '<' && ch3 === '=') {
              index += 3;
              return {
                  type: Token.Punctuator,
                  value: '<<=',
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          if (ch1 === '>' && ch2 === '>' && ch3 === '=') {
              index += 3;
              return {
                  type: Token.Punctuator,
                  value: '>>=',
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          // 2-character punctuators: <= >= == != ++ -- << >> && ||
          // += -= *= %= &= |= ^= /=
  
          if (ch2 === '=') {
              if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
                  index += 2;
                  return {
                      type: Token.Punctuator,
                      value: ch1 + ch2,
                      lineNumber: lineNumber,
                      lineStart: lineStart,
                      range: [start, index]
                  };
              }
          }
  
          if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) {
              if ('+-<>&|'.indexOf(ch2) >= 0) {
                  index += 2;
                  return {
                      type: Token.Punctuator,
                      value: ch1 + ch2,
                      lineNumber: lineNumber,
                      lineStart: lineStart,
                      range: [start, index]
                  };
              }
          }
  
          if (ch1 === '=' && ch2 === '>') {
              index += 2;
              return {
                  type: Token.Punctuator,
                  value: '=>',
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
  
          // The remaining 1-character punctuators.
  
          if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) {
              return {
                  type: Token.Punctuator,
                  value: nextChar(),
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [start, index]
              };
          }
      }
  
      // 7.8.3 Numeric Literals
  
      function scanNumericLiteral() {
          var number, start, ch, octal;
  
          ch = source[index];
          assert(isDecimalDigit(ch) || (ch === '.'),
              'Numeric literal must start with a decimal digit or a decimal point');
  
          start = index;
          number = '';
          if (ch !== '.') {
              number = nextChar();
              ch = source[index];
  
              // Hex number starts with '0x'.
              // Octal number starts with '0'.
              // Octal number in ES6 starts with '0o'.
              // Binary number in ES6 starts with '0b'.
              if (number === '0') {
                  if (ch === 'x' || ch === 'X') {
                      number += nextChar();
                      while (index < length) {
                          ch = source[index];
                          if (!isHexDigit(ch)) {
                              break;
                          }
                          number += nextChar();
                      }
  
                      if (number.length <= 2) {
                          // only 0x
                          throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                      }
  
                      if (index < length) {
                          ch = source[index];
                          if (isIdentifierStart(ch)) {
                              throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                          }
                      }
                      return {
                          type: Token.NumericLiteral,
                          value: parseInt(number, 16),
                          lineNumber: lineNumber,
                          lineStart: lineStart,
                          range: [start, index]
                      };
                  } else if (ch === 'b' || ch === 'B') {
                      nextChar();
                      number = '';
  
                      while (index < length) {
                          ch = source[index];
                          if (ch !== '0' && ch !== '1') {
                              break;
                          }
                          number += nextChar();
                      }
  
                      if (number.length === 0) {
                          // only 0b or 0B
                          throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                      }
  
                      if (index < length) {
                          ch = source[index];
                          if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
                              throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                          }
                      }
                      return {
                          type: Token.NumericLiteral,
                          value: parseInt(number, 2),
                          lineNumber: lineNumber,
                          lineStart: lineStart,
                          range: [start, index]
                      };
                  } else if (ch === 'o' || ch === 'O' || isOctalDigit(ch)) {
                      if (isOctalDigit(ch)) {
                          octal = true;
                          number = nextChar();
                      } else {
                          octal = false;
                          nextChar();
                          number = '';
                      }
  
                      while (index < length) {
                          ch = source[index];
                          if (!isOctalDigit(ch)) {
                              break;
                          }
                          number += nextChar();
                      }
  
                      if (number.length === 0) {
                          // only 0o or 0O
                          throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                      }
  
                      if (index < length) {
                          ch = source[index];
                          if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
                              throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                          }
                      }
  
                      return {
                          type: Token.NumericLiteral,
                          value: parseInt(number, 8),
                          octal: octal,
                          lineNumber: lineNumber,
                          lineStart: lineStart,
                          range: [start, index]
                      };
                  }
  
                  // decimal number starts with '0' such as '09' is illegal.
                  if (isDecimalDigit(ch)) {
                      throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                  }
              }
  
              while (index < length) {
                  ch = source[index];
                  if (!isDecimalDigit(ch)) {
                      break;
                  }
                  number += nextChar();
              }
          }
  
          if (ch === '.') {
              number += nextChar();
              while (index < length) {
                  ch = source[index];
                  if (!isDecimalDigit(ch)) {
                      break;
                  }
                  number += nextChar();
              }
          }
  
          if (ch === 'e' || ch === 'E') {
              number += nextChar();
  
              ch = source[index];
              if (ch === '+' || ch === '-') {
                  number += nextChar();
              }
  
              ch = source[index];
              if (isDecimalDigit(ch)) {
                  number += nextChar();
                  while (index < length) {
                      ch = source[index];
                      if (!isDecimalDigit(ch)) {
                          break;
                      }
                      number += nextChar();
                  }
              } else {
                  ch = 'character ' + ch;
                  if (index >= length) {
                      ch = '';
                  }
                  throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
              }
          }
  
          if (index < length) {
              ch = source[index];
              if (isIdentifierStart(ch)) {
                  throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
              }
          }
  
          return {
              type: Token.NumericLiteral,
              value: parseFloat(number),
              lineNumber: lineNumber,
              lineStart: lineStart,
              range: [start, index]
          };
      }
  
      // 7.8.4 String Literals
  
      function scanStringLiteral() {
          var str = '', quote, start, ch, code, unescaped, restore, octal = false;
  
          quote = source[index];
          assert((quote === '\'' || quote === '"'),
              'String literal must starts with a quote');
  
          start = index;
          ++index;
  
          while (index < length) {
              ch = nextChar();
  
              if (ch === quote) {
                  quote = '';
                  break;
              } else if (ch === '\\') {
                  ch = nextChar();
                  if (!isLineTerminator(ch)) {
                      switch (ch) {
                      case 'n':
                          str += '\n';
                          break;
                      case 'r':
                          str += '\r';
                          break;
                      case 't':
                          str += '\t';
                          break;
                      case 'u':
                      case 'x':
                          if (source[index] === '{') {
                              ++index;
                              str += scanUnicodeCodePointEscape();
                          } else {
                              restore = index;
                              unescaped = scanHexEscape(ch);
                              if (unescaped) {
                                  str += unescaped;
                              } else {
                                  index = restore;
                                  str += ch;
                              }
                          }
                          break;
                      case 'b':
                          str += '\b';
                          break;
                      case 'f':
                          str += '\f';
                          break;
                      case 'v':
                          str += '\v';
                          break;
  
                      default:
                          if (isOctalDigit(ch)) {
                              code = '01234567'.indexOf(ch);
  
                              // \0 is not octal escape sequence
                              if (code !== 0) {
                                  octal = true;
                              }
  
                              if (index < length && isOctalDigit(source[index])) {
                                  octal = true;
                                  code = code * 8 + '01234567'.indexOf(nextChar());
  
                                  // 3 digits are only allowed when string starts
                                  // with 0, 1, 2, 3
                                  if ('0123'.indexOf(ch) >= 0 &&
                                          index < length &&
                                          isOctalDigit(source[index])) {
                                      code = code * 8 + '01234567'.indexOf(nextChar());
                                  }
                              }
                              str += String.fromCharCode(code);
                          } else {
                              str += ch;
                          }
                          break;
                      }
                  } else {
                      ++lineNumber;
                      if (ch ===  '\r' && source[index] === '\n') {
                          ++index;
                      }
                  }
              } else if (isLineTerminator(ch)) {
                  break;
              } else {
                  str += ch;
              }
          }
  
          if (quote !== '') {
              throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
          }
  
          return {
              type: Token.StringLiteral,
              value: str,
              octal: octal,
              lineNumber: lineNumber,
              lineStart: lineStart,
              range: [start, index]
          };
      }
  
      function scanRegExp() {
          var str = '', ch, start, pattern, flags, value, classMarker = false, restore;
  
          buffer = null;
          skipComment();
  
          start = index;
          ch = source[index];
          assert(ch === '/', 'Regular expression literal must start with a slash');
          str = nextChar();
  
          while (index < length) {
              ch = nextChar();
              str += ch;
              if (classMarker) {
                  if (ch === ']') {
                      classMarker = false;
                  }
              } else {
                  if (ch === '\\') {
                      ch = nextChar();
                      // ECMA-262 7.8.5
                      if (isLineTerminator(ch)) {
                          throwError({}, Messages.UnterminatedRegExp);
                      }
                      str += ch;
                  } else if (ch === '/') {
                      break;
                  } else if (ch === '[') {
                      classMarker = true;
                  } else if (isLineTerminator(ch)) {
                      throwError({}, Messages.UnterminatedRegExp);
                  }
              }
          }
  
          if (str.length === 1) {
              throwError({}, Messages.UnterminatedRegExp);
          }
  
          // Exclude leading and trailing slash.
          pattern = str.substr(1, str.length - 2);
  
          flags = '';
          while (index < length) {
              ch = source[index];
              if (!isIdentifierPart(ch)) {
                  break;
              }
  
              ++index;
              if (ch === '\\' && index < length) {
                  ch = source[index];
                  if (ch === 'u') {
                      ++index;
                      restore = index;
                      ch = scanHexEscape('u');
                      if (ch) {
                          flags += ch;
                          str += '\\u';
                          for (; restore < index; ++restore) {
                              str += source[restore];
                          }
                      } else {
                          index = restore;
                          flags += 'u';
                          str += '\\u';
                      }
                  } else {
                      str += '\\';
                  }
              } else {
                  flags += ch;
                  str += ch;
              }
          }
  
          try {
              value = new RegExp(pattern, flags);
          } catch (e) {
              throwError({}, Messages.InvalidRegExp);
          }
  
          return {
              literal: str,
              value: value,
              range: [start, index]
          };
      }
  
      function isIdentifierName(token) {
          return token.type === Token.Identifier ||
              token.type === Token.Keyword ||
              token.type === Token.BooleanLiteral ||
              token.type === Token.NullLiteral;
      }
  
      function advance() {
          var ch, token;
  
          skipComment();
  
          if (index >= length) {
              return {
                  type: Token.EOF,
                  lineNumber: lineNumber,
                  lineStart: lineStart,
                  range: [index, index]
              };
          }
  
          token = scanPunctuator();
          if (typeof token !== 'undefined') {
              return token;
          }
  
          ch = source[index];
  
          if (ch === '\'' || ch === '"') {
              return scanStringLiteral();
          }
  
          if (ch === '.' || isDecimalDigit(ch)) {
              return scanNumericLiteral();
          }
  
          token = scanIdentifier();
          if (typeof token !== 'undefined') {
              return token;
          }
  
          throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
      }
  
      function lex() {
          var token;
  
          if (buffer) {
              index = buffer.range[1];
              lineNumber = buffer.lineNumber;
              lineStart = buffer.lineStart;
              token = buffer;
              buffer = null;
              return token;
          }
  
          buffer = null;
          return advance();
      }
  
      function lookahead() {
          var pos, line, start;
  
          if (buffer !== null) {
              return buffer;
          }
  
          pos = index;
          line = lineNumber;
          start = lineStart;
          buffer = advance();
          index = pos;
          lineNumber = line;
          lineStart = start;
  
          return buffer;
      }
  
      function lookahead2() {
          var adv, pos, line, start, result;
  
          // If we are collecting the tokens, don't grab the next one yet.
          adv = (typeof extra.advance === 'function') ? extra.advance : advance;
  
          pos = index;
          line = lineNumber;
          start = lineStart;
  
          // Scan for the next immediate token.
          if (buffer === null) {
              buffer = adv();
          }
          index = buffer.range[1];
          lineNumber = buffer.lineNumber;
          lineStart = buffer.lineStart;
  
          // Grab the token right after.
          result = adv();
          index = pos;
          lineNumber = line;
          lineStart = start;
  
          return result;
      }
  
      // Return true if there is a line terminator before the next token.
  
      function peekLineTerminator() {
          var pos, line, start, found;
  
          pos = index;
          line = lineNumber;
          start = lineStart;
          skipComment();
          found = lineNumber !== line;
          index = pos;
          lineNumber = line;
          lineStart = start;
  
          return found;
      }
  
      // Throw an exception
  
      function throwError(token, messageFormat) {
          var error,
              args = Array.prototype.slice.call(arguments, 2),
              msg = messageFormat.replace(
                  /%(\d)/g,
                  function (whole, index) {
                      return args[index] || '';
                  }
              );
  
          if (typeof token.lineNumber === 'number') {
              error = new Error('Line ' + token.lineNumber + ': ' + msg);
              error.index = token.range[0];
              error.lineNumber = token.lineNumber;
              error.column = token.range[0] - lineStart + 1;
          } else {
              error = new Error('Line ' + lineNumber + ': ' + msg);
              error.index = index;
              error.lineNumber = lineNumber;
              error.column = index - lineStart + 1;
          }
  
          throw error;
      }
  
      function throwErrorTolerant() {
          try {
              throwError.apply(null, arguments);
          } catch (e) {
              if (extra.errors) {
                  extra.errors.push(e);
              } else {
                  throw e;
              }
          }
      }
  
  
      // Throw an exception because of the token.
  
      function throwUnexpected(token) {
          if (token.type === Token.EOF) {
              throwError(token, Messages.UnexpectedEOS);
          }
  
          if (token.type === Token.NumericLiteral) {
              throwError(token, Messages.UnexpectedNumber);
          }
  
          if (token.type === Token.StringLiteral) {
              throwError(token, Messages.UnexpectedString);
          }
  
          if (token.type === Token.Identifier) {
              throwError(token, Messages.UnexpectedIdentifier);
          }
  
          if (token.type === Token.Keyword) {
              if (isFutureReservedWord(token.value)) {
                  throwError(token, Messages.UnexpectedReserved);
              } else if (strict && isStrictModeReservedWord(token.value)) {
                  throwError(token, Messages.StrictReservedWord);
              }
              throwError(token, Messages.UnexpectedToken, token.value);
          }
  
          // BooleanLiteral, NullLiteral, or Punctuator.
          throwError(token, Messages.UnexpectedToken, token.value);
      }
  
      // Expect the next token to match the specified punctuator.
      // If not, an exception will be thrown.
  
      function expect(value) {
          var token = lex();
          if (token.type !== Token.Punctuator || token.value !== value) {
              throwUnexpected(token);
          }
      }
  
      // Expect the next token to match the specified keyword.
      // If not, an exception will be thrown.
  
      function expectKeyword(keyword) {
          var token = lex();
          if (token.type !== Token.Keyword || token.value !== keyword) {
              throwUnexpected(token);
          }
      }
  
      // Return true if the next token matches the specified punctuator.
  
      function match(value) {
          var token = lookahead();
          return token.type === Token.Punctuator && token.value === value;
      }
  
      // Return true if the next token matches the specified keyword
  
      function matchKeyword(keyword) {
          var token = lookahead();
          return token.type === Token.Keyword && token.value === keyword;
      }
  
  
      // Return true if the next token matches the specified contextual keyword
  
      function matchContextualKeyword(keyword) {
          var token = lookahead();
          return token.type === Token.Identifier && token.value === keyword;
      }
  
      // Return true if the next token is an assignment operator
  
      function matchAssign() {
          var token = lookahead(),
              op = token.value;
  
          if (token.type !== Token.Punctuator) {
              return false;
          }
          return op === '=' ||
              op === '*=' ||
              op === '/=' ||
              op === '%=' ||
              op === '+=' ||
              op === '-=' ||
              op === '<<=' ||
              op === '>>=' ||
              op === '>>>=' ||
              op === '&=' ||
              op === '^=' ||
              op === '|=';
      }
  
      function consumeSemicolon() {
          var token, line;
  
          // Catch the very common case first.
          if (source[index] === ';') {
              lex();
              return;
          }
  
          line = lineNumber;
          skipComment();
          if (lineNumber !== line) {
              return;
          }
  
          if (match(';')) {
              lex();
              return;
          }
  
          token = lookahead();
          if (token.type !== Token.EOF && !match('}')) {
              throwUnexpected(token);
          }
          return;
      }
  
      // Return true if provided expression is LeftHandSideExpression
  
      function isLeftHandSide(expr) {
          return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression;
      }
  
      function isAssignableLeftHandSide(expr) {
          return isLeftHandSide(expr) || expr.type === Syntax.ObjectPattern || expr.type === Syntax.ArrayPattern;
      }
  
      // 11.1.4 Array Initialiser
  
      function parseArrayInitialiser() {
          var elements = [],
              undef;
  
          expect('[');
  
          while (!match(']')) {
              if (match(',')) {
                  lex();
                  elements.push(undef);
              } else {
                  elements.push(parseAssignmentExpression());
  
                  if (!match(']')) {
                      expect(',');
                  }
              }
          }
  
          expect(']');
  
          return {
              type: Syntax.ArrayExpression,
              elements: elements
          };
      }
  
      // 11.1.5 Object Initialiser
  
      function parsePropertyFunction(param, options) {
          var previousStrict, previousYieldAllowed, body;
  
          previousStrict = strict;
          previousYieldAllowed = yieldAllowed;
          yieldAllowed = options.generator;
          body = parseConciseBody();
          if (options.name && strict && isRestrictedWord(param[0].name)) {
              throwError(options.name, Messages.StrictParamName);
          }
          if (yieldAllowed && !yieldFound) {
              throwError({}, Messages.NoYieldInGenerator);
          }
          strict = previousStrict;
          yieldAllowed = previousYieldAllowed;
  
          return {
              type: Syntax.FunctionExpression,
              id: null,
              params: param,
              body: body,
              generator: options.generator
          };
      }
  
      function parsePropertyMethodFunction(options) {
          var token, previousStrict, param, params, paramSet, method;
  
          previousStrict = strict;
          strict = true;
          params = [];
  
          expect('(');
  
          if (!match(')')) {
              paramSet = {};
              while (index < length) {
                  token = lookahead();
                  param = parseVariableIdentifier();
                  if (isRestrictedWord(token.value)) {
                      throwError(token, Messages.StrictParamName);
                  }
                  if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
                      throwError(token, Messages.StrictParamDupe);
                  }
                  params.push(param);
                  paramSet[param.name] = true;
                  if (match(')')) {
                      break;
                  }
                  expect(',');
              }
          }
  
          expect(')');
  
          method = parsePropertyFunction(params, { generator: options.generator });
  
          strict = previousStrict;
  
          return method;
      }
  
      function parseObjectPropertyKey() {
          var token = lex();
  
          // Note: This function is called only from parseObjectProperty(), where
          // EOF and Punctuator tokens are already filtered out.
  
          if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
              if (strict && token.octal) {
                  throwError(token, Messages.StrictOctalLiteral);
              }
              return createLiteral(token);
          }
  
          return {
              type: Syntax.Identifier,
              name: token.value
          };
      }
  
      function parseObjectProperty() {
          var token, key, id, param;
  
          token = lookahead();
  
          if (token.type === Token.Identifier) {
  
              id = parseObjectPropertyKey();
  
              // Property Assignment: Getter and Setter.
  
              if (token.value === 'get' && !(match(':') || match('('))) {
                  key = parseObjectPropertyKey();
                  expect('(');
                  expect(')');
                  return {
                      type: Syntax.Property,
                      key: key,
                      value: parsePropertyFunction([], { generator: false }),
                      kind: 'get'
                  };
              } else if (token.value === 'set' && !(match(':') || match('('))) {
                  key = parseObjectPropertyKey();
                  expect('(');
                  token = lookahead();
                  param = [ parseVariableIdentifier() ];
                  expect(')');
                  return {
                      type: Syntax.Property,
                      key: key,
                      value: parsePropertyFunction(param, { generator: false, name: token }),
                      kind: 'set'
                  };
              } else {
                  if (match(':')) {
                      lex();
                      return {
                          type: Syntax.Property,
                          key: id,
                          value: parseAssignmentExpression(),
                          kind: 'init'
                      };
                  } else if (match('(')) {
                      return {
                          type: Syntax.Property,
                          key: id,
                          value: parsePropertyMethodFunction({ generator: false }),
                          kind: 'init',
                          method: true
                      };
                  } else {
                      return {
                          type: Syntax.Property,
                          key: id,
                          value: id,
                          kind: 'init',
                          shorthand: true
                      };
                  }
              }
          } else if (token.type === Token.EOF || token.type === Token.Punctuator) {
              if (!match('*')) {
                  throwUnexpected(token);
              }
              lex();
  
              id = parseObjectPropertyKey();
  
              if (!match('(')) {
                  throwUnexpected(lex());
              }
  
              return {
                  type: Syntax.Property,
                  key: id,
                  value: parsePropertyMethodFunction({ generator: true }),
                  kind: 'init',
                  method: true
              };
          } else {
              key = parseObjectPropertyKey();
              if (match(':')) {
                  lex();
                  return {
                      type: Syntax.Property,
                      key: key,
                      value: parseAssignmentExpression(),
                      kind: 'init'
                  };
              } else if (match('(')) {
                  return {
                      type: Syntax.Property,
                      key: key,
                      value: parsePropertyMethodFunction({ generator: false }),
                      kind: 'init',
                      method: true
                  };
              } else {
                  return {
                      type: Syntax.Property,
                      key: key,
                      value: key,
                      kind: 'init',
                      shorthand: true
                  };
              }
          }
      }
  
      function parseObjectInitialiser() {
          var properties = [], property, name, kind, map = {}, toString = String;
  
          expect('{');
  
          while (!match('}')) {
              property = parseObjectProperty();
  
              if (property.key.type === Syntax.Identifier) {
                  name = property.key.name;
              } else {
                  name = toString(property.key.value);
              }
              kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set;
              if (Object.prototype.hasOwnProperty.call(map, name)) {
                  if (map[name] === PropertyKind.Data) {
                      if (strict && kind === PropertyKind.Data) {
                          throwErrorTolerant({}, Messages.StrictDuplicateProperty);
                      } else if (kind !== PropertyKind.Data) {
                          throwError({}, Messages.AccessorDataProperty);
                      }
                  } else {
                      if (kind === PropertyKind.Data) {
                          throwError({}, Messages.AccessorDataProperty);
                      } else if (map[name] & kind) {
                          throwError({}, Messages.AccessorGetSet);
                      }
                  }
                  map[name] |= kind;
              } else {
                  map[name] = kind;
              }
  
              properties.push(property);
  
              if (!match('}')) {
                  expect(',');
              }
          }
  
          expect('}');
  
          return {
              type: Syntax.ObjectExpression,
              properties: properties
          };
      }
  
      // 11.1 Primary Expressions
  
      function parsePrimaryExpression() {
          var expr,
              token = lookahead(),
              type = token.type;
  
          if (type === Token.Identifier) {
              return {
                  type: Syntax.Identifier,
                  name: lex().value
              };
          }
  
          if (type === Token.StringLiteral || type === Token.NumericLiteral) {
              if (strict && token.octal) {
                  throwErrorTolerant(token, Messages.StrictOctalLiteral);
              }
              return createLiteral(lex());
          }
  
          if (type === Token.Keyword) {
              if (matchKeyword('this')) {
                  lex();
                  return {
                      type: Syntax.ThisExpression
                  };
              }
  
              if (matchKeyword('function')) {
                  return parseFunctionExpression();
              }
  
              if (matchKeyword('class')) {
                  return parseClassExpression();
              }
  
              if (matchKeyword('super')) {
                  lex();
                  return {
                      type: Syntax.Identifier,
                      name: 'super'
                  };
              }
          }
  
          if (type === Token.BooleanLiteral) {
              lex();
              token.value = (token.value === 'true');
              return createLiteral(token);
          }
  
          if (type === Token.NullLiteral) {
              lex();
              token.value = null;
              return createLiteral(token);
          }
  
          if (match('[')) {
              return parseArrayInitialiser();
          }
  
          if (match('{')) {
              return parseObjectInitialiser();
          }
  
          if (match('(')) {
              lex();
              state.lastParenthesized = expr = parseExpression();
              state.parenthesizedCount += 1;
              expect(')');
              return expr;
          }
  
          if (match('/') || match('/=')) {
              return createLiteral(scanRegExp());
          }
  
          return throwUnexpected(lex());
      }
  
      // 11.2 Left-Hand-Side Expressions
  
      function parseArguments() {
          var args = [];
  
          expect('(');
  
          if (!match(')')) {
              while (index < length) {
                  args.push(parseAssignmentExpression());
                  if (match(')')) {
                      break;
                  }
                  expect(',');
              }
          }
  
          expect(')');
  
          return args;
      }
  
      function parseNonComputedProperty() {
          var token = lex();
  
          if (!isIdentifierName(token)) {
              throwUnexpected(token);
          }
  
          return {
              type: Syntax.Identifier,
              name: token.value
          };
      }
  
      function parseNonComputedMember(object) {
          return {
              type: Syntax.MemberExpression,
              computed: false,
              object: object,
              property: parseNonComputedProperty()
          };
      }
  
      function parseComputedMember(object) {
          var property, expr;
  
          expect('[');
          property = parseExpression();
          expr = {
              type: Syntax.MemberExpression,
              computed: true,
              object: object,
              property: property
          };
          expect(']');
          return expr;
      }
  
      function parseCallMember(object) {
          return {
              type: Syntax.CallExpression,
              callee: object,
              'arguments': parseArguments()
          };
      }
  
      function parseNewExpression() {
          var expr;
  
          expectKeyword('new');
  
          expr = {
              type: Syntax.NewExpression,
              callee: parseLeftHandSideExpression(),
              'arguments': []
          };
  
          if (match('(')) {
              expr['arguments'] = parseArguments();
          }
  
          return expr;
      }
  
      function parseLeftHandSideExpressionAllowCall() {
          var useNew, expr;
  
          useNew = matchKeyword('new');
          expr = useNew ? parseNewExpression() : parsePrimaryExpression();
  
          while (index < length) {
              if (match('.')) {
                  lex();
                  expr = parseNonComputedMember(expr);
              } else if (match('[')) {
                  expr = parseComputedMember(expr);
              } else if (match('(')) {
                  expr = parseCallMember(expr);
              } else {
                  break;
              }
          }
  
          return expr;
      }
  
      function parseLeftHandSideExpression() {
          var useNew, expr;
  
          useNew = matchKeyword('new');
          expr = useNew ? parseNewExpression() : parsePrimaryExpression();
  
          while (index < length) {
              if (match('.')) {
                  lex();
                  expr = parseNonComputedMember(expr);
              } else if (match('[')) {
                  expr = parseComputedMember(expr);
              } else {
                  break;
              }
          }
  
          return expr;
      }
  
      // 11.3 Postfix Expressions
  
      function parsePostfixExpression() {
          var expr = parseLeftHandSideExpressionAllowCall();
  
          if ((match('++') || match('--')) && !peekLineTerminator()) {
              // 11.3.1, 11.3.2
              if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
                  throwError({}, Messages.StrictLHSPostfix);
              }
  
              if (!isLeftHandSide(expr)) {
                  throwError({}, Messages.InvalidLHSInAssignment);
              }
  
              expr = {
                  type: Syntax.UpdateExpression,
                  operator: lex().value,
                  argument: expr,
                  prefix: false
              };
          }
  
          return expr;
      }
  
      // 11.4 Unary Operators
  
      function parseUnaryExpression() {
          var token, expr;
  
          if (match('++') || match('--')) {
              token = lex();
              expr = parseUnaryExpression();
              // 11.4.4, 11.4.5
              if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
                  throwError({}, Messages.StrictLHSPrefix);
              }
  
              if (!isLeftHandSide(expr)) {
                  throwError({}, Messages.InvalidLHSInAssignment);
              }
  
              expr = {
                  type: Syntax.UpdateExpression,
                  operator: token.value,
                  argument: expr,
                  prefix: true
              };
              return expr;
          }
  
          if (match('+') || match('-') || match('~') || match('!')) {
              expr = {
                  type: Syntax.UnaryExpression,
                  operator: lex().value,
                  argument: parseUnaryExpression()
              };
              return expr;
          }
  
          if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
              expr = {
                  type: Syntax.UnaryExpression,
                  operator: lex().value,
                  argument: parseUnaryExpression()
              };
              if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) {
                  throwErrorTolerant({}, Messages.StrictDelete);
              }
              return expr;
          }
  
          return parsePostfixExpression();
      }
  
      // 11.5 Multiplicative Operators
  
      function parseMultiplicativeExpression() {
          var expr = parseUnaryExpression();
  
          while (match('*') || match('/') || match('%')) {
              expr = {
                  type: Syntax.BinaryExpression,
                  operator: lex().value,
                  left: expr,
                  right: parseUnaryExpression()
              };
          }
  
          return expr;
      }
  
      // 11.6 Additive Operators
  
      function parseAdditiveExpression() {
          var expr = parseMultiplicativeExpression();
  
          while (match('+') || match('-')) {
              expr = {
                  type: Syntax.BinaryExpression,
                  operator: lex().value,
                  left: expr,
                  right: parseMultiplicativeExpression()
              };
          }
  
          return expr;
      }
  
      // 11.7 Bitwise Shift Operators
  
      function parseShiftExpression() {
          var expr = parseAdditiveExpression();
  
          while (match('<<') || match('>>') || match('>>>')) {
              expr = {
                  type: Syntax.BinaryExpression,
                  operator: lex().value,
                  left: expr,
                  right: parseAdditiveExpression()
              };
          }
  
          return expr;
      }
      // 11.8 Relational Operators
  
      function parseRelationalExpression() {
          var expr, previousAllowIn;
  
          previousAllowIn = state.allowIn;
          state.allowIn = true;
  
          expr = parseShiftExpression();
  
          while (match('<') || match('>') || match('<=') || match('>=') || (previousAllowIn && matchKeyword('in')) || matchKeyword('instanceof')) {
              expr = {
                  type: Syntax.BinaryExpression,
                  operator: lex().value,
                  left: expr,
                  right: parseShiftExpression()
              };
          }
  
          state.allowIn = previousAllowIn;
          return expr;
      }
  
      // 11.9 Equality Operators
  
      function parseEqualityExpression() {
          var expr = parseRelationalExpression();
  
          while ((!peekLineTerminator() && (matchContextualKeyword('is') || matchContextualKeyword('isnt'))) || match('==') || match('!=') || match('===') || match('!==')) {
              expr = {
                  type: Syntax.BinaryExpression,
                  operator: lex().value,
                  left: expr,
                  right: parseRelationalExpression()
              };
          }
  
          return expr;
      }
  
      // 11.10 Binary Bitwise Operators
  
      function parseBitwiseANDExpression() {
          var expr = parseEqualityExpression();
  
          while (match('&')) {
              lex();
              expr = {
                  type: Syntax.BinaryExpression,
                  operator: '&',
                  left: expr,
                  right: parseEqualityExpression()
              };
          }
  
          return expr;
      }
  
      function parseBitwiseXORExpression() {
          var expr = parseBitwiseANDExpression();
  
          while (match('^')) {
              lex();
              expr = {
                  type: Syntax.BinaryExpression,
                  operator: '^',
                  left: expr,
                  right: parseBitwiseANDExpression()
              };
          }
  
          return expr;
      }
  
      function parseBitwiseORExpression() {
          var expr = parseBitwiseXORExpression();
  
          while (match('|')) {
              lex();
              expr = {
                  type: Syntax.BinaryExpression,
                  operator: '|',
                  left: expr,
                  right: parseBitwiseXORExpression()
              };
          }
  
          return expr;
      }
  
      // 11.11 Binary Logical Operators
  
      function parseLogicalANDExpression() {
          var expr = parseBitwiseORExpression();
  
          while (match('&&')) {
              lex();
              expr = {
                  type: Syntax.LogicalExpression,
                  operator: '&&',
                  left: expr,
                  right: parseBitwiseORExpression()
              };
          }
  
          return expr;
      }
  
      function parseLogicalORExpression() {
          var expr = parseLogicalANDExpression();
  
          while (match('||')) {
              lex();
              expr = {
                  type: Syntax.LogicalExpression,
                  operator: '||',
                  left: expr,
                  right: parseLogicalANDExpression()
              };
          }
  
          return expr;
      }
  
      // 11.12 Conditional Operator
  
      function parseConditionalExpression() {
          var expr, previousAllowIn, consequent;
  
          expr = parseLogicalORExpression();
  
          if (match('?')) {
              lex();
              previousAllowIn = state.allowIn;
              state.allowIn = true;
              consequent = parseAssignmentExpression();
              state.allowIn = previousAllowIn;
              expect(':');
  
              expr = {
                  type: Syntax.ConditionalExpression,
                  test: expr,
                  consequent: consequent,
                  alternate: parseAssignmentExpression()
              };
          }
  
          return expr;
      }
  
      // 11.13 Assignment Operators
  
      function reinterpretAsAssignmentBindingPattern(expr) {
          var i, len, property, element;
  
          if (expr.type === Syntax.ObjectExpression) {
              expr.type = Syntax.ObjectPattern;
              for (i = 0, len = expr.properties.length; i < len; i += 1) {
                  property = expr.properties[i];
                  if (property.kind !== 'init') {
                      throwError({}, Messages.InvalidLHSInAssignment);
                  }
                  reinterpretAsAssignmentBindingPattern(property.value);
              }
          } else if (expr.type === Syntax.ArrayExpression) {
              expr.type = Syntax.ArrayPattern;
              for (i = 0, len = expr.elements.length; i < len; i += 1) {
                  element = expr.elements[i];
                  if (element) {
                      reinterpretAsAssignmentBindingPattern(element);
                  }
              }
          } else if (expr.type === Syntax.Identifier) {
              if (isRestrictedWord(expr.name)) {
                  throwError({}, Messages.InvalidLHSInAssignment);
              }
          } else {
              if (expr.type !== Syntax.MemberExpression && expr.type !== Syntax.CallExpression && expr.type !== Syntax.NewExpression) {
                  throwError({}, Messages.InvalidLHSInAssignment);
              }
          }
      }
  
      function reinterpretAsCoverFormalsList(expr) {
          var i, len, param, paramSet;
          assert(expr.type === Syntax.SequenceExpression);
  
          paramSet = {};
  
          for (i = 0, len = expr.expressions.length; i < len; i += 1) {
              param = expr.expressions[i];
              if (param.type !== Syntax.Identifier) {
                  return null;
              }
              if (isRestrictedWord(param.name)) {
                  throwError({}, Messages.StrictParamName);
              }
              if (Object.prototype.hasOwnProperty.call(paramSet, param.name)) {
                  throwError({}, Messages.StrictParamDupe);
              }
              paramSet[param.name] = true;
          }
          return expr.expressions;
      }
  
      function parseArrowFunctionExpression(param) {
          var previousStrict, previousYieldAllowed, body;
  
          expect('=>');
  
          previousStrict = strict;
          previousYieldAllowed = yieldAllowed;
          strict = true;
          yieldAllowed = false;
          body = parseConciseBody();
          strict = previousStrict;
          yieldAllowed = previousYieldAllowed;
  
          return {
              type: Syntax.ArrowFunctionExpression,
              id: null,
              params: param,
              body: body
          };
      }
  
      function parseAssignmentExpression() {
          var expr, token, oldParenthesizedCount, coverFormalsList;
  
          if (matchKeyword('yield')) {
              return parseYieldExpression();
          }
  
          oldParenthesizedCount = state.parenthesizedCount;
  
          if (match('(')) {
              token = lookahead2();
              if (token.type === Token.Punctuator && token.value === ')') {
                  lex();
                  lex();
                  if (!match('=>')) {
                      throwUnexpected(lex());
                  }
                  return parseArrowFunctionExpression([]);
              }
          }
  
          expr = parseConditionalExpression();
  
          if (match('=>')) {
              if (expr.type === Syntax.Identifier) {
                  if (state.parenthesizedCount === oldParenthesizedCount || state.parenthesizedCount === (oldParenthesizedCount + 1)) {
                      if (isRestrictedWord(expr.name)) {
                          throwError({}, Messages.StrictParamName);
                      }
                      return parseArrowFunctionExpression([ expr ]);
                  }
              } else if (expr.type === Syntax.SequenceExpression) {
                  if (state.parenthesizedCount === (oldParenthesizedCount + 1)) {
                      coverFormalsList = reinterpretAsCoverFormalsList(expr);
                      if (coverFormalsList) {
                          return parseArrowFunctionExpression(coverFormalsList);
                      }
                  }
              }
          }
  
          if (matchAssign()) {
              // 11.13.1
              if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
                  throwError({}, Messages.StrictLHSAssignment);
              }
  
              // ES.next draf 11.13 Runtime Semantics step 1
              if (match('=') && (expr.type === Syntax.ObjectExpression || expr.type === Syntax.ArrayExpression)) {
                  reinterpretAsAssignmentBindingPattern(expr);
              } else if (!isLeftHandSide(expr)) {
                  throwError({}, Messages.InvalidLHSInAssignment);
              }
  
              expr = {
                  type: Syntax.AssignmentExpression,
                  operator: lex().value,
                  left: expr,
                  right: parseAssignmentExpression()
              };
          }
  
          return expr;
      }
  
      // 11.14 Comma Operator
  
      function parseExpression() {
          var expr = parseAssignmentExpression();
  
          if (match(',')) {
              expr = {
                  type: Syntax.SequenceExpression,
                  expressions: [ expr ]
              };
  
              while (index < length) {
                  if (!match(',')) {
                      break;
                  }
                  lex();
                  expr.expressions.push(parseAssignmentExpression());
              }
  
          }
          return expr;
      }
  
      // 12.1 Block
  
      function parseStatementList() {
          var list = [],
              statement;
  
          while (index < length) {
              if (match('}')) {
                  break;
              }
              statement = parseSourceElement();
              if (typeof statement === 'undefined') {
                  break;
              }
              list.push(statement);
          }
  
          return list;
      }
  
      function parseBlock() {
          var block;
  
          expect('{');
  
          block = parseStatementList();
  
          expect('}');
  
          return {
              type: Syntax.BlockStatement,
              body: block
          };
      }
  
      // 12.2 Variable Statement
  
      function parseVariableIdentifier() {
          var token = lex();
  
          if (token.type !== Token.Identifier) {
              throwUnexpected(token);
          }
  
          return {
              type: Syntax.Identifier,
              name: token.value
          };
      }
  
      function parseVariableDeclaration(kind) {
          var id = parseVariableIdentifier(),
              init = null;
  
          // 12.2.1
          if (strict && isRestrictedWord(id.name)) {
              throwErrorTolerant({}, Messages.StrictVarName);
          }
  
          if (kind === 'const') {
              expect('=');
              init = parseAssignmentExpression();
          } else if (match('=')) {
              lex();
              init = parseAssignmentExpression();
          }
  
          return {
              type: Syntax.VariableDeclarator,
              id: id,
              init: init
          };
      }
  
      function parseVariableDeclarationList(kind) {
          var list = [];
  
          while (index < length) {
              list.push(parseVariableDeclaration(kind));
              if (!match(',')) {
                  break;
              }
              lex();
          }
  
          return list;
      }
  
      function parseVariableStatement() {
          var declarations;
  
          expectKeyword('var');
  
          declarations = parseVariableDeclarationList();
  
          consumeSemicolon();
  
          return {
              type: Syntax.VariableDeclaration,
              declarations: declarations,
              kind: 'var'
          };
      }
  
      // kind may be `const` or `let`
      // Both are experimental and not in the specification yet.
      // see http://wiki.ecmascript.org/doku.php?id=harmony:const
      // and http://wiki.ecmascript.org/doku.php?id=harmony:let
      function parseConstLetDeclaration(kind) {
          var declarations;
  
          expectKeyword(kind);
  
          declarations = parseVariableDeclarationList(kind);
  
          consumeSemicolon();
  
          return {
              type: Syntax.VariableDeclaration,
              declarations: declarations,
              kind: kind
          };
      }
  
      // http://wiki.ecmascript.org/doku.php?id=harmony:modules
  
      function parsePath() {
          var result, id;
  
          result = {
              type: Syntax.Path,
              body: []
          };
  
          while (true) {
              id = parseVariableIdentifier();
              result.body.push(id);
              if (!match('.')) {
                  break;
              }
              lex();
          }
  
          return result;
      }
  
      function parseGlob() {
          expect('*');
          return {
              type: Syntax.Glob
          };
      }
  
      function parseModuleDeclaration() {
          var id, token, declaration;
  
          token = lex();
          if (token.value !== 'module') {
              throwUnexpected(token);
          }
  
          id = parseVariableIdentifier();
  
          if (match('{')) {
              return {
                  type: Syntax.ModuleDeclaration,
                  id: id,
                  body: parseModuleBlock()
              };
          }
  
          expect('=');
  
          token = lookahead();
          if (token.type === Token.StringLiteral) {
              declaration = {
                  type: Syntax.ModuleDeclaration,
                  id: id,
                  from: parsePrimaryExpression()
              };
          } else {
              declaration = {
                  type: Syntax.ModuleDeclaration,
                  id: id,
                  from: parsePath()
              };
          }
  
          consumeSemicolon();
  
          return declaration;
      }
  
      function parseExportSpecifierSetProperty() {
          var specifier;
  
          specifier = {
              type: Syntax.ExportSpecifier,
              id: parseVariableIdentifier(),
              from: null
          };
  
          if (match(':')) {
              lex();
              specifier.from = parsePath();
          }
  
          return specifier;
      }
  
      function parseExportSpecifier() {
          var specifier, specifiers;
  
          if (match('{')) {
              lex();
              specifiers = [];
  
              do {
                  specifiers.push(parseExportSpecifierSetProperty());
              } while (match(',') && lex());
  
              expect('}');
  
              return {
                  type: Syntax.ExportSpecifierSet,
                  specifiers: specifiers
              };
          }
  
          if (match('*')) {
              specifier = {
                  type: Syntax.ExportSpecifier,
                  id: parseGlob(),
                  from: null
              };
  
              if (matchContextualKeyword('from')) {
                  lex();
                  specifier.from = parsePath();
              }
          } else {
              specifier = {
                  type: Syntax.ExportSpecifier,
                  id: parseVariableIdentifier(),
                  from: null
              };
          }
          return specifier;
      }
  
      function parseExportDeclaration() {
          var token, specifiers;
  
          expectKeyword('export');
  
          token = lookahead();
  
          if (token.type === Token.Keyword || (token.type === Token.Identifier && token.value === 'module')) {
              switch (token.value) {
              case 'function':
                  return {
                      type: Syntax.ExportDeclaration,
                      declaration: parseFunctionDeclaration()
                  };
              case 'module':
                  return {
                      type: Syntax.ExportDeclaration,
                      declaration: parseModuleDeclaration()
                  };
              case 'let':
              case 'const':
                  return {
                      type: Syntax.ExportDeclaration,
                      declaration: parseConstLetDeclaration(token.value)
                  };
              case 'var':
                  return {
                      type: Syntax.ExportDeclaration,
                      declaration: parseStatement()
                  };
              }
              throwUnexpected(lex());
          }
  
          specifiers = [ parseExportSpecifier() ];
          if (match(',')) {
              while (index < length) {
                  if (!match(',')) {
                      break;
                  }
                  lex();
                  specifiers.push(parseExportSpecifier());
              }
          }
  
          consumeSemicolon();
  
          return {
              type: Syntax.ExportDeclaration,
              specifiers: specifiers
          };
      }
  
      function parseImportDeclaration() {
          var specifiers, from;
  
          expectKeyword('import');
  
          if (match('*')) {
              specifiers = [parseGlob()];
          } else if (match('{')) {
              lex();
              specifiers = [];
  
              do {
                  specifiers.push(parseImportSpecifier());
              } while (match(',') && lex());
  
              expect('}');
          } else {
              specifiers = [parseVariableIdentifier()];
          }
  
          if (!matchContextualKeyword('from')) {
              throwError({}, Messages.NoFromAfterImport);
          }
  
          lex();
  
          if (lookahead().type === Token.StringLiteral) {
              from = parsePrimaryExpression();
          } else {
              from = parsePath();
          }
  
          consumeSemicolon();
  
          return {
              type: Syntax.ImportDeclaration,
              specifiers: specifiers,
              from: from
          };
      }
  
      function parseImportSpecifier() {
          var specifier;
  
          specifier = {
              type: Syntax.ImportSpecifier,
              id: parseVariableIdentifier(),
              from: null
          };
  
          if (match(':')) {
              lex();
              specifier.from = parsePath();
          }
  
          return specifier;
      }
  
      // 12.3 Empty Statement
  
      function parseEmptyStatement() {
          expect(';');
  
          return {
              type: Syntax.EmptyStatement
          };
      }
  
      // 12.4 Expression Statement
  
      function parseExpressionStatement() {
          var expr = parseExpression();
  
          consumeSemicolon();
  
          return {
              type: Syntax.ExpressionStatement,
              expression: expr
          };
      }
  
      // 12.5 If statement
  
      function parseIfStatement() {
          var test, consequent, alternate;
  
          expectKeyword('if');
  
          expect('(');
  
          test = parseExpression();
  
          expect(')');
  
          consequent = parseStatement();
  
          if (matchKeyword('else')) {
              lex();
              alternate = parseStatement();
          } else {
              alternate = null;
          }
  
          return {
              type: Syntax.IfStatement,
              test: test,
              consequent: consequent,
              alternate: alternate
          };
      }
  
      // 12.6 Iteration Statements
  
      function parseDoWhileStatement() {
          var body, test, oldInIteration;
  
          expectKeyword('do');
  
          oldInIteration = state.inIteration;
          state.inIteration = true;
  
          body = parseStatement();
  
          state.inIteration = oldInIteration;
  
          expectKeyword('while');
  
          expect('(');
  
          test = parseExpression();
  
          expect(')');
  
          if (match(';')) {
              lex();
          }
  
          return {
              type: Syntax.DoWhileStatement,
              body: body,
              test: test
          };
      }
  
      function parseWhileStatement() {
          var test, body, oldInIteration;
  
          expectKeyword('while');
  
          expect('(');
  
          test = parseExpression();
  
          expect(')');
  
          oldInIteration = state.inIteration;
          state.inIteration = true;
  
          body = parseStatement();
  
          state.inIteration = oldInIteration;
  
          return {
              type: Syntax.WhileStatement,
              test: test,
              body: body
          };
      }
  
      function parseForVariableDeclaration() {
          var token = lex();
  
          return {
              type: Syntax.VariableDeclaration,
              declarations: parseVariableDeclarationList(),
              kind: token.value
          };
      }
  
      function parseForStatement() {
          var init, test, update, left, right, body, operator, oldInIteration;
  
          init = test = update = null;
  
          expectKeyword('for');
  
          expect('(');
  
          if (match(';')) {
              lex();
          } else {
              if (matchKeyword('var') || matchKeyword('let') || matchKeyword('const')) {
                  state.allowIn = false;
                  init = parseForVariableDeclaration();
                  state.allowIn = true;
  
                  if (init.declarations.length === 1) {
                      if (matchKeyword('in') || matchContextualKeyword('of')) {
                          operator = lookahead();
                          if (!((operator.value === 'in' || init.kind !== 'var') && init.declarations[0].init)) {
                              lex();
                              left = init;
                              right = parseExpression();
                              init = null;
                          }
                      }
                  }
              } else {
                  state.allowIn = false;
                  init = parseExpression();
                  state.allowIn = true;
  
                  if (matchContextualKeyword('of')) {
                      operator = lex();
                      left = init;
                      right = parseExpression();
                      init = null;
                  } else if (matchKeyword('in')) {
                      // LeftHandSideExpression
                      if (!isAssignableLeftHandSide(init)) {
                          throwError({}, Messages.InvalidLHSInForIn);
                      }
                      operator = lex();
                      left = init;
                      right = parseExpression();
                      init = null;
                  }
              }
  
              if (typeof left === 'undefined') {
                  expect(';');
              }
          }
  
          if (typeof left === 'undefined') {
  
              if (!match(';')) {
                  test = parseExpression();
              }
              expect(';');
  
              if (!match(')')) {
                  update = parseExpression();
              }
          }
  
          expect(')');
  
          oldInIteration = state.inIteration;
          state.inIteration = true;
  
          body = parseStatement();
  
          state.inIteration = oldInIteration;
  
          if (typeof left === 'undefined') {
              return {
                  type: Syntax.ForStatement,
                  init: init,
                  test: test,
                  update: update,
                  body: body
              };
          }
  
          if (operator.value === 'in') {
              return {
                  type: Syntax.ForInStatement,
                  left: left,
                  right: right,
                  body: body,
                  each: false
              };
          } else {
              return {
                  type: Syntax.ForOfStatement,
                  left: left,
                  right: right,
                  body: body
              };
          }
      }
  
      // 12.7 The continue statement
  
      function parseContinueStatement() {
          var token, label = null;
  
          expectKeyword('continue');
  
          // Optimize the most common form: 'continue;'.
          if (source[index] === ';') {
              lex();
  
              if (!state.inIteration) {
                  throwError({}, Messages.IllegalContinue);
              }
  
              return {
                  type: Syntax.ContinueStatement,
                  label: null
              };
          }
  
          if (peekLineTerminator()) {
              if (!state.inIteration) {
                  throwError({}, Messages.IllegalContinue);
              }
  
              return {
                  type: Syntax.ContinueStatement,
                  label: null
              };
          }
  
          token = lookahead();
          if (token.type === Token.Identifier) {
              label = parseVariableIdentifier();
  
              if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
                  throwError({}, Messages.UnknownLabel, label.name);
              }
          }
  
          consumeSemicolon();
  
          if (label === null && !state.inIteration) {
              throwError({}, Messages.IllegalContinue);
          }
  
          return {
              type: Syntax.ContinueStatement,
              label: label
          };
      }
  
      // 12.8 The break statement
  
      function parseBreakStatement() {
          var token, label = null;
  
          expectKeyword('break');
  
          // Optimize the most common form: 'break;'.
          if (source[index] === ';') {
              lex();
  
              if (!(state.inIteration || state.inSwitch)) {
                  throwError({}, Messages.IllegalBreak);
              }
  
              return {
                  type: Syntax.BreakStatement,
                  label: null
              };
          }
  
          if (peekLineTerminator()) {
              if (!(state.inIteration || state.inSwitch)) {
                  throwError({}, Messages.IllegalBreak);
              }
  
              return {
                  type: Syntax.BreakStatement,
                  label: null
              };
          }
  
          token = lookahead();
          if (token.type === Token.Identifier) {
              label = parseVariableIdentifier();
  
              if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
                  throwError({}, Messages.UnknownLabel, label.name);
              }
          }
  
          consumeSemicolon();
  
          if (label === null && !(state.inIteration || state.inSwitch)) {
              throwError({}, Messages.IllegalBreak);
          }
  
          return {
              type: Syntax.BreakStatement,
              label: label
          };
      }
  
      // 12.9 The return statement
  
      function parseReturnStatement() {
          var token, argument = null;
  
          expectKeyword('return');
  
          if (!state.inFunctionBody) {
              throwErrorTolerant({}, Messages.IllegalReturn);
          }
  
          // 'return' followed by a space and an identifier is very common.
          if (source[index] === ' ') {
              if (isIdentifierStart(source[index + 1])) {
                  argument = parseExpression();
                  consumeSemicolon();
                  return {
                      type: Syntax.ReturnStatement,
                      argument: argument
                  };
              }
          }
  
          if (peekLineTerminator()) {
              return {
                  type: Syntax.ReturnStatement,
                  argument: null
              };
          }
  
          if (!match(';')) {
              token = lookahead();
              if (!match('}') && token.type !== Token.EOF) {
                  argument = parseExpression();
              }
          }
  
          consumeSemicolon();
  
          return {
              type: Syntax.ReturnStatement,
              argument: argument
          };
      }
  
      // 12.10 The with statement
  
      function parseWithStatement() {
          var object, body;
  
          if (strict) {
              throwErrorTolerant({}, Messages.StrictModeWith);
          }
  
          expectKeyword('with');
  
          expect('(');
  
          object = parseExpression();
  
          expect(')');
  
          body = parseStatement();
  
          return {
              type: Syntax.WithStatement,
              object: object,
              body: body
          };
      }
  
      // 12.10 The swith statement
  
      function parseSwitchCase() {
          var test,
              consequent = [],
              statement;
  
          if (matchKeyword('default')) {
              lex();
              test = null;
          } else {
              expectKeyword('case');
              test = parseExpression();
          }
          expect(':');
  
          while (index < length) {
              if (match('}') || matchKeyword('default') || matchKeyword('case')) {
                  break;
              }
              statement = parseSourceElement();
              if (typeof statement === 'undefined') {
                  break;
              }
              consequent.push(statement);
          }
  
          return {
              type: Syntax.SwitchCase,
              test: test,
              consequent: consequent
          };
      }
  
      function parseSwitchStatement() {
          var discriminant, cases, clause, oldInSwitch, defaultFound;
  
          expectKeyword('switch');
  
          expect('(');
  
          discriminant = parseExpression();
  
          expect(')');
  
          expect('{');
  
          if (match('}')) {
              lex();
              return {
                  type: Syntax.SwitchStatement,
                  discriminant: discriminant
              };
          }
  
          cases = [];
  
          oldInSwitch = state.inSwitch;
          state.inSwitch = true;
          defaultFound = false;
  
          while (index < length) {
              if (match('}')) {
                  break;
              }
              clause = parseSwitchCase();
              if (clause.test === null) {
                  if (defaultFound) {
                      throwError({}, Messages.MultipleDefaultsInSwitch);
                  }
                  defaultFound = true;
              }
              cases.push(clause);
          }
  
          state.inSwitch = oldInSwitch;
  
          expect('}');
  
          return {
              type: Syntax.SwitchStatement,
              discriminant: discriminant,
              cases: cases
          };
      }
  
      // 12.13 The throw statement
  
      function parseThrowStatement() {
          var argument;
  
          expectKeyword('throw');
  
          if (peekLineTerminator()) {
              throwError({}, Messages.NewlineAfterThrow);
          }
  
          argument = parseExpression();
  
          consumeSemicolon();
  
          return {
              type: Syntax.ThrowStatement,
              argument: argument
          };
      }
  
      // 12.14 The try statement
  
      function parseCatchClause() {
          var param;
  
          expectKeyword('catch');
  
          expect('(');
          if (!match(')')) {
              param = parseExpression();
              // 12.14.1
              if (strict && param.type === Syntax.Identifier && isRestrictedWord(param.name)) {
                  throwErrorTolerant({}, Messages.StrictCatchVariable);
              }
          }
          expect(')');
  
          return {
              type: Syntax.CatchClause,
              param: param,
              guard: null,
              body: parseBlock()
          };
      }
  
      function parseTryStatement() {
          var block, handlers = [], finalizer = null;
  
          expectKeyword('try');
  
          block = parseBlock();
  
          if (matchKeyword('catch')) {
              handlers.push(parseCatchClause());
          }
  
          if (matchKeyword('finally')) {
              lex();
              finalizer = parseBlock();
          }
  
          if (handlers.length === 0 && !finalizer) {
              throwError({}, Messages.NoCatchOrFinally);
          }
  
          return {
              type: Syntax.TryStatement,
              block: block,
              handlers: handlers,
              finalizer: finalizer
          };
      }
  
      // 12.15 The debugger statement
  
      function parseDebuggerStatement() {
          expectKeyword('debugger');
  
          consumeSemicolon();
  
          return {
              type: Syntax.DebuggerStatement
          };
      }
  
      // 12 Statements
  
      function parseStatement() {
          var token = lookahead(),
              expr,
              labeledBody;
  
          if (token.type === Token.EOF) {
              throwUnexpected(token);
          }
  
          if (token.type === Token.Punctuator) {
              switch (token.value) {
              case ';':
                  return parseEmptyStatement();
              case '{':
                  return parseBlock();
              case '(':
                  return parseExpressionStatement();
              default:
                  break;
              }
          }
  
          if (token.type === Token.Keyword) {
              switch (token.value) {
              case 'break':
                  return parseBreakStatement();
              case 'continue':
                  return parseContinueStatement();
              case 'debugger':
                  return parseDebuggerStatement();
              case 'do':
                  return parseDoWhileStatement();
              case 'for':
                  return parseForStatement();
              case 'function':
                  return parseFunctionDeclaration();
              case 'class':
                  return parseClassDeclaration();
              case 'if':
                  return parseIfStatement();
              case 'return':
                  return parseReturnStatement();
              case 'switch':
                  return parseSwitchStatement();
              case 'throw':
                  return parseThrowStatement();
              case 'try':
                  return parseTryStatement();
              case 'var':
                  return parseVariableStatement();
              case 'while':
                  return parseWhileStatement();
              case 'with':
                  return parseWithStatement();
              default:
                  break;
              }
          }
  
          expr = parseExpression();
  
          // 12.12 Labelled Statements
          if ((expr.type === Syntax.Identifier) && match(':')) {
              lex();
  
              if (Object.prototype.hasOwnProperty.call(state.labelSet, expr.name)) {
                  throwError({}, Messages.Redeclaration, 'Label', expr.name);
              }
  
              state.labelSet[expr.name] = true;
              labeledBody = parseStatement();
              delete state.labelSet[expr.name];
  
              return {
                  type: Syntax.LabeledStatement,
                  label: expr,
                  body: labeledBody
              };
          }
  
          consumeSemicolon();
  
          return {
              type: Syntax.ExpressionStatement,
              expression: expr
          };
      }
  
      // 13 Function Definition
  
      function parseConciseBody() {
          if (match('{')) {
              return parseFunctionSourceElements();
          } else {
              return parseAssignmentExpression();
          }
      }
  
      function parseFunctionSourceElements() {
          var sourceElement, sourceElements = [], token, directive, firstRestricted,
              oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, oldParenthesizedCount;
  
          expect('{');
  
          while (index < length) {
              token = lookahead();
              if (token.type !== Token.StringLiteral) {
                  break;
              }
  
              sourceElement = parseSourceElement();
              sourceElements.push(sourceElement);
              if (sourceElement.expression.type !== Syntax.Literal) {
                  // this is not directive
                  break;
              }
              directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
              if (directive === 'use strict') {
                  strict = true;
                  if (firstRestricted) {
                      throwError(firstRestricted, Messages.StrictOctalLiteral);
                  }
              } else {
                  if (!firstRestricted && token.octal) {
                      firstRestricted = token;
                  }
              }
          }
  
          oldLabelSet = state.labelSet;
          oldInIteration = state.inIteration;
          oldInSwitch = state.inSwitch;
          oldInFunctionBody = state.inFunctionBody;
          oldParenthesizedCount = state.parenthesizedCount;
  
          state.labelSet = {};
          state.inIteration = false;
          state.inSwitch = false;
          state.inFunctionBody = true;
          state.parenthesizedCount = 0;
  
          while (index < length) {
              if (match('}')) {
                  break;
              }
              sourceElement = parseSourceElement();
              if (typeof sourceElement === 'undefined') {
                  break;
              }
              sourceElements.push(sourceElement);
          }
  
          expect('}');
  
          state.labelSet = oldLabelSet;
          state.inIteration = oldInIteration;
          state.inSwitch = oldInSwitch;
          state.inFunctionBody = oldInFunctionBody;
          state.parenthesizedCount = oldParenthesizedCount;
  
          return {
              type: Syntax.BlockStatement,
              body: sourceElements
          };
      }
  
      function parseFunctionDeclaration() {
          var id, param, params = [], body, token, firstRestricted, message, previousStrict, previousYieldAllowed, paramSet, generator;
  
          expectKeyword('function');
  
          generator = false;
          if (match('*')) {
              lex();
              generator = true;
          }
  
          token = lookahead();
  
          id = parseVariableIdentifier();
          if (strict) {
              if (isRestrictedWord(token.value)) {
                  throwError(token, Messages.StrictFunctionName);
              }
          } else {
              if (isRestrictedWord(token.value)) {
                  firstRestricted = token;
                  message = Messages.StrictFunctionName;
              } else if (isStrictModeReservedWord(token.value)) {
                  firstRestricted = token;
                  message = Messages.StrictReservedWord;
              }
          }
  
          expect('(');
  
          if (!match(')')) {
              paramSet = {};
              while (index < length) {
                  token = lookahead();
                  param = parseVariableIdentifier();
                  if (strict) {
                      if (isRestrictedWord(token.value)) {
                          throwError(token, Messages.StrictParamName);
                      }
                      if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
                          throwError(token, Messages.StrictParamDupe);
                      }
                  } else if (!firstRestricted) {
                      if (isRestrictedWord(token.value)) {
                          firstRestricted = token;
                          message = Messages.StrictParamName;
                      } else if (isStrictModeReservedWord(token.value)) {
                          firstRestricted = token;
                          message = Messages.StrictReservedWord;
                      } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
                          firstRestricted = token;
                          message = Messages.StrictParamDupe;
                      }
                  }
                  params.push(param);
                  paramSet[param.name] = true;
                  if (match(')')) {
                      break;
                  }
                  expect(',');
              }
          }
  
          expect(')');
  
          previousStrict = strict;
          previousYieldAllowed = yieldAllowed;
          yieldAllowed = generator;
          body = parseFunctionSourceElements();
          if (strict && firstRestricted) {
              throwError(firstRestricted, message);
          }
          if (yieldAllowed && !yieldFound) {
              throwError({}, Messages.NoYieldInGenerator);
          }
          strict = previousStrict;
          yieldAllowed = previousYieldAllowed;
  
          return {
              type: Syntax.FunctionDeclaration,
              id: id,
              params: params,
              body: body,
              generator: generator
          };
      }
  
      function parseFunctionExpression() {
          var token, id = null, firstRestricted, message, param, params = [], body, previousStrict, previousYieldAllowed, paramSet, generator;
  
          expectKeyword('function');
  
          generator = false;
  
          if (match('*')) {
              lex();
              generator = true;
          }
  
          if (!match('(')) {
              token = lookahead();
              id = parseVariableIdentifier();
              if (strict) {
                  if (isRestrictedWord(token.value)) {
                      throwError(token, Messages.StrictFunctionName);
                  }
              } else {
                  if (isRestrictedWord(token.value)) {
                      firstRestricted = token;
                      message = Messages.StrictFunctionName;
                  } else if (isStrictModeReservedWord(token.value)) {
                      firstRestricted = token;
                      message = Messages.StrictReservedWord;
                  }
              }
          }
  
          expect('(');
  
          if (!match(')')) {
              paramSet = {};
              while (index < length) {
                  token = lookahead();
                  param = parseVariableIdentifier();
                  if (strict) {
                      if (isRestrictedWord(token.value)) {
                          throwError(token, Messages.StrictParamName);
                      }
                      if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
                          throwError(token, Messages.StrictParamDupe);
                      }
                  } else if (!firstRestricted) {
                      if (isRestrictedWord(token.value)) {
                          firstRestricted = token;
                          message = Messages.StrictParamName;
                      } else if (isStrictModeReservedWord(token.value)) {
                          firstRestricted = token;
                          message = Messages.StrictReservedWord;
                      } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
                          firstRestricted = token;
                          message = Messages.StrictParamDupe;
                      }
                  }
                  params.push(param);
                  paramSet[param.name] = true;
                  if (match(')')) {
                      break;
                  }
                  expect(',');
              }
          }
  
          expect(')');
  
          previousStrict = strict;
          previousYieldAllowed = yieldAllowed;
          yieldAllowed = generator;
          body = parseFunctionSourceElements();
          if (strict && firstRestricted) {
              throwError(firstRestricted, message);
          }
          if (yieldAllowed && !yieldFound) {
              throwError({}, Messages.NoYieldInGenerator);
          }
          strict = previousStrict;
          yieldAllowed = previousYieldAllowed;
  
  
          return {
              type: Syntax.FunctionExpression,
              id: id,
              params: params,
              body: body,
              generator: generator
          };
      }
  
      function parseYieldExpression() {
          var delegate, expr, previousYieldAllowed;
  
          expectKeyword('yield');
  
          if (!yieldAllowed) {
              throwErrorTolerant({}, Messages.IllegalYield);
          }
  
          delegate = false;
          if (match('*')) {
              lex();
              delegate = true;
          }
  
          // It is a Syntax Error if any AssignmentExpression Contains YieldExpression.
          previousYieldAllowed = yieldAllowed;
          yieldAllowed = false;
          expr = parseAssignmentExpression();
          yieldAllowed = previousYieldAllowed;
          yieldFound = true;
  
          return {
              type: Syntax.YieldExpression,
              argument: expr,
              delegate: delegate
          };
      }
  
      // 14 Classes
  
      function parseMethodDefinition() {
          var token, key, param;
  
          if (match('*')) {
              lex();
              return {
                  type: Syntax.MethodDefinition,
                  key: parseObjectPropertyKey(),
                  value: parsePropertyMethodFunction({ generator: true }),
                  kind: ''
              };
          }
  
          token = lookahead();
          key = parseObjectPropertyKey();
  
          if (token.value === 'get' && !match('(')) {
              key = parseObjectPropertyKey();
              expect('(');
              expect(')');
              return {
                  type: Syntax.MethodDefinition,
                  key: key,
                  value: parsePropertyFunction([], { generator: false }),
                  kind: 'get'
              };
          } else if (token.value === 'set' && !match('(')) {
              key = parseObjectPropertyKey();
              expect('(');
              token = lookahead();
              param = [ parseVariableIdentifier() ];
              expect(')');
              return {
                  type: Syntax.MethodDefinition,
                  key: key,
                  value: parsePropertyFunction(param, { generator: false, name: token }),
                  kind: 'set'
              };
          } else {
              return {
                  type: Syntax.MethodDefinition,
                  key: key,
                  value: parsePropertyMethodFunction({ generator: false }),
                  kind: ''
              };
          }
      }
  
      function parseClassElement() {
          if (match(';')) {
              lex();
              return;
          } else {
              return parseMethodDefinition();
          }
      }
  
      function parseClassBody() {
          var classElement, classElements = [];
  
          expect('{');
  
          while (index < length) {
              if (match('}')) {
                  break;
              }
              classElement = parseClassElement();
              if (typeof classElement !== 'undefined') {
                  classElements.push(classElement);
              }
          }
  
          expect('}');
  
          return {
              type: Syntax.ClassBody,
              body: classElements
          };
      }
  
      function parseClassExpression() {
          var id, body, previousYieldAllowed, superClass;
  
          expectKeyword('class');
  
          if (!matchKeyword('extends') && !match('{')) {
              id = parseVariableIdentifier();
          }
  
          if (matchKeyword('extends')) {
              expectKeyword('extends');
              previousYieldAllowed = yieldAllowed;
              yieldAllowed = false;
              superClass = parseAssignmentExpression();
              yieldAllowed = previousYieldAllowed;
          }
  
          body = parseClassBody();
          return {
              id: id,
              type: Syntax.ClassExpression,
              body: body,
              superClass: superClass
          };
      }
  
      function parseClassDeclaration() {
          var token, id, body, previousYieldAllowed, superClass;
  
          expectKeyword('class');
  
          token = lookahead();
          id = parseVariableIdentifier();
  
          if (matchKeyword('extends')) {
              expectKeyword('extends');
              previousYieldAllowed = yieldAllowed;
              yieldAllowed = false;
              superClass = parseAssignmentExpression();
              yieldAllowed = previousYieldAllowed;
          }
  
          body = parseClassBody();
          return {
              id: id,
              type: Syntax.ClassDeclaration,
              body: body,
              superClass: superClass
          };
      }
  
      // 15 Program
  
      function parseSourceElement() {
          var token = lookahead();
  
          if (token.type === Token.Keyword) {
              switch (token.value) {
              case 'const':
              case 'let':
                  return parseConstLetDeclaration(token.value);
              case 'function':
                  return parseFunctionDeclaration();
              default:
                  return parseStatement();
              }
          }
  
          if (token.type !== Token.EOF) {
              return parseStatement();
          }
      }
  
      function parseProgramElement() {
          var token = lookahead(), lineNumber;
  
          if (token.type === Token.Keyword) {
              switch (token.value) {
              case 'export':
                  return parseExportDeclaration();
              case 'import':
                  return parseImportDeclaration();
              }
          }
  
          if (token.value === 'module' && token.type === Token.Identifier) {
              lineNumber = token.lineNumber;
              token = lookahead2();
              if (token.type === Token.Identifier && token.lineNumber === lineNumber) {
                  return parseModuleDeclaration();
              }
          }
  
          return parseSourceElement();
      }
  
      function parseProgramElements() {
          var sourceElement, sourceElements = [], token, directive, firstRestricted;
  
          while (index < length) {
              token = lookahead();
              if (token.type !== Token.StringLiteral) {
                  break;
              }
  
              sourceElement = parseProgramElement();
              sourceElements.push(sourceElement);
              if (sourceElement.expression.type !== Syntax.Literal) {
                  // this is not directive
                  break;
              }
              directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
              if (directive === 'use strict') {
                  strict = true;
                  if (firstRestricted) {
                      throwError(firstRestricted, Messages.StrictOctalLiteral);
                  }
              } else {
                  if (!firstRestricted && token.octal) {
                      firstRestricted = token;
                  }
              }
          }
  
          while (index < length) {
              sourceElement = parseProgramElement();
              if (typeof sourceElement === 'undefined') {
                  break;
              }
              sourceElements.push(sourceElement);
          }
          return sourceElements;
      }
  
      function parseModuleElement() {
          return parseProgramElement();
      }
  
      function parseModuleElements() {
          var list = [],
              statement;
  
          while (index < length) {
              if (match('}')) {
                  break;
              }
              statement = parseModuleElement();
              if (typeof statement === 'undefined') {
                  break;
              }
              list.push(statement);
          }
  
          return list;
      }
  
      function parseModuleBlock() {
          var block;
  
          expect('{');
  
          block = parseModuleElements();
  
          expect('}');
  
          return {
              type: Syntax.BlockStatement,
              body: block
          };
      }
  
      function parseProgram() {
          var program;
          strict = false;
          yieldAllowed = false;
          yieldFound = false;
          program = {
              type: Syntax.Program,
              body: parseProgramElements()
          };
          return program;
      }
  
      // The following functions are needed only when the option to preserve
      // the comments is active.
  
      function addComment(type, value, start, end, loc) {
          assert(typeof start === 'number', 'Comment must have valid position');
  
          // Because the way the actual token is scanned, often the comments
          // (if any) are skipped twice during the lexical analysis.
          // Thus, we need to skip adding a comment if the comment array already
          // handled it.
          if (extra.comments.length > 0) {
              if (extra.comments[extra.comments.length - 1].range[1] > start) {
                  return;
              }
          }
  
          extra.comments.push({
              type: type,
              value: value,
              range: [start, end],
              loc: loc
          });
      }
  
      function scanComment() {
          var comment, ch, loc, start, blockComment, lineComment;
  
          comment = '';
          blockComment = false;
          lineComment = false;
  
          while (index < length) {
              ch = source[index];
  
              if (lineComment) {
                  ch = nextChar();
                  if (isLineTerminator(ch)) {
                      loc.end = {
                          line: lineNumber,
                          column: index - lineStart - 1
                      };
                      lineComment = false;
                      addComment('Line', comment, start, index - 1, loc);
                      if (ch === '\r' && source[index] === '\n') {
                          ++index;
                      }
                      ++lineNumber;
                      lineStart = index;
                      comment = '';
                  } else if (index >= length) {
                      lineComment = false;
                      comment += ch;
                      loc.end = {
                          line: lineNumber,
                          column: length - lineStart
                      };
                      addComment('Line', comment, start, length, loc);
                  } else {
                      comment += ch;
                  }
              } else if (blockComment) {
                  if (isLineTerminator(ch)) {
                      if (ch === '\r' && source[index + 1] === '\n') {
                          ++index;
                          comment += '\r\n';
                      } else {
                          comment += ch;
                      }
                      ++lineNumber;
                      ++index;
                      lineStart = index;
                      if (index >= length) {
                          throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                      }
                  } else {
                      ch = nextChar();
                      if (index >= length) {
                          throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                      }
                      comment += ch;
                      if (ch === '*') {
                          ch = source[index];
                          if (ch === '/') {
                              comment = comment.substr(0, comment.length - 1);
                              blockComment = false;
                              ++index;
                              loc.end = {
                                  line: lineNumber,
                                  column: index - lineStart
                              };
                              addComment('Block', comment, start, index, loc);
                              comment = '';
                          }
                      }
                  }
              } else if (ch === '/') {
                  ch = source[index + 1];
                  if (ch === '/') {
                      loc = {
                          start: {
                              line: lineNumber,
                              column: index - lineStart
                          }
                      };
                      start = index;
                      index += 2;
                      lineComment = true;
                      if (index >= length) {
                          loc.end = {
                              line: lineNumber,
                              column: index - lineStart
                          };
                          lineComment = false;
                          addComment('Line', comment, start, index, loc);
                      }
                  } else if (ch === '*') {
                      start = index;
                      index += 2;
                      blockComment = true;
                      loc = {
                          start: {
                              line: lineNumber,
                              column: index - lineStart - 2
                          }
                      };
                      if (index >= length) {
                          throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                      }
                  } else {
                      break;
                  }
              } else if (isWhiteSpace(ch)) {
                  ++index;
              } else if (isLineTerminator(ch)) {
                  ++index;
                  if (ch ===  '\r' && source[index] === '\n') {
                      ++index;
                  }
                  ++lineNumber;
                  lineStart = index;
              } else {
                  break;
              }
          }
      }
  
      function filterCommentLocation() {
          var i, entry, comment, comments = [];
  
          for (i = 0; i < extra.comments.length; ++i) {
              entry = extra.comments[i];
              comment = {
                  type: entry.type,
                  value: entry.value
              };
              if (extra.range) {
                  comment.range = entry.range;
              }
              if (extra.loc) {
                  comment.loc = entry.loc;
              }
              comments.push(comment);
          }
  
          extra.comments = comments;
      }
  
      function collectToken() {
          var start, loc, token, range, value;
  
          skipComment();
          start = index;
          loc = {
              start: {
                  line: lineNumber,
                  column: index - lineStart
              }
          };
  
          token = extra.advance();
          loc.end = {
              line: lineNumber,
              column: index - lineStart
          };
  
          if (token.type !== Token.EOF) {
              range = [token.range[0], token.range[1]];
              value = sliceSource(token.range[0], token.range[1]);
              extra.tokens.push({
                  type: TokenName[token.type],
                  value: value,
                  range: range,
                  loc: loc
              });
          }
  
          return token;
      }
  
      function collectRegex() {
          var pos, loc, regex, token;
  
          skipComment();
  
          pos = index;
          loc = {
              start: {
                  line: lineNumber,
                  column: index - lineStart
              }
          };
  
          regex = extra.scanRegExp();
          loc.end = {
              line: lineNumber,
              column: index - lineStart
          };
  
          // Pop the previous token, which is likely '/' or '/='
          if (extra.tokens.length > 0) {
              token = extra.tokens[extra.tokens.length - 1];
              if (token.range[0] === pos && token.type === 'Punctuator') {
                  if (token.value === '/' || token.value === '/=') {
                      extra.tokens.pop();
                  }
              }
          }
  
          extra.tokens.push({
              type: 'RegularExpression',
              value: regex.literal,
              range: [pos, index],
              loc: loc
          });
  
          return regex;
      }
  
      function filterTokenLocation() {
          var i, entry, token, tokens = [];
  
          for (i = 0; i < extra.tokens.length; ++i) {
              entry = extra.tokens[i];
              token = {
                  type: entry.type,
                  value: entry.value
              };
              if (extra.range) {
                  token.range = entry.range;
              }
              if (extra.loc) {
                  token.loc = entry.loc;
              }
              tokens.push(token);
          }
  
          extra.tokens = tokens;
      }
  
      function createLiteral(token) {
          return {
              type: Syntax.Literal,
              value: token.value
          };
      }
  
      function createRawLiteral(token) {
          return {
              type: Syntax.Literal,
              value: token.value,
              raw: sliceSource(token.range[0], token.range[1])
          };
      }
  
      function wrapTrackingFunction(range, loc) {
  
          return function (parseFunction) {
  
              function isBinary(node) {
                  return node.type === Syntax.LogicalExpression ||
                      node.type === Syntax.BinaryExpression;
              }
  
              function visit(node) {
                  if (isBinary(node.left)) {
                      visit(node.left);
                  }
                  if (isBinary(node.right)) {
                      visit(node.right);
                  }
  
                  if (range && typeof node.range === 'undefined') {
                      node.range = [node.left.range[0], node.right.range[1]];
                  }
                  if (loc && typeof node.loc === 'undefined') {
                      node.loc = {
                          start: node.left.loc.start,
                          end: node.right.loc.end
                      };
                  }
              }
  
              return function () {
                  var node, rangeInfo, locInfo;
  
                  skipComment();
                  rangeInfo = [index, 0];
                  locInfo = {
                      start: {
                          line: lineNumber,
                          column: index - lineStart
                      }
                  };
  
                  node = parseFunction.apply(null, arguments);
                  if (typeof node !== 'undefined') {
  
                      if (range) {
                          rangeInfo[1] = index;
                          node.range = rangeInfo;
                      }
  
                      if (loc) {
                          locInfo.end = {
                              line: lineNumber,
                              column: index - lineStart
                          };
                          node.loc = locInfo;
                      }
  
                      if (isBinary(node)) {
                          visit(node);
                      }
  
                      if (node.type === Syntax.MemberExpression) {
                          if (typeof node.object.range !== 'undefined') {
                              node.range[0] = node.object.range[0];
                          }
                          if (typeof node.object.loc !== 'undefined') {
                              node.loc.start = node.object.loc.start;
                          }
                      }
  
                      if (node.type === Syntax.CallExpression) {
                          if (typeof node.callee.range !== 'undefined') {
                              node.range[0] = node.callee.range[0];
                          }
                          if (typeof node.callee.loc !== 'undefined') {
                              node.loc.start = node.callee.loc.start;
                          }
                      }
                      return node;
                  }
              };
  
          };
      }
  
      function patch() {
  
          var wrapTracking;
  
          if (extra.comments) {
              extra.skipComment = skipComment;
              skipComment = scanComment;
          }
  
          if (extra.raw) {
              extra.createLiteral = createLiteral;
              createLiteral = createRawLiteral;
          }
  
          if (extra.range || extra.loc) {
  
              wrapTracking = wrapTrackingFunction(extra.range, extra.loc);
  
              extra.parseAdditiveExpression = parseAdditiveExpression;
              extra.parseAssignmentExpression = parseAssignmentExpression;
              extra.parseBitwiseANDExpression = parseBitwiseANDExpression;
              extra.parseBitwiseORExpression = parseBitwiseORExpression;
              extra.parseBitwiseXORExpression = parseBitwiseXORExpression;
              extra.parseBlock = parseBlock;
              extra.parseFunctionSourceElements = parseFunctionSourceElements;
              extra.parseCallMember = parseCallMember;
              extra.parseCatchClause = parseCatchClause;
              extra.parseComputedMember = parseComputedMember;
              extra.parseConditionalExpression = parseConditionalExpression;
              extra.parseConstLetDeclaration = parseConstLetDeclaration;
              extra.parseEqualityExpression = parseEqualityExpression;
              extra.parseExportDeclaration = parseExportDeclaration;
              extra.parseExportSpecifier = parseExportSpecifier;
              extra.parseExportSpecifierSetProperty = parseExportSpecifierSetProperty;
              extra.parseExpression = parseExpression;
              extra.parseForVariableDeclaration = parseForVariableDeclaration;
              extra.parseFunctionDeclaration = parseFunctionDeclaration;
              extra.parseFunctionExpression = parseFunctionExpression;
              extra.parseGlob = parseGlob;
              extra.parseImportDeclaration = parseImportDeclaration;
              extra.parseImportSpecifier = parseImportSpecifier;
              extra.parseLogicalANDExpression = parseLogicalANDExpression;
              extra.parseLogicalORExpression = parseLogicalORExpression;
              extra.parseMultiplicativeExpression = parseMultiplicativeExpression;
              extra.parseModuleDeclaration = parseModuleDeclaration;
              extra.parseModuleBlock = parseModuleBlock;
              extra.parseNewExpression = parseNewExpression;
              extra.parseNonComputedMember = parseNonComputedMember;
              extra.parseNonComputedProperty = parseNonComputedProperty;
              extra.parseObjectProperty = parseObjectProperty;
              extra.parseObjectPropertyKey = parseObjectPropertyKey;
              extra.parsePath = parsePath;
              extra.parsePostfixExpression = parsePostfixExpression;
              extra.parsePrimaryExpression = parsePrimaryExpression;
              extra.parseProgram = parseProgram;
              extra.parsePropertyFunction = parsePropertyFunction;
              extra.parseRelationalExpression = parseRelationalExpression;
              extra.parseStatement = parseStatement;
              extra.parseShiftExpression = parseShiftExpression;
              extra.parseSwitchCase = parseSwitchCase;
              extra.parseUnaryExpression = parseUnaryExpression;
              extra.parseVariableDeclaration = parseVariableDeclaration;
              extra.parseVariableIdentifier = parseVariableIdentifier;
              extra.parseMethodDefinition = parseMethodDefinition;
              extra.parseClassDeclaration = parseClassDeclaration;
              extra.parseClassExpression = parseClassExpression;
              extra.parseClassBody = parseClassBody;
  
              parseAdditiveExpression = wrapTracking(extra.parseAdditiveExpression);
              parseAssignmentExpression = wrapTracking(extra.parseAssignmentExpression);
              parseBitwiseANDExpression = wrapTracking(extra.parseBitwiseANDExpression);
              parseBitwiseORExpression = wrapTracking(extra.parseBitwiseORExpression);
              parseBitwiseXORExpression = wrapTracking(extra.parseBitwiseXORExpression);
              parseBlock = wrapTracking(extra.parseBlock);
              parseFunctionSourceElements = wrapTracking(extra.parseFunctionSourceElements);
              parseCallMember = wrapTracking(extra.parseCallMember);
              parseCatchClause = wrapTracking(extra.parseCatchClause);
              parseComputedMember = wrapTracking(extra.parseComputedMember);
              parseConditionalExpression = wrapTracking(extra.parseConditionalExpression);
              parseConstLetDeclaration = wrapTracking(extra.parseConstLetDeclaration);
              parseExportDeclaration = wrapTracking(parseExportDeclaration);
              parseExportSpecifier = wrapTracking(parseExportSpecifier);
              parseExportSpecifierSetProperty = wrapTracking(parseExportSpecifierSetProperty);
              parseEqualityExpression = wrapTracking(extra.parseEqualityExpression);
              parseExpression = wrapTracking(extra.parseExpression);
              parseForVariableDeclaration = wrapTracking(extra.parseForVariableDeclaration);
              parseFunctionDeclaration = wrapTracking(extra.parseFunctionDeclaration);
              parseFunctionExpression = wrapTracking(extra.parseFunctionExpression);
              parseGlob = wrapTracking(extra.parseGlob);
              parseImportDeclaration = wrapTracking(extra.parseImportDeclaration);
              parseImportSpecifier = wrapTracking(extra.parseImportSpecifier);
              parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression);
              parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression);
              parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression);
              parseModuleDeclaration = wrapTracking(extra.parseModuleDeclaration);
              parseModuleBlock = wrapTracking(extra.parseModuleBlock);
              parseNewExpression = wrapTracking(extra.parseNewExpression);
              parseNonComputedMember = wrapTracking(extra.parseNonComputedMember);
              parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty);
              parseObjectProperty = wrapTracking(extra.parseObjectProperty);
              parseObjectPropertyKey = wrapTracking(extra.parseObjectPropertyKey);
              parsePath = wrapTracking(extra.parsePath);
              parsePostfixExpression = wrapTracking(extra.parsePostfixExpression);
              parsePrimaryExpression = wrapTracking(extra.parsePrimaryExpression);
              parseProgram = wrapTracking(extra.parseProgram);
              parsePropertyFunction = wrapTracking(extra.parsePropertyFunction);
              parseRelationalExpression = wrapTracking(extra.parseRelationalExpression);
              parseStatement = wrapTracking(extra.parseStatement);
              parseShiftExpression = wrapTracking(extra.parseShiftExpression);
              parseSwitchCase = wrapTracking(extra.parseSwitchCase);
              parseUnaryExpression = wrapTracking(extra.parseUnaryExpression);
              parseVariableDeclaration = wrapTracking(extra.parseVariableDeclaration);
              parseVariableIdentifier = wrapTracking(extra.parseVariableIdentifier);
              parseMethodDefinition = wrapTracking(extra.parseMethodDefinition);
              parseClassDeclaration = wrapTracking(extra.parseClassDeclaration);
              parseClassExpression = wrapTracking(extra.parseClassExpression);
              parseClassBody = wrapTracking(extra.parseClassBody);
          }
  
          if (typeof extra.tokens !== 'undefined') {
              extra.advance = advance;
              extra.scanRegExp = scanRegExp;
  
              advance = collectToken;
              scanRegExp = collectRegex;
          }
      }
  
      function unpatch() {
          if (typeof extra.skipComment === 'function') {
              skipComment = extra.skipComment;
          }
  
          if (extra.raw) {
              createLiteral = extra.createLiteral;
          }
  
          if (extra.range || extra.loc) {
              parseAdditiveExpression = extra.parseAdditiveExpression;
              parseAssignmentExpression = extra.parseAssignmentExpression;
              parseBitwiseANDExpression = extra.parseBitwiseANDExpression;
              parseBitwiseORExpression = extra.parseBitwiseORExpression;
              parseBitwiseXORExpression = extra.parseBitwiseXORExpression;
              parseBlock = extra.parseBlock;
              parseFunctionSourceElements = extra.parseFunctionSourceElements;
              parseCallMember = extra.parseCallMember;
              parseCatchClause = extra.parseCatchClause;
              parseComputedMember = extra.parseComputedMember;
              parseConditionalExpression = extra.parseConditionalExpression;
              parseConstLetDeclaration = extra.parseConstLetDeclaration;
              parseEqualityExpression = extra.parseEqualityExpression;
              parseExportDeclaration = extra.parseExportDeclaration;
              parseExportSpecifier = extra.parseExportSpecifier;
              parseExportSpecifierSetProperty = extra.parseExportSpecifierSetProperty;
              parseExpression = extra.parseExpression;
              parseForVariableDeclaration = extra.parseForVariableDeclaration;
              parseFunctionDeclaration = extra.parseFunctionDeclaration;
              parseFunctionExpression = extra.parseFunctionExpression;
              parseGlob = extra.parseGlob;
              parseImportDeclaration = extra.parseImportDeclaration;
              parseImportSpecifier = extra.parseImportSpecifier;
              parseLogicalANDExpression = extra.parseLogicalANDExpression;
              parseLogicalORExpression = extra.parseLogicalORExpression;
              parseMultiplicativeExpression = extra.parseMultiplicativeExpression;
              parseModuleDeclaration = extra.parseModuleDeclaration;
              parseModuleBlock = extra.parseModuleBlock;
              parseNewExpression = extra.parseNewExpression;
              parseNonComputedMember = extra.parseNonComputedMember;
              parseNonComputedProperty = extra.parseNonComputedProperty;
              parseObjectProperty = extra.parseObjectProperty;
              parseObjectPropertyKey = extra.parseObjectPropertyKey;
              parsePath = extra.parsePath;
              parsePostfixExpression = extra.parsePostfixExpression;
              parsePrimaryExpression = extra.parsePrimaryExpression;
              parseProgram = extra.parseProgram;
              parsePropertyFunction = extra.parsePropertyFunction;
              parseRelationalExpression = extra.parseRelationalExpression;
              parseStatement = extra.parseStatement;
              parseShiftExpression = extra.parseShiftExpression;
              parseSwitchCase = extra.parseSwitchCase;
              parseUnaryExpression = extra.parseUnaryExpression;
              parseVariableDeclaration = extra.parseVariableDeclaration;
              parseVariableIdentifier = extra.parseVariableIdentifier;
              parseMethodDefinition = extra.parseMethodDefinition;
              parseClassDeclaration = extra.parseClassDeclaration;
              parseClassExpression = extra.parseClassExpression;
              parseClassBody = extra.parseClassBody;
          }
  
          if (typeof extra.scanRegExp === 'function') {
              advance = extra.advance;
              scanRegExp = extra.scanRegExp;
          }
      }
  
      function stringToArray(str) {
          var length = str.length,
              result = [],
              i;
          for (i = 0; i < length; ++i) {
              result[i] = str.charAt(i);
          }
          return result;
      }
  
      function parse(code, options) {
          var program, toString;
  
          toString = String;
          if (typeof code !== 'string' && !(code instanceof String)) {
              code = toString(code);
          }
  
          source = code;
          index = 0;
          lineNumber = (source.length > 0) ? 1 : 0;
          lineStart = 0;
          length = source.length;
          buffer = null;
          state = {
              allowIn: true,
              labelSet: {},
              parenthesizedCount: 0,
              lastParenthesized: null,
              inFunctionBody: false,
              inIteration: false,
              inSwitch: false
          };
  
          extra = {};
          if (typeof options !== 'undefined') {
              extra.range = (typeof options.range === 'boolean') && options.range;
              extra.loc = (typeof options.loc === 'boolean') && options.loc;
              extra.raw = (typeof options.raw === 'boolean') && options.raw;
              if (typeof options.tokens === 'boolean' && options.tokens) {
                  extra.tokens = [];
              }
              if (typeof options.comment === 'boolean' && options.comment) {
                  extra.comments = [];
              }
              if (typeof options.tolerant === 'boolean' && options.tolerant) {
                  extra.errors = [];
              }
          }
  
          if (length > 0) {
              if (typeof source[0] === 'undefined') {
                  // Try first to convert to a string. This is good as fast path
                  // for old IE which understands string indexing for string
                  // literals only and not for string object.
                  if (code instanceof String) {
                      source = code.valueOf();
                  }
  
                  // Force accessing the characters via an array.
                  if (typeof source[0] === 'undefined') {
                      source = stringToArray(code);
                  }
              }
          }
  
          patch();
          try {
              program = parseProgram();
              if (typeof extra.comments !== 'undefined') {
                  filterCommentLocation();
                  program.comments = extra.comments;
              }
              if (typeof extra.tokens !== 'undefined') {
                  filterTokenLocation();
                  program.tokens = extra.tokens;
              }
              if (typeof extra.errors !== 'undefined') {
                  program.errors = extra.errors;
              }
          } catch (e) {
              throw e;
          } finally {
              unpatch();
              extra = {};
          }
  
          return program;
      }
  
      // Sync with package.json.
      exports.version = '1.0.0-dev-harmony';
  
      exports.parse = parse;
  
      // Deep copy.
      exports.Syntax = (function () {
          var name, types = {};
  
          if (typeof Object.create === 'function') {
              types = Object.create(null);
          }
  
          for (name in Syntax) {
              if (Syntax.hasOwnProperty(name)) {
                  types[name] = Syntax[name];
              }
          }
  
          if (typeof Object.freeze === 'function') {
              Object.freeze(types);
          }
  
          return types;
      }());
  
  }));
  /* vim: set sw=4 ts=4 et tw=80 : */