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,
|
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)
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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],
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user