Allow yield expressions without a RHS.
authorwingo@igalia.com <wingo@igalia.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 2 Jul 2014 13:48:28 +0000 (13:48 +0000)
committerwingo@igalia.com <wingo@igalia.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 2 Jul 2014 13:48:28 +0000 (13:48 +0000)
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

src/ast.h
src/preparser.h
test/cctest/test-parsing.cc
test/mjsunit/harmony/generators-iteration.js
test/mjsunit/harmony/generators-parsing.js

index aecee37..15be52f 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -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)
index 6799087..d6be902 100644 (file)
@@ -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) {
index af553ed..47f5e6a 100644 (file)
@@ -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
   };
 
index d86a20f..e54aeab 100644 (file)
@@ -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],
index 2a4a68c..21790b0 100644 (file)
@@ -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);