[Pkg-javascript-commits] [node-acorn-jsx] 08/484: Add esprima and uglifyjs to compare against
Bastien Roucariès
rouca at moszumanska.debian.org
Sat Aug 19 14:19:56 UTC 2017
This is an automated email from the git hooks/post-receive script.
rouca pushed a commit to branch master
in repository node-acorn-jsx.
commit a1d958751911faa06a18d0f99d5ca98d053ce655
Author: Marijn Haverbeke <marijnh at gmail.com>
Date: Mon Sep 24 13:40:48 2012 +0200
Add esprima and uglifyjs to compare against
---
test/bench.html | 16 +-
test/compare/esprima.js | 3808 ++++++++++++++++++++++++++++++++++++++++++++++
test/compare/uglifyjs.js | 1372 +++++++++++++++++
3 files changed, 5190 insertions(+), 6 deletions(-)
diff --git a/test/bench.html b/test/bench.html
index 34af946..9556422 100644
--- a/test/bench.html
+++ b/test/bench.html
@@ -3,16 +3,20 @@
<meta charset="utf-8">
<title>Acorn benchmark</title>
<script src="../acorn.js"></script>
+ <script src="compare/esprima.js"></script>
+ <script src="compare/uglifyjs.js"></script>
<script src="jquery-string.js"></script>
</head>
-<button onclick="test()">GO</button>
+<button onclick="test('acorn')">Acorn</button>
+<button onclick="test('esprima')">Esprima</button>
+<button onclick="test('uglifyjs')">UglifyJS</button>
<script>
- function test() {
- for (var i =0, t0 = +new Date; i < 10; ++i) acorn.parse(jquery164);
- var n = (+new Date - t0) + "ms";
- console.log(n);
- document.body.appendChild(document.createElement("pre")).innerHTML = n;
+ function test(which) {
+ var lib = window[which];
+ for (var i =0, t0 = +new Date; i < 10; ++i) lib.parse(jquery164);
+ document.body.appendChild(document.createElement("pre")).innerHTML =
+ which + ": " + (+new Date - t0) + "ms";
}
</script>
diff --git a/test/compare/esprima.js b/test/compare/esprima.js
new file mode 100644
index 0000000..90d1b14
--- /dev/null
+++ b/test/compare/esprima.js
@@ -0,0 +1,3808 @@
+/*
+ Copyright (C) 2012 Ariya Hidayat <ariya.hidayat at gmail.com>
+ Copyright (C) 2012 Mathias Bynens <mathias at qiwi.be>
+ Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim at boekesteijn.nl>
+ Copyright (C) 2012 Kris Kowal <kris.kowal at cixar.com>
+ Copyright (C) 2012 Yusuke Suzuki <utatane.tea at gmail.com>
+ Copyright (C) 2012 Arpad Borsos <arpad.borsos at googlemail.com>
+ Copyright (C) 2011 Ariya Hidayat <ariya.hidayat at gmail.com>
+
+ 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 <COPYRIGHT HOLDER> 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, parseExpression: true,
+parseFunctionDeclaration: true, parseFunctionExpression: true,
+parseFunctionSourceElements: true, parseVariableIdentifier: true,
+parseLeftHandSideExpression: true,
+parseStatement: true, parseSourceElement: 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,
+ 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] = '<end>';
+ 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 = {
+ AssignmentExpression: 'AssignmentExpression',
+ ArrayExpression: 'ArrayExpression',
+ BlockStatement: 'BlockStatement',
+ BinaryExpression: 'BinaryExpression',
+ BreakStatement: 'BreakStatement',
+ CallExpression: 'CallExpression',
+ CatchClause: 'CatchClause',
+ ConditionalExpression: 'ConditionalExpression',
+ ContinueStatement: 'ContinueStatement',
+ DoWhileStatement: 'DoWhileStatement',
+ DebuggerStatement: 'DebuggerStatement',
+ EmptyStatement: 'EmptyStatement',
+ ExpressionStatement: 'ExpressionStatement',
+ ForStatement: 'ForStatement',
+ ForInStatement: 'ForInStatement',
+ FunctionDeclaration: 'FunctionDeclaration',
+ FunctionExpression: 'FunctionExpression',
+ Identifier: 'Identifier',
+ IfStatement: 'IfStatement',
+ Literal: 'Literal',
+ LabeledStatement: 'LabeledStatement',
+ LogicalExpression: 'LogicalExpression',
+ MemberExpression: 'MemberExpression',
+ NewExpression: 'NewExpression',
+ ObjectExpression: 'ObjectExpression',
+ Program: 'Program',
+ Property: 'Property',
+ ReturnStatement: 'ReturnStatement',
+ SequenceExpression: 'SequenceExpression',
+ SwitchStatement: 'SwitchStatement',
+ SwitchCase: 'SwitchCase',
+ ThisExpression: 'ThisExpression',
+ ThrowStatement: 'ThrowStatement',
+ TryStatement: 'TryStatement',
+ UnaryExpression: 'UnaryExpression',
+ UpdateExpression: 'UpdateExpression',
+ VariableDeclaration: 'VariableDeclaration',
+ VariableDeclarator: 'VariableDeclarator',
+ WhileStatement: 'WhileStatement',
+ WithStatement: 'WithStatement'
+ };
+
+ 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',
+ 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'
+ };
+
+ // 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- [...]
+ 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\u08 [...]
+ };
+
+ // 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 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]
+ };
+ }
+ }
+
+ // 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;
+
+ 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'.
+ 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 (isOctalDigit(ch)) {
+ number += nextChar();
+ while (index < length) {
+ ch = source[index];
+ if (!isOctalDigit(ch)) {
+ break;
+ }
+ number += nextChar();
+ }
+
+ if (index < length) {
+ ch = source[index];
+ if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
+ throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+ }
+ }
+ return {
+ type: Token.NumericLiteral,
+ value: parseInt(number, 8),
+ octal: true,
+ 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 = '<end>';
+ }
+ 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':
+ 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, terminated = false;
+
+ 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 === '/') {
+ terminated = true;
+ break;
+ } else if (ch === '[') {
+ classMarker = true;
+ } else if (isLineTerminator(ch)) {
+ throwError({}, Messages.UnterminatedRegExp);
+ }
+ }
+ }
+
+ if (!terminated) {
+ 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;
+ }
+
+ // 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)) {
+ throwErrorTolerant(token, Messages.StrictReservedWord);
+ return;
+ }
+ 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 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;
+ }
+
+ // 11.1.4 Array Initialiser
+
+ function parseArrayInitialiser() {
+ var elements = [];
+
+ expect('[');
+
+ while (!match(']')) {
+ if (match(',')) {
+ lex();
+ elements.push(null);
+ } else {
+ elements.push(parseAssignmentExpression());
+
+ if (!match(']')) {
+ expect(',');
+ }
+ }
+ }
+
+ expect(']');
+
+ return {
+ type: Syntax.ArrayExpression,
+ elements: elements
+ };
+ }
+
+ // 11.1.5 Object Initialiser
+
+ function parsePropertyFunction(param, first) {
+ var previousStrict, body;
+
+ previousStrict = strict;
+ body = parseFunctionSourceElements();
+ if (first && strict && isRestrictedWord(param[0].name)) {
+ throwErrorTolerant(first, Messages.StrictParamName);
+ }
+ strict = previousStrict;
+
+ return {
+ type: Syntax.FunctionExpression,
+ id: null,
+ params: param,
+ defaults: [],
+ body: body,
+ rest: null,
+ generator: false,
+ expression: false
+ };
+ }
+
+ 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) {
+ throwErrorTolerant(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(':')) {
+ key = parseObjectPropertyKey();
+ expect('(');
+ expect(')');
+ return {
+ type: Syntax.Property,
+ key: key,
+ value: parsePropertyFunction([]),
+ kind: 'get'
+ };
+ } else if (token.value === 'set' && !match(':')) {
+ key = parseObjectPropertyKey();
+ expect('(');
+ token = lookahead();
+ if (token.type !== Token.Identifier) {
+ throwUnexpected(lex());
+ }
+ param = [ parseVariableIdentifier() ];
+ expect(')');
+ return {
+ type: Syntax.Property,
+ key: key,
+ value: parsePropertyFunction(param, token),
+ kind: 'set'
+ };
+ } else {
+ expect(':');
+ return {
+ type: Syntax.Property,
+ key: id,
+ value: parseAssignmentExpression(),
+ kind: 'init'
+ };
+ }
+ } else if (token.type === Token.EOF || token.type === Token.Punctuator) {
+ throwUnexpected(token);
+ } else {
+ key = parseObjectPropertyKey();
+ expect(':');
+ return {
+ type: Syntax.Property,
+ key: key,
+ value: parseAssignmentExpression(),
+ kind: 'init'
+ };
+ }
+ }
+
+ 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) {
+ throwErrorTolerant({}, Messages.AccessorDataProperty);
+ }
+ } else {
+ if (kind === PropertyKind.Data) {
+ throwErrorTolerant({}, Messages.AccessorDataProperty);
+ } else if (map[name] & kind) {
+ throwErrorTolerant({}, 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 (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();
+ 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() {
+ expect('.');
+
+ return parseNonComputedProperty();
+ }
+
+ function parseComputedMember() {
+ var expr;
+
+ expect('[');
+
+ expr = parseExpression();
+
+ expect(']');
+
+ return expr;
+ }
+
+ function parseNewExpression() {
+ var expr;
+
+ expectKeyword('new');
+
+ expr = {
+ type: Syntax.NewExpression,
+ callee: parseLeftHandSideExpression(),
+ 'arguments': []
+ };
+
+ if (match('(')) {
+ expr['arguments'] = parseArguments();
+ }
+
+ return expr;
+ }
+
+ function parseLeftHandSideExpressionAllowCall() {
+ var expr;
+
+ expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
+
+ while (match('.') || match('[') || match('(')) {
+ if (match('(')) {
+ expr = {
+ type: Syntax.CallExpression,
+ callee: expr,
+ 'arguments': parseArguments()
+ };
+ } else if (match('[')) {
+ expr = {
+ type: Syntax.MemberExpression,
+ computed: true,
+ object: expr,
+ property: parseComputedMember()
+ };
+ } else {
+ expr = {
+ type: Syntax.MemberExpression,
+ computed: false,
+ object: expr,
+ property: parseNonComputedMember()
+ };
+ }
+ }
+
+ return expr;
+ }
+
+
+ function parseLeftHandSideExpression() {
+ var expr;
+
+ expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
+
+ while (match('.') || match('[')) {
+ if (match('[')) {
+ expr = {
+ type: Syntax.MemberExpression,
+ computed: true,
+ object: expr,
+ property: parseComputedMember()
+ };
+ } else {
+ expr = {
+ type: Syntax.MemberExpression,
+ computed: false,
+ object: expr,
+ property: parseNonComputedMember()
+ };
+ }
+ }
+
+ 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)) {
+ throwErrorTolerant({}, 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)) {
+ throwErrorTolerant({}, 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 (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 parseAssignmentExpression() {
+ var token, expr;
+
+ token = lookahead();
+ expr = parseConditionalExpression();
+
+ if (matchAssign()) {
+ // LeftHandSideExpression
+ if (!isLeftHandSide(expr)) {
+ throwError({}, Messages.InvalidLHSInAssignment);
+ }
+
+ // 11.13.1
+ if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
+ throwErrorTolerant(token, Messages.StrictLHSAssignment);
+ }
+
+ 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
+ };
+ }
+
+ // 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, oldInIteration;
+
+ init = test = update = null;
+
+ expectKeyword('for');
+
+ expect('(');
+
+ if (match(';')) {
+ lex();
+ } else {
+ if (matchKeyword('var') || matchKeyword('let')) {
+ state.allowIn = false;
+ init = parseForVariableDeclaration();
+ state.allowIn = true;
+
+ if (init.declarations.length === 1 && matchKeyword('in')) {
+ lex();
+ left = init;
+ right = parseExpression();
+ init = null;
+ }
+ } else {
+ state.allowIn = false;
+ init = parseExpression();
+ state.allowIn = true;
+
+ if (matchKeyword('in')) {
+ // LeftHandSideExpression
+ if (!isLeftHandSide(init)) {
+ throwError({}, Messages.InvalidLHSInForIn);
+ }
+
+ 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
+ };
+ }
+
+ return {
+ type: Syntax.ForInStatement,
+ left: left,
+ right: right,
+ body: body,
+ each: false
+ };
+ }
+
+ // 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 = parseStatement();
+ 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,
+ 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,
+ guardedHandlers: [],
+ 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 '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 parseFunctionSourceElements() {
+ var sourceElement, sourceElements = [], token, directive, firstRestricted,
+ oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody;
+
+ 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) {
+ throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
+ }
+ } else {
+ if (!firstRestricted && token.octal) {
+ firstRestricted = token;
+ }
+ }
+ }
+
+ oldLabelSet = state.labelSet;
+ oldInIteration = state.inIteration;
+ oldInSwitch = state.inSwitch;
+ oldInFunctionBody = state.inFunctionBody;
+
+ state.labelSet = {};
+ state.inIteration = false;
+ state.inSwitch = false;
+ state.inFunctionBody = true;
+
+ 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;
+
+ return {
+ type: Syntax.BlockStatement,
+ body: sourceElements
+ };
+ }
+
+ function parseFunctionDeclaration() {
+ var id, param, params = [], body, token, stricted, firstRestricted, message, previousStrict, paramSet;
+
+ expectKeyword('function');
+ token = lookahead();
+ id = parseVariableIdentifier();
+ if (strict) {
+ if (isRestrictedWord(token.value)) {
+ throwErrorTolerant(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)) {
+ stricted = token;
+ message = Messages.StrictParamName;
+ }
+ if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
+ stricted = token;
+ message = 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;
+ body = parseFunctionSourceElements();
+ if (strict && firstRestricted) {
+ throwError(firstRestricted, message);
+ }
+ if (strict && stricted) {
+ throwErrorTolerant(stricted, message);
+ }
+ strict = previousStrict;
+
+ return {
+ type: Syntax.FunctionDeclaration,
+ id: id,
+ params: params,
+ defaults: [],
+ body: body,
+ rest: null,
+ generator: false,
+ expression: false
+ };
+ }
+
+ function parseFunctionExpression() {
+ var token, id = null, stricted, firstRestricted, message, param, params = [], body, previousStrict, paramSet;
+
+ expectKeyword('function');
+
+ if (!match('(')) {
+ token = lookahead();
+ id = parseVariableIdentifier();
+ if (strict) {
+ if (isRestrictedWord(token.value)) {
+ throwErrorTolerant(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)) {
+ stricted = token;
+ message = Messages.StrictParamName;
+ }
+ if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
+ stricted = token;
+ message = 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;
+ body = parseFunctionSourceElements();
+ if (strict && firstRestricted) {
+ throwError(firstRestricted, message);
+ }
+ if (strict && stricted) {
+ throwErrorTolerant(stricted, message);
+ }
+ strict = previousStrict;
+
+ return {
+ type: Syntax.FunctionExpression,
+ id: id,
+ params: params,
+ defaults: [],
+ body: body,
+ rest: null,
+ generator: false,
+ expression: false
+ };
+ }
+
+ // 14 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 parseSourceElements() {
+ var sourceElement, sourceElements = [], token, directive, firstRestricted;
+
+ 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) {
+ throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
+ }
+ } else {
+ if (!firstRestricted && token.octal) {
+ firstRestricted = token;
+ }
+ }
+ }
+
+ while (index < length) {
+ sourceElement = parseSourceElement();
+ if (typeof sourceElement === 'undefined') {
+ break;
+ }
+ sourceElements.push(sourceElement);
+ }
+ return sourceElements;
+ }
+
+ function parseProgram() {
+ var program;
+ strict = false;
+ program = {
+ type: Syntax.Program,
+ body: parseSourceElements()
+ };
+ 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 createLocationMarker() {
+ var marker = {};
+
+ marker.range = [index, index];
+ marker.loc = {
+ start: {
+ line: lineNumber,
+ column: index - lineStart
+ },
+ end: {
+ line: lineNumber,
+ column: index - lineStart
+ }
+ };
+
+ marker.end = function () {
+ this.range[1] = index;
+ this.loc.end.line = lineNumber;
+ this.loc.end.column = index - lineStart;
+ };
+
+ marker.apply = function (node) {
+ if (extra.range) {
+ node.range = [this.range[0], this.range[1]];
+ }
+ if (extra.loc) {
+ node.loc = {
+ start: {
+ line: this.loc.start.line,
+ column: this.loc.start.column
+ },
+ end: {
+ line: this.loc.end.line,
+ column: this.loc.end.column
+ }
+ };
+ }
+ };
+
+ return marker;
+ }
+
+ function trackLeftHandSideExpression() {
+ var marker, expr;
+
+ skipComment();
+ marker = createLocationMarker();
+
+ expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
+
+ while (match('.') || match('[')) {
+ if (match('[')) {
+ expr = {
+ type: Syntax.MemberExpression,
+ computed: true,
+ object: expr,
+ property: parseComputedMember()
+ };
+ marker.end();
+ marker.apply(expr);
+ } else {
+ expr = {
+ type: Syntax.MemberExpression,
+ computed: false,
+ object: expr,
+ property: parseNonComputedMember()
+ };
+ marker.end();
+ marker.apply(expr);
+ }
+ }
+
+ return expr;
+ }
+
+ function trackLeftHandSideExpressionAllowCall() {
+ var marker, expr;
+
+ skipComment();
+ marker = createLocationMarker();
+
+ expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
+
+ while (match('.') || match('[') || match('(')) {
+ if (match('(')) {
+ expr = {
+ type: Syntax.CallExpression,
+ callee: expr,
+ 'arguments': parseArguments()
+ };
+ marker.end();
+ marker.apply(expr);
+ } else if (match('[')) {
+ expr = {
+ type: Syntax.MemberExpression,
+ computed: true,
+ object: expr,
+ property: parseComputedMember()
+ };
+ marker.end();
+ marker.apply(expr);
+ } else {
+ expr = {
+ type: Syntax.MemberExpression,
+ computed: false,
+ object: expr,
+ property: parseNonComputedMember()
+ };
+ marker.end();
+ marker.apply(expr);
+ }
+ }
+
+ return expr;
+ }
+
+ 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 marker, node;
+
+ skipComment();
+
+ marker = createLocationMarker();
+ node = parseFunction.apply(null, arguments);
+ marker.end();
+
+ if (range && typeof node.range === 'undefined') {
+ marker.apply(node);
+ }
+
+ if (loc && typeof node.loc === 'undefined') {
+ marker.apply(node);
+ }
+
+ if (isBinary(node)) {
+ visit(node);
+ }
+
+ 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) {
+
+ extra.parseLeftHandSideExpression = parseLeftHandSideExpression;
+ extra.parseLeftHandSideExpressionAllowCall = parseLeftHandSideExpressionAllowCall;
+ parseLeftHandSideExpression = trackLeftHandSideExpression;
+ parseLeftHandSideExpressionAllowCall = trackLeftHandSideExpressionAllowCall;
+
+ 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.parseCatchClause = parseCatchClause;
+ extra.parseComputedMember = parseComputedMember;
+ extra.parseConditionalExpression = parseConditionalExpression;
+ extra.parseConstLetDeclaration = parseConstLetDeclaration;
+ extra.parseEqualityExpression = parseEqualityExpression;
+ extra.parseExpression = parseExpression;
+ extra.parseForVariableDeclaration = parseForVariableDeclaration;
+ extra.parseFunctionDeclaration = parseFunctionDeclaration;
+ extra.parseFunctionExpression = parseFunctionExpression;
+ extra.parseLeftHandSideExpression = parseLeftHandSideExpression;
+ extra.parseLeftHandSideExpressionAllowCall = parseLeftHandSideExpressionAllowCall;
+ extra.parseLogicalANDExpression = parseLogicalANDExpression;
+ extra.parseLogicalORExpression = parseLogicalORExpression;
+ extra.parseMultiplicativeExpression = parseMultiplicativeExpression;
+ extra.parseNewExpression = parseNewExpression;
+ extra.parseNonComputedProperty = parseNonComputedProperty;
+ extra.parseObjectProperty = parseObjectProperty;
+ extra.parseObjectPropertyKey = parseObjectPropertyKey;
+ 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;
+
+ 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);
+ parseCatchClause = wrapTracking(extra.parseCatchClause);
+ parseComputedMember = wrapTracking(extra.parseComputedMember);
+ parseConditionalExpression = wrapTracking(extra.parseConditionalExpression);
+ parseConstLetDeclaration = wrapTracking(extra.parseConstLetDeclaration);
+ parseEqualityExpression = wrapTracking(extra.parseEqualityExpression);
+ parseExpression = wrapTracking(extra.parseExpression);
+ parseForVariableDeclaration = wrapTracking(extra.parseForVariableDeclaration);
+ parseFunctionDeclaration = wrapTracking(extra.parseFunctionDeclaration);
+ parseFunctionExpression = wrapTracking(extra.parseFunctionExpression);
+ parseLeftHandSideExpression = wrapTracking(parseLeftHandSideExpression);
+ parseLeftHandSideExpressionAllowCall = wrapTracking(parseLeftHandSideExpressionAllowCall);
+ parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression);
+ parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression);
+ parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression);
+ parseNewExpression = wrapTracking(extra.parseNewExpression);
+ parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty);
+ parseObjectProperty = wrapTracking(extra.parseObjectProperty);
+ parseObjectPropertyKey = wrapTracking(extra.parseObjectPropertyKey);
+ 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);
+ }
+
+ 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;
+ parseCatchClause = extra.parseCatchClause;
+ parseComputedMember = extra.parseComputedMember;
+ parseConditionalExpression = extra.parseConditionalExpression;
+ parseConstLetDeclaration = extra.parseConstLetDeclaration;
+ parseEqualityExpression = extra.parseEqualityExpression;
+ parseExpression = extra.parseExpression;
+ parseForVariableDeclaration = extra.parseForVariableDeclaration;
+ parseFunctionDeclaration = extra.parseFunctionDeclaration;
+ parseFunctionExpression = extra.parseFunctionExpression;
+ parseLeftHandSideExpression = extra.parseLeftHandSideExpression;
+ parseLeftHandSideExpressionAllowCall = extra.parseLeftHandSideExpressionAllowCall;
+ parseLogicalANDExpression = extra.parseLogicalANDExpression;
+ parseLogicalORExpression = extra.parseLogicalORExpression;
+ parseMultiplicativeExpression = extra.parseMultiplicativeExpression;
+ parseNewExpression = extra.parseNewExpression;
+ parseNonComputedProperty = extra.parseNonComputedProperty;
+ parseObjectProperty = extra.parseObjectProperty;
+ parseObjectPropertyKey = extra.parseObjectPropertyKey;
+ parsePrimaryExpression = extra.parsePrimaryExpression;
+ parsePostfixExpression = extra.parsePostfixExpression;
+ 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;
+ }
+
+ 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: {},
+ 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';
+
+ 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 : */
diff --git a/test/compare/uglifyjs.js b/test/compare/uglifyjs.js
new file mode 100644
index 0000000..b22fdeb
--- /dev/null
+++ b/test/compare/uglifyjs.js
@@ -0,0 +1,1372 @@
+/***********************************************************************
+
+ A JavaScript tokenizer / parser / beautifier / compressor.
+
+ This version is suitable for Node.js. With minimal changes (the
+ exports stuff) it should work on any JS platform.
+
+ This file contains the tokenizer/parser. It is a port to JavaScript
+ of parse-js [1], a JavaScript parser library written in Common Lisp
+ by Marijn Haverbeke. Thank you Marijn!
+
+ [1] http://marijn.haverbeke.nl/parse-js/
+
+ Exported functions:
+
+ - tokenizer(code) -- returns a function. Call the returned
+ function to fetch the next token.
+
+ - parse(code) -- returns an AST of the given JavaScript code.
+
+ -------------------------------- (C) ---------------------------------
+
+ Author: Mihai Bazon
+ <mihai.bazon at gmail.com>
+ http://mihai.bazon.net/blog
+
+ Distributed under the BSD license:
+
+ Copyright 2010 (c) Mihai Bazon <mihai.bazon at gmail.com>
+ Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
+
+ 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 HOLDER “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 THE COPYRIGHT HOLDER 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.
+
+ ***********************************************************************/
+
+/* -----[ Tokenizer (constants) ]----- */
+(function(exports) {
+var KEYWORDS = array_to_hash([
+ "break",
+ "case",
+ "catch",
+ "const",
+ "continue",
+ "debugger",
+ "default",
+ "delete",
+ "do",
+ "else",
+ "finally",
+ "for",
+ "function",
+ "if",
+ "in",
+ "instanceof",
+ "new",
+ "return",
+ "switch",
+ "throw",
+ "try",
+ "typeof",
+ "var",
+ "void",
+ "while",
+ "with"
+]);
+
+var RESERVED_WORDS = array_to_hash([
+ "abstract",
+ "boolean",
+ "byte",
+ "char",
+ "class",
+ "double",
+ "enum",
+ "export",
+ "extends",
+ "final",
+ "float",
+ "goto",
+ "implements",
+ "import",
+ "int",
+ "interface",
+ "long",
+ "native",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "short",
+ "static",
+ "super",
+ "synchronized",
+ "throws",
+ "transient",
+ "volatile"
+]);
+
+var KEYWORDS_BEFORE_EXPRESSION = array_to_hash([
+ "return",
+ "new",
+ "delete",
+ "throw",
+ "else",
+ "case"
+]);
+
+var KEYWORDS_ATOM = array_to_hash([
+ "false",
+ "null",
+ "true",
+ "undefined"
+]);
+
+var OPERATOR_CHARS = array_to_hash(characters("+-*&%=<>!?|~^"));
+
+var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
+var RE_OCT_NUMBER = /^0[0-7]+$/;
+var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
+
+var OPERATORS = array_to_hash([
+ "in",
+ "instanceof",
+ "typeof",
+ "new",
+ "void",
+ "delete",
+ "++",
+ "--",
+ "+",
+ "-",
+ "!",
+ "~",
+ "&",
+ "|",
+ "^",
+ "*",
+ "/",
+ "%",
+ ">>",
+ "<<",
+ ">>>",
+ "<",
+ ">",
+ "<=",
+ ">=",
+ "==",
+ "===",
+ "!=",
+ "!==",
+ "?",
+ "=",
+ "+=",
+ "-=",
+ "/=",
+ "*=",
+ "%=",
+ ">>=",
+ "<<=",
+ ">>>=",
+ "|=",
+ "^=",
+ "&=",
+ "&&",
+ "||"
+]);
+
+var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
+
+var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{(,.;:"));
+
+var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
+
+var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy"));
+
+/* -----[ Tokenizer ]----- */
+
+var UNICODE = { // Unicode 6.1
+ letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\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 [...]
+ combining_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065F\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0859-\\u085B\\u08E4-\\u08FE\\u0900-\\u0903\\u093A-\\u093C\\u093E-\\u094F\\u0951-\\u0957\\u0962\\u0963\\u0981-\\u0983\\u09BC\\u09BE-\\u09C4\\u09C7\\u09C8\\u09 [...]
+ connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]"),
+ digit: new RegExp("[\\u0030-\\u0039\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0BE6-\\u0BEF\\u0C66-\\u0C6F\\u0CE6-\\u0CEF\\u0D66-\\u0D6F\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F29\\u1040-\\u1049\\u1090-\\u1099\\u17E0-\\u17E9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19D9\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\uA620-\\uA629\\uA8D0-\\uA8D9\\uA900-\\uA90 [...]
+};
+
+function is_letter(ch) {
+ return UNICODE.letter.test(ch);
+};
+
+function is_digit(ch) {
+ ch = ch.charCodeAt(0);
+ return ch >= 48 && ch <= 57;
+};
+
+function is_unicode_digit(ch) {
+ return UNICODE.digit.test(ch);
+}
+
+function is_alphanumeric_char(ch) {
+ return is_digit(ch) || is_letter(ch);
+};
+
+function is_unicode_combining_mark(ch) {
+ return UNICODE.combining_mark.test(ch);
+};
+
+function is_unicode_connector_punctuation(ch) {
+ return UNICODE.connector_punctuation.test(ch);
+};
+
+function is_identifier_start(ch) {
+ return ch == "$" || ch == "_" || is_letter(ch);
+};
+
+function is_identifier_char(ch) {
+ return is_identifier_start(ch)
+ || is_unicode_combining_mark(ch)
+ || is_unicode_digit(ch)
+ || is_unicode_connector_punctuation(ch)
+ || ch == "\u200c" // zero-width non-joiner <ZWNJ>
+ || ch == "\u200d" // zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
+ ;
+};
+
+function parse_js_number(num) {
+ if (RE_HEX_NUMBER.test(num)) {
+ return parseInt(num.substr(2), 16);
+ } else if (RE_OCT_NUMBER.test(num)) {
+ return parseInt(num.substr(1), 8);
+ } else if (RE_DEC_NUMBER.test(num)) {
+ return parseFloat(num);
+ }
+};
+
+function JS_Parse_Error(message, line, col, pos) {
+ this.message = message;
+ this.line = line + 1;
+ this.col = col + 1;
+ this.pos = pos + 1;
+ this.stack = new Error().stack;
+};
+
+JS_Parse_Error.prototype.toString = function() {
+ return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
+};
+
+function js_error(message, line, col, pos) {
+ throw new JS_Parse_Error(message, line, col, pos);
+};
+
+function is_token(token, type, val) {
+ return token.type == type && (val == null || token.value == val);
+};
+
+var EX_EOF = {};
+
+function tokenizer($TEXT) {
+
+ var S = {
+ text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''),
+ pos : 0,
+ tokpos : 0,
+ line : 0,
+ tokline : 0,
+ col : 0,
+ tokcol : 0,
+ newline_before : false,
+ regex_allowed : false,
+ comments_before : []
+ };
+
+ function peek() { return S.text.charAt(S.pos); };
+
+ function next(signal_eof, in_string) {
+ var ch = S.text.charAt(S.pos++);
+ if (signal_eof && !ch)
+ throw EX_EOF;
+ if (ch == "\n") {
+ S.newline_before = S.newline_before || !in_string;
+ ++S.line;
+ S.col = 0;
+ } else {
+ ++S.col;
+ }
+ return ch;
+ };
+
+ function eof() {
+ return !S.peek();
+ };
+
+ function find(what, signal_eof) {
+ var pos = S.text.indexOf(what, S.pos);
+ if (signal_eof && pos == -1) throw EX_EOF;
+ return pos;
+ };
+
+ function start_token() {
+ S.tokline = S.line;
+ S.tokcol = S.col;
+ S.tokpos = S.pos;
+ };
+
+ function token(type, value, is_comment) {
+ S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) ||
+ (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) ||
+ (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value)));
+ var ret = {
+ type : type,
+ value : value,
+ line : S.tokline,
+ col : S.tokcol,
+ pos : S.tokpos,
+ endpos : S.pos,
+ nlb : S.newline_before
+ };
+ if (!is_comment) {
+ ret.comments_before = S.comments_before;
+ S.comments_before = [];
+ // make note of any newlines in the comments that came before
+ for (var i = 0, len = ret.comments_before.length; i < len; i++) {
+ ret.nlb = ret.nlb || ret.comments_before[i].nlb;
+ }
+ }
+ S.newline_before = false;
+ return ret;
+ };
+
+ function skip_whitespace() {
+ while (HOP(WHITESPACE_CHARS, peek()))
+ next();
+ };
+
+ function read_while(pred) {
+ var ret = "", ch = peek(), i = 0;
+ while (ch && pred(ch, i++)) {
+ ret += next();
+ ch = peek();
+ }
+ return ret;
+ };
+
+ function parse_error(err) {
+ js_error(err, S.tokline, S.tokcol, S.tokpos);
+ };
+
+ function read_num(prefix) {
+ var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
+ var num = read_while(function(ch, i){
+ if (ch == "x" || ch == "X") {
+ if (has_x) return false;
+ return has_x = true;
+ }
+ if (!has_x && (ch == "E" || ch == "e")) {
+ if (has_e) return false;
+ return has_e = after_e = true;
+ }
+ if (ch == "-") {
+ if (after_e || (i == 0 && !prefix)) return true;
+ return false;
+ }
+ if (ch == "+") return after_e;
+ after_e = false;
+ if (ch == ".") {
+ if (!has_dot && !has_x && !has_e)
+ return has_dot = true;
+ return false;
+ }
+ return is_alphanumeric_char(ch);
+ });
+ if (prefix)
+ num = prefix + num;
+ var valid = parse_js_number(num);
+ if (!isNaN(valid)) {
+ return token("num", valid);
+ } else {
+ parse_error("Invalid syntax: " + num);
+ }
+ };
+
+ function read_escaped_char(in_string) {
+ var ch = next(true, in_string);
+ switch (ch) {
+ case "n" : return "\n";
+ case "r" : return "\r";
+ case "t" : return "\t";
+ case "b" : return "\b";
+ case "v" : return "\u000b";
+ case "f" : return "\f";
+ case "0" : return "\0";
+ case "x" : return String.fromCharCode(hex_bytes(2));
+ case "u" : return String.fromCharCode(hex_bytes(4));
+ case "\n": return "";
+ default : return ch;
+ }
+ };
+
+ function hex_bytes(n) {
+ var num = 0;
+ for (; n > 0; --n) {
+ var digit = parseInt(next(true), 16);
+ if (isNaN(digit))
+ parse_error("Invalid hex-character pattern in string");
+ num = (num << 4) | digit;
+ }
+ return num;
+ };
+
+ function read_string() {
+ return with_eof_error("Unterminated string constant", function(){
+ var quote = next(), ret = "";
+ for (;;) {
+ var ch = next(true);
+ if (ch == "\\") {
+ // read OctalEscapeSequence (XXX: deprecated if "strict mode")
+ // https://github.com/mishoo/UglifyJS/issues/178
+ var octal_len = 0, first = null;
+ ch = read_while(function(ch){
+ if (ch >= "0" && ch <= "7") {
+ if (!first) {
+ first = ch;
+ return ++octal_len;
+ }
+ else if (first <= "3" && octal_len <= 2) return ++octal_len;
+ else if (first >= "4" && octal_len <= 1) return ++octal_len;
+ }
+ return false;
+ });
+ if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
+ else ch = read_escaped_char(true);
+ }
+ else if (ch == quote) break;
+ ret += ch;
+ }
+ return token("string", ret);
+ });
+ };
+
+ function read_line_comment() {
+ next();
+ var i = find("\n"), ret;
+ if (i == -1) {
+ ret = S.text.substr(S.pos);
+ S.pos = S.text.length;
+ } else {
+ ret = S.text.substring(S.pos, i);
+ S.pos = i;
+ }
+ return token("comment1", ret, true);
+ };
+
+ function read_multiline_comment() {
+ next();
+ return with_eof_error("Unterminated multiline comment", function(){
+ var i = find("*/", true),
+ text = S.text.substring(S.pos, i);
+ S.pos = i + 2;
+ S.line += text.split("\n").length - 1;
+ S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
+
+ // https://github.com/mishoo/UglifyJS/issues/#issue/100
+ if (/^@cc_on/i.test(text)) {
+ warn("WARNING: at line " + S.line);
+ warn("*** Found \"conditional comment\": " + text);
+ warn("*** UglifyJS DISCARDS ALL COMMENTS. This means your code might no longer work properly in Internet Explorer.");
+ }
+
+ return token("comment2", text, true);
+ });
+ };
+
+ function read_name() {
+ var backslash = false, name = "", ch, escaped = false, hex;
+ while ((ch = peek()) != null) {
+ if (!backslash) {
+ if (ch == "\\") escaped = backslash = true, next();
+ else if (is_identifier_char(ch)) name += next();
+ else break;
+ }
+ else {
+ if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
+ ch = read_escaped_char();
+ if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
+ name += ch;
+ backslash = false;
+ }
+ }
+ if (HOP(KEYWORDS, name) && escaped) {
+ hex = name.charCodeAt(0).toString(16).toUpperCase();
+ name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
+ }
+ return name;
+ };
+
+ function read_regexp(regexp) {
+ return with_eof_error("Unterminated regular expression", function(){
+ var prev_backslash = false, ch, in_class = false;
+ while ((ch = next(true))) if (prev_backslash) {
+ regexp += "\\" + ch;
+ prev_backslash = false;
+ } else if (ch == "[") {
+ in_class = true;
+ regexp += ch;
+ } else if (ch == "]" && in_class) {
+ in_class = false;
+ regexp += ch;
+ } else if (ch == "/" && !in_class) {
+ break;
+ } else if (ch == "\\") {
+ prev_backslash = true;
+ } else {
+ regexp += ch;
+ }
+ var mods = read_name();
+ return token("regexp", [ regexp, mods ]);
+ });
+ };
+
+ function read_operator(prefix) {
+ function grow(op) {
+ if (!peek()) return op;
+ var bigger = op + peek();
+ if (HOP(OPERATORS, bigger)) {
+ next();
+ return grow(bigger);
+ } else {
+ return op;
+ }
+ };
+ return token("operator", grow(prefix || next()));
+ };
+
+ function handle_slash() {
+ next();
+ var regex_allowed = S.regex_allowed;
+ switch (peek()) {
+ case "/":
+ S.comments_before.push(read_line_comment());
+ S.regex_allowed = regex_allowed;
+ return next_token();
+ case "*":
+ S.comments_before.push(read_multiline_comment());
+ S.regex_allowed = regex_allowed;
+ return next_token();
+ }
+ return S.regex_allowed ? read_regexp("") : read_operator("/");
+ };
+
+ function handle_dot() {
+ next();
+ return is_digit(peek())
+ ? read_num(".")
+ : token("punc", ".");
+ };
+
+ function read_word() {
+ var word = read_name();
+ return !HOP(KEYWORDS, word)
+ ? token("name", word)
+ : HOP(OPERATORS, word)
+ ? token("operator", word)
+ : HOP(KEYWORDS_ATOM, word)
+ ? token("atom", word)
+ : token("keyword", word);
+ };
+
+ function with_eof_error(eof_error, cont) {
+ try {
+ return cont();
+ } catch(ex) {
+ if (ex === EX_EOF) parse_error(eof_error);
+ else throw ex;
+ }
+ };
+
+ function next_token(force_regexp) {
+ if (force_regexp != null)
+ return read_regexp(force_regexp);
+ skip_whitespace();
+ start_token();
+ var ch = peek();
+ if (!ch) return token("eof");
+ if (is_digit(ch)) return read_num();
+ if (ch == '"' || ch == "'") return read_string();
+ if (HOP(PUNC_CHARS, ch)) return token("punc", next());
+ if (ch == ".") return handle_dot();
+ if (ch == "/") return handle_slash();
+ if (HOP(OPERATOR_CHARS, ch)) return read_operator();
+ if (ch == "\\" || is_identifier_start(ch)) return read_word();
+ parse_error("Unexpected character '" + ch + "'");
+ };
+
+ next_token.context = function(nc) {
+ if (nc) S = nc;
+ return S;
+ };
+
+ return next_token;
+
+};
+
+/* -----[ Parser (constants) ]----- */
+
+var UNARY_PREFIX = array_to_hash([
+ "typeof",
+ "void",
+ "delete",
+ "--",
+ "++",
+ "!",
+ "~",
+ "-",
+ "+"
+]);
+
+var UNARY_POSTFIX = array_to_hash([ "--", "++" ]);
+
+var ASSIGNMENT = (function(a, ret, i){
+ while (i < a.length) {
+ ret[a[i]] = a[i].substr(0, a[i].length - 1);
+ i++;
+ }
+ return ret;
+})(
+ ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="],
+ { "=": true },
+ 0
+);
+
+var PRECEDENCE = (function(a, ret){
+ for (var i = 0, n = 1; i < a.length; ++i, ++n) {
+ var b = a[i];
+ for (var j = 0; j < b.length; ++j) {
+ ret[b[j]] = n;
+ }
+ }
+ return ret;
+})(
+ [
+ ["||"],
+ ["&&"],
+ ["|"],
+ ["^"],
+ ["&"],
+ ["==", "===", "!=", "!=="],
+ ["<", ">", "<=", ">=", "in", "instanceof"],
+ [">>", "<<", ">>>"],
+ ["+", "-"],
+ ["*", "/", "%"]
+ ],
+ {}
+);
+
+var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
+
+var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
+
+/* -----[ Parser ]----- */
+
+function NodeWithToken(str, start, end) {
+ this.name = str;
+ this.start = start;
+ this.end = end;
+};
+
+NodeWithToken.prototype.toString = function() { return this.name; };
+
+function parse($TEXT, exigent_mode, embed_tokens) {
+
+ var S = {
+ input : typeof $TEXT == "string" ? tokenizer($TEXT, true) : $TEXT,
+ token : null,
+ prev : null,
+ peeked : null,
+ in_function : 0,
+ in_directives : true,
+ in_loop : 0,
+ labels : []
+ };
+
+ S.token = next();
+
+ function is(type, value) {
+ return is_token(S.token, type, value);
+ };
+
+ function peek() { return S.peeked || (S.peeked = S.input()); };
+
+ function next() {
+ S.prev = S.token;
+ if (S.peeked) {
+ S.token = S.peeked;
+ S.peeked = null;
+ } else {
+ S.token = S.input();
+ }
+ S.in_directives = S.in_directives && (
+ S.token.type == "string" || is("punc", ";")
+ );
+ return S.token;
+ };
+
+ function prev() {
+ return S.prev;
+ };
+
+ function croak(msg, line, col, pos) {
+ var ctx = S.input.context();
+ js_error(msg,
+ line != null ? line : ctx.tokline,
+ col != null ? col : ctx.tokcol,
+ pos != null ? pos : ctx.tokpos);
+ };
+
+ function token_error(token, msg) {
+ croak(msg, token.line, token.col);
+ };
+
+ function unexpected(token) {
+ if (token == null)
+ token = S.token;
+ token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
+ };
+
+ function expect_token(type, val) {
+ if (is(type, val)) {
+ return next();
+ }
+ token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type);
+ };
+
+ function expect(punc) { return expect_token("punc", punc); };
+
+ function can_insert_semicolon() {
+ return !exigent_mode && (
+ S.token.nlb || is("eof") || is("punc", "}")
+ );
+ };
+
+ function semicolon() {
+ if (is("punc", ";")) next();
+ else if (!can_insert_semicolon()) unexpected();
+ };
+
+ function as() {
+ return slice(arguments);
+ };
+
+ function parenthesised() {
+ expect("(");
+ var ex = expression();
+ expect(")");
+ return ex;
+ };
+
+ function add_tokens(str, start, end) {
+ return str instanceof NodeWithToken ? str : new NodeWithToken(str, start, end);
+ };
+
+ function maybe_embed_tokens(parser) {
+ if (embed_tokens) return function() {
+ var start = S.token;
+ var ast = parser.apply(this, arguments);
+ ast[0] = add_tokens(ast[0], start, prev());
+ return ast;
+ };
+ else return parser;
+ };
+
+ var statement = maybe_embed_tokens(function() {
+ if (is("operator", "/") || is("operator", "/=")) {
+ S.peeked = null;
+ S.token = S.input(S.token.value.substr(1)); // force regexp
+ }
+ switch (S.token.type) {
+ case "string":
+ var dir = S.in_directives, stat = simple_statement();
+ if (dir && stat[1][0] == "string" && !is("punc", ","))
+ return as("directive", stat[1][1]);
+ return stat;
+ case "num":
+ case "regexp":
+ case "operator":
+ case "atom":
+ return simple_statement();
+
+ case "name":
+ return is_token(peek(), "punc", ":")
+ ? labeled_statement(prog1(S.token.value, next, next))
+ : simple_statement();
+
+ case "punc":
+ switch (S.token.value) {
+ case "{":
+ return as("block", block_());
+ case "[":
+ case "(":
+ return simple_statement();
+ case ";":
+ next();
+ return as("block");
+ default:
+ unexpected();
+ }
+
+ case "keyword":
+ switch (prog1(S.token.value, next)) {
+ case "break":
+ return break_cont("break");
+
+ case "continue":
+ return break_cont("continue");
+
+ case "debugger":
+ semicolon();
+ return as("debugger");
+
+ case "do":
+ return (function(body){
+ expect_token("keyword", "while");
+ return as("do", prog1(parenthesised, semicolon), body);
+ })(in_loop(statement));
+
+ case "for":
+ return for_();
+
+ case "function":
+ return function_(true);
+
+ case "if":
+ return if_();
+
+ case "return":
+ if (S.in_function == 0)
+ croak("'return' outside of function");
+ return as("return",
+ is("punc", ";")
+ ? (next(), null)
+ : can_insert_semicolon()
+ ? null
+ : prog1(expression, semicolon));
+
+ case "switch":
+ return as("switch", parenthesised(), switch_block_());
+
+ case "throw":
+ if (S.token.nlb)
+ croak("Illegal newline after 'throw'");
+ return as("throw", prog1(expression, semicolon));
+
+ case "try":
+ return try_();
+
+ case "var":
+ return prog1(var_, semicolon);
+
+ case "const":
+ return prog1(const_, semicolon);
+
+ case "while":
+ return as("while", parenthesised(), in_loop(statement));
+
+ case "with":
+ return as("with", parenthesised(), statement());
+
+ default:
+ unexpected();
+ }
+ }
+ });
+
+ function labeled_statement(label) {
+ S.labels.push(label);
+ var start = S.token, stat = statement();
+ if (exigent_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0]))
+ unexpected(start);
+ S.labels.pop();
+ return as("label", label, stat);
+ };
+
+ function simple_statement() {
+ return as("stat", prog1(expression, semicolon));
+ };
+
+ function break_cont(type) {
+ var name;
+ if (!can_insert_semicolon()) {
+ name = is("name") ? S.token.value : null;
+ }
+ if (name != null) {
+ next();
+ if (!member(name, S.labels))
+ croak("Label " + name + " without matching loop or statement");
+ }
+ else if (S.in_loop == 0)
+ croak(type + " not inside a loop or switch");
+ semicolon();
+ return as(type, name);
+ };
+
+ function for_() {
+ expect("(");
+ var init = null;
+ if (!is("punc", ";")) {
+ init = is("keyword", "var")
+ ? (next(), var_(true))
+ : expression(true, true);
+ if (is("operator", "in")) {
+ if (init[0] == "var" && init[1].length > 1)
+ croak("Only one variable declaration allowed in for..in loop");
+ return for_in(init);
+ }
+ }
+ return regular_for(init);
+ };
+
+ function regular_for(init) {
+ expect(";");
+ var test = is("punc", ";") ? null : expression();
+ expect(";");
+ var step = is("punc", ")") ? null : expression();
+ expect(")");
+ return as("for", init, test, step, in_loop(statement));
+ };
+
+ function for_in(init) {
+ var lhs = init[0] == "var" ? as("name", init[1][0]) : init;
+ next();
+ var obj = expression();
+ expect(")");
+ return as("for-in", init, lhs, obj, in_loop(statement));
+ };
+
+ var function_ = function(in_statement) {
+ var name = is("name") ? prog1(S.token.value, next) : null;
+ if (in_statement && !name)
+ unexpected();
+ expect("(");
+ return as(in_statement ? "defun" : "function",
+ name,
+ // arguments
+ (function(first, a){
+ while (!is("punc", ")")) {
+ if (first) first = false; else expect(",");
+ if (!is("name")) unexpected();
+ a.push(S.token.value);
+ next();
+ }
+ next();
+ return a;
+ })(true, []),
+ // body
+ (function(){
+ ++S.in_function;
+ var loop = S.in_loop;
+ S.in_directives = true;
+ S.in_loop = 0;
+ var a = block_();
+ --S.in_function;
+ S.in_loop = loop;
+ return a;
+ })());
+ };
+
+ function if_() {
+ var cond = parenthesised(), body = statement(), belse;
+ if (is("keyword", "else")) {
+ next();
+ belse = statement();
+ }
+ return as("if", cond, body, belse);
+ };
+
+ function block_() {
+ expect("{");
+ var a = [];
+ while (!is("punc", "}")) {
+ if (is("eof")) unexpected();
+ a.push(statement());
+ }
+ next();
+ return a;
+ };
+
+ var switch_block_ = curry(in_loop, function(){
+ expect("{");
+ var a = [], cur = null;
+ while (!is("punc", "}")) {
+ if (is("eof")) unexpected();
+ if (is("keyword", "case")) {
+ next();
+ cur = [];
+ a.push([ expression(), cur ]);
+ expect(":");
+ }
+ else if (is("keyword", "default")) {
+ next();
+ expect(":");
+ cur = [];
+ a.push([ null, cur ]);
+ }
+ else {
+ if (!cur) unexpected();
+ cur.push(statement());
+ }
+ }
+ next();
+ return a;
+ });
+
+ function try_() {
+ var body = block_(), bcatch, bfinally;
+ if (is("keyword", "catch")) {
+ next();
+ expect("(");
+ if (!is("name"))
+ croak("Name expected");
+ var name = S.token.value;
+ next();
+ expect(")");
+ bcatch = [ name, block_() ];
+ }
+ if (is("keyword", "finally")) {
+ next();
+ bfinally = block_();
+ }
+ if (!bcatch && !bfinally)
+ croak("Missing catch/finally blocks");
+ return as("try", body, bcatch, bfinally);
+ };
+
+ function vardefs(no_in) {
+ var a = [];
+ for (;;) {
+ if (!is("name"))
+ unexpected();
+ var name = S.token.value;
+ next();
+ if (is("operator", "=")) {
+ next();
+ a.push([ name, expression(false, no_in) ]);
+ } else {
+ a.push([ name ]);
+ }
+ if (!is("punc", ","))
+ break;
+ next();
+ }
+ return a;
+ };
+
+ function var_(no_in) {
+ return as("var", vardefs(no_in));
+ };
+
+ function const_() {
+ return as("const", vardefs());
+ };
+
+ function new_() {
+ var newexp = expr_atom(false), args;
+ if (is("punc", "(")) {
+ next();
+ args = expr_list(")");
+ } else {
+ args = [];
+ }
+ return subscripts(as("new", newexp, args), true);
+ };
+
+ var expr_atom = maybe_embed_tokens(function(allow_calls) {
+ if (is("operator", "new")) {
+ next();
+ return new_();
+ }
+ if (is("punc")) {
+ switch (S.token.value) {
+ case "(":
+ next();
+ return subscripts(prog1(expression, curry(expect, ")")), allow_calls);
+ case "[":
+ next();
+ return subscripts(array_(), allow_calls);
+ case "{":
+ next();
+ return subscripts(object_(), allow_calls);
+ }
+ unexpected();
+ }
+ if (is("keyword", "function")) {
+ next();
+ return subscripts(function_(false), allow_calls);
+ }
+ if (HOP(ATOMIC_START_TOKEN, S.token.type)) {
+ var atom = S.token.type == "regexp"
+ ? as("regexp", S.token.value[0], S.token.value[1])
+ : as(S.token.type, S.token.value);
+ return subscripts(prog1(atom, next), allow_calls);
+ }
+ unexpected();
+ });
+
+ function expr_list(closing, allow_trailing_comma, allow_empty) {
+ var first = true, a = [];
+ while (!is("punc", closing)) {
+ if (first) first = false; else expect(",");
+ if (allow_trailing_comma && is("punc", closing)) break;
+ if (is("punc", ",") && allow_empty) {
+ a.push([ "atom", "undefined" ]);
+ } else {
+ a.push(expression(false));
+ }
+ }
+ next();
+ return a;
+ };
+
+ function array_() {
+ return as("array", expr_list("]", !exigent_mode, true));
+ };
+
+ function object_() {
+ var first = true, a = [];
+ while (!is("punc", "}")) {
+ if (first) first = false; else expect(",");
+ if (!exigent_mode && is("punc", "}"))
+ // allow trailing comma
+ break;
+ var type = S.token.type;
+ var name = as_property_name();
+ if (type == "name" && (name == "get" || name == "set") && !is("punc", ":")) {
+ a.push([ as_name(), function_(false), name ]);
+ } else {
+ expect(":");
+ a.push([ name, expression(false) ]);
+ }
+ // FIXME [!!] Line not in original parse-js,
+ // added to be able to warn about unquoted
+ // keyword properties
+ a[a.length - 1].type = type;
+ }
+ next();
+ return as("object", a);
+ };
+
+ function as_property_name() {
+ switch (S.token.type) {
+ case "num":
+ case "string":
+ return prog1(S.token.value, next);
+ }
+ return as_name();
+ };
+
+ function as_name() {
+ switch (S.token.type) {
+ case "name":
+ case "operator":
+ case "keyword":
+ case "atom":
+ return prog1(S.token.value, next);
+ default:
+ unexpected();
+ }
+ };
+
+ function subscripts(expr, allow_calls) {
+ if (is("punc", ".")) {
+ next();
+ return subscripts(as("dot", expr, as_name()), allow_calls);
+ }
+ if (is("punc", "[")) {
+ next();
+ return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls);
+ }
+ if (allow_calls && is("punc", "(")) {
+ next();
+ return subscripts(as("call", expr, expr_list(")")), true);
+ }
+ return expr;
+ };
+
+ function maybe_unary(allow_calls) {
+ if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
+ return make_unary("unary-prefix",
+ prog1(S.token.value, next),
+ maybe_unary(allow_calls));
+ }
+ var val = expr_atom(allow_calls);
+ while (is("operator") && HOP(UNARY_POSTFIX, S.token.value) && !S.token.nlb) {
+ val = make_unary("unary-postfix", S.token.value, val);
+ next();
+ }
+ return val;
+ };
+
+ function make_unary(tag, op, expr) {
+ if ((op == "++" || op == "--") && !is_assignable(expr))
+ croak("Invalid use of " + op + " operator");
+ return as(tag, op, expr);
+ };
+
+ function expr_op(left, min_prec, no_in) {
+ var op = is("operator") ? S.token.value : null;
+ if (op && op == "in" && no_in) op = null;
+ var prec = op != null ? PRECEDENCE[op] : null;
+ if (prec != null && prec > min_prec) {
+ next();
+ var right = expr_op(maybe_unary(true), prec, no_in);
+ return expr_op(as("binary", op, left, right), min_prec, no_in);
+ }
+ return left;
+ };
+
+ function expr_ops(no_in) {
+ return expr_op(maybe_unary(true), 0, no_in);
+ };
+
+ function maybe_conditional(no_in) {
+ var expr = expr_ops(no_in);
+ if (is("operator", "?")) {
+ next();
+ var yes = expression(false);
+ expect(":");
+ return as("conditional", expr, yes, expression(false, no_in));
+ }
+ return expr;
+ };
+
+ function is_assignable(expr) {
+ if (!exigent_mode) return true;
+ switch (expr[0]+"") {
+ case "dot":
+ case "sub":
+ case "new":
+ case "call":
+ return true;
+ case "name":
+ return expr[1] != "this";
+ }
+ };
+
+ function maybe_assign(no_in) {
+ var left = maybe_conditional(no_in), val = S.token.value;
+ if (is("operator") && HOP(ASSIGNMENT, val)) {
+ if (is_assignable(left)) {
+ next();
+ return as("assign", ASSIGNMENT[val], left, maybe_assign(no_in));
+ }
+ croak("Invalid assignment");
+ }
+ return left;
+ };
+
+ var expression = maybe_embed_tokens(function(commas, no_in) {
+ if (arguments.length == 0)
+ commas = true;
+ var expr = maybe_assign(no_in);
+ if (commas && is("punc", ",")) {
+ next();
+ return as("seq", expr, expression(true, no_in));
+ }
+ return expr;
+ });
+
+ function in_loop(cont) {
+ try {
+ ++S.in_loop;
+ return cont();
+ } finally {
+ --S.in_loop;
+ }
+ };
+
+ return as("toplevel", (function(a){
+ while (!is("eof"))
+ a.push(statement());
+ return a;
+ })([]));
+
+};
+
+/* -----[ Utilities ]----- */
+
+function curry(f) {
+ var args = slice(arguments, 1);
+ return function() { return f.apply(this, args.concat(slice(arguments))); };
+};
+
+function prog1(ret) {
+ if (ret instanceof Function)
+ ret = ret();
+ for (var i = 1, n = arguments.length; --n > 0; ++i)
+ arguments[i]();
+ return ret;
+};
+
+function array_to_hash(a) {
+ var ret = {};
+ for (var i = 0; i < a.length; ++i)
+ ret[a[i]] = true;
+ return ret;
+};
+
+function slice(a, start) {
+ return Array.prototype.slice.call(a, start || 0);
+};
+
+function characters(str) {
+ return str.split("");
+};
+
+function member(name, array) {
+ for (var i = array.length; --i >= 0;)
+ if (array[i] == name)
+ return true;
+ return false;
+};
+
+function HOP(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+};
+
+var warn = function() {};
+
+/* -----[ Exports ]----- */
+
+exports.tokenizer = tokenizer;
+exports.parse = parse;
+exports.slice = slice;
+exports.curry = curry;
+exports.member = member;
+exports.array_to_hash = array_to_hash;
+exports.PRECEDENCE = PRECEDENCE;
+exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
+exports.RESERVED_WORDS = RESERVED_WORDS;
+exports.KEYWORDS = KEYWORDS;
+exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
+exports.OPERATORS = OPERATORS;
+exports.is_alphanumeric_char = is_alphanumeric_char;
+exports.is_identifier_start = is_identifier_start;
+exports.is_identifier_char = is_identifier_char;
+exports.set_logger = function(logger) {
+ warn = logger;
+};
+})(typeof exports == "undefined" ? window.uglifyjs = {} : exports);
+// Local variables:
+// js-indent-level: 8
+// End:
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-acorn-jsx.git
More information about the Pkg-javascript-commits
mailing list