Allow yield expressions without a RHS.

R=marja@chromium.org
BUG=

Review URL: https://codereview.chromium.org/348893007

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22163 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
wingo@igalia.com 2014-07-02 13:48:28 +00:00
parent a481d753d0
commit 341d61867c
5 changed files with 109 additions and 29 deletions

View File

@ -3368,6 +3368,7 @@ class AstNodeFactory V8_FINAL BASE_EMBEDDED {
Expression* expression, Expression* expression,
Yield::Kind yield_kind, Yield::Kind yield_kind,
int pos) { int pos) {
if (!expression) expression = NewUndefinedLiteral(pos);
Yield* yield = new(zone_) Yield( Yield* yield = new(zone_) Yield(
zone_, generator_object, expression, yield_kind, pos); zone_, generator_object, expression, yield_kind, pos);
VISIT_AND_RETURN(Yield, yield) VISIT_AND_RETURN(Yield, yield)

View File

@ -1792,15 +1792,36 @@ template <class Traits>
typename ParserBase<Traits>::ExpressionT typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::ParseYieldExpression(bool* ok) { ParserBase<Traits>::ParseYieldExpression(bool* ok) {
// YieldExpression :: // YieldExpression ::
// 'yield' '*'? AssignmentExpression // 'yield' ([no line terminator] '*'? AssignmentExpression)?
int pos = peek_position(); int pos = peek_position();
Expect(Token::YIELD, CHECK_OK); Expect(Token::YIELD, CHECK_OK);
Yield::Kind kind =
Check(Token::MUL) ? Yield::DELEGATING : Yield::SUSPEND;
ExpressionT generator_object = ExpressionT generator_object =
factory()->NewVariableProxy(function_state_->generator_object_variable()); factory()->NewVariableProxy(function_state_->generator_object_variable());
ExpressionT expression = ExpressionT expression = Traits::EmptyExpression();
ParseAssignmentExpression(false, CHECK_OK); Yield::Kind kind = Yield::SUSPEND;
if (!scanner()->HasAnyLineTerminatorBeforeNext()) {
if (Check(Token::MUL)) kind = Yield::DELEGATING;
switch (peek()) {
case Token::EOS:
case Token::SEMICOLON:
case Token::RBRACE:
case Token::RBRACK:
case Token::RPAREN:
case Token::COLON:
case Token::COMMA:
// The above set of tokens is the complete set of tokens that can appear
// after an AssignmentExpression, and none of them can start an
// AssignmentExpression. This allows us to avoid looking for an RHS for
// a Yield::SUSPEND operation, given only one look-ahead token.
if (kind == Yield::SUSPEND)
break;
ASSERT(kind == Yield::DELEGATING);
// Delegating yields require an RHS; fall through.
default:
expression = ParseAssignmentExpression(false, CHECK_OK);
break;
}
}
typename Traits::Type::YieldExpression yield = typename Traits::Type::YieldExpression yield =
factory()->NewYield(generator_object, expression, kind, pos); factory()->NewYield(generator_object, expression, kind, pos);
if (kind == Yield::DELEGATING) { if (kind == Yield::DELEGATING) {

View File

@ -1856,6 +1856,10 @@ TEST(NoErrorsGenerator) {
"yield 3; yield 4;", "yield 3; yield 4;",
"yield * 3; yield * 4;", "yield * 3; yield * 4;",
"(function (yield) { })", "(function (yield) { })",
"yield { yield: 12 }",
"yield /* comment */ { yield: 12 }",
"yield * \n { yield: 12 }",
"yield /* comment */ * \n { yield: 12 }",
// You can return in a generator. // You can return in a generator.
"yield 1; return", "yield 1; return",
"yield * 1; return", "yield * 1; return",
@ -1866,8 +1870,21 @@ TEST(NoErrorsGenerator) {
// Yield is still a valid key in object literals. // Yield is still a valid key in object literals.
"({ yield: 1 })", "({ yield: 1 })",
"({ get yield() { } })", "({ get yield() { } })",
// TODO(348893007): Invalid (no newline allowed between yield and *). // Yield without RHS.
"yield\n*3", "yield;",
"yield",
"yield\n",
"yield /* comment */"
"yield // comment\n"
"(yield)",
"[yield]",
"{yield}",
"yield, yield",
"yield; yield",
"(yield) ? yield : yield",
"(yield) \n ? yield : yield",
// If there is a newline before the next token, we don't look for RHS.
"yield\nfor (;;) {}",
NULL NULL
}; };
@ -1913,19 +1930,15 @@ TEST(ErrorsYieldGenerator) {
"yield ? 1 : 2", "yield ? 1 : 2",
// Parses as yield (/ yield): invalid. // Parses as yield (/ yield): invalid.
"yield / yield", "yield / yield",
// TODO(348893007): The rest of these should be valid. "+ yield",
"yield;", "+ yield 3",
"yield", // Invalid (no newline allowed between yield and *).
"yield\n", "yield\n*3",
"(yield)", // Invalid (we see a newline, so we parse {yield:42} as a statement, not an
"[yield]", // object literal, and yield is not a valid label).
"{yield}", "yield\n{yield: 42}",
"yield, yield", "yield /* comment */\n {yield: 42}",
"yield; yield", "yield //comment\n {yield: 42}",
"(yield) ? yield : yield",
"(yield) \n ? yield : yield",
// Parses as yield (+ yield).
"yield + yield",
NULL NULL
}; };

View File

@ -337,6 +337,24 @@ TestGenerator(
"foo", "foo",
[2, "1foo3", 5, "4foo6", "foofoo"]); [2, "1foo3", 5, "4foo6", "foofoo"]);
// Yield with no arguments yields undefined.
TestGenerator(
function* g26() { return yield yield },
[undefined, undefined, undefined],
"foo",
[undefined, "foo", "foo"]);
// A newline causes the parser to stop looking for an argument to yield.
TestGenerator(
function* g27() {
yield
3
return
},
[undefined, undefined],
"foo",
[undefined, undefined]);
// Generator function instances. // Generator function instances.
TestGenerator(GeneratorFunction(), TestGenerator(GeneratorFunction(),
[undefined], [undefined],

View File

@ -35,6 +35,40 @@ function* g() { yield 3; yield 4; }
// Yield expressions. // Yield expressions.
function* g() { (yield 3) + (yield 4); } function* g() { (yield 3) + (yield 4); }
// Yield without a RHS.
function* g() { yield; }
function* g() { yield }
function* g() {
yield
}
function* g() { (yield) }
function* g() { [yield] }
function* g() { {yield} }
function* g() { yield, yield }
function* g() { yield; yield }
function* g() { (yield) ? yield : yield }
function* g() {
(yield)
? yield
: yield
}
// If yield has a RHS, it needs to start on the same line. The * in a
// yield* counts as starting the RHS.
function* g() {
yield *
foo
}
assertThrows("function* g() { yield\n* foo }", SyntaxError);
assertEquals(undefined,
(function*(){
yield
3
})().next().value);
// A YieldExpression is not a LogicalORExpression.
assertThrows("function* g() { yield ? yield : yield }", SyntaxError);
// You can have a generator in strict mode. // You can have a generator in strict mode.
function* g() { "use strict"; yield 3; yield 4; } function* g() { "use strict"; yield 3; yield 4; }
@ -50,14 +84,10 @@ function* g() { yield 1; return 2; yield "dead"; }
// Named generator expression. // Named generator expression.
(function* g() { yield 3; }); (function* g() { yield 3; });
// A generator without a yield is specified as causing an early error. This // You can have a generator without a yield.
// behavior is currently unimplemented. See
// https://bugs.ecmascript.org/show_bug.cgi?id=1283.
function* g() { } function* g() { }
// A YieldExpression in the RHS of a YieldExpression is currently specified as // A YieldExpression is valid as the RHS of a YieldExpression.
// causing an early error. This behavior is currently unimplemented. See
// https://bugs.ecmascript.org/show_bug.cgi?id=1283.
function* g() { yield yield 1; } function* g() { yield yield 1; }
function* g() { yield 3 + (yield 4); } function* g() { yield 3 + (yield 4); }
@ -86,9 +116,6 @@ assertThrows("function* g() { yield: 1 }", SyntaxError)
// functions. // functions.
function* g() { function f() { yield (yield + yield (0)); } } function* g() { function f() { yield (yield + yield (0)); } }
// Yield needs a RHS.
assertThrows("function* g() { yield; }", SyntaxError);
// Yield in a generator is not an identifier. // Yield in a generator is not an identifier.
assertThrows("function* g() { yield = 10; }", SyntaxError); assertThrows("function* g() { yield = 10; }", SyntaxError);