You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2019/12/18 17:56:32 UTC
[couchdb-escodegen] 01/11: First commit to gh-pages
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch gh-pages
in repository https://gitbox.apache.org/repos/asf/couchdb-escodegen.git
commit 480a003098c6fc7658ddf4c04fad569e1a9a1613
Author: Constellation <ut...@gmail.com>
AuthorDate: Fri Mar 16 04:29:02 2012 +0900
First commit to gh-pages
---
assets/esprima.js | 3744 +++++++++++++++++++++++++++++++++++++++++++++++++++++
assets/style.css | 52 +
demo/index.html | 71 +
escodegen.js | 843 ++++++++++++
4 files changed, 4710 insertions(+)
diff --git a/assets/esprima.js b/assets/esprima.js
new file mode 100644
index 0000000..8a11eba
--- /dev/null
+++ b/assets/esprima.js
@@ -0,0 +1,3744 @@
+/*
+ Copyright (C) 2012 Ariya Hidayat <ar...@gmail.com>
+ Copyright (C) 2012 Mathias Bynens <ma...@qiwi.be>
+ Copyright (C) 2012 Joost-Wim Boekesteijn <jo...@boekesteijn.nl>
+ Copyright (C) 2012 Kris Kowal <kr...@cixar.com>
+ Copyright (C) 2012 Yusuke Suzuki <ut...@gmail.com>
+ Copyright (C) 2012 Arpad Borsos <ar...@googlemail.com>
+ Copyright (C) 2011 Ariya Hidayat <ar...@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 */
+/*global esprima:true, exports: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 (exports) {
+ 'use strict';
+
+ var Token,
+ TokenName,
+ Syntax,
+ PropertyKind,
+ Messages,
+ Regex,
+ source,
+ allowIn,
+ lastParenthesized,
+ strict,
+ index,
+ lineNumber,
+ lineStart,
+ length,
+ buffer,
+ extra,
+ labelSet,
+ inIteration,
+ inSwitch,
+ inFunctionBody;
+
+ 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',
+ 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 [...]
+ };
+
+ if (typeof Object.freeze === 'function') {
+ Object.freeze(Token);
+ Object.freeze(TokenName);
+ Object.freeze(Syntax);
+ Object.freeze(PropertyKind);
+ Object.freeze(Messages);
+ Object.freeze(Regex);
+ }
+
+ // 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() {
+ var ch;
+ if (index < length) {
+ ch = source[index];
+ index += 1;
+ }
+ return ch;
+ }
+
+ // 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 += 1;
+ }
+ lineNumber += 1;
+ lineStart = index;
+ }
+ } else if (blockComment) {
+ if (isLineTerminator(ch)) {
+ if (ch === '\r' && source[index + 1] === '\n') {
+ index += 1;
+ }
+ lineNumber += 1;
+ index += 1;
+ 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 += 1;
+ 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 += 1;
+ } else if (isLineTerminator(ch)) {
+ index += 1;
+ if (ch === '\r' && source[index] === '\n') {
+ index += 1;
+ }
+ lineNumber += 1;
+ lineStart = index;
+ } else {
+ break;
+ }
+ }
+ }
+
+ function scanHexEscape(prefix) {
+ var i, len, ch, code = 0;
+
+ len = (prefix === 'u') ? 4 : 2;
+ for (i = 0; i < len; i += 1) {
+ 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 += 1;
+ if (source[index] !== 'u') {
+ return;
+ }
+ index += 1;
+ 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 += 1;
+ if (source[index] !== 'u') {
+ return;
+ }
+ index += 1;
+ 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 += 1;
+ return {
+ type: Token.Punctuator,
+ value: ch1,
+ lineNumber: lineNumber,
+ lineStart: lineStart,
+ range: [start, index]
+ };
+ }
+
+ if (ch1 === ',' || ch1 === '(' || ch1 === ')') {
+ index += 1;
+ 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 += 1;
+
+ 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 += 1;
+ if (ch === '\r' && source[index] === '\n') {
+ index += 1;
+ }
+ }
+ } else if (isLineTerminator(ch)) {
+ break;
+ } else {
+ str += ch;
+ }
+ }
+
+ if (quote !== '') {
+ throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+ }
+
+ return {
+ type: Token.StringLiteral,
+ value: str,
+ octal: octal,
+ lineNumber: lineNumber,
+ lineStart: lineStart,
+ range: [start, index]
+ };
+ }
+
+ function scanRegExp() {
+ var str = '', ch, start, pattern, flags, value, classMarker = false, restore;
+
+ buffer = null;
+ skipComment();
+
+ start = index;
+ ch = source[index];
+ assert(ch === '/', 'Regular expression literal must start with a slash');
+ str = nextChar();
+
+ while (index < length) {
+ ch = nextChar();
+ str += ch;
+ if (classMarker) {
+ if (ch === ']') {
+ classMarker = false;
+ }
+ } else {
+ if (ch === '\\') {
+ str += nextChar();
+ }
+ if (ch === '/') {
+ break;
+ }
+ if (ch === '[') {
+ classMarker = true;
+ }
+ if (isLineTerminator(ch)) {
+ throwError({}, Messages.UnterminatedRegExp);
+ }
+ }
+ }
+
+ if (str.length === 1) {
+ throwError({}, Messages.UnterminatedRegExp);
+ }
+
+ // Exclude leading and trailing slash.
+ pattern = str.substr(1, str.length - 2);
+
+ flags = '';
+ while (index < length) {
+ ch = source[index];
+ if (!isIdentifierPart(ch)) {
+ break;
+ }
+
+ index += 1;
+ if (ch === '\\' && index < length) {
+ ch = source[index];
+ if (ch === 'u') {
+ index += 1;
+ restore = index;
+ ch = scanHexEscape('u');
+ if (ch) {
+ flags += ch;
+ str += '\\u';
+ for (; restore < index; restore += 1) {
+ 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;
+ }
+
+ // Throw an exception because of the token.
+
+ function throwUnexpected(token) {
+ var s;
+
+ if (token.type === Token.EOF) {
+ throwError(token, Messages.UnexpectedEOS);
+ }
+
+ if (token.type === Token.NumericLiteral) {
+ throwError(token, Messages.UnexpectedNumber);
+ }
+
+ if (token.type === Token.StringLiteral) {
+ throwError(token, Messages.UnexpectedString);
+ }
+
+ if (token.type === Token.Identifier) {
+ throwError(token, Messages.UnexpectedIdentifier);
+ }
+
+ if (token.type === Token.Keyword) {
+ if (isFutureReservedWord(token.value)) {
+ throwError(token, Messages.UnexpectedReserved);
+ } else if (strict && isStrictModeReservedWord(token.value)) {
+ throwError(token, Messages.StrictReservedWord);
+ }
+ throwError(token, Messages.UnexpectedToken, token.value);
+ }
+
+ // BooleanLiteral, NullLiteral, or Punctuator.
+ throwError(token, Messages.UnexpectedToken, token.value);
+ }
+
+ // Expect the next token to match the specified punctuator.
+ // If not, an exception will be thrown.
+
+ function expect(value) {
+ var token = lex();
+ if (token.type !== Token.Punctuator || token.value !== value) {
+ throwUnexpected(token);
+ }
+ }
+
+ // Expect the next token to match the specified keyword.
+ // If not, an exception will be thrown.
+
+ function expectKeyword(keyword) {
+ var token = lex();
+ if (token.type !== Token.Keyword || token.value !== keyword) {
+ throwUnexpected(token);
+ }
+ }
+
+ // Return true if the next token matches the specified punctuator.
+
+ function match(value) {
+ var token = lookahead();
+ return token.type === Token.Punctuator && token.value === value;
+ }
+
+ // Return true if the next token matches the specified keyword
+
+ function matchKeyword(keyword) {
+ var token = lookahead();
+ return token.type === Token.Keyword && token.value === keyword;
+ }
+
+ // Return true if the next token 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) {
+ switch (expr.type) {
+ case 'AssignmentExpression':
+ case 'BinaryExpression':
+ case 'ConditionalExpression':
+ case 'LogicalExpression':
+ case 'SequenceExpression':
+ case 'UnaryExpression':
+ case 'UpdateExpression':
+ return false;
+ }
+ return true;
+ }
+
+ // 11.1.4 Array Initialiser
+
+ function parseArrayInitialiser() {
+ var elements = [],
+ undef;
+
+ expect('[');
+
+ while (!match(']')) {
+ if (match(',')) {
+ lex();
+ elements.push(undef);
+ } else {
+ elements.push(parseAssignmentExpression());
+
+ if (!match(']')) {
+ expect(',');
+ }
+ }
+ }
+
+ expect(']');
+
+ return {
+ type: Syntax.ArrayExpression,
+ elements: elements
+ };
+ }
+
+ // 11.1.5 Object Initialiser
+
+ function parsePropertyFunction(param, first) {
+ var previousStrict, body;
+
+ previousStrict = strict;
+ body = parseFunctionSourceElements();
+ if (first && strict && isRestrictedWord(param[0].name)) {
+ throwError(first, Messages.StrictParamName);
+ }
+ strict = previousStrict;
+
+ return {
+ type: Syntax.FunctionExpression,
+ id: null,
+ params: param,
+ body: body
+ };
+ }
+
+ function parseObjectPropertyKey() {
+ var token = lex(),
+ key;
+
+ switch (token.type) {
+
+ case Token.StringLiteral:
+ case Token.NumericLiteral:
+ if (strict && token.octal) {
+ throwError(token, Messages.StrictOctalLiteral);
+ }
+ key = createLiteral(token);
+ break;
+
+ case Token.Identifier:
+ case Token.Keyword:
+ case Token.BooleanLiteral:
+ case Token.NullLiteral:
+ key = {
+ type: Syntax.Identifier,
+ name: token.value
+ };
+ break;
+
+ default:
+ // Unreachable, since parseObjectProperty() will not call this
+ // function with any other type of token.
+ }
+
+ return key;
+ }
+
+ function parseObjectProperty() {
+ var token, property, key, id, param;
+
+ token = lookahead();
+
+ switch (token.type) {
+
+ case Token.Identifier:
+ id = parseObjectPropertyKey();
+
+ // Property Assignment: Getter and Setter.
+
+ if (token.value === 'get' && !match(':')) {
+ key = parseObjectPropertyKey();
+ expect('(');
+ expect(')');
+ property = {
+ 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(')');
+ property = {
+ type: Syntax.Property,
+ key: key,
+ value: parsePropertyFunction(param, token),
+ kind: 'set'
+ };
+ } else {
+ expect(':');
+ property = {
+ type: Syntax.Property,
+ key: id,
+ value: parseAssignmentExpression(),
+ kind: 'init'
+ };
+ }
+ break;
+
+ case Token.Keyword:
+ case Token.BooleanLiteral:
+ case Token.NullLiteral:
+ case Token.StringLiteral:
+ case Token.NumericLiteral:
+ key = parseObjectPropertyKey();
+ expect(':');
+ property = {
+ type: Syntax.Property,
+ key: key,
+ value: parseAssignmentExpression(),
+ kind: 'init'
+ };
+ break;
+
+ default:
+ throwUnexpected(token);
+ }
+
+ return property;
+ }
+
+ function parseObjectInitialiser() {
+ var token, 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) {
+ throwError({}, Messages.StrictDuplicateProperty);
+ } else if (kind !== PropertyKind.Data) {
+ throwError({}, Messages.AccessorDataProperty);
+ }
+ } else {
+ if (kind === PropertyKind.Data) {
+ throwError({}, Messages.AccessorDataProperty);
+ } else if (map[name] & kind) {
+ throwError({}, Messages.AccessorGetSet);
+ }
+ }
+ map[name] |= kind;
+ } else {
+ map[name] = kind;
+ }
+
+ properties.push(property);
+
+ if (!match('}')) {
+ expect(',');
+ }
+ }
+
+ expect('}');
+
+ return {
+ type: Syntax.ObjectExpression,
+ properties: properties
+ };
+ }
+
+ // 11.1 Primary Expressions
+
+ function parsePrimaryExpression() {
+ var expr,
+ token = lookahead(),
+ type = token.type;
+
+ if (type === Token.Identifier) {
+ return {
+ type: Syntax.Identifier,
+ name: lex().value
+ };
+ }
+
+ if (type === Token.StringLiteral || type === Token.NumericLiteral) {
+ if (strict && token.octal) {
+ throwError(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();
+ 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(object) {
+ return {
+ type: Syntax.MemberExpression,
+ computed: false,
+ object: object,
+ property: parseNonComputedProperty()
+ };
+ }
+
+ function parseComputedMember(object) {
+ var property, expr;
+
+ expect('[');
+ property = parseExpression();
+ expr = {
+ type: Syntax.MemberExpression,
+ computed: true,
+ object: object,
+ property: property
+ };
+ expect(']');
+ return expr;
+ }
+
+ function parseCallMember(object) {
+ return {
+ type: Syntax.CallExpression,
+ callee: object,
+ 'arguments': parseArguments()
+ };
+ }
+
+ function parseNewExpression() {
+ var expr;
+
+ expectKeyword('new');
+
+ expr = {
+ type: Syntax.NewExpression,
+ callee: parseLeftHandSideExpression(),
+ 'arguments': []
+ };
+
+ if (match('(')) {
+ expr['arguments'] = parseArguments();
+ }
+
+ return expr;
+ }
+
+ function parseLeftHandSideExpressionAllowCall() {
+ var useNew, expr;
+
+ useNew = matchKeyword('new');
+ expr = useNew ? parseNewExpression() : parsePrimaryExpression();
+
+ while (index < length) {
+ if (match('.')) {
+ lex();
+ expr = parseNonComputedMember(expr);
+ } else if (match('[')) {
+ expr = parseComputedMember(expr);
+ } else if (match('(')) {
+ expr = parseCallMember(expr);
+ } else {
+ break;
+ }
+ }
+
+ return expr;
+ }
+
+ function parseLeftHandSideExpression() {
+ var useNew, expr;
+
+ useNew = matchKeyword('new');
+ expr = useNew ? parseNewExpression() : parsePrimaryExpression();
+
+ while (index < length) {
+ if (match('.')) {
+ lex();
+ expr = parseNonComputedMember(expr);
+ } else if (match('[')) {
+ expr = parseComputedMember(expr);
+ } else {
+ break;
+ }
+ }
+
+ return expr;
+ }
+
+ // 11.3 Postfix Expressions
+
+ function parsePostfixExpression() {
+ var expr = parseLeftHandSideExpressionAllowCall();
+
+ if ((match('++') || match('--')) && !peekLineTerminator()) {
+ // 11.3.1, 11.3.2
+ if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
+ throwError({}, Messages.StrictLHSPostfix);
+ }
+ expr = {
+ type: Syntax.UpdateExpression,
+ operator: lex().value,
+ argument: expr,
+ prefix: false
+ };
+ }
+
+ return expr;
+ }
+
+ // 11.4 Unary Operators
+
+ function parseUnaryExpression() {
+ var token, expr;
+
+ if (match('++') || match('--')) {
+ token = lex();
+ expr = parseUnaryExpression();
+ // 11.4.4, 11.4.5
+ if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
+ throwError({}, Messages.StrictLHSPrefix);
+ }
+ 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) {
+ throwError({}, 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 = allowIn;
+ allowIn = true;
+ expr = parseShiftExpression();
+ allowIn = previousAllowIn;
+
+ if (match('<') || match('>') || match('<=') || match('>=')) {
+ expr = {
+ type: Syntax.BinaryExpression,
+ operator: lex().value,
+ left: expr,
+ right: parseRelationalExpression()
+ };
+ } else if (allowIn && matchKeyword('in')) {
+ lex();
+ expr = {
+ type: Syntax.BinaryExpression,
+ operator: 'in',
+ left: expr,
+ right: parseRelationalExpression()
+ };
+ } else if (matchKeyword('instanceof')) {
+ lex();
+ expr = {
+ type: Syntax.BinaryExpression,
+ operator: 'instanceof',
+ left: expr,
+ right: parseRelationalExpression()
+ };
+ }
+
+ 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 parseBitwiseORExpression() {
+ var expr = parseBitwiseANDExpression();
+
+ while (match('|')) {
+ lex();
+ expr = {
+ type: Syntax.BinaryExpression,
+ operator: '|',
+ left: expr,
+ right: parseBitwiseANDExpression()
+ };
+ }
+
+ return expr;
+ }
+
+ function parseBitwiseXORExpression() {
+ var expr = parseBitwiseORExpression();
+
+ while (match('^')) {
+ lex();
+ expr = {
+ type: Syntax.BinaryExpression,
+ operator: '^',
+ left: expr,
+ right: parseBitwiseORExpression()
+ };
+ }
+
+ return expr;
+ }
+
+ // 11.11 Binary Logical Operators
+
+ function parseLogicalANDExpression() {
+ var expr = parseBitwiseXORExpression();
+
+ while (match('&&')) {
+ lex();
+ expr = {
+ type: Syntax.LogicalExpression,
+ operator: '&&',
+ left: expr,
+ right: parseBitwiseXORExpression()
+ };
+ }
+
+ 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 = allowIn;
+ allowIn = true;
+ consequent = parseAssignmentExpression();
+ allowIn = previousAllowIn;
+ expect(':');
+
+ expr = {
+ type: Syntax.ConditionalExpression,
+ test: expr,
+ consequent: consequent,
+ alternate: parseAssignmentExpression()
+ };
+ }
+
+ return expr;
+ }
+
+ // 11.13 Assignment Operators
+
+ function parseAssignmentExpression() {
+ var expr;
+
+ expr = parseConditionalExpression();
+
+ if (matchAssign()) {
+ // LeftHandSideExpression
+ if (lastParenthesized !== expr && !isLeftHandSide(expr)) {
+ throwError({}, Messages.InvalidLHSInAssignment);
+ }
+
+ // 11.13.1
+ if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
+ throwError({}, 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)) {
+ throwError({}, 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 = inIteration;
+ inIteration = true;
+
+ body = parseStatement();
+
+ 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 = inIteration;
+ inIteration = true;
+
+ body = parseStatement();
+
+ 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')) {
+ allowIn = false;
+ init = parseForVariableDeclaration();
+ allowIn = true;
+
+ if (init.declarations.length === 1 && matchKeyword('in')) {
+ lex();
+ left = init;
+ right = parseExpression();
+ init = null;
+ }
+ } else {
+ allowIn = false;
+ init = parseExpression();
+ allowIn = true;
+
+ if (matchKeyword('in')) {
+ // LeftHandSideExpression
+ if (matchKeyword('in') && (lastParenthesized !== init && !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 = inIteration;
+ inIteration = true;
+
+ body = parseStatement();
+
+ 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 (!inIteration) {
+ throwError({}, Messages.IllegalContinue);
+ }
+
+ return {
+ type: Syntax.ContinueStatement,
+ label: null
+ };
+ }
+
+ if (peekLineTerminator()) {
+ if (!inIteration) {
+ throwError({}, Messages.IllegalContinue);
+ }
+
+ return {
+ type: Syntax.ContinueStatement,
+ label: null
+ };
+ }
+
+ token = lookahead();
+ if (token.type === Token.Identifier) {
+ label = parseVariableIdentifier();
+
+ if (!Object.prototype.hasOwnProperty.call(labelSet, label.name)) {
+ throwError({}, Messages.UnknownLabel, label.name);
+ }
+ }
+
+ consumeSemicolon();
+
+ if (label === null && !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 (!(inIteration || inSwitch)) {
+ throwError({}, Messages.IllegalBreak);
+ }
+
+ return {
+ type: Syntax.BreakStatement,
+ label: null
+ };
+ }
+
+ if (peekLineTerminator()) {
+ if (!(inIteration || inSwitch)) {
+ throwError({}, Messages.IllegalBreak);
+ }
+
+ return {
+ type: Syntax.BreakStatement,
+ label: null
+ };
+ }
+
+ token = lookahead();
+ if (token.type === Token.Identifier) {
+ label = parseVariableIdentifier();
+
+ if (!Object.prototype.hasOwnProperty.call(labelSet, label.name)) {
+ throwError({}, Messages.UnknownLabel, label.name);
+ }
+ }
+
+ consumeSemicolon();
+
+ if (label === null && !(inIteration || inSwitch)) {
+ throwError({}, Messages.IllegalBreak);
+ }
+
+ return {
+ type: Syntax.BreakStatement,
+ label: label
+ };
+ }
+
+ // 12.9 The return statement
+
+ function parseReturnStatement() {
+ var token, argument = null;
+
+ expectKeyword('return');
+
+ if (!inFunctionBody) {
+ throwError({}, 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) {
+ throwError({}, 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(test) {
+ var consequent = [],
+ statement;
+
+ 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, test, oldInSwitch;
+
+ expectKeyword('switch');
+
+ expect('(');
+
+ discriminant = parseExpression();
+
+ expect(')');
+
+ expect('{');
+
+ if (match('}')) {
+ lex();
+ return {
+ type: Syntax.SwitchStatement,
+ discriminant: discriminant
+ };
+ }
+
+ cases = [];
+
+ oldInSwitch = inSwitch;
+ inSwitch = true;
+
+ while (index < length) {
+ if (match('}')) {
+ break;
+ }
+
+ if (matchKeyword('default')) {
+ lex();
+ test = null;
+ } else {
+ expectKeyword('case');
+ test = parseExpression();
+ }
+ expect(':');
+
+ cases.push(parseSwitchCase(test));
+ }
+
+ 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)) {
+ throwError({}, Messages.StrictCatchVariable);
+ }
+ }
+ expect(')');
+
+ return {
+ type: Syntax.CatchClause,
+ param: param,
+ guard: null,
+ body: parseBlock()
+ };
+ }
+
+ function parseTryStatement() {
+ var block, handlers = [], finalizer = null;
+
+ expectKeyword('try');
+
+ block = parseBlock();
+
+ if (matchKeyword('catch')) {
+ handlers.push(parseCatchClause());
+ }
+
+ if (matchKeyword('finally')) {
+ lex();
+ finalizer = parseBlock();
+ }
+
+ if (handlers.length === 0 && !finalizer) {
+ throwError({}, Messages.NoCatchOrFinally);
+ }
+
+ return {
+ type: Syntax.TryStatement,
+ block: block,
+ handlers: handlers,
+ finalizer: finalizer
+ };
+ }
+
+ // 12.15 The debugger statement
+
+ function parseDebuggerStatement() {
+ expectKeyword('debugger');
+
+ consumeSemicolon();
+
+ return {
+ type: Syntax.DebuggerStatement
+ };
+ }
+
+ // 12 Statements
+
+ function parseStatement() {
+ var token = lookahead(),
+ expr,
+ labeledBody;
+
+ if (token.type === Token.EOF) {
+ throwUnexpected(token);
+ }
+
+ if (token.type === Token.Punctuator) {
+ switch (token.value) {
+ case ';':
+ return parseEmptyStatement();
+ case '{':
+ return parseBlock();
+ case '(':
+ return parseExpressionStatement();
+ default:
+ break;
+ }
+ }
+
+ if (token.type === Token.Keyword) {
+ switch (token.value) {
+ case 'break':
+ return parseBreakStatement();
+ case 'continue':
+ return parseContinueStatement();
+ case 'debugger':
+ return parseDebuggerStatement();
+ case 'do':
+ return parseDoWhileStatement();
+ case 'for':
+ return parseForStatement();
+ case 'function':
+ return parseFunctionDeclaration();
+ case '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(labelSet, expr.name)) {
+ throwError({}, Messages.Redeclaration, 'Label', expr.name);
+ }
+
+ labelSet[expr.name] = true;
+ labeledBody = parseStatement();
+ delete 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) {
+ throwError(firstRestricted, Messages.StrictOctalLiteral);
+ }
+ } else {
+ if (!firstRestricted && token.octal) {
+ firstRestricted = token;
+ }
+ }
+ }
+
+ oldLabelSet = labelSet;
+ oldInIteration = inIteration;
+ oldInSwitch = inSwitch;
+ oldInFunctionBody = inFunctionBody;
+
+ labelSet = {};
+ inIteration = false;
+ inSwitch = false;
+ inFunctionBody = true;
+
+ while (index < length) {
+ if (match('}')) {
+ break;
+ }
+ sourceElement = parseSourceElement();
+ if (typeof sourceElement === 'undefined') {
+ break;
+ }
+ sourceElements.push(sourceElement);
+ }
+
+ expect('}');
+
+ labelSet = oldLabelSet;
+ inIteration = oldInIteration;
+ inSwitch = oldInSwitch;
+ inFunctionBody = oldInFunctionBody;
+
+ return {
+ type: Syntax.BlockStatement,
+ body: sourceElements
+ };
+ }
+
+ function parseFunctionDeclaration() {
+ var id, param, params = [], body, token, firstRestricted, message, previousStrict, paramSet;
+
+ expectKeyword('function');
+ token = lookahead();
+ id = parseVariableIdentifier();
+ if (strict) {
+ if (isRestrictedWord(token.value)) {
+ throwError(token, Messages.StrictFunctionName);
+ }
+ } else {
+ if (isRestrictedWord(token.value)) {
+ firstRestricted = token;
+ message = Messages.StrictFunctionName;
+ } else if (isStrictModeReservedWord(token.value)) {
+ firstRestricted = token;
+ message = Messages.StrictReservedWord;
+ }
+ }
+
+ expect('(');
+
+ if (!match(')')) {
+ paramSet = {};
+ while (index < length) {
+ token = lookahead();
+ param = parseVariableIdentifier();
+ if (strict) {
+ if (isRestrictedWord(token.value)) {
+ throwError(token, Messages.StrictParamName);
+ }
+ if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
+ throwError(token, Messages.StrictParamDupe);
+ }
+ } else if (!firstRestricted) {
+ if (isRestrictedWord(token.value)) {
+ firstRestricted = token;
+ message = Messages.StrictParamName;
+ } else if (isStrictModeReservedWord(token.value)) {
+ firstRestricted = token;
+ message = Messages.StrictReservedWord;
+ } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
+ firstRestricted = token;
+ message = Messages.StrictParamDupe;
+ }
+ }
+ params.push(param);
+ paramSet[param.name] = true;
+ if (match(')')) {
+ break;
+ }
+ expect(',');
+ }
+ }
+
+ expect(')');
+
+ previousStrict = strict;
+ body = parseFunctionSourceElements();
+ if (strict && firstRestricted) {
+ throwError(firstRestricted, message);
+ }
+ strict = previousStrict;
+
+ return {
+ type: Syntax.FunctionDeclaration,
+ id: id,
+ params: params,
+ body: body
+ };
+ }
+
+ function parseFunctionExpression() {
+ var token, id = null, firstRestricted, message, param, params = [], body, previousStrict, paramSet;
+
+ expectKeyword('function');
+
+ if (!match('(')) {
+ token = lookahead();
+ id = parseVariableIdentifier();
+ if (strict) {
+ if (isRestrictedWord(token.value)) {
+ throwError(token, Messages.StrictFunctionName);
+ }
+ } else {
+ if (isRestrictedWord(token.value)) {
+ firstRestricted = token;
+ message = Messages.StrictFunctionName;
+ } else if (isStrictModeReservedWord(token.value)) {
+ firstRestricted = token;
+ message = Messages.StrictReservedWord;
+ }
+ }
+ }
+
+ expect('(');
+
+ if (!match(')')) {
+ paramSet = {};
+ while (index < length) {
+ token = lookahead();
+ param = parseVariableIdentifier();
+ if (strict) {
+ if (isRestrictedWord(token.value)) {
+ throwError(token, Messages.StrictParamName);
+ }
+ if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
+ throwError(token, Messages.StrictParamDupe);
+ }
+ } else if (!firstRestricted) {
+ if (isRestrictedWord(token.value)) {
+ firstRestricted = token;
+ message = Messages.StrictParamName;
+ } else if (isStrictModeReservedWord(token.value)) {
+ firstRestricted = token;
+ message = Messages.StrictReservedWord;
+ } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
+ firstRestricted = token;
+ message = Messages.StrictParamDupe;
+ }
+ }
+ params.push(param);
+ paramSet[param.name] = true;
+ if (match(')')) {
+ break;
+ }
+ expect(',');
+ }
+ }
+
+ expect(')');
+
+ previousStrict = strict;
+ body = parseFunctionSourceElements();
+ if (strict && firstRestricted) {
+ throwError(firstRestricted, message);
+ }
+ strict = previousStrict;
+
+ return {
+ type: Syntax.FunctionExpression,
+ id: id,
+ params: params,
+ body: body
+ };
+ }
+
+ // 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) {
+ throwError(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(start, end, type, value) {
+ if (typeof start !== 'number') {
+ return;
+ }
+
+ // 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({
+ range: [start, end],
+ type: type,
+ value: value
+ });
+ }
+
+ function scanComment() {
+ var comment, ch, start, blockComment, lineComment;
+
+ comment = '';
+ blockComment = false;
+ lineComment = false;
+
+ while (index < length) {
+ ch = source[index];
+
+ if (lineComment) {
+ ch = nextChar();
+ if (isLineTerminator(ch)) {
+ lineComment = false;
+ addComment(start, index - 1, 'Line', comment);
+ if (ch === '\r' && source[index] === '\n') {
+ index += 1;
+ }
+ lineNumber += 1;
+ lineStart = index;
+ comment = '';
+ } else {
+ comment += ch;
+ }
+ } else if (blockComment) {
+ if (isLineTerminator(ch)) {
+ if (ch === '\r' && source[index + 1] === '\n') {
+ index += 1;
+ }
+ lineNumber += 1;
+ index += 1;
+ 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 += 1;
+ addComment(start, index - 1, 'Block', comment);
+ comment = '';
+ }
+ }
+ }
+ } else if (ch === '/') {
+ ch = source[index + 1];
+ if (ch === '/') {
+ start = index;
+ index += 2;
+ lineComment = true;
+ } else if (ch === '*') {
+ start = index;
+ index += 2;
+ blockComment = true;
+ if (index >= length) {
+ throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+ }
+ } else {
+ break;
+ }
+ } else if (isWhiteSpace(ch)) {
+ index += 1;
+ } else if (isLineTerminator(ch)) {
+ index += 1;
+ if (ch === '\r' && source[index] === '\n') {
+ index += 1;
+ }
+ lineNumber += 1;
+ lineStart = index;
+ } else {
+ break;
+ }
+ }
+
+ addComment(start, index, (blockComment) ? 'Block' : 'Line', comment);
+ }
+
+ function collectToken() {
+ var token = extra.advance(),
+ range,
+ value;
+
+ if (token.type !== Token.EOF) {
+ range = [token.range[0], token.range[1] - 1];
+ value = sliceSource(token.range[0], token.range[1]);
+ extra.tokens.push({
+ type: TokenName[token.type],
+ value: value,
+ range: range
+ });
+ }
+
+ return token;
+ }
+
+ function collectRegex() {
+ var pos, regex, token;
+
+ skipComment();
+
+ pos = index;
+ regex = extra.scanRegExp();
+
+ // 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 - 1]
+ });
+
+ return regex;
+ }
+
+ function createLiteral(token) {
+ return {
+ type: Syntax.Literal,
+ value: token.value
+ };
+ }
+
+ function createRawLiteral(token) {
+ return {
+ type: Syntax.Literal,
+ value: token.value,
+ raw: sliceSource(token.range[0], token.range[1])
+ };
+ }
+
+ function wrapTrackingFunction(range, loc) {
+
+ return function (parseFunction) {
+
+ function isBinary(node) {
+ return node.type === Syntax.LogicalExpression ||
+ node.type === Syntax.BinaryExpression;
+ }
+
+ function visit(node) {
+ if (range) {
+ if (isBinary(node.left) && (typeof node.left.range === 'undefined')) {
+ visit(node.left);
+ }
+ if (isBinary(node.right) && (typeof node.right.range === 'undefined')) {
+ visit(node.right);
+ }
+
+ // Expression enclosed in brackets () already has the correct range.
+ if (typeof node.range === 'undefined') {
+ node.range = [node.left.range[0], node.right.range[1]];
+ }
+ }
+
+ if (loc) {
+ if (isBinary(node.left) && (typeof node.left.loc === 'undefined')) {
+ visit(node.left);
+ }
+ if (isBinary(node.right) && (typeof node.right.loc === 'undefined')) {
+ visit(node.right);
+ }
+
+ if (typeof node.loc === 'undefined') {
+ node.loc = {
+ start: node.left.loc.start,
+ end: node.right.loc.end
+ };
+ }
+ }
+ }
+
+ return function () {
+ var node, rangeInfo, locInfo;
+
+ skipComment();
+ rangeInfo = [index, 0];
+ locInfo = {
+ start: {
+ line: lineNumber,
+ column: index - lineStart
+ }
+ };
+
+ node = parseFunction.apply(null, arguments);
+ if (typeof node !== 'undefined') {
+
+ if (range) {
+ rangeInfo[1] = index - 1;
+ node.range = rangeInfo;
+ }
+
+ if (loc) {
+ locInfo.end = {
+ line: lineNumber,
+ column: index - lineStart
+ };
+ node.loc = locInfo;
+ }
+
+ if (isBinary(node)) {
+ visit(node);
+ }
+
+ if (node.type === Syntax.MemberExpression) {
+ if (typeof node.object.range !== 'undefined') {
+ node.range[0] = node.object.range[0];
+ }
+ if (typeof node.object.loc !== 'undefined') {
+ node.loc.start = node.object.loc.start;
+ }
+ }
+ return node;
+ }
+ };
+
+ };
+ }
+
+ function patch() {
+
+ var wrapTracking;
+
+ if (extra.comments) {
+ extra.skipComment = skipComment;
+ skipComment = scanComment;
+ }
+
+ if (extra.raw) {
+ extra.createLiteral = createLiteral;
+ createLiteral = createRawLiteral;
+ }
+
+ if (extra.range || extra.loc) {
+
+ wrapTracking = wrapTrackingFunction(extra.range, extra.loc);
+
+ extra.parseAdditiveExpression = parseAdditiveExpression;
+ extra.parseAssignmentExpression = parseAssignmentExpression;
+ extra.parseBitwiseANDExpression = parseBitwiseANDExpression;
+ extra.parseBitwiseORExpression = parseBitwiseORExpression;
+ extra.parseBitwiseXORExpression = parseBitwiseXORExpression;
+ extra.parseBlock = parseBlock;
+ extra.parseFunctionSourceElements = parseFunctionSourceElements;
+ extra.parseCallMember = parseCallMember;
+ extra.parseCatchClause = parseCatchClause;
+ extra.parseComputedMember = parseComputedMember;
+ extra.parseConditionalExpression = parseConditionalExpression;
+ extra.parseConstLetDeclaration = parseConstLetDeclaration;
+ extra.parseEqualityExpression = parseEqualityExpression;
+ extra.parseExpression = parseExpression;
+ extra.parseForVariableDeclaration = parseForVariableDeclaration;
+ extra.parseFunctionDeclaration = parseFunctionDeclaration;
+ extra.parseFunctionExpression = parseFunctionExpression;
+ extra.parseLogicalANDExpression = parseLogicalANDExpression;
+ extra.parseLogicalORExpression = parseLogicalORExpression;
+ extra.parseMultiplicativeExpression = parseMultiplicativeExpression;
+ extra.parseNewExpression = parseNewExpression;
+ extra.parseNonComputedMember = parseNonComputedMember;
+ 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);
+ parseCallMember = wrapTracking(extra.parseCallMember);
+ 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);
+ parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression);
+ parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression);
+ parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression);
+ parseNewExpression = wrapTracking(extra.parseNewExpression);
+ parseNonComputedMember = wrapTracking(extra.parseNonComputedMember);
+ 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;
+ parseCallMember = extra.parseCallMember;
+ 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;
+ parseLogicalANDExpression = extra.parseLogicalANDExpression;
+ parseLogicalORExpression = extra.parseLogicalORExpression;
+ parseMultiplicativeExpression = extra.parseMultiplicativeExpression;
+ parseNewExpression = extra.parseNewExpression;
+ parseNonComputedMember = extra.parseNonComputedMember;
+ 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 += 1) {
+ 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;
+ allowIn = true;
+ labelSet = {};
+ inSwitch = false;
+ inIteration = false;
+ lastParenthesized = null;
+ inFunctionBody = 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 (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') {
+ program.comments = extra.comments;
+ }
+ if (typeof extra.tokens !== 'undefined') {
+ program.tokens = extra.tokens;
+ }
+ } catch (e) {
+ throw e;
+ } finally {
+ unpatch();
+ extra = {};
+ }
+
+ return program;
+ }
+
+ // Executes visitor on the object and its children (recursively).
+
+ function traverse(object, visitor, master) {
+ var key, child, parent, path;
+
+ parent = (typeof master === 'undefined') ? [] : master;
+
+ if (visitor.call(null, object, parent) === false) {
+ return;
+ }
+ for (key in object) {
+ if (object.hasOwnProperty(key)) {
+ child = object[key];
+ path = [ object ];
+ path.push(parent);
+ if (typeof child === 'object' && child !== null) {
+ traverse(child, visitor, path);
+ }
+ }
+ }
+ }
+
+ // Insert a prolog in the body of every function.
+ // It will be in the form of a function call:
+ //
+ // traceName(object);
+ //
+ // where the object contains the following properties:
+ //
+ // 'name' holds the name of the function
+ // 'lineNumber' holds the starting line number of the function block
+ // 'range' contains the index-based range of the function
+ //
+ // The name of the function represents the associated reference for
+ // the function (deduced on a best-effort basis if it is not
+ // a function declaration).
+ //
+ // If traceName is a function instead of a string, it will be invoked and
+ // the result will be used as the entire prolog. The arguments for the
+ // invocation are the function name, range, and location info.
+
+ function traceFunctionEntrance(traceName) {
+
+ return function (code) {
+ var tree,
+ functionList,
+ param,
+ signature,
+ pos,
+ i;
+
+
+ tree = parse(code, { range: true, loc: true });
+
+ functionList = [];
+ traverse(tree, function (node, path) {
+ var parent;
+ if (node.type === Syntax.FunctionDeclaration) {
+ functionList.push({
+ name: node.id.name,
+ range: node.range,
+ loc: node.loc,
+ blockStart: node.body.range[0]
+ });
+ } else if (node.type === Syntax.FunctionExpression) {
+ parent = path[0];
+ if (parent.type === Syntax.AssignmentExpression) {
+ if (typeof parent.left.range !== 'undefined') {
+ functionList.push({
+ name: code.slice(parent.left.range[0],
+ parent.left.range[1] + 1),
+ range: node.range,
+ loc: node.loc,
+ blockStart: node.body.range[0]
+ });
+ }
+ } else if (parent.type === Syntax.VariableDeclarator) {
+ functionList.push({
+ name: parent.id.name,
+ range: node.range,
+ loc: node.loc,
+ blockStart: node.body.range[0]
+ });
+ } else if (parent.type === Syntax.CallExpression) {
+ functionList.push({
+ name: parent.id ? parent.id.name : '[Anonymous]',
+ range: node.range,
+ loc: node.loc,
+ blockStart: node.body.range[0]
+ });
+ } else if (typeof parent.length === 'number') {
+ functionList.push({
+ name: parent.id ? parent.id.name : '[Anonymous]',
+ range: node.range,
+ loc: node.loc,
+ blockStart: node.body.range[0]
+ });
+ } else if (typeof parent.key !== 'undefined') {
+ if (parent.key.type === 'Identifier') {
+ if (parent.value === node && parent.key.name) {
+ functionList.push({
+ name: parent.key.name,
+ range: node.range,
+ loc: node.loc,
+ blockStart: node.body.range[0]
+ });
+ }
+ }
+ }
+ }
+ });
+
+ // Insert the instrumentation code from the last entry.
+ // This is to ensure that the range for each entry remains valid)
+ // (it won't shift due to some new inserting string before the range).
+ for (i = functionList.length - 1; i >= 0; i -= 1) {
+ param = {
+ name: functionList[i].name,
+ range: functionList[i].range,
+ loc: functionList[i].loc
+ };
+ if (typeof traceName === 'function') {
+ signature = traceName.call(null, param);
+ } else {
+ signature = traceName + '({ ';
+ signature += 'name: \'' + functionList[i].name + '\', ';
+ if (typeof functionList[i].loc !== 'undefined') {
+ signature += 'lineNumber: ' + functionList[i].loc.start.line + ', ';
+ }
+ signature += 'range: [' + functionList[i].range[0] + ', ' +
+ functionList[i].range[1] + '] ';
+ signature += '});';
+ }
+ pos = functionList[i].blockStart + 1;
+ code = code.slice(0, pos) + '\n' + signature + code.slice(pos, code.length);
+ }
+
+ return code;
+ };
+ }
+
+ function modify(code, modifiers) {
+ var i;
+
+ if (Object.prototype.toString.call(modifiers) === '[object Array]') {
+ for (i = 0; i < modifiers.length; i += 1) {
+ code = modifiers[i].call(null, code);
+ }
+ } else if (typeof modifiers === 'function') {
+ code = modifiers.call(null, code);
+ } else {
+ throw new Error('Wrong use of esprima.modify() function');
+ }
+
+ return code;
+ }
+
+ // Sync with package.json.
+ exports.version = '0.9.9-dev';
+
+ exports.parse = parse;
+
+ exports.modify = modify;
+
+ exports.Tracer = {
+ FunctionEntrance: traceFunctionEntrance
+ };
+
+}(typeof exports === 'undefined' ? (esprima = {}) : exports));
+/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/assets/style.css b/assets/style.css
new file mode 100644
index 0000000..86efaf0
--- /dev/null
+++ b/assets/style.css
@@ -0,0 +1,52 @@
+* {
+ font-size 12px;
+ font-family:'Trebuchet MS', Arial, Helvetica, sans-serif;
+}
+
+pre {
+ font-family:'Bitstream Vera Sans Mono', Courier, monospace;
+ font-size: 12px;
+ background-color: #EEE;
+ padding:10px;
+ overflow: auto;
+}
+
+#console {
+ -webkit-border-radius: 5px;
+ -moz-border-radius : 5px;
+ -o-border-radius : 5px;
+ border-radius : 5px;
+ border : 1px solid #C6C6C6;
+ outline: none;
+ padding:10px;
+ display: block;
+ width : 80%;
+ margin: 10px auto 10px auto;
+}
+
+.invalid {
+ border-color: #fbc2c4 !important;
+}
+
+#output {
+ -webkit-border-radius: 5px;
+ -moz-border-radius : 5px;
+ -o-border-radius : 5px;
+ border-radius : 5px;
+ border : 1px solid #C6C6C6;
+ width : 80%;
+ margin : 10px auto 10px auto;
+}
+
+h1, h2, h3, h4, h5, p {
+ text-align: center;
+}
+
+p {
+ color: #849EAA;
+}
+a {
+ text-decoration: none;
+ color: #4183C4;
+}
+
diff --git a/demo/index.html b/demo/index.html
new file mode 100644
index 0000000..e25c485
--- /dev/null
+++ b/demo/index.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>JS code generator demo: JS → AST → JS</title>
+ <link rel="stylesheet" href="../assets/style.css" type="text/css" />
+ <script src='../assets/esprima.js'></script>
+ <script src='../escodegen.js'></script>
+ <script type="text/javascript">
+function $D(elm) {
+ var range = document.createRange();
+ range.selectNodeContents(elm);
+ range.deleteContents();
+ range.detach();
+}
+function $T(mes) {
+ return document.createTextNode(mes);
+}
+function hasClass (e, name){
+ name = name.toLowerCase();
+ var cn = e.className;
+ if (!cn) return false;
+ var cnlist = cn.toLowerCase().split(/\s+/);
+ for (var i=0,l=cnlist.length;i<l;i++)
+ if(cnlist[i] == name) return true;
+ return false;
+}
+function addClass (e, name){
+ var cn = e.className || '';
+ if(hasClass(e, name)) return;
+ e.className = cn+' '+name;
+}
+function removeClass(e, name){
+ if(!hasClass(e, name)) return;
+ var cn = e.className || '';
+ name = name.toLowerCase();
+ var cnlist = cn.toLowerCase().split(/\s+/);
+ cnlist.splice(cnlist.indexOf(name), 1);
+ e.className = cnlist.join(' ');
+}
+function toggleClass(e, name){
+ (hasClass(e, name))? removeClass(e, name) : addClass(e, name);
+}
+document.addEventListener('DOMContentLoaded', function(ev) {
+ var textarea = document.getElementById("console");
+ var pre = document.getElementById("output");
+ textarea.value = "for(var i=1;i<=100;++i){if(i%15===0){console.log('FizzBuzz')}else if(i%3===0){console.log('Fizz')}else if(i%5===0){console.log('Buzz')}else{console.log(i)}}";
+ function show() {
+ $D(pre);
+ try {
+ removeClass(textarea, "invalid");
+ var AST = esprima.parse(textarea.value);
+ pre.appendChild($T(escodegen.generate(AST)));
+ } catch (e) {
+ addClass(textarea, "invalid")
+ pre.appendChild($T("Invalid"));
+ }
+ }
+ show();
+ textarea.addEventListener("input", show, false);
+}, false);
+ </script>
+</head>
+<body>
+<h1>JS code generator demo: JS → AST → JS</h1>
+<p>using <a href="http://www.esprima.org/">esprima</a> as parser</p>
+<p>and using <a href="https://github.com/Constellation/escodegen">escodegen</a> as code generator</p>
+<textarea id="console" rows="10" cols="30" placeholder="write your code"></textarea>
+<pre id="output"></pre>
+</body>
+</html>
diff --git a/escodegen.js b/escodegen.js
new file mode 100644
index 0000000..b4ea9ad
--- /dev/null
+++ b/escodegen.js
@@ -0,0 +1,843 @@
+/*
+ Copyright (C) 2012 Ariya Hidayat <ar...@gmail.com>
+ Copyright (C) 2012 Mathias Bynens <ma...@qiwi.be>
+ Copyright (C) 2012 Joost-Wim Boekesteijn <jo...@boekesteijn.nl>
+ Copyright (C) 2012 Kris Kowal <kr...@cixar.com>
+ Copyright (C) 2012 Yusuke Suzuki <ut...@gmail.com>
+ Copyright (C) 2012 Arpad Borsos <ar...@googlemail.com>
+ Copyright (C) 2011 Ariya Hidayat <ar...@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 */
+/*global escodegen:true, exports:true, generateStatement: true*/
+
+(function (exports) {
+ 'use strict';
+
+ var Syntax,
+ Precedence,
+ BinaryPrecedence,
+ base,
+ indent,
+ extra,
+ parse;
+
+ 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'
+ };
+
+ Precedence = {
+ Sequence: 0,
+ Assignment: 1,
+ Conditional: 2,
+ LogicalOR: 3,
+ LogicalAND: 4,
+ LogicalXOR: 5,
+ BitwiseOR: 6,
+ BitwiseAND: 7,
+ Equality: 8,
+ Relational: 9,
+ BitwiseSHIFT: 10,
+ Additive: 11,
+ Multiplicative: 12,
+ Unary: 13,
+ Postfix: 14,
+ Call: 15,
+ New: 16,
+ Member: 17,
+ Primary: 18
+ };
+
+ BinaryPrecedence = {
+ '||': Precedence.LogicalOR,
+ '&&': Precedence.LogicalAND,
+ '^': Precedence.LogicalXOR,
+ '|': Precedence.BitwiseOR,
+ '&': Precedence.BitwiseAND,
+ '==': Precedence.Equality,
+ '!=': Precedence.Equality,
+ '===': Precedence.Equality,
+ '!==': Precedence.Equality,
+ '<': Precedence.Relational,
+ '>': Precedence.Relational,
+ '<=': Precedence.Relational,
+ '>=': Precedence.Relational,
+ 'in': Precedence.Relational,
+ 'instanceof': Precedence.Relational,
+ '<<': Precedence.BitwiseSHIFT,
+ '>>': Precedence.BitwiseSHIFT,
+ '>>>': Precedence.BitwiseSHIFT,
+ '+': Precedence.Additive,
+ '-': Precedence.Additive,
+ '*': Precedence.Multiplicative,
+ '%': Precedence.Multiplicative,
+ '/': Precedence.Multiplicative
+ };
+
+ if (typeof Object.freeze === 'function') {
+ Object.freeze(Syntax);
+ Object.freeze(Precedence);
+ Object.freeze(BinaryPrecedence);
+ }
+
+ function unicodeEscape(ch) {
+ var result, i;
+ result = ch.charCodeAt(0).toString(16);
+ for (i = result.length; i < 4; i += 1) {
+ result = '0' + result;
+ }
+ return '\\u' + result;
+ }
+
+ function stringToArray(str) {
+ var length = str.length,
+ result = [],
+ i;
+ for (i = 0; i < length; i += 1) {
+ result[i] = str.charAt(i);
+ }
+ return result;
+ }
+
+ function escapeString(str) {
+ var result = '', i, len, ch;
+
+ if (typeof str[0] === 'undefined') {
+ str = stringToArray(str);
+ }
+
+ for (i = 0, len = str.length; i < len; i += 1) {
+ ch = str[i];
+ if ('\'\\\b\f\n\r\t'.indexOf(ch) >= 0) {
+ result += '\\';
+ switch (ch) {
+ case '\'':
+ result += '\'';
+ break;
+ case '\\':
+ result += '\\';
+ break;
+ case '\b':
+ result += 'b';
+ break;
+ case '\f':
+ result += 'f';
+ break;
+ case '\n':
+ result += 'n';
+ break;
+ case '\r':
+ result += 'r';
+ break;
+ case '\t':
+ result += 't';
+ break;
+ }
+ } else if (ch < ' ' || ch.charCodeAt(0) >= 0x80) {
+ result += unicodeEscape(ch);
+ } else {
+ result += ch;
+ }
+ }
+
+ return '\'' + result + '\'';
+ }
+
+ function addIndent(stmt) {
+ return base + stmt;
+ }
+
+ function parenthesize(text, current, should) {
+ return (current < should) ? '(' + text + ')' : text;
+ }
+
+ function maybeBlock(stmt, suffix) {
+ var previousBase, result;
+
+ if (stmt.type === Syntax.BlockStatement) {
+ result = ' ' + generateStatement(stmt);
+ if (suffix) {
+ return result + ' ';
+ }
+ return result;
+ }
+
+ if (stmt.type === Syntax.EmptyStatement) {
+ result = ';';
+ } else {
+ previousBase = base;
+ base += indent;
+ result = '\n' + addIndent(generateStatement(stmt));
+ base = previousBase;
+ }
+
+ if (suffix) {
+ return result + '\n' + addIndent('');
+ }
+ return result;
+ }
+
+ function generateFunctionBody(node) {
+ var result, i, len;
+ result = '(';
+ for (i = 0, len = node.params.length; i < len; i += 1) {
+ result += node.params[i].name;
+ if ((i + 1) < len) {
+ result += ', ';
+ }
+ }
+ return result + ')' + maybeBlock(node.body);
+ }
+
+ function generateExpression(expr, precedence) {
+ var result, currentPrecedence, previousBase, i, len, raw;
+
+ if (!precedence) {
+ precedence = Precedence.Sequence;
+ }
+
+ switch (expr.type) {
+ case Syntax.SequenceExpression:
+ result = '';
+ for (i = 0, len = expr.expressions.length; i < len; i += 1) {
+ result += generateExpression(expr.expressions[i], Precedence.Assignment);
+ if ((i + 1) < len) {
+ result += ', ';
+ }
+ }
+ result = parenthesize(result, Precedence.Sequence, precedence);
+ break;
+
+ case Syntax.AssignmentExpression:
+ result = parenthesize(
+ generateExpression(expr.left, Precedence.Call) + ' ' + expr.operator + ' ' +
+ generateExpression(expr.right, Precedence.Assignment),
+ Precedence.Assignment,
+ precedence
+ );
+ break;
+
+ case Syntax.ConditionalExpression:
+ result = parenthesize(
+ generateExpression(expr.test, Precedence.LogicalOR) + ' ? ' +
+ generateExpression(expr.consequent, Precedence.Assignment) + ' : ' +
+ generateExpression(expr.alternate, Precedence.Assignment),
+ Precedence.Conditional,
+ precedence
+ );
+ break;
+
+ case Syntax.LogicalExpression:
+ case Syntax.BinaryExpression:
+ currentPrecedence = BinaryPrecedence[expr.operator];
+
+ result = generateExpression(expr.left, currentPrecedence) +
+ ' ' + expr.operator + ' ' +
+ generateExpression(expr.right, currentPrecedence + 1);
+ if (expr.operator === 'in') {
+ // TODO parenthesize only in allowIn = false case
+ result = '(' + result + ')';
+ } else {
+ result = parenthesize(result, currentPrecedence, precedence);
+ }
+ break;
+
+ case Syntax.CallExpression:
+ result = '';
+ for (i = 0, len = expr['arguments'].length; i < len; i += 1) {
+ result += generateExpression(expr['arguments'][i], Precedence.Assignment);
+ if ((i + 1) < len) {
+ result += ', ';
+ }
+ }
+ result = parenthesize(
+ generateExpression(expr.callee, Precedence.Call) + '(' + result + ')',
+ Precedence.Call,
+ precedence
+ );
+ break;
+
+ case Syntax.NewExpression:
+ result = '';
+ for (i = 0, len = expr['arguments'].length; i < len; i += 1) {
+ result += generateExpression(expr['arguments'][i], Precedence.Assignment);
+ if ((i + 1) < len) {
+ result += ', ';
+ }
+ }
+ result = parenthesize(
+ 'new ' + generateExpression(expr.callee, Precedence.New) + '(' + result + ')',
+ Precedence.New,
+ precedence
+ );
+ break;
+
+ case Syntax.MemberExpression:
+ result = generateExpression(expr.object, Precedence.Call);
+ if (expr.computed) {
+ result += '[' + generateExpression(expr.property) + ']';
+ } else {
+ if (expr.object.type === Syntax.Literal && typeof expr.object.value === 'number') {
+ if (result.indexOf('.') < 0) {
+ if (!/[eExX]/.test(result) && !(result.length >= 2 && result[0] === '0')) {
+ result += '.';
+ }
+ }
+ }
+ result += '.' + expr.property.name;
+ }
+ result = parenthesize(result, Precedence.Member, precedence);
+ break;
+
+ case Syntax.UnaryExpression:
+ result = expr.operator;
+ if (result.length > 2) {
+ result += ' ';
+ }
+ result = parenthesize(
+ result + generateExpression(expr.argument, Precedence.Unary +
+ (
+ expr.argument.type === Syntax.UnaryExpression &&
+ expr.operator.length < 3 &&
+ expr.argument.operator === expr.operator ? 1 : 0
+ )
+ ),
+ Precedence.Unary,
+ precedence
+ );
+ break;
+
+ case Syntax.UpdateExpression:
+ if (expr.prefix) {
+ result = parenthesize(
+ expr.operator +
+ generateExpression(expr.argument, Precedence.Unary),
+ Precedence.Unary,
+ precedence
+ );
+ } else {
+ result = parenthesize(
+ generateExpression(expr.argument, Precedence.Postfix) +
+ expr.operator,
+ Precedence.Postfix,
+ precedence
+ );
+ }
+ break;
+
+ case Syntax.FunctionExpression:
+ result = 'function ';
+ if (expr.id) {
+ result += expr.id.name;
+ }
+ result += generateFunctionBody(expr);
+ break;
+
+ case Syntax.ArrayExpression:
+ if (!expr.elements.length) {
+ result = '[]';
+ break;
+ }
+ result = '[\n';
+ previousBase = base;
+ base += indent;
+ for (i = 0, len = expr.elements.length; i < len; i += 1) {
+ if (!expr.elements[i]) {
+ result += addIndent('');
+ if ((i + 1) === len) {
+ result += ',';
+ }
+ } else {
+ result += addIndent(generateExpression(expr.elements[i], Precedence.Assignment));
+ }
+ if ((i + 1) < len) {
+ result += ',\n';
+ }
+ }
+ base = previousBase;
+ result += '\n' + addIndent(']');
+ break;
+
+ case Syntax.Property:
+ if (expr.kind === 'get' || expr.kind === 'set') {
+ result = expr.kind + ' ' + generateExpression(expr.key) +
+ generateFunctionBody(expr.value);
+ } else {
+ result = generateExpression(expr.key) + ': ' +
+ generateExpression(expr.value, Precedence.Assignment);
+ }
+ break;
+
+ case Syntax.ObjectExpression:
+ if (!expr.properties.length) {
+ result = '{}';
+ break;
+ }
+ result = '{\n';
+ previousBase = base;
+ base += indent;
+ for (i = 0, len = expr.properties.length; i < len; i += 1) {
+ result += addIndent(generateExpression(expr.properties[i]));
+ if ((i + 1) < len) {
+ result += ',\n';
+ }
+ }
+ base = previousBase;
+ result += '\n' + addIndent('}');
+ break;
+
+ case Syntax.ThisExpression:
+ result = 'this';
+ break;
+
+ case Syntax.Identifier:
+ result = expr.name;
+ break;
+
+ case Syntax.Literal:
+ if (expr.hasOwnProperty('raw') && parse) {
+ try {
+ raw = parse(expr.raw).body[0].expression;
+ if (raw.type === Syntax.Literal) {
+ if (raw.value === expr.value) {
+ result = expr.raw;
+ break;
+ }
+ }
+ } catch (e) {
+ // not use raw property
+ }
+ }
+
+ if (expr.value === null) {
+ result = 'null';
+ break;
+ }
+
+ if (typeof expr.value === 'string') {
+ result = escapeString(expr.value);
+ break;
+ }
+
+ if (typeof expr.value === 'number' && expr.value === Infinity) {
+ // Infinity is variable
+ result = '1e+1000';
+ break;
+ }
+
+ result = expr.value.toString();
+ break;
+
+ default:
+ break;
+ }
+
+ if (result === undefined) {
+ throw new Error('Unknown expression type: ' + expr.type);
+ }
+ return result;
+ }
+
+ function generateStatement(stmt) {
+ var i, len, result, previousBase;
+
+ switch (stmt.type) {
+ case Syntax.BlockStatement:
+ result = '{\n';
+
+ previousBase = base;
+ base += indent;
+ for (i = 0, len = stmt.body.length; i < len; i += 1) {
+ result += addIndent(generateStatement(stmt.body[i])) + '\n';
+ }
+ base = previousBase;
+
+ result += addIndent('}');
+ break;
+
+ case Syntax.BreakStatement:
+ if (stmt.label) {
+ result = 'break ' + stmt.label.name + ';';
+ } else {
+ result = 'break;';
+ }
+ break;
+
+ case Syntax.ContinueStatement:
+ if (stmt.label) {
+ result = 'continue ' + stmt.label.name + ';';
+ } else {
+ result = 'continue;';
+ }
+ break;
+
+ case Syntax.DoWhileStatement:
+ result = 'do' + maybeBlock(stmt.body, true) + 'while (' + generateExpression(stmt.test) + ');';
+ break;
+
+ case Syntax.CatchClause:
+ previousBase = base;
+ base += indent;
+ result = ' catch (' + generateExpression(stmt.param) + ')';
+ base = previousBase;
+ result += maybeBlock(stmt.body);
+ break;
+
+ case Syntax.DebuggerStatement:
+ result = 'debugger;';
+ break;
+
+ case Syntax.EmptyStatement:
+ result = ';';
+ break;
+
+ case Syntax.ExpressionStatement:
+ result = generateExpression(stmt.expression);
+ // 12.4 '{', 'function' is not allowed in this position.
+ // wrap espression with parentheses
+ if (result[0] === '{' || result.indexOf('function ') === 0) {
+ result = '(' + result + ');';
+ } else {
+ result += ';';
+ }
+ break;
+
+ case Syntax.VariableDeclarator:
+ if (stmt.init) {
+ result = stmt.id.name + ' = ' + generateExpression(stmt.init, Precedence.Assignment);
+ } else {
+ result = stmt.id.name;
+ }
+ break;
+
+ case Syntax.VariableDeclaration:
+ result = stmt.kind + ' ';
+ // special path for
+ // var x = function () {
+ // };
+ if (stmt.declarations.length === 1 && stmt.declarations[0].init &&
+ stmt.declarations[0].init.type === Syntax.FunctionExpression) {
+ result += generateStatement(stmt.declarations[0]);
+ } else {
+ previousBase = base;
+ base += indent;
+ for (i = 0, len = stmt.declarations.length; i < len; i += 1) {
+ result += generateStatement(stmt.declarations[i]);
+ if ((i + 1) < len) {
+ result += ', ';
+ }
+ }
+ base = previousBase;
+ }
+ result += ';';
+ break;
+
+ case Syntax.ThrowStatement:
+ result = 'throw ' + generateExpression(stmt.argument) + ';';
+ break;
+
+ case Syntax.TryStatement:
+ result = 'try' + maybeBlock(stmt.block);
+ for (i = 0, len = stmt.handlers.length; i < len; i += 1) {
+ result += generateStatement(stmt.handlers[i]);
+ }
+ if (stmt.finalizer) {
+ result += ' finally' + maybeBlock(stmt.finalizer);
+ }
+ break;
+
+ case Syntax.SwitchStatement:
+ previousBase = base;
+ base += indent;
+ result = 'switch (' + generateExpression(stmt.discriminant) + ') {\n';
+ base = previousBase;
+ if (stmt.cases) {
+ for (i = 0, len = stmt.cases.length; i < len; i += 1) {
+ result += addIndent(generateStatement(stmt.cases[i])) + '\n';
+ }
+ }
+ result += addIndent('}');
+ break;
+
+ case Syntax.SwitchCase:
+ previousBase = base;
+ base += indent;
+ if (stmt.test) {
+ result = 'case ' + generateExpression(stmt.test) + ':';
+ } else {
+ result = 'default:';
+ }
+
+ i = 0;
+ len = stmt.consequent.length;
+ if (len && stmt.consequent[0].type === Syntax.BlockStatement) {
+ result += maybeBlock(stmt.consequent[0]);
+ i = 1;
+ }
+
+ for (; i < len; i += 1) {
+ result += '\n' + addIndent(generateStatement(stmt.consequent[i]));
+ }
+
+ base = previousBase;
+ break;
+
+ case Syntax.IfStatement:
+ if (stmt.alternate) {
+ if (stmt.alternate.type === Syntax.IfStatement) {
+ previousBase = base;
+ base += indent;
+ result = 'if (' + generateExpression(stmt.test) + ')';
+ base = previousBase;
+ result += maybeBlock(stmt.consequent, true) + 'else ' + generateStatement(stmt.alternate);
+ } else {
+ previousBase = base;
+ base += indent;
+ result = 'if (' + generateExpression(stmt.test) + ')';
+ base = previousBase;
+ result += maybeBlock(stmt.consequent, true) + 'else' + maybeBlock(stmt.alternate);
+ }
+ } else {
+ previousBase = base;
+ base += indent;
+ result = 'if (' + generateExpression(stmt.test) + ')';
+ base = previousBase;
+ result += maybeBlock(stmt.consequent);
+ }
+ break;
+
+ case Syntax.ForStatement:
+ previousBase = base;
+ base += indent;
+ result = 'for (';
+ if (stmt.init) {
+ if (stmt.init.type === Syntax.VariableDeclaration) {
+ result += generateStatement(stmt.init);
+ } else {
+ result += generateExpression(stmt.init) + ';';
+ }
+ } else {
+ result += ';';
+ }
+
+ if (stmt.test) {
+ result += ' ' + generateExpression(stmt.test) + ';';
+ } else {
+ result += ';';
+ }
+
+ if (stmt.update) {
+ result += ' ' + generateExpression(stmt.update) + ')';
+ } else {
+ result += ')';
+ }
+ base = previousBase;
+
+ result += maybeBlock(stmt.body);
+ break;
+
+ case Syntax.ForInStatement:
+ result = 'for (';
+ if (stmt.left.type === Syntax.VariableDeclaration) {
+ previousBase = base;
+ base += indent + indent;
+ result += stmt.left.kind + ' ' + generateStatement(stmt.left.declarations[0]);
+ base = previousBase;
+ } else {
+ previousBase = base;
+ base += indent;
+ result += generateExpression(stmt.left, Precedence.Call);
+ base = previousBase;
+ }
+
+ previousBase = base;
+ base += indent;
+ result += ' in ' + generateExpression(stmt.right) + ')';
+ base = previousBase;
+ result += maybeBlock(stmt.body);
+ break;
+
+ case Syntax.LabeledStatement:
+ result = stmt.label.name + ':' + maybeBlock(stmt.body);
+ break;
+
+ case Syntax.Program:
+ result = '';
+ for (i = 0, len = stmt.body.length; i < len; i += 1) {
+ result += generateStatement(stmt.body[i]);
+ if ((i + 1) < len) {
+ result += '\n';
+ }
+ }
+ break;
+
+ case Syntax.FunctionDeclaration:
+ result = 'function ';
+ if (stmt.id) {
+ result += stmt.id.name;
+ }
+ result += generateFunctionBody(stmt);
+ break;
+
+ case Syntax.ReturnStatement:
+ if (stmt.argument) {
+ result = 'return ' + generateExpression(stmt.argument) + ';';
+ } else {
+ result = 'return;';
+ }
+ break;
+
+ case Syntax.WhileStatement:
+ previousBase = base;
+ base += indent;
+ result = 'while (' + generateExpression(stmt.test) + ')';
+ base = previousBase;
+ result += maybeBlock(stmt.body);
+ break;
+
+ case Syntax.WithStatement:
+ previousBase = base;
+ base += indent;
+ result = 'with (' + generateExpression(stmt.object) + ')';
+ base = previousBase;
+ result += maybeBlock(stmt.body);
+ break;
+
+ default:
+ break;
+ }
+
+ if (result === undefined) {
+ throw new Error('Unknown statement type: ' + stmt.type);
+ }
+ return result;
+ }
+
+ function generate(node, options) {
+ if (typeof options !== 'undefined') {
+ base = options.base || '';
+ indent = options.indent || ' ';
+ parse = options.parse;
+ } else {
+ base = '';
+ indent = ' ';
+ parse = null;
+ }
+
+ switch (node.type) {
+ case Syntax.BlockStatement:
+ case Syntax.BreakStatement:
+ case Syntax.CatchClause:
+ case Syntax.ContinueStatement:
+ case Syntax.DoWhileStatement:
+ case Syntax.DebuggerStatement:
+ case Syntax.EmptyStatement:
+ case Syntax.ExpressionStatement:
+ case Syntax.ForStatement:
+ case Syntax.ForInStatement:
+ case Syntax.FunctionDeclaration:
+ case Syntax.IfStatement:
+ case Syntax.LabeledStatement:
+ case Syntax.Program:
+ case Syntax.ReturnStatement:
+ case Syntax.SwitchStatement:
+ case Syntax.SwitchCase:
+ case Syntax.ThrowStatement:
+ case Syntax.TryStatement:
+ case Syntax.VariableDeclaration:
+ case Syntax.VariableDeclarator:
+ case Syntax.WhileStatement:
+ case Syntax.WithStatement:
+ return generateStatement(node);
+
+ case Syntax.AssignmentExpression:
+ case Syntax.ArrayExpression:
+ case Syntax.BinaryExpression:
+ case Syntax.CallExpression:
+ case Syntax.ConditionalExpression:
+ case Syntax.FunctionExpression:
+ case Syntax.Identifier:
+ case Syntax.Literal:
+ case Syntax.LogicalExpression:
+ case Syntax.MemberExpression:
+ case Syntax.NewExpression:
+ case Syntax.ObjectExpression:
+ case Syntax.Property:
+ case Syntax.SequenceExpression:
+ case Syntax.ThisExpression:
+ case Syntax.UnaryExpression:
+ case Syntax.UpdateExpression:
+ return generateExpression(node);
+
+ default:
+ break;
+ }
+ throw new Error('Unknown node type: ' + node.type);
+ }
+
+ // Sync with package.json.
+ exports.version = '0.0.3-dev';
+
+ exports.generate = generate;
+
+}(typeof exports === 'undefined' ? (escodegen = {}) : exports));
+/* vim: set sw=4 ts=4 et tw=80 : */