Skip to content

Commit

Permalink
Use stack-based approach for parsing binary expressions.
Browse files Browse the repository at this point in the history
Instead of going recursively, binary expressions are parsed using
shift-reduce strategy with a simple stack mechanism. It makes Esprima
more similar to the parser in V8 and JavaScriptCore. Also, this means
that we don't exhaust JavaScript engine's stack anymore for a really
complex expression.

Speed-up is noticeable due to the elimination of the long chain of
recursive calls. V8 3.13.7 shows at least 8% improvement.

See also the discussion in the mailing-list:
https://groups.google.com/d/topic/esprima/FqjWJc6UawA/

http://code.google.com/p/esprima/issues/detail?id=352
  • Loading branch information
ariya committed Oct 8, 2012
1 parent a0511bb commit 8c65326
Showing 1 changed file with 102 additions and 167 deletions.
269 changes: 102 additions & 167 deletions esprima.js
Original file line number Diff line number Diff line change
Expand Up @@ -1783,186 +1783,148 @@ 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
switch (token.value) {
case '||':
prec = 1;
break;

function parseAdditiveExpression() {
var expr = parseMultiplicativeExpression();
case '&&':
prec = 2;
break;

while (match('+') || match('-')) {
expr = {
type: Syntax.BinaryExpression,
operator: lex().value,
left: expr,
right: parseMultiplicativeExpression()
};
}
case '|':
prec = 3;
break;

return expr;
}
case '^':
prec = 4;
break;

// 11.7 Bitwise Shift Operators
case '&':
prec = 5;
break;

function parseShiftExpression() {
var expr = parseAdditiveExpression();
case '==':
case '!=':
case '===':
case '!==':
prec = 6;
break;

while (match('<<') || match('>>') || match('>>>')) {
expr = {
type: Syntax.BinaryExpression,
operator: lex().value,
left: expr,
right: parseAdditiveExpression()
};
}
case '<':
case '>':
case '<=':
case '>=':
case 'instanceof':
prec = 7;
break;

return expr;
}
// 11.8 Relational Operators
case 'in':
prec = allowIn ? 7 : 0;
break;

function parseRelationalExpression() {
var expr, previousAllowIn;
case '<<':
case '>>':
case '>>>':
prec = 8;
break;

previousAllowIn = state.allowIn;
state.allowIn = true;
case '+':
case '-':
prec = 9;
break;

expr = parseShiftExpression();
case '*':
case '/':
case '%':
prec = 11;
break;

while (match('<') || match('>') || match('<=') || match('>=') || (previousAllowIn && matchKeyword('in')) || matchKeyword('instanceof')) {
expr = {
type: Syntax.BinaryExpression,
operator: lex().value,
left: expr,
right: parseShiftExpression()
};
default:
break;
}

state.allowIn = previousAllowIn;
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

function parseEqualityExpression() {
var expr = parseRelationalExpression();

while (match('==') || match('!=') || match('===') || match('!==')) {
expr = {
type: Syntax.BinaryExpression,
operator: lex().value,
left: expr,
right: parseRelationalExpression()
};
}

return expr;
}

// 11.10 Binary Bitwise Operators
// 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(),
logical = (operator === '||' || operator === '&&');

while (match('&')) {
lex();
expr = {
type: Syntax.BinaryExpression,
operator: '&',
left: expr,
right: parseEqualityExpression()
};
}

return expr;
stack.push({
type: logical ? Syntax.LogicalExpression : Syntax.BinaryExpression,
operator: operator,
left: left,
right: right
});
}

function parseBitwiseXORExpression() {
var expr = parseBitwiseANDExpression();
function parseBinaryExpression() {
var expr, token, prec, previousAllowIn, stack;

while (match('^')) {
lex();
expr = {
type: Syntax.BinaryExpression,
operator: '^',
left: expr,
right: parseBitwiseANDExpression()
};
}

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;

expr = parseLogicalORExpression();
expr = parseBinaryExpression();

if (match('?')) {
lex();
Expand Down Expand Up @@ -3646,25 +3608,18 @@ parseStatement: true, parseSourceElement: true */

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.parseLogicalANDExpression = parseLogicalANDExpression;
extra.parseLogicalORExpression = parseLogicalORExpression;
extra.parseMultiplicativeExpression = parseMultiplicativeExpression;
extra.parseNewExpression = parseNewExpression;
extra.parseNonComputedProperty = parseNonComputedProperty;
extra.parseObjectProperty = parseObjectProperty;
Expand All @@ -3673,34 +3628,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);
parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression);
parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression);
parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression);
parseNewExpression = wrapTracking(extra.parseNewExpression);
parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty);
parseObjectProperty = wrapTracking(extra.parseObjectProperty);
Expand All @@ -3709,9 +3655,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);
Expand All @@ -3737,28 +3681,21 @@ parseStatement: true, parseSourceElement: true */
}

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;
Expand All @@ -3767,9 +3704,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;
Expand Down

0 comments on commit 8c65326

Please sign in to comment.