Skip to content

Commit

Permalink
Distinguish yield as a keyword vs an identifier.
Browse files Browse the repository at this point in the history
`yield` is a keyword in strict mode, inside a generator, or as part of a
yield expression. `yield` may be used as an identifier for the name of
generators, methods, object properties, and function expressions.

Fixes jquery#1186
  • Loading branch information
ariya committed Jul 7, 2015
1 parent f1587c3 commit 40543bb
Show file tree
Hide file tree
Showing 146 changed files with 4,807 additions and 341 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ with the help of [many contributors](https://github.com/jquery/esprima/contribut
- Full support for ECMAScript 5.1 ([ECMA-262](http://www.ecma-international.org/publications/standards/Ecma-262.htm))
- Sensible [syntax tree format](https://github.com/estree/estree/blob/master/spec.md) as standardized by [EStree project](https://github.com/estree/estree)
- Optional tracking of syntax node location (index-based and line-column)
- Heavily tested (~1000 [unit tests](https://github.com/jquery/esprima/tree/master/test/fixtures) with [full code coverage](https://travis-ci.org/jquery/esprima))
- Heavily tested (~1100 [unit tests](https://github.com/jquery/esprima/tree/master/test/fixtures) with [full code coverage](https://travis-ci.org/jquery/esprima))
- [Partial support](https://github.com/jquery/esprima/issues/1099) for ECMAScript 6

Esprima serves as a **building block** for some JavaScript
Expand Down
104 changes: 68 additions & 36 deletions esprima.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@
IllegalContinue: 'Illegal continue statement',
IllegalBreak: 'Illegal break statement',
IllegalReturn: 'Illegal return statement',
IllegalYield: 'Unexpected token yield',
StrictModeWith: 'Strict mode code may not include a with statement',
StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
StrictVarName: 'Variable name may not be eval or arguments in strict mode',
Expand Down Expand Up @@ -2679,25 +2678,24 @@
}

function parsePattern(params) {
var identifier;
if (lookahead.type === Token.Identifier) {
params.push(lookahead);
identifier = parseVariableIdentifier();
return identifier;
} else if (match('[')) {
if (match('[')) {
return parseArrayPattern(params);
} else if (match('{')) {
return parseObjectPattern(params);
}
throwUnexpectedToken(lookahead);
params.push(lookahead);
return parseVariableIdentifier();
}

function parsePatternWithDefault(params) {
var startToken = lookahead, pattern, right;
var startToken = lookahead, pattern, previousAllowYield, right;
pattern = parsePattern(params);
if (match('=')) {
lex();
previousAllowYield = state.allowYield;
state.allowYield = true;
right = isolateCoverGrammar(parseAssignmentExpression);
state.allowYield = previousAllowYield;
pattern = new WrappingNode(startToken).finishAssignmentPattern(pattern, right);
}
return pattern;
Expand Down Expand Up @@ -2887,11 +2885,11 @@
key = parseObjectPropertyKey();
methodNode = new Node();

state.allowYield = false;
state.allowYield = true;
params = parseParams();
state.allowYield = previousAllowYield;

state.allowYield = true;
state.allowYield = false;
value = parsePropertyFunction(methodNode, params, true);
state.allowYield = previousAllowYield;

Expand Down Expand Up @@ -3119,6 +3117,13 @@
expect(')');

if (match('=>')) {
if (expr.type === Syntax.Identifier && expr.name === 'yield') {
return {
type: PlaceHolders.ArrowParameterPlaceHolder,
params: [expr]
};
}

if (!isBindingElement) {
throwUnexpectedToken(lookahead);
}
Expand Down Expand Up @@ -3171,6 +3176,9 @@
}
expr = node.finishLiteral(lex());
} else if (type === Token.Keyword) {
if (!strict && state.allowYield && matchKeyword('yield')) {
return parseNonComputedProperty();
}
isAssignmentTarget = isBindingElement = false;
if (matchKeyword('function')) {
return parseFunctionExpression();
Expand Down Expand Up @@ -3635,6 +3643,8 @@
}
}
break;
case Syntax.YieldExpression:
break;
default:
assert(param.type === Syntax.ObjectPattern, 'Invalid type');
for (i = 0; i < param.properties.length; i++) {
Expand Down Expand Up @@ -3669,6 +3679,15 @@
switch (param.type) {
case Syntax.AssignmentPattern:
params[i] = param.left;
if (param.right.type === Syntax.YieldExpression) {
if (param.right.argument) {
throwUnexpectedToken(lookahead);
}
param.right.type = Syntax.Identifier;
param.right.name = 'yield';
delete param.right.argument;
delete param.right.delegate;
}
defaults.push(param.right);
++defaultCount;
checkPatternParam(options, param.left);
Expand All @@ -3681,6 +3700,15 @@
}
}

if (strict || !state.allowYield) {
for (i = 0, len = params.length; i < len; i += 1) {
param = params[i];
if (param.type === Syntax.YieldExpression) {
throwUnexpectedToken(lookahead);
}
}
}

if (options.message === Messages.StrictParamDupe) {
token = strict ? options.stricted : options.firstRestricted;
throwUnexpectedToken(token, options.message);
Expand All @@ -3700,13 +3728,16 @@
}

function parseArrowFunctionExpression(options, node) {
var previousStrict, body;
var previousStrict, previousAllowYield, body;

if (hasLineTerminator) {
tolerateUnexpectedToken(lookahead);
}
expect('=>');

previousStrict = strict;
previousAllowYield = state.allowYield;
state.allowYield = true;

body = parseConciseBody();

Expand All @@ -3718,33 +3749,33 @@
}

strict = previousStrict;
state.allowYield = previousAllowYield;

return node.finishArrowFunctionExpression(options.params, options.defaults, body, body.type !== Syntax.BlockStatement);
}

// [ES6] 14.4 Yield expression

function parseYieldExpression() {
var argument, expr, delegate;
var argument, expr, delegate, previousAllowYield;

expr = new Node();

if (!state.allowYield) {
tolerateUnexpectedToken(lookahead, Messages.IllegalYield);
}

expectKeyword('yield');

if (!hasLineTerminator) {
previousAllowYield = state.allowYield;
state.allowYield = false;
delegate = match('*');
if (delegate) {
lex();
argument = parseExpression();
} else {
if (!match(';') && !match('}') && lookahead.type !== Token.EOF) {
if (!match(';') && !match('}') && !match(')') && lookahead.type !== Token.EOF) {
argument = parseExpression();
}
}
state.allowYield = previousAllowYield;
}

return expr.finishYieldExpression(argument, delegate);
Expand All @@ -3758,7 +3789,7 @@
startToken = lookahead;
token = lookahead;

if (matchKeyword('yield')) {
if (!state.allowYield && matchKeyword('yield')) {
return parseYieldExpression();
}

Expand Down Expand Up @@ -3884,7 +3915,13 @@

token = lex();

if (token.type !== Token.Identifier) {
if (token.type === Token.Keyword && token.value === 'yield') {
if (strict) {
tolerateUnexpectedToken(token, Messages.StrictReservedWord);
} if (!state.allowYield) {
throwUnexpectedToken(token);
}
} else if (token.type !== Token.Identifier) {
if (strict && token.type === Token.Keyword && isStrictModeReservedWord(token.value)) {
tolerateUnexpectedToken(token, Messages.StrictReservedWord);
} else {
Expand Down Expand Up @@ -4777,6 +4814,8 @@
var id = null, params = [], defaults = [], body, token, stricted, tmp, firstRestricted, message, previousStrict,
isGenerator, previousAllowYield;

previousAllowYield = state.allowYield;

expectKeyword('function');

isGenerator = match('*');
Expand All @@ -4802,11 +4841,8 @@
}
}

previousAllowYield = state.allowYield;
state.allowYield = false;
state.allowYield = !isGenerator;
tmp = parseParams(firstRestricted);
state.allowYield = previousAllowYield;

params = tmp.params;
defaults = tmp.defaults;
stricted = tmp.stricted;
Expand All @@ -4815,16 +4851,16 @@
message = tmp.message;
}

previousAllowYield = state.allowYield;

previousStrict = strict;
state.allowYield = isGenerator;
body = parseFunctionSourceElements();
if (strict && firstRestricted) {
throwUnexpectedToken(firstRestricted, message);
}
if (strict && stricted) {
tolerateUnexpectedToken(stricted, message);
}

strict = previousStrict;
state.allowYield = previousAllowYield;

Expand All @@ -4836,16 +4872,19 @@
params = [], defaults = [], body, previousStrict, node = new Node(),
isGenerator, previousAllowYield;

previousAllowYield = state.allowYield;

expectKeyword('function');

isGenerator = match('*');
if (isGenerator) {
lex();
}

state.allowYield = !isGenerator;
if (!match('(')) {
token = lookahead;
id = parseVariableIdentifier();
id = (!strict && !isGenerator && matchKeyword('yield')) ? parseNonComputedProperty() : parseVariableIdentifier();
if (strict) {
if (isRestrictedWord(token.value)) {
tolerateUnexpectedToken(token, Messages.StrictFunctionName);
Expand All @@ -4861,11 +4900,7 @@
}
}

previousAllowYield = state.allowYield;
state.allowYield = false;
tmp = parseParams(firstRestricted);
state.allowYield = previousAllowYield;

params = tmp.params;
defaults = tmp.defaults;
stricted = tmp.stricted;
Expand All @@ -4875,10 +4910,7 @@
}

previousStrict = strict;
previousAllowYield = state.allowYield;
state.allowYield = isGenerator;
body = parseFunctionSourceElements();

if (strict && firstRestricted) {
throwUnexpectedToken(firstRestricted, message);
}
Expand Down Expand Up @@ -5347,7 +5379,7 @@
lookahead = null;
state = {
allowIn: true,
allowYield: false,
allowYield: true,
labelSet: {},
inFunctionBody: false,
inIteration: false,
Expand Down Expand Up @@ -5436,7 +5468,7 @@
lookahead = null;
state = {
allowIn: true,
allowYield: false,
allowYield: true,
labelSet: {},
inFunctionBody: false,
inIteration: false,
Expand Down
3 changes: 0 additions & 3 deletions test/fixtures/ES6/generator/generator-binding-property.js

This file was deleted.

Loading

0 comments on commit 40543bb

Please sign in to comment.