[Pkg-javascript-commits] [node-acorn-jsx] 31/484: Upgrade Esprima and UglifyJS in benchmark comparison
Bastien Roucariès
rouca at moszumanska.debian.org
Sat Aug 19 14:20:00 UTC 2017
This is an automated email from the git hooks/post-receive script.
rouca pushed a commit to branch master
in repository node-acorn-jsx.
commit d198b7ed64294bb1b5a59573f948006775a7762e
Author: Marijn Haverbeke <marijnh at gmail.com>
Date: Fri Oct 12 23:27:59 2012 +0200
Upgrade Esprima and UglifyJS in benchmark comparison
---
test/bench.html | 6 +-
test/compare/esprima.js | 1350 +++++++++++++-----------
test/compare/uglifyjs2.js | 2494 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 3230 insertions(+), 620 deletions(-)
diff --git a/test/bench.html b/test/bench.html
index b763388..4266268 100644
--- a/test/bench.html
+++ b/test/bench.html
@@ -4,7 +4,7 @@
<title>Acorn benchmark</title>
<script src="../acorn.js"></script>
<script src="compare/esprima.js"></script>
- <script src="compare/uglifyjs.js"></script>
+ <script src="compare/uglifyjs2.js"></script>
<script src="jquery-string.js"></script>
<script src="codemirror-string.js"></script>
<style>
@@ -15,7 +15,7 @@
</style>
</head>
-<h1>Acorn/Esprima/UglifyJS speed comparison</h1>
+<h1>Acorn/Esprima/UglifyJS2 speed comparison</h1>
<p>This will run each of the three parsers on the source code of
jQuery 1.6.4 and CodeMirror 3.0b1 for two seconds, and show a table
@@ -74,7 +74,7 @@ numbers.</p>
running.innerHTML = "Running benchmark...";
var data = [{name: "Acorn", runner: runAcorn},
{name: "Esprima", runner: runEsprima},
- {name: "UglifyJS", runner: runUglifyJS}];
+ {name: "UglifyJS2", runner: runUglifyJS}];
var pos = 0;
function next() {
data[pos].score = benchmark(data[pos].runner, locations);
diff --git a/test/compare/esprima.js b/test/compare/esprima.js
index 90d1b14..2db7379 100644
--- a/test/compare/esprima.js
+++ b/test/compare/esprima.js
@@ -30,7 +30,7 @@
/*jslint bitwise:true plusplus:true */
/*global esprima:true, define:true, exports:true, window: true,
-throwError: true, createLiteral: true, generateStatement: true,
+throwError: true, generateStatement: true,
parseAssignmentExpression: true, parseBlock: true, parseExpression: true,
parseFunctionDeclaration: true, parseFunctionExpression: true,
parseFunctionSourceElements: true, parseVariableIdentifier: true,
@@ -58,12 +58,14 @@ parseStatement: true, parseSourceElement: true */
PropertyKind,
Messages,
Regex,
+ SyntaxTreeDelegate,
source,
strict,
index,
lineNumber,
lineStart,
length,
+ delegate,
buffer,
state,
extra;
@@ -340,12 +342,6 @@ parseStatement: true, parseSourceElement: true */
return isFutureReservedWord(id);
}
- // Return the next character and move forward.
-
- function nextChar() {
- return source[index++];
- }
-
// 7.4 Comments
function skipComment() {
@@ -358,7 +354,7 @@ parseStatement: true, parseSourceElement: true */
ch = source[index];
if (lineComment) {
- ch = nextChar();
+ ch = source[index++];
if (isLineTerminator(ch)) {
lineComment = false;
if (ch === '\r' && source[index] === '\n') {
@@ -379,7 +375,7 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
} else {
- ch = nextChar();
+ ch = source[index++];
if (index >= length) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
@@ -426,7 +422,7 @@ parseStatement: true, parseSourceElement: true */
len = (prefix === 'u') ? 4 : 2;
for (i = 0; i < len; ++i) {
if (index < length && isHexDigit(source[index])) {
- ch = nextChar();
+ ch = source[index++];
code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
} else {
return '';
@@ -462,7 +458,7 @@ parseStatement: true, parseSourceElement: true */
id = 'u';
}
} else {
- id = nextChar();
+ id = source[index++];
}
while (index < length) {
@@ -488,7 +484,7 @@ parseStatement: true, parseSourceElement: true */
id += 'u';
}
} else {
- id += nextChar();
+ id += source[index++];
}
}
@@ -587,7 +583,7 @@ parseStatement: true, parseSourceElement: true */
if (ch1 === '.' && !isDecimalDigit(ch2)) {
return {
type: Token.Punctuator,
- value: nextChar(),
+ value: source[index++],
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
@@ -705,7 +701,7 @@ parseStatement: true, parseSourceElement: true */
if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) {
return {
type: Token.Punctuator,
- value: nextChar(),
+ value: source[index++],
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
@@ -725,20 +721,20 @@ parseStatement: true, parseSourceElement: true */
start = index;
number = '';
if (ch !== '.') {
- number = nextChar();
+ number = source[index++];
ch = source[index];
// Hex number starts with '0x'.
// Octal number starts with '0'.
if (number === '0') {
if (ch === 'x' || ch === 'X') {
- number += nextChar();
+ number += source[index++];
while (index < length) {
ch = source[index];
if (!isHexDigit(ch)) {
break;
}
- number += nextChar();
+ number += source[index++];
}
if (number.length <= 2) {
@@ -760,13 +756,13 @@ parseStatement: true, parseSourceElement: true */
range: [start, index]
};
} else if (isOctalDigit(ch)) {
- number += nextChar();
+ number += source[index++];
while (index < length) {
ch = source[index];
if (!isOctalDigit(ch)) {
break;
}
- number += nextChar();
+ number += source[index++];
}
if (index < length) {
@@ -796,38 +792,38 @@ parseStatement: true, parseSourceElement: true */
if (!isDecimalDigit(ch)) {
break;
}
- number += nextChar();
+ number += source[index++];
}
}
if (ch === '.') {
- number += nextChar();
+ number += source[index++];
while (index < length) {
ch = source[index];
if (!isDecimalDigit(ch)) {
break;
}
- number += nextChar();
+ number += source[index++];
}
}
if (ch === 'e' || ch === 'E') {
- number += nextChar();
+ number += source[index++];
ch = source[index];
if (ch === '+' || ch === '-') {
- number += nextChar();
+ number += source[index++];
}
ch = source[index];
if (isDecimalDigit(ch)) {
- number += nextChar();
+ number += source[index++];
while (index < length) {
ch = source[index];
if (!isDecimalDigit(ch)) {
break;
}
- number += nextChar();
+ number += source[index++];
}
} else {
ch = 'character ' + ch;
@@ -867,13 +863,13 @@ parseStatement: true, parseSourceElement: true */
++index;
while (index < length) {
- ch = nextChar();
+ ch = source[index++];
if (ch === quote) {
quote = '';
break;
} else if (ch === '\\') {
- ch = nextChar();
+ ch = source[index++];
if (!isLineTerminator(ch)) {
switch (ch) {
case 'n':
@@ -917,14 +913,14 @@ parseStatement: true, parseSourceElement: true */
if (index < length && isOctalDigit(source[index])) {
octal = true;
- code = code * 8 + '01234567'.indexOf(nextChar());
+ code = code * 8 + '01234567'.indexOf(source[index++]);
// 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());
+ code = code * 8 + '01234567'.indexOf(source[index++]);
}
}
str += String.fromCharCode(code);
@@ -969,10 +965,10 @@ parseStatement: true, parseSourceElement: true */
start = index;
ch = source[index];
assert(ch === '/', 'Regular expression literal must start with a slash');
- str = nextChar();
+ str = source[index++];
while (index < length) {
- ch = nextChar();
+ ch = source[index++];
str += ch;
if (classMarker) {
if (ch === ']') {
@@ -980,7 +976,7 @@ parseStatement: true, parseSourceElement: true */
}
} else {
if (ch === '\\') {
- ch = nextChar();
+ ch = source[index++];
// ECMA-262 7.8.5
if (isLineTerminator(ch)) {
throwError({}, Messages.UnterminatedRegExp);
@@ -1129,6 +1125,346 @@ parseStatement: true, parseSourceElement: true */
return buffer;
}
+ SyntaxTreeDelegate = {
+
+ name: 'SyntaxTree',
+
+ createArrayExpression: function (elements) {
+ return {
+ type: Syntax.ArrayExpression,
+ elements: elements
+ };
+ },
+
+ createAssignmentExpression: function (operator, left, right) {
+ return {
+ type: Syntax.AssignmentExpression,
+ operator: operator,
+ left: left,
+ right: right
+ };
+ },
+
+ createBinaryExpression: function (operator, left, right) {
+ return {
+ type: Syntax.BinaryExpression,
+ operator: operator,
+ left: left,
+ right: right
+ };
+ },
+
+ createBlockStatement: function (body) {
+ return {
+ type: Syntax.BlockStatement,
+ body: body
+ };
+ },
+
+ createBreakStatement: function (label) {
+ return {
+ type: Syntax.BreakStatement,
+ label: label
+ };
+ },
+
+ createCallExpression: function (callee, args) {
+ return {
+ type: Syntax.CallExpression,
+ callee: callee,
+ 'arguments': args
+ };
+ },
+
+ createCatchClause: function (param, body) {
+ return {
+ type: Syntax.CatchClause,
+ param: param,
+ body: body
+ };
+ },
+
+ createConditionalExpression: function (test, consequent, alternate) {
+ return {
+ type: Syntax.ConditionalExpression,
+ test: test,
+ consequent: consequent,
+ alternate: alternate
+ };
+ },
+
+ createContinueStatement: function (label) {
+ return {
+ type: Syntax.ContinueStatement,
+ label: label
+ };
+ },
+
+ createDebuggerStatement: function () {
+ return {
+ type: Syntax.DebuggerStatement
+ };
+ },
+
+ createDoWhileStatement: function (body, test) {
+ return {
+ type: Syntax.DoWhileStatement,
+ body: body,
+ test: test
+ };
+ },
+
+ createEmptyStatement: function () {
+ return {
+ type: Syntax.EmptyStatement
+ };
+ },
+
+ createExpressionStatement: function (expression) {
+ return {
+ type: Syntax.ExpressionStatement,
+ expression: expression
+ };
+ },
+
+ createForStatement: function (init, test, update, body) {
+ return {
+ type: Syntax.ForStatement,
+ init: init,
+ test: test,
+ update: update,
+ body: body
+ };
+ },
+
+ createForInStatement: function (left, right, body) {
+ return {
+ type: Syntax.ForInStatement,
+ left: left,
+ right: right,
+ body: body,
+ each: false
+ };
+ },
+
+ createFunctionDeclaration: function (id, params, defaults, body) {
+ return {
+ type: Syntax.FunctionDeclaration,
+ id: id,
+ params: params,
+ defaults: defaults,
+ body: body,
+ rest: null,
+ generator: false,
+ expression: false
+ };
+ },
+
+ createFunctionExpression: function (id, params, defaults, body) {
+ return {
+ type: Syntax.FunctionExpression,
+ id: id,
+ params: params,
+ defaults: defaults,
+ body: body,
+ rest: null,
+ generator: false,
+ expression: false
+ };
+ },
+
+ createIdentifier: function (name) {
+ return {
+ type: Syntax.Identifier,
+ name: name
+ };
+ },
+
+ createIfStatement: function (test, consequent, alternate) {
+ return {
+ type: Syntax.IfStatement,
+ test: test,
+ consequent: consequent,
+ alternate: alternate
+ };
+ },
+
+ createLabeledStatement: function (label, body) {
+ return {
+ type: Syntax.LabeledStatement,
+ label: label,
+ body: body
+ };
+ },
+
+ createLiteral: function (token) {
+ return {
+ type: Syntax.Literal,
+ value: token.value
+ };
+ },
+
+ createLogicalExpression: function (operator, left, right) {
+ return {
+ type: Syntax.LogicalExpression,
+ operator: operator,
+ left: left,
+ right: right
+ };
+ },
+
+ createMemberExpression: function (accessor, object, property) {
+ return {
+ type: Syntax.MemberExpression,
+ computed: accessor === '[',
+ object: object,
+ property: property
+ };
+ },
+
+ createNewExpression: function (callee, args) {
+ return {
+ type: Syntax.NewExpression,
+ callee: callee,
+ 'arguments': args
+ };
+ },
+
+ createObjectExpression: function (properties) {
+ return {
+ type: Syntax.ObjectExpression,
+ properties: properties
+ };
+ },
+
+ createPostfixExpression: function (operator, argument) {
+ return {
+ type: Syntax.UpdateExpression,
+ operator: operator,
+ argument: argument,
+ prefix: false
+ };
+ },
+
+ createProgram: function (body) {
+ return {
+ type: Syntax.Program,
+ body: body
+ };
+ },
+
+ createProperty: function (kind, key, value) {
+ return {
+ type: Syntax.Property,
+ key: key,
+ value: value,
+ kind: kind
+ };
+ },
+
+ createReturnStatement: function (argument) {
+ return {
+ type: Syntax.ReturnStatement,
+ argument: argument
+ };
+ },
+
+ createSequenceExpression: function (expressions) {
+ return {
+ type: Syntax.SequenceExpression,
+ expressions: expressions
+ };
+ },
+
+ createSwitchCase: function (test, consequent) {
+ return {
+ type: Syntax.SwitchCase,
+ test: test,
+ consequent: consequent
+ };
+ },
+
+ createSwitchStatement: function (discriminant, cases) {
+ return {
+ type: Syntax.SwitchStatement,
+ discriminant: discriminant,
+ cases: cases
+ };
+ },
+
+ createThisExpression: function () {
+ return {
+ type: Syntax.ThisExpression
+ };
+ },
+
+ createThrowStatement: function (argument) {
+ return {
+ type: Syntax.ThrowStatement,
+ argument: argument
+ };
+ },
+
+ createTryStatement: function (block, guardedHandlers, handlers, finalizer) {
+ return {
+ type: Syntax.TryStatement,
+ block: block,
+ guardedHandlers: guardedHandlers,
+ handlers: handlers,
+ finalizer: finalizer
+ };
+ },
+
+ createUnaryExpression: function (operator, argument) {
+ if (operator === '++' || operator === '--') {
+ return {
+ type: Syntax.UpdateExpression,
+ operator: operator,
+ argument: argument,
+ prefix: true
+ };
+ } else {
+ return {
+ type: Syntax.UnaryExpression,
+ operator: operator,
+ argument: argument
+ };
+ }
+ },
+
+ createVariableDeclaration: function (declarations, kind) {
+ return {
+ type: Syntax.VariableDeclaration,
+ declarations: declarations,
+ kind: kind
+ };
+ },
+
+ createVariableDeclarator: function (id, init) {
+ return {
+ type: Syntax.VariableDeclarator,
+ id: id,
+ init: init
+ };
+ },
+
+ createWhileStatement: function (test, body) {
+ return {
+ type: Syntax.WhileStatement,
+ test: test,
+ body: body
+ };
+ },
+
+ createWithStatement: function (object, body) {
+ return {
+ type: Syntax.WithStatement,
+ object: object,
+ body: body
+ };
+ }
+ };
+
// Return true if there is a line terminator before the next token.
function peekLineTerminator() {
@@ -1331,10 +1667,7 @@ parseStatement: true, parseSourceElement: true */
expect(']');
- return {
- type: Syntax.ArrayExpression,
- elements: elements
- };
+ return delegate.createArrayExpression(elements);
}
// 11.1.5 Object Initialiser
@@ -1348,17 +1681,7 @@ parseStatement: true, parseSourceElement: true */
throwErrorTolerant(first, Messages.StrictParamName);
}
strict = previousStrict;
-
- return {
- type: Syntax.FunctionExpression,
- id: null,
- params: param,
- defaults: [],
- body: body,
- rest: null,
- generator: false,
- expression: false
- };
+ return delegate.createFunctionExpression(null, param, [], body);
}
function parseObjectPropertyKey() {
@@ -1371,17 +1694,14 @@ parseStatement: true, parseSourceElement: true */
if (strict && token.octal) {
throwErrorTolerant(token, Messages.StrictOctalLiteral);
}
- return createLiteral(token);
+ return delegate.createLiteral(token);
}
- return {
- type: Syntax.Identifier,
- name: token.value
- };
+ return delegate.createIdentifier(token.value);
}
function parseObjectProperty() {
- var token, key, id, param;
+ var token, key, id, value, param;
token = lookahead();
@@ -1395,12 +1715,8 @@ parseStatement: true, parseSourceElement: true */
key = parseObjectPropertyKey();
expect('(');
expect(')');
- return {
- type: Syntax.Property,
- key: key,
- value: parsePropertyFunction([]),
- kind: 'get'
- };
+ value = parsePropertyFunction([]);
+ return delegate.createProperty('get', key, value);
} else if (token.value === 'set' && !match(':')) {
key = parseObjectPropertyKey();
expect('(');
@@ -1410,32 +1726,20 @@ parseStatement: true, parseSourceElement: true */
}
param = [ parseVariableIdentifier() ];
expect(')');
- return {
- type: Syntax.Property,
- key: key,
- value: parsePropertyFunction(param, token),
- kind: 'set'
- };
+ value = parsePropertyFunction(param, token);
+ return delegate.createProperty('set', key, value);
} else {
expect(':');
- return {
- type: Syntax.Property,
- key: id,
- value: parseAssignmentExpression(),
- kind: 'init'
- };
+ value = parseAssignmentExpression();
+ return delegate.createProperty('init', id, value);
}
} else if (token.type === Token.EOF || token.type === Token.Punctuator) {
throwUnexpected(token);
} else {
key = parseObjectPropertyKey();
expect(':');
- return {
- type: Syntax.Property,
- key: key,
- value: parseAssignmentExpression(),
- kind: 'init'
- };
+ value = parseAssignmentExpression();
+ return delegate.createProperty('init', key, value);
}
}
@@ -1481,12 +1785,24 @@ parseStatement: true, parseSourceElement: true */
expect('}');
- return {
- type: Syntax.ObjectExpression,
- properties: properties
- };
+ return delegate.createObjectExpression(properties);
+ }
+
+ // 11.1.6 The Grouping Operator
+
+ function parseGroupExpression() {
+ var expr;
+
+ expect('(');
+
+ expr = parseExpression();
+
+ expect(')');
+
+ return expr;
}
+
// 11.1 Primary Expressions
function parsePrimaryExpression() {
@@ -1495,25 +1811,21 @@ parseStatement: true, parseSourceElement: true */
type = token.type;
if (type === Token.Identifier) {
- return {
- type: Syntax.Identifier,
- name: lex().value
- };
+ lex();
+ return delegate.createIdentifier(token.value);
}
if (type === Token.StringLiteral || type === Token.NumericLiteral) {
if (strict && token.octal) {
throwErrorTolerant(token, Messages.StrictOctalLiteral);
}
- return createLiteral(lex());
+ return delegate.createLiteral(lex());
}
if (type === Token.Keyword) {
if (matchKeyword('this')) {
lex();
- return {
- type: Syntax.ThisExpression
- };
+ return delegate.createThisExpression();
}
if (matchKeyword('function')) {
@@ -1524,13 +1836,13 @@ parseStatement: true, parseSourceElement: true */
if (type === Token.BooleanLiteral) {
lex();
token.value = (token.value === 'true');
- return createLiteral(token);
+ return delegate.createLiteral(token);
}
if (type === Token.NullLiteral) {
lex();
token.value = null;
- return createLiteral(token);
+ return delegate.createLiteral(token);
}
if (match('[')) {
@@ -1542,14 +1854,11 @@ parseStatement: true, parseSourceElement: true */
}
if (match('(')) {
- lex();
- state.lastParenthesized = expr = parseExpression();
- expect(')');
- return expr;
+ return parseGroupExpression();
}
if (match('/') || match('/=')) {
- return createLiteral(scanRegExp());
+ return delegate.createLiteral(scanRegExp());
}
return throwUnexpected(lex());
@@ -1584,10 +1893,7 @@ parseStatement: true, parseSourceElement: true */
throwUnexpected(token);
}
- return {
- type: Syntax.Identifier,
- name: token.value
- };
+ return delegate.createIdentifier(token.value);
}
function parseNonComputedMember() {
@@ -1609,49 +1915,30 @@ parseStatement: true, parseSourceElement: true */
}
function parseNewExpression() {
- var expr;
+ var expr, callee, args;
expectKeyword('new');
+ callee = parseLeftHandSideExpression();
+ args = match('(') ? parseArguments() : [];
- expr = {
- type: Syntax.NewExpression,
- callee: parseLeftHandSideExpression(),
- 'arguments': []
- };
-
- if (match('(')) {
- expr['arguments'] = parseArguments();
- }
-
- return expr;
+ return delegate.createNewExpression(callee, args);
}
function parseLeftHandSideExpressionAllowCall() {
- var expr;
+ var expr, args, property;
expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
while (match('.') || match('[') || match('(')) {
if (match('(')) {
- expr = {
- type: Syntax.CallExpression,
- callee: expr,
- 'arguments': parseArguments()
- };
+ args = parseArguments();
+ expr = delegate.createCallExpression(expr, args);
} else if (match('[')) {
- expr = {
- type: Syntax.MemberExpression,
- computed: true,
- object: expr,
- property: parseComputedMember()
- };
+ property = parseComputedMember();
+ expr = delegate.createMemberExpression('[', expr, property);
} else {
- expr = {
- type: Syntax.MemberExpression,
- computed: false,
- object: expr,
- property: parseNonComputedMember()
- };
+ property = parseNonComputedMember();
+ expr = delegate.createMemberExpression('.', expr, property);
}
}
@@ -1660,25 +1947,17 @@ parseStatement: true, parseSourceElement: true */
function parseLeftHandSideExpression() {
- var expr;
+ var expr, property;
expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
while (match('.') || match('[')) {
if (match('[')) {
- expr = {
- type: Syntax.MemberExpression,
- computed: true,
- object: expr,
- property: parseComputedMember()
- };
+ property = parseComputedMember();
+ expr = delegate.createMemberExpression('[', expr, property);
} else {
- expr = {
- type: Syntax.MemberExpression,
- computed: false,
- object: expr,
- property: parseNonComputedMember()
- };
+ property = parseNonComputedMember();
+ expr = delegate.createMemberExpression('.', expr, property);
}
}
@@ -1688,7 +1967,12 @@ parseStatement: true, parseSourceElement: true */
// 11.3 Postfix Expressions
function parsePostfixExpression() {
- var expr = parseLeftHandSideExpressionAllowCall();
+ var expr = parseLeftHandSideExpressionAllowCall(), token;
+
+ token = lookahead();
+ if (token.type !== Token.Punctuator) {
+ return expr;
+ }
if ((match('++') || match('--')) && !peekLineTerminator()) {
// 11.3.1, 11.3.2
@@ -1700,12 +1984,8 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.InvalidLHSInAssignment);
}
- expr = {
- type: Syntax.UpdateExpression,
- operator: lex().value,
- argument: expr,
- prefix: false
- };
+ token = lex();
+ expr = delegate.createPostfixExpression(token.value, expr);
}
return expr;
@@ -1716,6 +1996,11 @@ parseStatement: true, parseSourceElement: true */
function parseUnaryExpression() {
var token, expr;
+ token = lookahead();
+ if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
+ return parsePostfixExpression();
+ }
+
if (match('++') || match('--')) {
token = lex();
expr = parseUnaryExpression();
@@ -1728,30 +2013,19 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.InvalidLHSInAssignment);
}
- expr = {
- type: Syntax.UpdateExpression,
- operator: token.value,
- argument: expr,
- prefix: true
- };
- return expr;
+ return delegate.createUnaryExpression(token.value, expr);
}
if (match('+') || match('-') || match('~') || match('!')) {
- expr = {
- type: Syntax.UnaryExpression,
- operator: lex().value,
- argument: parseUnaryExpression()
- };
- return expr;
+ token = lex();
+ expr = parseUnaryExpression();
+ return delegate.createUnaryExpression(token.value, expr);
}
if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
- expr = {
- type: Syntax.UnaryExpression,
- operator: lex().value,
- argument: parseUnaryExpression()
- };
+ token = lex();
+ expr = parseUnaryExpression();
+ expr = delegate.createUnaryExpression(token.value, expr);
if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) {
throwErrorTolerant({}, Messages.StrictDelete);
}
@@ -1761,186 +2035,147 @@ parseStatement: true, parseSourceElement: true */
return parsePostfixExpression();
}
- // 11.5 Multiplicative Operators
-
- function parseMultiplicativeExpression() {
- var expr = parseUnaryExpression();
+ function binaryPrecedence(token, allowIn) {
+ var prec = 0;
- while (match('*') || match('/') || match('%')) {
- expr = {
- type: Syntax.BinaryExpression,
- operator: lex().value,
- left: expr,
- right: parseUnaryExpression()
- };
+ if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
+ return 0;
}
- 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();
+ switch (token.value) {
+ case '||':
+ prec = 1;
+ break;
- while (match('<<') || match('>>') || match('>>>')) {
- expr = {
- type: Syntax.BinaryExpression,
- operator: lex().value,
- left: expr,
- right: parseAdditiveExpression()
- };
- }
+ case '&&':
+ prec = 2;
+ break;
- return expr;
- }
- // 11.8 Relational Operators
+ case '|':
+ prec = 3;
+ break;
- function parseRelationalExpression() {
- var expr, previousAllowIn;
+ case '^':
+ prec = 4;
+ break;
- previousAllowIn = state.allowIn;
- state.allowIn = true;
+ case '&':
+ prec = 5;
+ break;
- expr = parseShiftExpression();
+ case '==':
+ case '!=':
+ case '===':
+ case '!==':
+ prec = 6;
+ break;
- while (match('<') || match('>') || match('<=') || match('>=') || (previousAllowIn && matchKeyword('in')) || matchKeyword('instanceof')) {
- expr = {
- type: Syntax.BinaryExpression,
- operator: lex().value,
- left: expr,
- right: parseShiftExpression()
- };
- }
+ case '<':
+ case '>':
+ case '<=':
+ case '>=':
+ case 'instanceof':
+ prec = 7;
+ break;
- state.allowIn = previousAllowIn;
- return expr;
- }
+ case 'in':
+ prec = allowIn ? 7 : 0;
+ break;
- // 11.9 Equality Operators
+ case '<<':
+ case '>>':
+ case '>>>':
+ prec = 8;
+ break;
- function parseEqualityExpression() {
- var expr = parseRelationalExpression();
+ case '+':
+ case '-':
+ prec = 9;
+ break;
- while (match('==') || match('!=') || match('===') || match('!==')) {
- expr = {
- type: Syntax.BinaryExpression,
- operator: lex().value,
- left: expr,
- right: parseRelationalExpression()
- };
+ case '*':
+ case '/':
+ case '%':
+ prec = 11;
+ break;
+
+ default:
+ break;
}
- return expr;
+ return prec;
}
+ // 11.5 Multiplicative Operators
+ // 11.6 Additive Operators
+ // 11.7 Bitwise Shift Operators
+ // 11.8 Relational Operators
+ // 11.9 Equality Operators
// 11.10 Binary Bitwise Operators
+ // 11.11 Binary Logical Operators
- function parseBitwiseANDExpression() {
- var expr = parseEqualityExpression();
+ // Reduce: make a binary expression from the three topmost entries.
+ function reduceBinary(stack) {
+ var right = stack.pop(),
+ operator = stack.pop().value,
+ left = stack.pop();
- while (match('&')) {
- lex();
- expr = {
- type: Syntax.BinaryExpression,
- operator: '&',
- left: expr,
- right: parseEqualityExpression()
- };
- }
- return expr;
+ if (operator === '||' || operator === '&&') {
+ stack.push(delegate.createLogicalExpression(operator, left, right));
+ } else {
+ stack.push(delegate.createBinaryExpression(operator, left, right));
+ }
}
- function parseBitwiseXORExpression() {
- var expr = parseBitwiseANDExpression();
-
- while (match('^')) {
- lex();
- expr = {
- type: Syntax.BinaryExpression,
- operator: '^',
- left: expr,
- right: parseBitwiseANDExpression()
- };
- }
+ function parseBinaryExpression() {
+ var expr, token, prec, previousAllowIn, stack;
- return expr;
- }
+ previousAllowIn = state.allowIn;
+ state.allowIn = true;
- function parseBitwiseORExpression() {
- var expr = parseBitwiseXORExpression();
+ expr = parseUnaryExpression();
- while (match('|')) {
- lex();
- expr = {
- type: Syntax.BinaryExpression,
- operator: '|',
- left: expr,
- right: parseBitwiseXORExpression()
- };
+ token = lookahead();
+ prec = binaryPrecedence(token, previousAllowIn);
+ if (prec === 0) {
+ return expr;
}
+ token.prec = prec;
+ lex();
- return expr;
- }
+ stack = [expr, token, parseUnaryExpression()];
- // 11.11 Binary Logical Operators
+ while ((prec = binaryPrecedence(lookahead(), previousAllowIn)) > 0) {
- function parseLogicalANDExpression() {
- var expr = parseBitwiseORExpression();
+ // Reduce.
+ while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
+ reduceBinary(stack);
+ }
- while (match('&&')) {
- lex();
- expr = {
- type: Syntax.LogicalExpression,
- operator: '&&',
- left: expr,
- right: parseBitwiseORExpression()
- };
+ // Shift.
+ token = lex();
+ token.prec = prec;
+ stack.push(token);
+ stack.push(parseUnaryExpression());
}
- return expr;
- }
-
- function parseLogicalORExpression() {
- var expr = parseLogicalANDExpression();
-
- while (match('||')) {
- lex();
- expr = {
- type: Syntax.LogicalExpression,
- operator: '||',
- left: expr,
- right: parseLogicalANDExpression()
- };
+ // Final reduce to clean-up the stack.
+ while (stack.length > 1) {
+ reduceBinary(stack);
}
- return expr;
+ state.allowIn = previousAllowIn;
+ return stack[0];
}
+
// 11.12 Conditional Operator
function parseConditionalExpression() {
- var expr, previousAllowIn, consequent;
+ var expr, previousAllowIn, consequent, alternate;
- expr = parseLogicalORExpression();
+ expr = parseBinaryExpression();
if (match('?')) {
lex();
@@ -1949,13 +2184,9 @@ parseStatement: true, parseSourceElement: true */
consequent = parseAssignmentExpression();
state.allowIn = previousAllowIn;
expect(':');
+ alternate = parseAssignmentExpression();
- expr = {
- type: Syntax.ConditionalExpression,
- test: expr,
- consequent: consequent,
- alternate: parseAssignmentExpression()
- };
+ expr = delegate.createConditionalExpression(expr, consequent, alternate);
}
return expr;
@@ -1964,31 +2195,28 @@ parseStatement: true, parseSourceElement: true */
// 11.13 Assignment Operators
function parseAssignmentExpression() {
- var token, expr;
+ var token, left, right;
token = lookahead();
- expr = parseConditionalExpression();
+ left = parseConditionalExpression();
if (matchAssign()) {
// LeftHandSideExpression
- if (!isLeftHandSide(expr)) {
+ if (!isLeftHandSide(left)) {
throwError({}, Messages.InvalidLHSInAssignment);
}
// 11.13.1
- if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
+ if (strict && left.type === Syntax.Identifier && isRestrictedWord(left.name)) {
throwErrorTolerant(token, Messages.StrictLHSAssignment);
}
- expr = {
- type: Syntax.AssignmentExpression,
- operator: lex().value,
- left: expr,
- right: parseAssignmentExpression()
- };
+ token = lex();
+ right = parseAssignmentExpression();
+ return delegate.createAssignmentExpression(token.value, left, right);
}
- return expr;
+ return left;
}
// 11.14 Comma Operator
@@ -1997,10 +2225,7 @@ parseStatement: true, parseSourceElement: true */
var expr = parseAssignmentExpression();
if (match(',')) {
- expr = {
- type: Syntax.SequenceExpression,
- expressions: [ expr ]
- };
+ expr = delegate.createSequenceExpression([ expr ]);
while (index < length) {
if (!match(',')) {
@@ -2043,10 +2268,7 @@ parseStatement: true, parseSourceElement: true */
expect('}');
- return {
- type: Syntax.BlockStatement,
- body: block
- };
+ return delegate.createBlockStatement(block);
}
// 12.2 Variable Statement
@@ -2058,10 +2280,7 @@ parseStatement: true, parseSourceElement: true */
throwUnexpected(token);
}
- return {
- type: Syntax.Identifier,
- name: token.value
- };
+ return delegate.createIdentifier(token.value);
}
function parseVariableDeclaration(kind) {
@@ -2081,11 +2300,7 @@ parseStatement: true, parseSourceElement: true */
init = parseAssignmentExpression();
}
- return {
- type: Syntax.VariableDeclarator,
- id: id,
- init: init
- };
+ return delegate.createVariableDeclarator(id, init);
}
function parseVariableDeclarationList(kind) {
@@ -2111,11 +2326,7 @@ parseStatement: true, parseSourceElement: true */
consumeSemicolon();
- return {
- type: Syntax.VariableDeclaration,
- declarations: declarations,
- kind: 'var'
- };
+ return delegate.createVariableDeclaration(declarations, 'var');
}
// kind may be `const` or `let`
@@ -2131,34 +2342,22 @@ parseStatement: true, parseSourceElement: true */
consumeSemicolon();
- return {
- type: Syntax.VariableDeclaration,
- declarations: declarations,
- kind: kind
- };
+ return delegate.createVariableDeclaration(declarations, kind);
}
// 12.3 Empty Statement
function parseEmptyStatement() {
expect(';');
-
- return {
- type: Syntax.EmptyStatement
- };
+ return delegate.createEmptyStatement();
}
// 12.4 Expression Statement
function parseExpressionStatement() {
var expr = parseExpression();
-
consumeSemicolon();
-
- return {
- type: Syntax.ExpressionStatement,
- expression: expr
- };
+ return delegate.createExpressionStatement(expr);
}
// 12.5 If statement
@@ -2183,12 +2382,7 @@ parseStatement: true, parseSourceElement: true */
alternate = null;
}
- return {
- type: Syntax.IfStatement,
- test: test,
- consequent: consequent,
- alternate: alternate
- };
+ return delegate.createIfStatement(test, consequent, alternate);
}
// 12.6 Iteration Statements
@@ -2217,11 +2411,7 @@ parseStatement: true, parseSourceElement: true */
lex();
}
- return {
- type: Syntax.DoWhileStatement,
- body: body,
- test: test
- };
+ return delegate.createDoWhileStatement(body, test);
}
function parseWhileStatement() {
@@ -2242,21 +2432,14 @@ parseStatement: true, parseSourceElement: true */
state.inIteration = oldInIteration;
- return {
- type: Syntax.WhileStatement,
- test: test,
- body: body
- };
+ return delegate.createWhileStatement(test, body);
}
function parseForVariableDeclaration() {
- var token = lex();
+ var token = lex(),
+ declarations = parseVariableDeclarationList();
- return {
- type: Syntax.VariableDeclaration,
- declarations: parseVariableDeclarationList(),
- kind: token.value
- };
+ return delegate.createVariableDeclaration(declarations, token.value);
}
function parseForStatement() {
@@ -2326,23 +2509,9 @@ parseStatement: true, parseSourceElement: true */
state.inIteration = oldInIteration;
- if (typeof left === 'undefined') {
- return {
- type: Syntax.ForStatement,
- init: init,
- test: test,
- update: update,
- body: body
- };
- }
-
- return {
- type: Syntax.ForInStatement,
- left: left,
- right: right,
- body: body,
- each: false
- };
+ return (typeof left === 'undefined') ?
+ delegate.createForStatement(init, test, update, body) :
+ delegate.createForInStatement(left, right, body);
}
// 12.7 The continue statement
@@ -2360,10 +2529,7 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.IllegalContinue);
}
- return {
- type: Syntax.ContinueStatement,
- label: null
- };
+ return delegate.createContinueStatement(null);
}
if (peekLineTerminator()) {
@@ -2371,10 +2537,7 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.IllegalContinue);
}
- return {
- type: Syntax.ContinueStatement,
- label: null
- };
+ return delegate.createContinueStatement(null);
}
token = lookahead();
@@ -2392,10 +2555,7 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.IllegalContinue);
}
- return {
- type: Syntax.ContinueStatement,
- label: label
- };
+ return delegate.createContinueStatement(label);
}
// 12.8 The break statement
@@ -2413,10 +2573,7 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.IllegalBreak);
}
- return {
- type: Syntax.BreakStatement,
- label: null
- };
+ return delegate.createBreakStatement(null);
}
if (peekLineTerminator()) {
@@ -2424,10 +2581,7 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.IllegalBreak);
}
- return {
- type: Syntax.BreakStatement,
- label: null
- };
+ return delegate.createBreakStatement(null);
}
token = lookahead();
@@ -2445,10 +2599,7 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.IllegalBreak);
}
- return {
- type: Syntax.BreakStatement,
- label: label
- };
+ return delegate.createBreakStatement(label);
}
// 12.9 The return statement
@@ -2467,18 +2618,12 @@ parseStatement: true, parseSourceElement: true */
if (isIdentifierStart(source[index + 1])) {
argument = parseExpression();
consumeSemicolon();
- return {
- type: Syntax.ReturnStatement,
- argument: argument
- };
+ return delegate.createReturnStatement(argument);
}
}
if (peekLineTerminator()) {
- return {
- type: Syntax.ReturnStatement,
- argument: null
- };
+ return delegate.createReturnStatement(null);
}
if (!match(';')) {
@@ -2490,10 +2635,7 @@ parseStatement: true, parseSourceElement: true */
consumeSemicolon();
- return {
- type: Syntax.ReturnStatement,
- argument: argument
- };
+ return delegate.createReturnStatement(argument);
}
// 12.10 The with statement
@@ -2515,11 +2657,7 @@ parseStatement: true, parseSourceElement: true */
body = parseStatement();
- return {
- type: Syntax.WithStatement,
- object: object,
- body: body
- };
+ return delegate.createWithStatement(object, body);
}
// 12.10 The swith statement
@@ -2549,11 +2687,7 @@ parseStatement: true, parseSourceElement: true */
consequent.push(statement);
}
- return {
- type: Syntax.SwitchCase,
- test: test,
- consequent: consequent
- };
+ return delegate.createSwitchCase(test, consequent);
}
function parseSwitchStatement() {
@@ -2571,10 +2705,7 @@ parseStatement: true, parseSourceElement: true */
if (match('}')) {
lex();
- return {
- type: Syntax.SwitchStatement,
- discriminant: discriminant
- };
+ return delegate.createSwitchStatement(discriminant);
}
cases = [];
@@ -2601,11 +2732,7 @@ parseStatement: true, parseSourceElement: true */
expect('}');
- return {
- type: Syntax.SwitchStatement,
- discriminant: discriminant,
- cases: cases
- };
+ return delegate.createSwitchStatement(discriminant, cases);
}
// 12.13 The throw statement
@@ -2623,16 +2750,13 @@ parseStatement: true, parseSourceElement: true */
consumeSemicolon();
- return {
- type: Syntax.ThrowStatement,
- argument: argument
- };
+ return delegate.createThrowStatement(argument);
}
// 12.14 The try statement
function parseCatchClause() {
- var param;
+ var param, body;
expectKeyword('catch');
@@ -2645,12 +2769,8 @@ parseStatement: true, parseSourceElement: true */
}
}
expect(')');
-
- return {
- type: Syntax.CatchClause,
- param: param,
- body: parseBlock()
- };
+ body = parseBlock();
+ return delegate.createCatchClause(param, body);
}
function parseTryStatement() {
@@ -2673,13 +2793,7 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.NoCatchOrFinally);
}
- return {
- type: Syntax.TryStatement,
- block: block,
- guardedHandlers: [],
- handlers: handlers,
- finalizer: finalizer
- };
+ return delegate.createTryStatement(block, [], handlers, finalizer);
}
// 12.15 The debugger statement
@@ -2689,9 +2803,7 @@ parseStatement: true, parseSourceElement: true */
consumeSemicolon();
- return {
- type: Syntax.DebuggerStatement
- };
+ return delegate.createDebuggerStatement();
}
// 12 Statements
@@ -2766,20 +2878,12 @@ parseStatement: true, parseSourceElement: true */
state.labelSet[expr.name] = true;
labeledBody = parseStatement();
delete state.labelSet[expr.name];
-
- return {
- type: Syntax.LabeledStatement,
- label: expr,
- body: labeledBody
- };
+ return delegate.createLabeledStatement(expr, labeledBody);
}
consumeSemicolon();
- return {
- type: Syntax.ExpressionStatement,
- expression: expr
- };
+ return delegate.createExpressionStatement(expr);
}
// 13 Function Definition
@@ -2843,10 +2947,7 @@ parseStatement: true, parseSourceElement: true */
state.inSwitch = oldInSwitch;
state.inFunctionBody = oldInFunctionBody;
- return {
- type: Syntax.BlockStatement,
- body: sourceElements
- };
+ return delegate.createBlockStatement(sourceElements);
}
function parseFunctionDeclaration() {
@@ -2918,16 +3019,7 @@ parseStatement: true, parseSourceElement: true */
}
strict = previousStrict;
- return {
- type: Syntax.FunctionDeclaration,
- id: id,
- params: params,
- defaults: [],
- body: body,
- rest: null,
- generator: false,
- expression: false
- };
+ return delegate.createFunctionDeclaration(id, params, [], body);
}
function parseFunctionExpression() {
@@ -3002,16 +3094,7 @@ parseStatement: true, parseSourceElement: true */
}
strict = previousStrict;
- return {
- type: Syntax.FunctionExpression,
- id: id,
- params: params,
- defaults: [],
- body: body,
- rest: null,
- generator: false,
- expression: false
- };
+ return delegate.createFunctionExpression(id, params, [], body);
}
// 14 Program
@@ -3075,13 +3158,10 @@ parseStatement: true, parseSourceElement: true */
}
function parseProgram() {
- var program;
+ var body;
strict = false;
- program = {
- type: Syntax.Program,
- body: parseSourceElements()
- };
- return program;
+ body = parseSourceElements();
+ return delegate.createProgram(body);
}
// The following functions are needed only when the option to preserve
@@ -3119,7 +3199,7 @@ parseStatement: true, parseSourceElement: true */
ch = source[index];
if (lineComment) {
- ch = nextChar();
+ ch = source[index++];
if (isLineTerminator(ch)) {
loc.end = {
line: lineNumber,
@@ -3159,7 +3239,7 @@ parseStatement: true, parseSourceElement: true */
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
} else {
- ch = nextChar();
+ ch = source[index++];
if (index >= length) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
@@ -3343,21 +3423,6 @@ parseStatement: true, parseSourceElement: true */
extra.tokens = tokens;
}
- function createLiteral(token) {
- return {
- type: Syntax.Literal,
- value: token.value
- };
- }
-
- function createRawLiteral(token) {
- return {
- type: Syntax.Literal,
- value: token.value,
- raw: sliceSource(token.range[0], token.range[1])
- };
- }
-
function createLocationMarker() {
var marker = {};
@@ -3379,6 +3444,24 @@ parseStatement: true, parseSourceElement: true */
this.loc.end.column = index - lineStart;
};
+ marker.applyGroup = function (node) {
+ if (extra.range) {
+ node.groupRange = [this.range[0], this.range[1]];
+ }
+ if (extra.loc) {
+ node.groupLoc = {
+ start: {
+ line: this.loc.start.line,
+ column: this.loc.start.column
+ },
+ end: {
+ line: this.loc.end.line,
+ column: this.loc.end.column
+ }
+ };
+ }
+ };
+
marker.apply = function (node) {
if (extra.range) {
node.range = [this.range[0], this.range[1]];
@@ -3400,31 +3483,40 @@ parseStatement: true, parseSourceElement: true */
return marker;
}
- function trackLeftHandSideExpression() {
+ function trackGroupExpression() {
var marker, expr;
skipComment();
marker = createLocationMarker();
+ expect('(');
+
+ expr = parseExpression();
+
+ expect(')');
+
+ marker.end();
+ marker.applyGroup(expr);
+
+ return expr;
+ }
+
+ function trackLeftHandSideExpression() {
+ var marker, expr, property;
+
+ skipComment();
+ marker = createLocationMarker();
expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
while (match('.') || match('[')) {
if (match('[')) {
- expr = {
- type: Syntax.MemberExpression,
- computed: true,
- object: expr,
- property: parseComputedMember()
- };
+ property = parseComputedMember();
+ expr = delegate.createMemberExpression('[', expr, property);
marker.end();
marker.apply(expr);
} else {
- expr = {
- type: Syntax.MemberExpression,
- computed: false,
- object: expr,
- property: parseNonComputedMember()
- };
+ property = parseNonComputedMember();
+ expr = delegate.createMemberExpression('.', expr, property);
marker.end();
marker.apply(expr);
}
@@ -3434,7 +3526,7 @@ parseStatement: true, parseSourceElement: true */
}
function trackLeftHandSideExpressionAllowCall() {
- var marker, expr;
+ var marker, expr, args, property;
skipComment();
marker = createLocationMarker();
@@ -3443,29 +3535,18 @@ parseStatement: true, parseSourceElement: true */
while (match('.') || match('[') || match('(')) {
if (match('(')) {
- expr = {
- type: Syntax.CallExpression,
- callee: expr,
- 'arguments': parseArguments()
- };
+ args = parseArguments();
+ expr = delegate.createCallExpression(expr, args);
marker.end();
marker.apply(expr);
} else if (match('[')) {
- expr = {
- type: Syntax.MemberExpression,
- computed: true,
- object: expr,
- property: parseComputedMember()
- };
+ property = parseComputedMember();
+ expr = delegate.createMemberExpression('[', expr, property);
marker.end();
marker.apply(expr);
} else {
- expr = {
- type: Syntax.MemberExpression,
- computed: false,
- object: expr,
- property: parseNonComputedMember()
- };
+ property = parseNonComputedMember();
+ expr = delegate.createMemberExpression('.', expr, property);
marker.end();
marker.apply(expr);
}
@@ -3474,6 +3555,23 @@ parseStatement: true, parseSourceElement: true */
return expr;
}
+ function filterGroup(node) {
+ var n, i, entry;
+
+ n = (Object.prototype.toString.apply(node) === '[object Array]') ? [] : {};
+ for (i in node) {
+ if (node.hasOwnProperty(i) && i !== 'groupRange' && i !== 'groupLoc') {
+ entry = node[i];
+ if (entry === null || typeof entry !== 'object' || entry instanceof RegExp) {
+ n[i] = entry;
+ } else {
+ n[i] = filterGroup(entry);
+ }
+ }
+ }
+ return n;
+ }
+
function wrapTrackingFunction(range, loc) {
return function (parseFunction) {
@@ -3484,6 +3582,8 @@ parseStatement: true, parseSourceElement: true */
}
function visit(node) {
+ var start, end;
+
if (isBinary(node.left)) {
visit(node.left);
}
@@ -3491,14 +3591,31 @@ parseStatement: true, parseSourceElement: true */
visit(node.right);
}
- if (range && typeof node.range === 'undefined') {
- node.range = [node.left.range[0], node.right.range[1]];
+ if (range) {
+ if (node.left.groupRange || node.right.groupRange) {
+ start = node.left.groupRange ? node.left.groupRange[0] : node.left.range[0];
+ end = node.right.groupRange ? node.right.groupRange[1] : node.right.range[1];
+ node.range = [start, end];
+ } else if (typeof node.range === 'undefined') {
+ start = node.left.range[0];
+ end = node.right.range[1];
+ node.range = [start, end];
+ }
}
- if (loc && typeof node.loc === 'undefined') {
- node.loc = {
- start: node.left.loc.start,
- end: node.right.loc.end
- };
+ if (loc) {
+ if (node.left.groupLoc || node.right.groupLoc) {
+ start = node.left.groupLoc ? node.left.groupLoc.start : node.left.loc.start;
+ end = node.right.groupLoc ? node.right.groupLoc.end : node.right.loc.end;
+ node.loc = {
+ start: start,
+ end: end
+ };
+ } else if (typeof node.loc === 'undefined') {
+ node.loc = {
+ start: node.left.loc.start,
+ end: node.right.loc.end
+ };
+ }
}
}
@@ -3537,41 +3654,29 @@ parseStatement: true, parseSourceElement: true */
skipComment = scanComment;
}
- if (extra.raw) {
- extra.createLiteral = createLiteral;
- createLiteral = createRawLiteral;
- }
-
if (extra.range || extra.loc) {
+ extra.parseGroupExpression = parseGroupExpression;
extra.parseLeftHandSideExpression = parseLeftHandSideExpression;
extra.parseLeftHandSideExpressionAllowCall = parseLeftHandSideExpressionAllowCall;
+ parseGroupExpression = trackGroupExpression;
parseLeftHandSideExpression = trackLeftHandSideExpression;
parseLeftHandSideExpressionAllowCall = trackLeftHandSideExpressionAllowCall;
wrapTracking = wrapTrackingFunction(extra.range, extra.loc);
- extra.parseAdditiveExpression = parseAdditiveExpression;
extra.parseAssignmentExpression = parseAssignmentExpression;
- extra.parseBitwiseANDExpression = parseBitwiseANDExpression;
- extra.parseBitwiseORExpression = parseBitwiseORExpression;
- extra.parseBitwiseXORExpression = parseBitwiseXORExpression;
+ extra.parseBinaryExpression = parseBinaryExpression;
extra.parseBlock = parseBlock;
extra.parseFunctionSourceElements = parseFunctionSourceElements;
extra.parseCatchClause = parseCatchClause;
extra.parseComputedMember = parseComputedMember;
extra.parseConditionalExpression = parseConditionalExpression;
extra.parseConstLetDeclaration = parseConstLetDeclaration;
- extra.parseEqualityExpression = parseEqualityExpression;
extra.parseExpression = parseExpression;
extra.parseForVariableDeclaration = parseForVariableDeclaration;
extra.parseFunctionDeclaration = parseFunctionDeclaration;
extra.parseFunctionExpression = parseFunctionExpression;
- extra.parseLeftHandSideExpression = parseLeftHandSideExpression;
- extra.parseLeftHandSideExpressionAllowCall = parseLeftHandSideExpressionAllowCall;
- extra.parseLogicalANDExpression = parseLogicalANDExpression;
- extra.parseLogicalORExpression = parseLogicalORExpression;
- extra.parseMultiplicativeExpression = parseMultiplicativeExpression;
extra.parseNewExpression = parseNewExpression;
extra.parseNonComputedProperty = parseNonComputedProperty;
extra.parseObjectProperty = parseObjectProperty;
@@ -3580,35 +3685,25 @@ parseStatement: true, parseSourceElement: true */
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);
+ parseBinaryExpression = wrapTracking(extra.parseBinaryExpression);
parseBlock = wrapTracking(extra.parseBlock);
parseFunctionSourceElements = wrapTracking(extra.parseFunctionSourceElements);
parseCatchClause = wrapTracking(extra.parseCatchClause);
parseComputedMember = wrapTracking(extra.parseComputedMember);
parseConditionalExpression = wrapTracking(extra.parseConditionalExpression);
parseConstLetDeclaration = wrapTracking(extra.parseConstLetDeclaration);
- parseEqualityExpression = wrapTracking(extra.parseEqualityExpression);
parseExpression = wrapTracking(extra.parseExpression);
parseForVariableDeclaration = wrapTracking(extra.parseForVariableDeclaration);
parseFunctionDeclaration = wrapTracking(extra.parseFunctionDeclaration);
parseFunctionExpression = wrapTracking(extra.parseFunctionExpression);
parseLeftHandSideExpression = wrapTracking(parseLeftHandSideExpression);
- parseLeftHandSideExpressionAllowCall = wrapTracking(parseLeftHandSideExpressionAllowCall);
- parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression);
- parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression);
- parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression);
parseNewExpression = wrapTracking(extra.parseNewExpression);
parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty);
parseObjectProperty = wrapTracking(extra.parseObjectProperty);
@@ -3617,9 +3712,7 @@ parseStatement: true, parseSourceElement: true */
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);
@@ -3640,32 +3733,22 @@ parseStatement: true, parseSourceElement: true */
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;
+ parseBinaryExpression = extra.parseBinaryExpression;
parseBlock = extra.parseBlock;
parseFunctionSourceElements = extra.parseFunctionSourceElements;
parseCatchClause = extra.parseCatchClause;
parseComputedMember = extra.parseComputedMember;
parseConditionalExpression = extra.parseConditionalExpression;
parseConstLetDeclaration = extra.parseConstLetDeclaration;
- parseEqualityExpression = extra.parseEqualityExpression;
parseExpression = extra.parseExpression;
parseForVariableDeclaration = extra.parseForVariableDeclaration;
parseFunctionDeclaration = extra.parseFunctionDeclaration;
parseFunctionExpression = extra.parseFunctionExpression;
+ parseGroupExpression = extra.parseGroupExpression;
parseLeftHandSideExpression = extra.parseLeftHandSideExpression;
parseLeftHandSideExpressionAllowCall = extra.parseLeftHandSideExpressionAllowCall;
- parseLogicalANDExpression = extra.parseLogicalANDExpression;
- parseLogicalORExpression = extra.parseLogicalORExpression;
- parseMultiplicativeExpression = extra.parseMultiplicativeExpression;
parseNewExpression = extra.parseNewExpression;
parseNonComputedProperty = extra.parseNonComputedProperty;
parseObjectProperty = extra.parseObjectProperty;
@@ -3674,9 +3757,7 @@ parseStatement: true, parseSourceElement: true */
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;
@@ -3699,6 +3780,26 @@ parseStatement: true, parseSourceElement: true */
return result;
}
+ // This is used to modify the delegate.
+
+ function extend(object, properties) {
+ var entry, result = {};
+
+ for (entry in object) {
+ if (object.hasOwnProperty(entry)) {
+ result[entry] = object[entry];
+ }
+ }
+
+ for (entry in properties) {
+ if (properties.hasOwnProperty(entry)) {
+ result[entry] = properties[entry];
+ }
+ }
+
+ return result;
+ }
+
function parse(code, options) {
var program, toString;
@@ -3707,6 +3808,7 @@ parseStatement: true, parseSourceElement: true */
code = toString(code);
}
+ delegate = SyntaxTreeDelegate;
source = code;
index = 0;
lineNumber = (source.length > 0) ? 1 : 0;
@@ -3716,7 +3818,6 @@ parseStatement: true, parseSourceElement: true */
state = {
allowIn: true,
labelSet: {},
- lastParenthesized: null,
inFunctionBody: false,
inIteration: false,
inSwitch: false
@@ -3726,7 +3827,19 @@ parseStatement: true, parseSourceElement: true */
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.raw === 'boolean') && options.raw) {
+ delegate = extend(delegate, {
+ 'createLiteral': function (token) {
+ return {
+ type: Syntax.Literal,
+ value: token.value,
+ raw: sliceSource(token.range[0], token.range[1])
+ };
+ }
+ });
+ }
+
if (typeof options.tokens === 'boolean' && options.tokens) {
extra.tokens = [];
}
@@ -3768,6 +3881,9 @@ parseStatement: true, parseSourceElement: true */
if (typeof extra.errors !== 'undefined') {
program.errors = extra.errors;
}
+ if (extra.range || extra.loc) {
+ program.body = filterGroup(program.body);
+ }
} catch (e) {
throw e;
} finally {
@@ -3779,7 +3895,7 @@ parseStatement: true, parseSourceElement: true */
}
// Sync with package.json.
- exports.version = '1.0.0-dev';
+ exports.version = '1.1.0-dev';
exports.parse = parse;
diff --git a/test/compare/uglifyjs2.js b/test/compare/uglifyjs2.js
new file mode 100644
index 0000000..f02305e
--- /dev/null
+++ b/test/compare/uglifyjs2.js
@@ -0,0 +1,2494 @@
+/***********************************************************************
+
+ A JavaScript tokenizer / parser / beautifier / compressor.
+ https://github.com/mishoo/UglifyJS2
+
+ -------------------------------- (C) ---------------------------------
+
+ Author: Mihai Bazon
+ <mihai.bazon at gmail.com>
+ http://mihai.bazon.net/blog
+
+ Distributed under the BSD license:
+
+ Copyright 2012 (c) Mihai Bazon <mihai.bazon at gmail.com>
+ Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/).
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ ***********************************************************************/
+(function(exports){
+"use strict";
+
+function array_to_hash(a) {
+ var ret = Object.create(null);
+ for (var i = 0; i < a.length; ++i)
+ ret[a[i]] = true;
+ return ret;
+};
+
+function slice(a, start) {
+ return Array.prototype.slice.call(a, start || 0);
+};
+
+function characters(str) {
+ return str.split("");
+};
+
+function member(name, array) {
+ for (var i = array.length; --i >= 0;)
+ if (array[i] == name)
+ return true;
+ return false;
+};
+
+function find_if(func, array) {
+ for (var i = 0, n = array.length; i < n; ++i) {
+ if (func(array[i]))
+ return array[i];
+ }
+};
+
+function repeat_string(str, i) {
+ if (i <= 0) return "";
+ if (i == 1) return str;
+ var d = repeat_string(str, i >> 1);
+ d += d;
+ if (i & 1) d += str;
+ return d;
+};
+
+function DefaultsError(msg, defs) {
+ this.msg = msg;
+ this.defs = defs;
+};
+
+function defaults(args, defs, croak) {
+ if (args === true)
+ args = {};
+ var ret = args || {};
+ if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
+ throw new DefaultsError("`" + i + "` is not a supported option", defs);
+ for (var i in defs) if (defs.hasOwnProperty(i)) {
+ ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
+ }
+ return ret;
+};
+
+function merge(obj, ext) {
+ for (var i in ext) if (ext.hasOwnProperty(i)) {
+ obj[i] = ext[i];
+ }
+ return obj;
+};
+
+function noop() {};
+
+var MAP = (function(){
+ function MAP(a, f, backwards) {
+ var ret = [], top = [], i;
+ function doit() {
+ var val = f(a[i], i);
+ var is_last = val instanceof Last;
+ if (is_last) val = val.v;
+ if (val instanceof AtTop) {
+ val = val.v;
+ if (val instanceof Splice) {
+ top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
+ } else {
+ top.push(val);
+ }
+ }
+ else if (val !== skip) {
+ if (val instanceof Splice) {
+ ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
+ } else {
+ ret.push(val);
+ }
+ }
+ return is_last;
+ };
+ if (a instanceof Array) {
+ if (backwards) {
+ for (i = a.length; --i >= 0;) if (doit()) break;
+ ret.reverse();
+ top.reverse();
+ } else {
+ for (i = 0; i < a.length; ++i) if (doit()) break;
+ }
+ }
+ else {
+ for (i in a) if (a.hasOwnProperty(i)) if (doit()) break;
+ }
+ return top.concat(ret);
+ };
+ MAP.at_top = function(val) { return new AtTop(val) };
+ MAP.splice = function(val) { return new Splice(val) };
+ MAP.last = function(val) { return new Last(val) };
+ var skip = MAP.skip = {};
+ function AtTop(val) { this.v = val };
+ function Splice(val) { this.v = val };
+ function Last(val) { this.v = val };
+ return MAP;
+})();
+
+function push_uniq(array, el) {
+ if (array.indexOf(el) < 0)
+ array.push(el);
+};
+
+function string_template(text, props) {
+ return text.replace(/\{(.+?)\}/g, function(str, p){
+ return props[p];
+ });
+};
+
+function mergeSort(array, cmp) {
+ if (array.length < 2) return array.slice();
+ function merge(a, b) {
+ var r = [], ai = 0, bi = 0, i = 0;
+ while (ai < a.length && bi < b.length) {
+ cmp(a[ai], b[bi]) <= 0
+ ? r[i++] = a[ai++]
+ : r[i++] = b[bi++];
+ }
+ if (ai < a.length) r.push.apply(r, a.slice(ai));
+ if (bi < b.length) r.push.apply(r, b.slice(bi));
+ return r;
+ };
+ function _ms(a) {
+ if (a.length <= 1)
+ return a;
+ var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m);
+ left = _ms(left);
+ right = _ms(right);
+ return merge(left, right);
+ };
+ return _ms(array);
+};
+
+function set_difference(a, b) {
+ return a.filter(function(el){
+ return b.indexOf(el) < 0;
+ });
+};
+
+function set_intersection(a, b) {
+ return a.filter(function(el){
+ return b.indexOf(el) >= 0;
+ });
+};
+
+// this function is taken from Acorn [1], written by Marijn Haverbeke
+// [1] https://github.com/marijnh/acorn
+function makePredicate(words) {
+ if (!(words instanceof Array)) words = words.split(" ");
+ var f = "", cats = [];
+ out: for (var i = 0; i < words.length; ++i) {
+ for (var j = 0; j < cats.length; ++j)
+ if (cats[j][0].length == words[i].length) {
+ cats[j].push(words[i]);
+ continue out;
+ }
+ cats.push([words[i]]);
+ }
+ function compareTo(arr) {
+ if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
+ f += "switch(str){";
+ for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
+ f += "return true}return false;";
+ }
+ // When there are more than three length categories, an outer
+ // switch first dispatches on the lengths, to save on comparisons.
+ if (cats.length > 3) {
+ cats.sort(function(a, b) {return b.length - a.length;});
+ f += "switch(str.length){";
+ for (var i = 0; i < cats.length; ++i) {
+ var cat = cats[i];
+ f += "case " + cat[0].length + ":";
+ compareTo(cat);
+ }
+ f += "}";
+ // Otherwise, simply generate a flat `switch` statement.
+ } else {
+ compareTo(words);
+ }
+ return new Function("str", f);
+};
+
+function DEFNODE(type, props, methods, base) {
+ if (arguments.length < 4) base = AST_Node;
+ if (!props) props = [];
+ else props = props.split(/\s+/);
+ var self_props = props;
+ if (base && base.PROPS)
+ props = props.concat(base.PROPS);
+ var code = "return function AST_" + type + "(props){ if (props) { ";
+ for (var i = props.length; --i >= 0;) {
+ code += "this." + props[i] + " = props." + props[i] + ";";
+ }
+ var proto = base && new base;
+ if (proto && proto.initialize || (methods && methods.initialize))
+ code += "this.initialize();";
+ code += "}}";
+ var ctor = new Function(code)();
+ if (proto) {
+ ctor.prototype = proto;
+ ctor.BASE = base;
+ }
+ if (base) base.SUBCLASSES.push(ctor);
+ ctor.prototype.CTOR = ctor;
+ ctor.PROPS = props || null;
+ ctor.SELF_PROPS = self_props;
+ ctor.SUBCLASSES = [];
+ if (type) {
+ ctor.prototype.TYPE = ctor.TYPE = type;
+ }
+ if (methods) for (i in methods) if (methods.hasOwnProperty(i)) {
+ if (/^\$/.test(i)) {
+ ctor[i.substr(1)] = methods[i];
+ } else {
+ ctor.prototype[i] = methods[i];
+ }
+ }
+ ctor.DEFMETHOD = function(name, method) {
+ this.prototype[name] = method;
+ };
+ return ctor;
+};
+
+var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", {
+}, null);
+
+var AST_Node = DEFNODE("Node", "start end", {
+ clone: function() {
+ return new this.CTOR(this);
+ },
+ $documentation: "Base class of all AST nodes",
+ $propdoc: {
+ start: "[AST_Token] The first token of this node",
+ end: "[AST_Token] The last token of this node"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this);
+ },
+ walk: function(visitor) {
+ return this._walk(visitor); // not sure the indirection will be any help
+ }
+}, null);
+
+AST_Node.warn_function = null;
+AST_Node.warn = function(txt, props) {
+ if (AST_Node.warn_function)
+ AST_Node.warn_function(string_template(txt, props));
+};
+
+/* -----[ statements ]----- */
+
+var AST_Statement = DEFNODE("Statement", null, {
+ $documentation: "Base class of all statements",
+});
+
+var AST_Debugger = DEFNODE("Debugger", null, {
+ $documentation: "Represents a debugger statement",
+}, AST_Statement);
+
+var AST_Directive = DEFNODE("Directive", "value scope", {
+ $documentation: "Represents a directive, like \"use strict\";",
+ $propdoc: {
+ value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
+ scope: "[AST_Scope/S] The scope that this directive affects"
+ },
+}, AST_Statement);
+
+var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
+ $documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
+ $propdoc: {
+ body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.body._walk(visitor);
+ });
+ }
+}, AST_Statement);
+
+function walk_body(node, visitor) {
+ if (node.body instanceof AST_Statement) {
+ node.body._walk(visitor);
+ }
+ else node.body.forEach(function(stat){
+ stat._walk(visitor);
+ });
+};
+
+var AST_Block = DEFNODE("Block", "body", {
+ $documentation: "A body of statements (usually bracketed)",
+ $propdoc: {
+ body: "[AST_Statement*] an array of statements"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ walk_body(this, visitor);
+ });
+ }
+}, AST_Statement);
+
+var AST_BlockStatement = DEFNODE("BlockStatement", null, {
+ $documentation: "A block statement",
+}, AST_Block);
+
+var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
+ $documentation: "The empty statement (empty block or simply a semicolon)",
+ _walk: function(visitor) {
+ return visitor._visit(this);
+ }
+}, AST_Statement);
+
+var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
+ $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
+ $propdoc: {
+ body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.body._walk(visitor);
+ });
+ }
+}, AST_Statement);
+
+var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
+ $documentation: "Statement with a label",
+ $propdoc: {
+ label: "[AST_Label] a label definition"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.label._walk(visitor);
+ this.body._walk(visitor);
+ });
+ }
+}, AST_StatementWithBody);
+
+var AST_DWLoop = DEFNODE("DWLoop", "condition", {
+ $documentation: "Base class for do/while statements",
+ $propdoc: {
+ condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.condition._walk(visitor);
+ this.body._walk(visitor);
+ });
+ }
+}, AST_StatementWithBody);
+
+var AST_Do = DEFNODE("Do", null, {
+ $documentation: "A `do` statement",
+}, AST_DWLoop);
+
+var AST_While = DEFNODE("While", null, {
+ $documentation: "A `while` statement",
+}, AST_DWLoop);
+
+var AST_For = DEFNODE("For", "init condition step", {
+ $documentation: "A `for` statement",
+ $propdoc: {
+ init: "[AST_Node?] the `for` initialization code, or null if empty",
+ condition: "[AST_Node?] the `for` termination clause, or null if empty",
+ step: "[AST_Node?] the `for` update clause, or null if empty"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ if (this.init) this.init._walk(visitor);
+ if (this.condition) this.condition._walk(visitor);
+ if (this.step) this.step._walk(visitor);
+ this.body._walk(visitor);
+ });
+ }
+}, AST_StatementWithBody);
+
+var AST_ForIn = DEFNODE("ForIn", "init name object", {
+ $documentation: "A `for ... in` statement",
+ $propdoc: {
+ init: "[AST_Node] the `for/in` initialization code",
+ name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
+ object: "[AST_Node] the object that we're looping through"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.init._walk(visitor);
+ this.object._walk(visitor);
+ this.body._walk(visitor);
+ });
+ }
+}, AST_StatementWithBody);
+
+var AST_With = DEFNODE("With", "expression", {
+ $documentation: "A `with` statement",
+ $propdoc: {
+ expression: "[AST_Node] the `with` expression"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.expression._walk(visitor);
+ this.body._walk(visitor);
+ });
+ }
+}, AST_StatementWithBody);
+
+/* -----[ scope and functions ]----- */
+
+var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
+ $documentation: "Base class for all statements introducing a lexical scope",
+ $propdoc: {
+ directives: "[string*/S] an array of directives declared in this scope",
+ variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
+ functions: "[Object/S] like `variables`, but only lists function declarations",
+ uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
+ uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
+ parent_scope: "[AST_Scope?/S] link to the parent scope",
+ enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
+ cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
+ },
+}, AST_Block);
+
+var AST_Toplevel = DEFNODE("Toplevel", "globals", {
+ $documentation: "The toplevel scope",
+ $propdoc: {
+ globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
+ },
+ wrap_commonjs: function(name, export_all) {
+ var self = this;
+ if (export_all) {
+ self.figure_out_scope();
+ var to_export = [];
+ self.walk(new TreeWalker(function(node){
+ if (node instanceof AST_SymbolDeclaration && node.definition().global) {
+ if (!find_if(function(n){ return n.name == node.name }, to_export))
+ to_export.push(node);
+ }
+ }));
+ }
+ var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))";
+ wrapped_tl = parse(wrapped_tl);
+ wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
+ if (node instanceof AST_SimpleStatement) {
+ node = node.body;
+ if (node instanceof AST_String) switch (node.getValue()) {
+ case "$ORIG":
+ return MAP.splice(self.body);
+ case "$EXPORTS":
+ var body = [];
+ to_export.forEach(function(sym){
+ body.push(new AST_SimpleStatement({
+ body: new AST_Assign({
+ left: new AST_Sub({
+ expression: new AST_SymbolRef({ name: "exports" }),
+ property: new AST_String({ value: sym.name }),
+ }),
+ operator: "=",
+ right: new AST_SymbolRef(sym),
+ }),
+ }));
+ });
+ return MAP.splice(body);
+ }
+ }
+ }));
+ return wrapped_tl;
+ }
+}, AST_Scope);
+
+var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
+ $documentation: "Base class for functions",
+ $propdoc: {
+ name: "[AST_SymbolDeclaration?] the name of this function",
+ argnames: "[AST_SymbolFunarg*] array of function arguments",
+ uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ if (this.name) this.name._walk(visitor);
+ this.argnames.forEach(function(arg){
+ arg._walk(visitor);
+ });
+ walk_body(this, visitor);
+ });
+ }
+}, AST_Scope);
+
+var AST_Function = DEFNODE("Function", null, {
+ $documentation: "A function expression"
+}, AST_Lambda);
+
+var AST_Defun = DEFNODE("Defun", null, {
+ $documentation: "A function definition"
+}, AST_Lambda);
+
+/* -----[ JUMPS ]----- */
+
+var AST_Jump = DEFNODE("Jump", null, {
+ $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
+}, AST_Statement);
+
+var AST_Exit = DEFNODE("Exit", "value", {
+ $documentation: "Base class for “exits” (`return` and `throw`)",
+ $propdoc: {
+ value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, this.value && function(){
+ this.value._walk(visitor);
+ });
+ }
+}, AST_Jump);
+
+var AST_Return = DEFNODE("Return", null, {
+ $documentation: "A `return` statement"
+}, AST_Exit);
+
+var AST_Throw = DEFNODE("Throw", null, {
+ $documentation: "A `throw` statement"
+}, AST_Exit);
+
+var AST_LoopControl = DEFNODE("LoopControl", "label", {
+ $documentation: "Base class for loop control statements (`break` and `continue`)",
+ $propdoc: {
+ label: "[AST_LabelRef?] the label, or null if none",
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, this.label && function(){
+ this.label._walk(visitor);
+ });
+ }
+}, AST_Jump);
+
+var AST_Break = DEFNODE("Break", null, {
+ $documentation: "A `break` statement"
+}, AST_LoopControl);
+
+var AST_Continue = DEFNODE("Continue", null, {
+ $documentation: "A `continue` statement"
+}, AST_LoopControl);
+
+/* -----[ IF ]----- */
+
+var AST_If = DEFNODE("If", "condition alternative", {
+ $documentation: "A `if` statement",
+ $propdoc: {
+ condition: "[AST_Node] the `if` condition",
+ alternative: "[AST_Statement?] the `else` part, or null if not present"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.condition._walk(visitor);
+ this.body._walk(visitor);
+ if (this.alternative) this.alternative._walk(visitor);
+ });
+ }
+}, AST_StatementWithBody);
+
+/* -----[ SWITCH ]----- */
+
+var AST_Switch = DEFNODE("Switch", "expression", {
+ $documentation: "A `switch` statement",
+ $propdoc: {
+ expression: "[AST_Node] the `switch` “discriminant”"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.expression._walk(visitor);
+ walk_body(this, visitor);
+ });
+ }
+}, AST_Block);
+
+var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
+ $documentation: "Base class for `switch` branches",
+}, AST_Block);
+
+var AST_Default = DEFNODE("Default", null, {
+ $documentation: "A `default` switch branch",
+}, AST_SwitchBranch);
+
+var AST_Case = DEFNODE("Case", "expression", {
+ $documentation: "A `case` switch branch",
+ $propdoc: {
+ expression: "[AST_Node] the `case` expression"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.expression._walk(visitor);
+ walk_body(this, visitor);
+ });
+ }
+}, AST_SwitchBranch);
+
+/* -----[ EXCEPTIONS ]----- */
+
+var AST_Try = DEFNODE("Try", "bcatch bfinally", {
+ $documentation: "A `try` statement",
+ $propdoc: {
+ bcatch: "[AST_Catch?] the catch block, or null if not present",
+ bfinally: "[AST_Finally?] the finally block, or null if not present"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ walk_body(this, visitor);
+ if (this.bcatch) this.bcatch._walk(visitor);
+ if (this.bfinally) this.bfinally._walk(visitor);
+ });
+ }
+}, AST_Block);
+
+// XXX: this is wrong according to ECMA-262 (12.4). the catch block
+// should introduce another scope, as the argname should be visible
+// only inside the catch block. However, doing it this way because of
+// IE which simply introduces the name in the surrounding scope. If
+// we ever want to fix this then AST_Catch should inherit from
+// AST_Scope.
+var AST_Catch = DEFNODE("Catch", "argname", {
+ $documentation: "A `catch` node; only makes sense as part of a `try` statement",
+ $propdoc: {
+ argname: "[AST_SymbolCatch] symbol for the exception"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.argname._walk(visitor);
+ walk_body(this, visitor);
+ });
+ }
+}, AST_Block);
+
+var AST_Finally = DEFNODE("Finally", null, {
+ $documentation: "A `finally` node; only makes sense as part of a `try` statement"
+}, AST_Block);
+
+/* -----[ VAR/CONST ]----- */
+
+var AST_Definitions = DEFNODE("Definitions", "definitions", {
+ $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
+ $propdoc: {
+ definitions: "[AST_VarDef*] array of variable definitions"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.definitions.forEach(function(def){
+ def._walk(visitor);
+ });
+ });
+ }
+}, AST_Statement);
+
+var AST_Var = DEFNODE("Var", null, {
+ $documentation: "A `var` statement"
+}, AST_Definitions);
+
+var AST_Const = DEFNODE("Const", null, {
+ $documentation: "A `const` statement"
+}, AST_Definitions);
+
+var AST_VarDef = DEFNODE("VarDef", "name value", {
+ $documentation: "A variable declaration; only appears in a AST_Definitions node",
+ $propdoc: {
+ name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
+ value: "[AST_Node?] initializer, or null of there's no initializer"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.name._walk(visitor);
+ if (this.value) this.value._walk(visitor);
+ });
+ }
+});
+
+/* -----[ OTHER ]----- */
+
+var AST_Call = DEFNODE("Call", "expression args", {
+ $documentation: "A function call expression",
+ $propdoc: {
+ expression: "[AST_Node] expression to invoke as function",
+ args: "[AST_Node*] array of arguments"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.expression._walk(visitor);
+ this.args.forEach(function(arg){
+ arg._walk(visitor);
+ });
+ });
+ }
+});
+
+var AST_New = DEFNODE("New", null, {
+ $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties"
+}, AST_Call);
+
+var AST_Seq = DEFNODE("Seq", "car cdr", {
+ $documentation: "A sequence expression (two comma-separated expressions)",
+ $propdoc: {
+ car: "[AST_Node] first element in sequence",
+ cdr: "[AST_Node] second element in sequence"
+ },
+ $cons: function(x, y) {
+ var seq = new AST_Seq(x);
+ seq.car = x;
+ seq.cdr = y;
+ return seq;
+ },
+ $from_array: function(array) {
+ if (array.length == 0) return null;
+ if (array.length == 1) return array[0].clone();
+ var list = null;
+ for (var i = array.length; --i >= 0;) {
+ list = AST_Seq.cons(array[i], list);
+ }
+ var p = list;
+ while (p) {
+ if (p.cdr && !p.cdr.cdr) {
+ p.cdr = p.cdr.car;
+ break;
+ }
+ p = p.cdr;
+ }
+ return list;
+ },
+ add: function(node) {
+ var p = this;
+ while (p) {
+ if (!(p.cdr instanceof AST_Seq)) {
+ var cell = AST_Seq.cons(p.cdr, node);
+ return p.cdr = cell;
+ }
+ p = p.cdr;
+ }
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.car._walk(visitor);
+ if (this.cdr) this.cdr._walk(visitor);
+ });
+ }
+});
+
+var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
+ $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
+ $propdoc: {
+ expression: "[AST_Node] the “container” expression",
+ property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node"
+ }
+});
+
+var AST_Dot = DEFNODE("Dot", null, {
+ $documentation: "A dotted property access expression",
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.expression._walk(visitor);
+ });
+ }
+}, AST_PropAccess);
+
+var AST_Sub = DEFNODE("Sub", null, {
+ $documentation: "Index-style property access, i.e. `a[\"foo\"]`",
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.expression._walk(visitor);
+ this.property._walk(visitor);
+ });
+ }
+}, AST_PropAccess);
+
+var AST_Unary = DEFNODE("Unary", "operator expression", {
+ $documentation: "Base class for unary expressions",
+ $propdoc: {
+ operator: "[string] the operator",
+ expression: "[AST_Node] expression that this unary operator applies to"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.expression._walk(visitor);
+ });
+ }
+});
+
+var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
+ $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`"
+}, AST_Unary);
+
+var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
+ $documentation: "Unary postfix expression, i.e. `i++`"
+}, AST_Unary);
+
+var AST_Binary = DEFNODE("Binary", "left operator right", {
+ $documentation: "Binary expression, i.e. `a + b`",
+ $propdoc: {
+ left: "[AST_Node] left-hand side expression",
+ operator: "[string] the operator",
+ right: "[AST_Node] right-hand side expression"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.left._walk(visitor);
+ this.right._walk(visitor);
+ });
+ }
+});
+
+var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
+ $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
+ $propdoc: {
+ condition: "[AST_Node]",
+ consequent: "[AST_Node]",
+ alternative: "[AST_Node]"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.condition._walk(visitor);
+ this.consequent._walk(visitor);
+ this.alternative._walk(visitor);
+ });
+ }
+});
+
+var AST_Assign = DEFNODE("Assign", null, {
+ $documentation: "An assignment expression — `a = b + 5`",
+}, AST_Binary);
+
+/* -----[ LITERALS ]----- */
+
+var AST_Array = DEFNODE("Array", "elements", {
+ $documentation: "An array literal",
+ $propdoc: {
+ elements: "[AST_Node*] array of elements"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.elements.forEach(function(el){
+ el._walk(visitor);
+ });
+ });
+ }
+});
+
+var AST_Object = DEFNODE("Object", "properties", {
+ $documentation: "An object literal",
+ $propdoc: {
+ properties: "[AST_ObjectProperty*] array of properties"
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.properties.forEach(function(prop){
+ prop._walk(visitor);
+ });
+ });
+ }
+});
+
+var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
+ $documentation: "Base class for literal object properties",
+ $propdoc: {
+ key: "[string] the property name; it's always a plain string in our AST, no matter if it was a string, number or identifier in original code",
+ value: "[AST_Node] property value. For setters and getters this is an AST_Function."
+ },
+ _walk: function(visitor) {
+ return visitor._visit(this, function(){
+ this.value._walk(visitor);
+ });
+ }
+});
+
+var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
+ $documentation: "A key: value object property",
+}, AST_ObjectProperty);
+
+var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
+ $documentation: "An object setter property",
+}, AST_ObjectProperty);
+
+var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
+ $documentation: "An object getter property",
+}, AST_ObjectProperty);
+
+var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
+ $propdoc: {
+ name: "[string] name of this symbol",
+ scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
+ thedef: "[SymbolDef/S] the definition of this symbol"
+ },
+ $documentation: "Base class for all symbols",
+});
+
+var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
+ $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
+ $propdoc: {
+ init: "[AST_Node*/S] array of initializers for this declaration."
+ }
+}, AST_Symbol);
+
+var AST_SymbolVar = DEFNODE("SymbolVar", null, {
+ $documentation: "Symbol defining a variable",
+}, AST_SymbolDeclaration);
+
+var AST_SymbolConst = DEFNODE("SymbolConst", null, {
+ $documentation: "A constant declaration"
+}, AST_SymbolDeclaration);
+
+var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
+ $documentation: "Symbol naming a function argument",
+}, AST_SymbolVar);
+
+var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
+ $documentation: "Symbol defining a function",
+}, AST_SymbolDeclaration);
+
+var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
+ $documentation: "Symbol naming a function expression",
+}, AST_SymbolDeclaration);
+
+var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
+ $documentation: "Symbol naming the exception in catch",
+}, AST_SymbolDeclaration);
+
+var AST_Label = DEFNODE("Label", "references", {
+ $documentation: "Symbol naming a label (declaration)",
+ $propdoc: {
+ references: "[AST_LabelRef*] a list of nodes referring to this label"
+ }
+}, AST_Symbol);
+
+var AST_SymbolRef = DEFNODE("SymbolRef", null, {
+ $documentation: "Reference to some symbol (not definition/declaration)",
+}, AST_Symbol);
+
+var AST_LabelRef = DEFNODE("LabelRef", null, {
+ $documentation: "Reference to a label symbol",
+}, AST_SymbolRef);
+
+var AST_This = DEFNODE("This", null, {
+ $documentation: "The `this` symbol",
+}, AST_Symbol);
+
+var AST_Constant = DEFNODE("Constant", null, {
+ $documentation: "Base class for all constants",
+ getValue: function() {
+ return this.value;
+ }
+});
+
+var AST_String = DEFNODE("String", "value", {
+ $documentation: "A string literal",
+ $propdoc: {
+ value: "[string] the contents of this string"
+ }
+}, AST_Constant);
+
+var AST_Number = DEFNODE("Number", "value", {
+ $documentation: "A number literal",
+ $propdoc: {
+ value: "[number] the numeric value"
+ }
+}, AST_Constant);
+
+var AST_RegExp = DEFNODE("RegExp", "value", {
+ $documentation: "A regexp literal",
+ $propdoc: {
+ value: "[RegExp] the actual regexp"
+ }
+}, AST_Constant);
+
+var AST_Atom = DEFNODE("Atom", null, {
+ $documentation: "Base class for atoms",
+}, AST_Constant);
+
+var AST_Null = DEFNODE("Null", null, {
+ $documentation: "The `null` atom",
+ value: null
+}, AST_Atom);
+
+var AST_NaN = DEFNODE("NaN", null, {
+ $documentation: "The impossible value",
+ value: 0/0
+}, AST_Atom);
+
+var AST_Undefined = DEFNODE("Undefined", null, {
+ $documentation: "The `undefined` value",
+ value: (function(){}())
+}, AST_Atom);
+
+var AST_Infinity = DEFNODE("Infinity", null, {
+ $documentation: "The `Infinity` value",
+ value: 1/0
+}, AST_Atom);
+
+var AST_Boolean = DEFNODE("Boolean", null, {
+ $documentation: "Base class for booleans",
+}, AST_Atom);
+
+var AST_False = DEFNODE("False", null, {
+ $documentation: "The `false` atom",
+ value: false
+}, AST_Boolean);
+
+var AST_True = DEFNODE("True", null, {
+ $documentation: "The `true` atom",
+ value: true
+}, AST_Boolean);
+
+/* -----[ TreeWalker ]----- */
+
+function TreeWalker(callback) {
+ this.visit = callback;
+ this.stack = [];
+};
+TreeWalker.prototype = {
+ _visit: function(node, descend) {
+ this.stack.push(node);
+ var ret = this.visit(node, descend ? function(){
+ descend.call(node);
+ } : noop);
+ if (!ret && descend) {
+ descend.call(node);
+ }
+ this.stack.pop();
+ return ret;
+ },
+ parent: function(n) {
+ return this.stack[this.stack.length - 2 - (n || 0)];
+ },
+ push: function (node) {
+ this.stack.push(node);
+ },
+ pop: function() {
+ return this.stack.pop();
+ },
+ self: function() {
+ return this.stack[this.stack.length - 1];
+ },
+ find_parent: function(type) {
+ var stack = this.stack;
+ for (var i = stack.length; --i >= 0;) {
+ var x = stack[i];
+ if (x instanceof type) return x;
+ }
+ },
+ in_boolean_context: function() {
+ var stack = this.stack;
+ var i = stack.length, self = stack[--i];
+ while (i > 0) {
+ var p = stack[--i];
+ if ((p instanceof AST_If && p.condition === self) ||
+ (p instanceof AST_Conditional && p.condition === self) ||
+ (p instanceof AST_DWLoop && p.condition === self) ||
+ (p instanceof AST_For && p.condition === self) ||
+ (p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self))
+ {
+ return true;
+ }
+ if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")))
+ return false;
+ self = p;
+ }
+ },
+ loopcontrol_target: function(label) {
+ var stack = this.stack;
+ if (label) {
+ for (var i = stack.length; --i >= 0;) {
+ var x = stack[i];
+ if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
+ return x.body;
+ }
+ }
+ } else {
+ for (var i = stack.length; --i >= 0;) {
+ var x = stack[i];
+ if (x instanceof AST_Switch) return x;
+ if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
+ return (x.body instanceof AST_BlockStatement ? x.body : x);
+ }
+ }
+ }
+ }
+};
+
+var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with';
+var KEYWORDS_ATOM = 'false null true';
+var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile'
+ + " " + KEYWORDS_ATOM + " " + KEYWORDS;
+var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
+
+KEYWORDS = makePredicate(KEYWORDS);
+RESERVED_WORDS = makePredicate(RESERVED_WORDS);
+KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION);
+KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
+
+var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
+
+var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
+var RE_OCT_NUMBER = /^0[0-7]+$/;
+var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
+
+var OPERATORS = makePredicate([
+ "in",
+ "instanceof",
+ "typeof",
+ "new",
+ "void",
+ "delete",
+ "++",
+ "--",
+ "+",
+ "-",
+ "!",
+ "~",
+ "&",
+ "|",
+ "^",
+ "*",
+ "/",
+ "%",
+ ">>",
+ "<<",
+ ">>>",
+ "<",
+ ">",
+ "<=",
+ ">=",
+ "==",
+ "===",
+ "!=",
+ "!==",
+ "?",
+ "=",
+ "+=",
+ "-=",
+ "/=",
+ "*=",
+ "%=",
+ ">>=",
+ "<<=",
+ ">>>=",
+ "|=",
+ "^=",
+ "&=",
+ "&&",
+ "||"
+]);
+
+var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
+
+var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
+
+var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
+
+var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
+
+/* -----[ Tokenizer ]----- */
+
+// regexps adapted from http://xregexp.com/plugins/#unicode
+var UNICODE = {
+ letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u0 [...]
+ non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0 [...]
+ space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48 [...]
+ connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
+};
+
+function is_letter(code) {
+ return (code >= 97 && code <= 122)
+ || (code >= 65 && code <= 90)
+ || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code)));
+};
+
+function is_digit(code) {
+ return code >= 48 && code <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9
+};
+
+function is_alphanumeric_char(code) {
+ return is_digit(code) || is_letter(code);
+};
+
+function is_unicode_combining_mark(ch) {
+ return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
+};
+
+function is_unicode_connector_punctuation(ch) {
+ return UNICODE.connector_punctuation.test(ch);
+};
+
+function is_identifier(name) {
+ return /^[a-z_$][a-z0-9_$]*$/i.test(name) && !RESERVED_WORDS(name);
+};
+
+function is_identifier_start(code) {
+ return code == 36 || code == 95 || is_letter(code);
+};
+
+function is_identifier_char(ch) {
+ var code = ch.charCodeAt(0);
+ return is_identifier_start(code)
+ || is_digit(code)
+ || code == 8204 // \u200c: zero-width non-joiner <ZWNJ>
+ || code == 8205 // \u200d: zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
+ || is_unicode_combining_mark(ch)
+ || is_unicode_connector_punctuation(ch)
+ ;
+};
+
+function parse_js_number(num) {
+ if (RE_HEX_NUMBER.test(num)) {
+ return parseInt(num.substr(2), 16);
+ } else if (RE_OCT_NUMBER.test(num)) {
+ return parseInt(num.substr(1), 8);
+ } else if (RE_DEC_NUMBER.test(num)) {
+ return parseFloat(num);
+ }
+};
+
+function JS_Parse_Error(message, line, col, pos) {
+ this.message = message;
+ this.line = line;
+ this.col = col;
+ this.pos = pos;
+ this.stack = new Error().stack;
+};
+
+JS_Parse_Error.prototype.toString = function() {
+ return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
+};
+
+function js_error(message, filename, line, col, pos) {
+ AST_Node.warn("ERROR: {message} [{file}:{line},{col}]", {
+ message: message,
+ file: filename,
+ line: line,
+ col: col
+ });
+ throw new JS_Parse_Error(message, line, col, pos);
+};
+
+function is_token(token, type, val) {
+ return token.type == type && (val == null || token.value == val);
+};
+
+var EX_EOF = {};
+
+function tokenizer($TEXT, filename) {
+
+ var S = {
+ text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''),
+ filename : filename,
+ pos : 0,
+ tokpos : 0,
+ line : 1,
+ tokline : 0,
+ col : 0,
+ tokcol : 0,
+ newline_before : false,
+ regex_allowed : false,
+ comments_before : []
+ };
+
+ function peek() { return S.text.charAt(S.pos); };
+
+ function next(signal_eof, in_string) {
+ var ch = S.text.charAt(S.pos++);
+ if (signal_eof && !ch)
+ throw EX_EOF;
+ if (ch == "\n") {
+ S.newline_before = S.newline_before || !in_string;
+ ++S.line;
+ S.col = 0;
+ } else {
+ ++S.col;
+ }
+ return ch;
+ };
+
+ function find(what, signal_eof) {
+ var pos = S.text.indexOf(what, S.pos);
+ if (signal_eof && pos == -1) throw EX_EOF;
+ return pos;
+ };
+
+ function start_token() {
+ S.tokline = S.line;
+ S.tokcol = S.col;
+ S.tokpos = S.pos;
+ };
+
+ function token(type, value, is_comment) {
+ S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX[value]) ||
+ (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
+ (type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
+ var ret = {
+ type : type,
+ value : value,
+ line : S.tokline,
+ col : S.tokcol,
+ pos : S.tokpos,
+ endpos : S.pos,
+ nlb : S.newline_before,
+ file : filename
+ };
+ if (!is_comment) {
+ ret.comments_before = S.comments_before;
+ S.comments_before = [];
+ // make note of any newlines in the comments that came before
+ for (var i = 0, len = ret.comments_before.length; i < len; i++) {
+ ret.nlb = ret.nlb || ret.comments_before[i].nlb;
+ }
+ }
+ S.newline_before = false;
+ return new AST_Token(ret);
+ };
+
+ function skip_whitespace() {
+ while (WHITESPACE_CHARS(peek()))
+ next();
+ };
+
+ function read_while(pred) {
+ var ret = "", ch, i = 0;
+ while ((ch = peek()) && pred(ch, i++))
+ ret += next();
+ return ret;
+ };
+
+ function parse_error(err) {
+ js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
+ };
+
+ function read_num(prefix) {
+ var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
+ var num = read_while(function(ch, i){
+ var code = ch.charCodeAt(0);
+ switch (code) {
+ case 120: case 88: // xX
+ return has_x ? false : (has_x = true);
+ case 101: case 69: // eE
+ return has_x ? true : has_e ? false : (has_e = after_e = true);
+ case 45: // -
+ return after_e || (i == 0 && !prefix);
+ case 43: // +
+ return after_e;
+ case (after_e = false, 46): // .
+ return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
+ }
+ return is_alphanumeric_char(code);
+ });
+ if (prefix) num = prefix + num;
+ var valid = parse_js_number(num);
+ if (!isNaN(valid)) {
+ return token("num", valid);
+ } else {
+ parse_error("Invalid syntax: " + num);
+ }
+ };
+
+ function read_escaped_char(in_string) {
+ var ch = next(true, in_string);
+ switch (ch.charCodeAt(0)) {
+ case 110 : return "\n";
+ case 114 : return "\r";
+ case 116 : return "\t";
+ case 98 : return "\b";
+ case 118 : return "\u000b"; // \v
+ case 102 : return "\f";
+ case 48 : return "\0";
+ case 120 : return String.fromCharCode(hex_bytes(2)); // \x
+ case 117 : return String.fromCharCode(hex_bytes(4)); // \u
+ case 10 : return ""; // newline
+ default : return ch;
+ }
+ };
+
+ function hex_bytes(n) {
+ var num = 0;
+ for (; n > 0; --n) {
+ var digit = parseInt(next(true), 16);
+ if (isNaN(digit))
+ parse_error("Invalid hex-character pattern in string");
+ num = (num << 4) | digit;
+ }
+ return num;
+ };
+
+ var read_string = with_eof_error("Unterminated string constant", function(){
+ var quote = next(), ret = "";
+ for (;;) {
+ var ch = next(true);
+ if (ch == "\\") {
+ // read OctalEscapeSequence (XXX: deprecated if "strict mode")
+ // https://github.com/mishoo/UglifyJS/issues/178
+ var octal_len = 0, first = null;
+ ch = read_while(function(ch){
+ if (ch >= "0" && ch <= "7") {
+ if (!first) {
+ first = ch;
+ return ++octal_len;
+ }
+ else if (first <= "3" && octal_len <= 2) return ++octal_len;
+ else if (first >= "4" && octal_len <= 1) return ++octal_len;
+ }
+ return false;
+ });
+ if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
+ else ch = read_escaped_char(true);
+ }
+ else if (ch == quote) break;
+ ret += ch;
+ }
+ return token("string", ret);
+ });
+
+ function read_line_comment() {
+ next();
+ var i = find("\n"), ret;
+ if (i == -1) {
+ ret = S.text.substr(S.pos);
+ S.pos = S.text.length;
+ } else {
+ ret = S.text.substring(S.pos, i);
+ S.pos = i;
+ }
+ return token("comment1", ret, true);
+ };
+
+ var read_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
+ next();
+ var i = find("*/", true);
+ var text = S.text.substring(S.pos, i);
+ var a = text.split("\n"), n = a.length;
+ // update stream position
+ S.pos = i + 2;
+ S.line += n - 1;
+ if (n > 1) S.col = a[n - 1].length;
+ else S.col += a[n - 1].length;
+ S.col += 2;
+ S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
+ return token("comment2", text, true);
+ });
+
+ function read_name() {
+ var backslash = false, name = "", ch, escaped = false, hex;
+ while ((ch = peek()) != null) {
+ if (!backslash) {
+ if (ch == "\\") escaped = backslash = true, next();
+ else if (is_identifier_char(ch)) name += next();
+ else break;
+ }
+ else {
+ if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
+ ch = read_escaped_char();
+ if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
+ name += ch;
+ backslash = false;
+ }
+ }
+ if (KEYWORDS(name) && escaped) {
+ hex = name.charCodeAt(0).toString(16).toUpperCase();
+ name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
+ }
+ return name;
+ };
+
+ var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
+ var prev_backslash = false, ch, in_class = false;
+ while ((ch = next(true))) if (prev_backslash) {
+ regexp += "\\" + ch;
+ prev_backslash = false;
+ } else if (ch == "[") {
+ in_class = true;
+ regexp += ch;
+ } else if (ch == "]" && in_class) {
+ in_class = false;
+ regexp += ch;
+ } else if (ch == "/" && !in_class) {
+ break;
+ } else if (ch == "\\") {
+ prev_backslash = true;
+ } else {
+ regexp += ch;
+ }
+ var mods = read_name();
+ return token("regexp", new RegExp(regexp, mods));
+ });
+
+ function read_operator(prefix) {
+ function grow(op) {
+ if (!peek()) return op;
+ var bigger = op + peek();
+ if (OPERATORS(bigger)) {
+ next();
+ return grow(bigger);
+ } else {
+ return op;
+ }
+ };
+ return token("operator", grow(prefix || next()));
+ };
+
+ function handle_slash() {
+ next();
+ var regex_allowed = S.regex_allowed;
+ switch (peek()) {
+ case "/":
+ S.comments_before.push(read_line_comment());
+ S.regex_allowed = regex_allowed;
+ return next_token();
+ case "*":
+ S.comments_before.push(read_multiline_comment());
+ S.regex_allowed = regex_allowed;
+ return next_token();
+ }
+ return S.regex_allowed ? read_regexp("") : read_operator("/");
+ };
+
+ function handle_dot() {
+ next();
+ return is_digit(peek().charCodeAt(0))
+ ? read_num(".")
+ : token("punc", ".");
+ };
+
+ function read_word() {
+ var word = read_name();
+ return KEYWORDS_ATOM(word) ? token("atom", word)
+ : !KEYWORDS(word) ? token("name", word)
+ : OPERATORS(word) ? token("operator", word)
+ : token("keyword", word);
+ };
+
+ function with_eof_error(eof_error, cont) {
+ return function(x) {
+ try {
+ return cont(x);
+ } catch(ex) {
+ if (ex === EX_EOF) parse_error(eof_error);
+ else throw ex;
+ }
+ };
+ };
+
+ function next_token(force_regexp) {
+ if (force_regexp != null)
+ return read_regexp(force_regexp);
+ skip_whitespace();
+ start_token();
+ var ch = peek();
+ if (!ch) return token("eof");
+ var code = ch.charCodeAt(0);
+ switch (code) {
+ case 34: case 39: return read_string();
+ case 46: return handle_dot();
+ case 47: return handle_slash();
+ }
+ if (is_digit(code)) return read_num();
+ if (PUNC_CHARS(ch)) return token("punc", next());
+ if (OPERATOR_CHARS(ch)) return read_operator();
+ if (code == 92 || is_identifier_start(code)) return read_word();
+ parse_error("Unexpected character '" + ch + "'");
+ };
+
+ next_token.context = function(nc) {
+ if (nc) S = nc;
+ return S;
+ };
+
+ return next_token;
+
+};
+
+/* -----[ Parser (constants) ]----- */
+
+var UNARY_PREFIX = makePredicate([
+ "typeof",
+ "void",
+ "delete",
+ "--",
+ "++",
+ "!",
+ "~",
+ "-",
+ "+"
+]);
+
+var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
+
+var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
+
+var PRECEDENCE = (function(a, ret){
+ for (var i = 0, n = 1; i < a.length; ++i, ++n) {
+ var b = a[i];
+ for (var j = 0; j < b.length; ++j) {
+ ret[b[j]] = n;
+ }
+ }
+ return ret;
+})(
+ [
+ ["||"],
+ ["&&"],
+ ["|"],
+ ["^"],
+ ["&"],
+ ["==", "===", "!=", "!=="],
+ ["<", ">", "<=", ">=", "in", "instanceof"],
+ [">>", "<<", ">>>"],
+ ["+", "-"],
+ ["*", "/", "%"]
+ ],
+ {}
+);
+
+var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
+
+var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
+
+/* -----[ Parser ]----- */
+
+function parse($TEXT, options) {
+
+ options = defaults(options, {
+ strict : false,
+ filename : null,
+ toplevel : null
+ });
+
+ var S = {
+ input : typeof $TEXT == "string" ? tokenizer($TEXT, options.filename) : $TEXT,
+ token : null,
+ prev : null,
+ peeked : null,
+ in_function : 0,
+ in_directives : true,
+ in_loop : 0,
+ labels : []
+ };
+
+ S.token = next();
+
+ function is(type, value) {
+ return is_token(S.token, type, value);
+ };
+
+ function peek() { return S.peeked || (S.peeked = S.input()); };
+
+ function next() {
+ S.prev = S.token;
+ if (S.peeked) {
+ S.token = S.peeked;
+ S.peeked = null;
+ } else {
+ S.token = S.input();
+ }
+ S.in_directives = S.in_directives && (
+ S.token.type == "string" || is("punc", ";")
+ );
+ return S.token;
+ };
+
+ function prev() {
+ return S.prev;
+ };
+
+ function croak(msg, line, col, pos) {
+ var ctx = S.input.context();
+ js_error(msg,
+ ctx.filename,
+ line != null ? line : ctx.tokline,
+ col != null ? col : ctx.tokcol,
+ pos != null ? pos : ctx.tokpos);
+ };
+
+ function token_error(token, msg) {
+ croak(msg, token.line, token.col);
+ };
+
+ function unexpected(token) {
+ if (token == null)
+ token = S.token;
+ token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
+ };
+
+ function expect_token(type, val) {
+ if (is(type, val)) {
+ return next();
+ }
+ token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
+ };
+
+ function expect(punc) { return expect_token("punc", punc); };
+
+ function can_insert_semicolon() {
+ return !options.strict && (
+ S.token.nlb || is("eof") || is("punc", "}")
+ );
+ };
+
+ function semicolon() {
+ if (is("punc", ";")) next();
+ else if (!can_insert_semicolon()) unexpected();
+ };
+
+ function parenthesised() {
+ expect("(");
+ var exp = expression(true);
+ expect(")");
+ return exp;
+ };
+
+ function embed_tokens(parser) {
+ return function() {
+ var start = S.token;
+ var expr = parser();
+ var end = prev();
+ expr.start = start;
+ expr.end = end;
+ return expr;
+ };
+ };
+
+ var statement = embed_tokens(function() {
+ var tmp;
+ if (is("operator", "/") || is("operator", "/=")) {
+ S.peeked = null;
+ S.token = S.input(S.token.value.substr(1)); // force regexp
+ }
+ switch (S.token.type) {
+ case "string":
+ var dir = S.in_directives, stat = simple_statement();
+ // XXXv2: decide how to fix directives
+ if (dir && stat.body instanceof AST_String && !is("punc", ","))
+ return new AST_Directive({ value: stat.body.value });
+ return stat;
+ case "num":
+ case "regexp":
+ case "operator":
+ case "atom":
+ return simple_statement();
+
+ case "name":
+ return is_token(peek(), "punc", ":")
+ ? labeled_statement()
+ : simple_statement();
+
+ case "punc":
+ switch (S.token.value) {
+ case "{":
+ return new AST_BlockStatement({
+ start : S.token,
+ body : block_(),
+ end : prev()
+ });
+ case "[":
+ case "(":
+ return simple_statement();
+ case ";":
+ next();
+ return new AST_EmptyStatement();
+ default:
+ unexpected();
+ }
+
+ case "keyword":
+ switch (tmp = S.token.value, next(), tmp) {
+ case "break":
+ return break_cont(AST_Break);
+
+ case "continue":
+ return break_cont(AST_Continue);
+
+ case "debugger":
+ semicolon();
+ return new AST_Debugger();
+
+ case "do":
+ return new AST_Do({
+ body : in_loop(statement),
+ condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp)
+ });
+
+ case "while":
+ return new AST_While({
+ condition : parenthesised(),
+ body : in_loop(statement)
+ });
+
+ case "for":
+ return for_();
+
+ case "function":
+ return function_(true);
+
+ case "if":
+ return if_();
+
+ case "return":
+ if (S.in_function == 0)
+ croak("'return' outside of function");
+ return new AST_Return({
+ value: ( is("punc", ";")
+ ? (next(), null)
+ : can_insert_semicolon()
+ ? null
+ : (tmp = expression(true), semicolon(), tmp) )
+ });
+
+ case "switch":
+ return new AST_Switch({
+ expression : parenthesised(),
+ body : in_loop(switch_body_)
+ });
+
+ case "throw":
+ if (S.token.nlb)
+ croak("Illegal newline after 'throw'");
+ return new AST_Throw({
+ value: (tmp = expression(true), semicolon(), tmp)
+ });
+
+ case "try":
+ return try_();
+
+ case "var":
+ return tmp = var_(), semicolon(), tmp;
+
+ case "const":
+ return tmp = const_(), semicolon(), tmp;
+
+ case "with":
+ return new AST_With({
+ expression : parenthesised(),
+ body : statement()
+ });
+
+ default:
+ unexpected();
+ }
+ }
+ });
+
+ function labeled_statement() {
+ var label = as_symbol(AST_Label);
+ if (find_if(function(l){ return l.name == label.name }, S.labels)) {
+ // ECMA-262, 12.12: An ECMAScript program is considered
+ // syntactically incorrect if it contains a
+ // LabelledStatement that is enclosed by a
+ // LabelledStatement with the same Identifier as label.
+ croak("Label " + label.name + " defined twice");
+ }
+ expect(":");
+ S.labels.push(label);
+ var stat = statement();
+ S.labels.pop();
+ return new AST_LabeledStatement({ body: stat, label: label });
+ };
+
+ function simple_statement(tmp) {
+ return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) });
+ };
+
+ function break_cont(type) {
+ var label = null;
+ if (!can_insert_semicolon()) {
+ label = as_symbol(AST_LabelRef, true);
+ }
+ if (label != null) {
+ if (!find_if(function(l){ return l.name == label.name }, S.labels))
+ croak("Undefined label " + label.name);
+ }
+ else if (S.in_loop == 0)
+ croak(type.TYPE + " not inside a loop or switch");
+ semicolon();
+ return new type({ label: label });
+ };
+
+ function for_() {
+ expect("(");
+ var init = null;
+ if (!is("punc", ";")) {
+ init = is("keyword", "var")
+ ? (next(), var_(true))
+ : expression(true, true);
+ if (is("operator", "in")) {
+ if (init instanceof AST_Var && init.definitions.length > 1)
+ croak("Only one variable declaration allowed in for..in loop");
+ next();
+ return for_in(init);
+ }
+ }
+ return regular_for(init);
+ };
+
+ function regular_for(init) {
+ expect(";");
+ var test = is("punc", ";") ? null : expression(true);
+ expect(";");
+ var step = is("punc", ")") ? null : expression(true);
+ expect(")");
+ return new AST_For({
+ init : init,
+ condition : test,
+ step : step,
+ body : in_loop(statement)
+ });
+ };
+
+ function for_in(init) {
+ var lhs = init instanceof AST_Var ? init.definitions[0].name : null;
+ var obj = expression(true);
+ expect(")");
+ return new AST_ForIn({
+ init : init,
+ name : lhs,
+ object : obj,
+ body : in_loop(statement)
+ });
+ };
+
+ var function_ = function(in_statement, ctor) {
+ var name = is("name") ? as_symbol(in_statement
+ ? AST_SymbolDefun
+ : AST_SymbolLambda) : null;
+ if (in_statement && !name)
+ unexpected();
+ expect("(");
+ if (!ctor) ctor = in_statement ? AST_Defun : AST_Function;
+ return new ctor({
+ name: name,
+ argnames: (function(first, a){
+ while (!is("punc", ")")) {
+ if (first) first = false; else expect(",");
+ a.push(as_symbol(AST_SymbolFunarg));
+ }
+ next();
+ return a;
+ })(true, []),
+ body: (function(loop, labels){
+ ++S.in_function;
+ S.in_directives = true;
+ S.in_loop = 0;
+ S.labels = [];
+ var a = block_();
+ --S.in_function;
+ S.in_loop = loop;
+ S.labels = labels;
+ return a;
+ })(S.in_loop, S.labels)
+ });
+ };
+
+ function if_() {
+ var cond = parenthesised(), body = statement(), belse = null;
+ if (is("keyword", "else")) {
+ next();
+ belse = statement();
+ }
+ return new AST_If({
+ condition : cond,
+ body : body,
+ alternative : belse
+ });
+ };
+
+ function block_() {
+ expect("{");
+ var a = [];
+ while (!is("punc", "}")) {
+ if (is("eof")) unexpected();
+ a.push(statement());
+ }
+ next();
+ return a;
+ };
+
+ function switch_body_() {
+ expect("{");
+ var a = [], cur = null, branch = null, tmp;
+ while (!is("punc", "}")) {
+ if (is("eof")) unexpected();
+ if (is("keyword", "case")) {
+ if (branch) branch.end = prev();
+ cur = [];
+ branch = new AST_Case({
+ start : (tmp = S.token, next(), tmp),
+ expression : expression(true),
+ body : cur
+ });
+ a.push(branch);
+ expect(":");
+ }
+ else if (is("keyword", "default")) {
+ if (branch) branch.end = prev();
+ cur = [];
+ branch = new AST_Default({
+ start : (tmp = S.token, next(), expect(":"), tmp),
+ body : cur
+ });
+ a.push(branch);
+ }
+ else {
+ if (!cur) unexpected();
+ cur.push(statement());
+ }
+ }
+ if (branch) branch.end = prev();
+ next();
+ return a;
+ };
+
+ function try_() {
+ var body = block_(), bcatch = null, bfinally = null;
+ if (is("keyword", "catch")) {
+ var start = S.token;
+ next();
+ expect("(");
+ var name = as_symbol(AST_SymbolCatch);
+ expect(")");
+ bcatch = new AST_Catch({
+ start : start,
+ argname : name,
+ body : block_(),
+ end : prev()
+ });
+ }
+ if (is("keyword", "finally")) {
+ var start = S.token;
+ next();
+ bfinally = new AST_Finally({
+ start : start,
+ body : block_(),
+ end : prev()
+ });
+ }
+ if (!bcatch && !bfinally)
+ croak("Missing catch/finally blocks");
+ return new AST_Try({
+ body : body,
+ bcatch : bcatch,
+ bfinally : bfinally
+ });
+ };
+
+ function vardefs(no_in, in_const) {
+ var a = [];
+ for (;;) {
+ a.push(new AST_VarDef({
+ start : S.token,
+ name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
+ value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
+ end : prev()
+ }));
+ if (!is("punc", ","))
+ break;
+ next();
+ }
+ return a;
+ };
+
+ var var_ = function(no_in) {
+ return new AST_Var({
+ start : prev(),
+ definitions : vardefs(no_in, false),
+ end : prev()
+ });
+ };
+
+ var const_ = function() {
+ return new AST_Const({
+ start : prev(),
+ definitions : vardefs(false, true),
+ end : prev()
+ });
+ };
+
+ var new_ = function() {
+ var start = S.token;
+ expect_token("operator", "new");
+ var newexp = expr_atom(false), args;
+ if (is("punc", "(")) {
+ next();
+ args = expr_list(")");
+ } else {
+ args = [];
+ }
+ return subscripts(new AST_New({
+ start : start,
+ expression : newexp,
+ args : args,
+ end : prev()
+ }), true);
+ };
+
+ function as_atom_node() {
+ var tok = S.token, ret;
+ switch (tok.type) {
+ case "name":
+ return as_symbol(AST_SymbolRef);
+ case "num":
+ ret = new AST_Number({ start: tok, end: tok, value: tok.value });
+ break;
+ case "string":
+ ret = new AST_String({ start: tok, end: tok, value: tok.value });
+ break;
+ case "regexp":
+ ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
+ break;
+ case "atom":
+ switch (tok.value) {
+ case "false":
+ ret = new AST_False({ start: tok, end: tok });
+ break;
+ case "true":
+ ret = new AST_True({ start: tok, end: tok });
+ break;
+ case "null":
+ ret = new AST_Null({ start: tok, end: tok });
+ break;
+ }
+ break;
+ }
+ next();
+ return ret;
+ };
+
+ var expr_atom = function(allow_calls) {
+ if (is("operator", "new")) {
+ return new_();
+ }
+ var start = S.token;
+ if (is("punc")) {
+ switch (start.value) {
+ case "(":
+ next();
+ var ex = expression(true);
+ ex.start = start;
+ ex.end = S.token;
+ expect(")");
+ return subscripts(ex, allow_calls);
+ case "[":
+ return subscripts(array_(), allow_calls);
+ case "{":
+ return subscripts(object_(), allow_calls);
+ }
+ unexpected();
+ }
+ if (is("keyword", "function")) {
+ next();
+ var func = function_(false);
+ func.start = start;
+ func.end = prev();
+ return subscripts(func, allow_calls);
+ }
+ if (ATOMIC_START_TOKEN[S.token.type]) {
+ return subscripts(as_atom_node(), allow_calls);
+ }
+ unexpected();
+ };
+
+ function expr_list(closing, allow_trailing_comma, allow_empty) {
+ var first = true, a = [];
+ while (!is("punc", closing)) {
+ if (first) first = false; else expect(",");
+ if (allow_trailing_comma && is("punc", closing)) break;
+ if (is("punc", ",") && allow_empty) {
+ a.push(new AST_Undefined({ start: S.token, end: S.token }));
+ } else {
+ a.push(expression(false));
+ }
+ }
+ next();
+ return a;
+ };
+
+ var array_ = embed_tokens(function() {
+ expect("[");
+ return new AST_Array({
+ elements: expr_list("]", !options.strict, true)
+ });
+ });
+
+ var object_ = embed_tokens(function() {
+ expect("{");
+ var first = true, a = [];
+ while (!is("punc", "}")) {
+ if (first) first = false; else expect(",");
+ if (!options.strict && is("punc", "}"))
+ // allow trailing comma
+ break;
+ var start = S.token;
+ var type = start.type;
+ var name = as_property_name();
+ if (type == "name" && !is("punc", ":")) {
+ if (name == "get") {
+ a.push(new AST_ObjectGetter({
+ start : start,
+ key : name,
+ value : function_(false, AST_Lambda),
+ end : prev()
+ }));
+ continue;
+ }
+ if (name == "set") {
+ a.push(new AST_ObjectSetter({
+ start : start,
+ key : name,
+ value : function_(false, AST_Lambda),
+ end : prev()
+ }));
+ continue;
+ }
+ }
+ expect(":");
+ a.push(new AST_ObjectKeyVal({
+ start : start,
+ key : name,
+ value : expression(false),
+ end : prev()
+ }));
+ }
+ next();
+ return new AST_Object({ properties: a });
+ });
+
+ function as_property_name() {
+ var tmp;
+ switch (S.token.type) {
+ case "num":
+ case "string":
+ case "name":
+ case "operator":
+ case "keyword":
+ case "atom":
+ return (tmp = S.token.value, next(), tmp);
+ default:
+ unexpected();
+ }
+ };
+
+ function as_name() {
+ var tmp;
+ switch (S.token.type) {
+ case "name":
+ case "operator":
+ case "keyword":
+ case "atom":
+ return (tmp = S.token.value, next(), tmp);
+ default:
+ unexpected();
+ }
+ };
+
+ function as_symbol(type, noerror) {
+ if (!is("name")) {
+ if (!noerror) croak("Name expected");
+ return null;
+ }
+ var name = S.token.value;
+ var sym = new (name == "this" ? AST_This : type)({
+ name : String(S.token.value),
+ start : S.token,
+ end : S.token
+ });
+ next();
+ return sym;
+ };
+
+ var subscripts = function(expr, allow_calls) {
+ var start = expr.start;
+ if (is("punc", ".")) {
+ next();
+ return subscripts(new AST_Dot({
+ start : start,
+ expression : expr,
+ property : as_name(),
+ end : prev()
+ }), allow_calls);
+ }
+ if (is("punc", "[")) {
+ next();
+ var prop = expression(true);
+ expect("]");
+ return subscripts(new AST_Sub({
+ start : start,
+ expression : expr,
+ property : prop,
+ end : prev()
+ }), allow_calls);
+ }
+ if (allow_calls && is("punc", "(")) {
+ next();
+ return subscripts(new AST_Call({
+ start : start,
+ expression : expr,
+ args : expr_list(")"),
+ end : prev()
+ }), true);
+ }
+ return expr;
+ };
+
+ var maybe_unary = function(allow_calls) {
+ var start = S.token, tmp;
+ if (is("operator") && UNARY_PREFIX(S.token.value)) {
+ var ex = make_unary(AST_UnaryPrefix,
+ (tmp = S.token.value, next(), tmp),
+ maybe_unary(allow_calls));
+ ex.start = start;
+ ex.end = prev();
+ return ex;
+ }
+ var val = expr_atom(allow_calls);
+ while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
+ val = make_unary(AST_UnaryPostfix, S.token.value, val);
+ val.start = start;
+ val.end = S.token;
+ next();
+ }
+ return val;
+ };
+
+ function make_unary(ctor, op, expr) {
+ if ((op == "++" || op == "--") && !is_assignable(expr))
+ croak("Invalid use of " + op + " operator");
+ return new ctor({ operator: op, expression: expr });
+ };
+
+ var expr_op = function(left, min_prec, no_in) {
+ var op = is("operator") ? S.token.value : null;
+ if (op == "in" && no_in) op = null;
+ var prec = op != null ? PRECEDENCE[op] : null;
+ if (prec != null && prec > min_prec) {
+ next();
+ var right = expr_op(maybe_unary(true), prec, no_in);
+ return expr_op(new AST_Binary({
+ start : left.start,
+ left : left,
+ operator : op,
+ right : right,
+ end : right.end
+ }), min_prec, no_in);
+ }
+ return left;
+ };
+
+ function expr_ops(no_in) {
+ return expr_op(maybe_unary(true), 0, no_in);
+ };
+
+ var maybe_conditional = function(no_in) {
+ var start = S.token;
+ var expr = expr_ops(no_in);
+ if (is("operator", "?")) {
+ next();
+ var yes = expression(false);
+ expect(":");
+ return new AST_Conditional({
+ start : start,
+ condition : expr,
+ consequent : yes,
+ alternative : expression(false, no_in),
+ end : peek()
+ });
+ }
+ return expr;
+ };
+
+ function is_assignable(expr) {
+ if (!options.strict) return true;
+ switch (expr[0]+"") {
+ case "dot":
+ case "sub":
+ case "new":
+ case "call":
+ return true;
+ case "name":
+ return expr[1] != "this";
+ }
+ };
+
+ var maybe_assign = function(no_in) {
+ var start = S.token;
+ var left = maybe_conditional(no_in), val = S.token.value;
+ if (is("operator") && ASSIGNMENT(val)) {
+ if (is_assignable(left)) {
+ next();
+ return new AST_Assign({
+ start : start,
+ left : left,
+ operator : val,
+ right : maybe_assign(no_in),
+ end : peek()
+ });
+ }
+ croak("Invalid assignment");
+ }
+ return left;
+ };
+
+ var expression = function(commas, no_in) {
+ var start = S.token;
+ var expr = maybe_assign(no_in);
+ if (commas && is("punc", ",")) {
+ next();
+ return new AST_Seq({
+ start : start,
+ car : expr,
+ cdr : expression(true, no_in),
+ end : peek()
+ });
+ }
+ return expr;
+ };
+
+ function in_loop(cont) {
+ ++S.in_loop;
+ var ret = cont();
+ --S.in_loop;
+ return ret;
+ };
+
+ return (function(){
+ var start = S.token;
+ var body = [];
+ while (!is("eof"))
+ body.push(statement());
+ var end = prev();
+ var toplevel = options.toplevel;
+ if (toplevel) {
+ toplevel.body = toplevel.body.concat(body);
+ toplevel.end = end;
+ } else {
+ toplevel = new AST_Toplevel({ start: start, body: body, end: end });
+ }
+ return toplevel;
+ })();
+
+};
+exports.parse = parse;
+})(typeof exports === "undefined" ? (window.uglifyjs = {}) : exports);
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-acorn-jsx.git
More information about the Pkg-javascript-commits
mailing list