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)
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) {
"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",
// 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
};
"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
};
"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],
// 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; }
// 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); }
// 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);