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:
parent
a481d753d0
commit
341d61867c
@ -3368,6 +3368,7 @@ class AstNodeFactory V8_FINAL BASE_EMBEDDED {
|
||||
Expression* expression,
|
||||
Yield::Kind yield_kind,
|
||||
int pos) {
|
||||
if (!expression) expression = NewUndefinedLiteral(pos);
|
||||
Yield* yield = new(zone_) Yield(
|
||||
zone_, generator_object, expression, yield_kind, pos);
|
||||
VISIT_AND_RETURN(Yield, yield)
|
||||
|
@ -1792,15 +1792,36 @@ template <class Traits>
|
||||
typename ParserBase<Traits>::ExpressionT
|
||||
ParserBase<Traits>::ParseYieldExpression(bool* ok) {
|
||||
// YieldExpression ::
|
||||
// 'yield' '*'? AssignmentExpression
|
||||
// 'yield' ([no line terminator] '*'? AssignmentExpression)?
|
||||
int pos = peek_position();
|
||||
Expect(Token::YIELD, CHECK_OK);
|
||||
Yield::Kind kind =
|
||||
Check(Token::MUL) ? Yield::DELEGATING : Yield::SUSPEND;
|
||||
ExpressionT generator_object =
|
||||
factory()->NewVariableProxy(function_state_->generator_object_variable());
|
||||
ExpressionT expression =
|
||||
ParseAssignmentExpression(false, CHECK_OK);
|
||||
ExpressionT expression = Traits::EmptyExpression();
|
||||
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 =
|
||||
factory()->NewYield(generator_object, expression, kind, pos);
|
||||
if (kind == Yield::DELEGATING) {
|
||||
|
@ -1856,6 +1856,10 @@ TEST(NoErrorsGenerator) {
|
||||
"yield 3; yield 4;",
|
||||
"yield * 3; yield * 4;",
|
||||
"(function (yield) { })",
|
||||
"yield { yield: 12 }",
|
||||
"yield /* comment */ { yield: 12 }",
|
||||
"yield * \n { yield: 12 }",
|
||||
"yield /* comment */ * \n { yield: 12 }",
|
||||
// You can return in a generator.
|
||||
"yield 1; return",
|
||||
"yield * 1; return",
|
||||
@ -1866,8 +1870,21 @@ TEST(NoErrorsGenerator) {
|
||||
// Yield is still a valid key in object literals.
|
||||
"({ yield: 1 })",
|
||||
"({ get yield() { } })",
|
||||
// TODO(348893007): Invalid (no newline allowed between yield and *).
|
||||
"yield\n*3",
|
||||
// Yield without RHS.
|
||||
"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
|
||||
};
|
||||
|
||||
@ -1913,19 +1930,15 @@ TEST(ErrorsYieldGenerator) {
|
||||
"yield ? 1 : 2",
|
||||
// Parses as yield (/ yield): invalid.
|
||||
"yield / yield",
|
||||
// TODO(348893007): The rest of these should be valid.
|
||||
"yield;",
|
||||
"yield",
|
||||
"yield\n",
|
||||
"(yield)",
|
||||
"[yield]",
|
||||
"{yield}",
|
||||
"yield, yield",
|
||||
"yield; yield",
|
||||
"(yield) ? yield : yield",
|
||||
"(yield) \n ? yield : yield",
|
||||
// Parses as yield (+ yield).
|
||||
"yield + yield",
|
||||
"+ yield",
|
||||
"+ yield 3",
|
||||
// Invalid (no newline allowed between yield and *).
|
||||
"yield\n*3",
|
||||
// Invalid (we see a newline, so we parse {yield:42} as a statement, not an
|
||||
// object literal, and yield is not a valid label).
|
||||
"yield\n{yield: 42}",
|
||||
"yield /* comment */\n {yield: 42}",
|
||||
"yield //comment\n {yield: 42}",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -337,6 +337,24 @@ TestGenerator(
|
||||
"foo",
|
||||
[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.
|
||||
TestGenerator(GeneratorFunction(),
|
||||
[undefined],
|
||||
|
@ -35,6 +35,40 @@ function* g() { yield 3; yield 4; }
|
||||
// Yield expressions.
|
||||
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.
|
||||
function* g() { "use strict"; yield 3; yield 4; }
|
||||
|
||||
@ -50,14 +84,10 @@ function* g() { yield 1; return 2; yield "dead"; }
|
||||
// Named generator expression.
|
||||
(function* g() { yield 3; });
|
||||
|
||||
// A generator without a yield is specified as causing an early error. This
|
||||
// behavior is currently unimplemented. See
|
||||
// https://bugs.ecmascript.org/show_bug.cgi?id=1283.
|
||||
// You can have a generator without a yield.
|
||||
function* g() { }
|
||||
|
||||
// A YieldExpression in the RHS of a YieldExpression is currently specified as
|
||||
// causing an early error. This behavior is currently unimplemented. See
|
||||
// https://bugs.ecmascript.org/show_bug.cgi?id=1283.
|
||||
// A YieldExpression is valid as the RHS of a YieldExpression.
|
||||
function* g() { yield yield 1; }
|
||||
function* g() { yield 3 + (yield 4); }
|
||||
|
||||
@ -86,9 +116,6 @@ assertThrows("function* g() { yield: 1 }", SyntaxError)
|
||||
// functions.
|
||||
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.
|
||||
assertThrows("function* g() { yield = 10; }", SyntaxError);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user