8210923: JShell: support for switch expressions
authorrfield
Mon, 22 Oct 2018 08:30:39 -0700
changeset 52213 51c0b3936f01
parent 52212 e6973df15152
child 52214 b3c7c5a62521
8210923: JShell: support for switch expressions Reviewed-by: jlahoda
src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java
src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java
test/langtools/jdk/jshell/ToolLocalSimpleTest.java
test/langtools/jdk/jshell/ToolSimpleTest.java
--- a/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java	Mon Oct 22 12:13:29 2018 +0200
+++ b/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java	Mon Oct 22 08:30:39 2018 -0700
@@ -228,15 +228,15 @@
         // Declarations and type parameters (thus expressions)
         EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL),  //  extends
         COMMA(TokenKind.COMMA, XEXPR|XDECL),  //  ,
-        AMP(TokenKind.AMP, XEXPR|XDECL),  //  &
-        GT(TokenKind.GT, XEXPR|XDECL),  //  >
-        LT(TokenKind.LT, XEXPR|XDECL1),  //  <
-        LTLT(TokenKind.LTLT, XEXPR|XDECL1),  //  <<
-        GTGT(TokenKind.GTGT, XEXPR|XDECL),  //  >>
-        GTGTGT(TokenKind.GTGTGT, XEXPR|XDECL),  //  >>>
-        QUES(TokenKind.QUES, XEXPR|XDECL),  //  ?
+        AMP(TokenKind.AMP, XEXPR|XDECL, true),  //  &
+        GT(TokenKind.GT, XEXPR|XDECL, true),  //  >
+        LT(TokenKind.LT, XEXPR|XDECL1, true),  //  <
+        LTLT(TokenKind.LTLT, XEXPR|XDECL1, true),  //  <<
+        GTGT(TokenKind.GTGT, XEXPR|XDECL, true),  //  >>
+        GTGTGT(TokenKind.GTGTGT, XEXPR|XDECL, true),  //  >>>
+        QUES(TokenKind.QUES, XEXPR|XDECL, true),  //  ?
         DOT(TokenKind.DOT, XEXPR|XDECL),  //  .
-        STAR(TokenKind.STAR, XEXPR),  //  * (MAPPED: DOTSTAR)
+        STAR(TokenKind.STAR, XEXPR, true),  //  * (MAPPED: DOTSTAR)
 
         // Statement keywords
         ASSERT(TokenKind.ASSERT, XSTMT1|XSTART),  //  assert
@@ -249,7 +249,7 @@
         FOR(TokenKind.FOR, XSTMT1|XSTART),  //  for
         IF(TokenKind.IF, XSTMT1|XSTART),  //  if
         RETURN(TokenKind.RETURN, XSTMT1|XTERM|XSTART),  //  return
-        SWITCH(TokenKind.SWITCH, XSTMT1|XEXPR),  //  switch
+        SWITCH(TokenKind.SWITCH, XSTMT1|XEXPR1),  //  switch
         SYNCHRONIZED(TokenKind.SYNCHRONIZED, XSTMT1|XDECL),  //  synchronized
         THROW(TokenKind.THROW, XSTMT1|XSTART),  //  throw
         TRY(TokenKind.TRY, XSTMT1|XSTART),  //  try
@@ -276,7 +276,7 @@
         SUBSUB(TokenKind.SUBSUB, XEXPR1|XTERM),  //  --
 
         // Expressions cannot terminate
-        INSTANCEOF(TokenKind.INSTANCEOF, XEXPR),  //  instanceof
+        INSTANCEOF(TokenKind.INSTANCEOF, XEXPR, true),  //  instanceof
         NEW(TokenKind.NEW, XEXPR1),  //  new (MAPPED: COLCOLNEW)
         SUPER(TokenKind.SUPER, XEXPR1|XDECL),  //  super -- shouldn't see as rec. But in type parameters
         ARROW(TokenKind.ARROW, XEXPR),  //  ->
@@ -292,18 +292,18 @@
         BANG(TokenKind.BANG, XEXPR1),  //  !
         TILDE(TokenKind.TILDE, XEXPR1),  //  ~
         COLON(TokenKind.COLON, XEXPR|XTERM),  //  :
-        EQEQ(TokenKind.EQEQ, XEXPR),  //  ==
-        LTEQ(TokenKind.LTEQ, XEXPR),  //  <=
-        GTEQ(TokenKind.GTEQ, XEXPR),  //  >=
-        BANGEQ(TokenKind.BANGEQ, XEXPR),  //  !=
-        AMPAMP(TokenKind.AMPAMP, XEXPR),  //  &&
-        BARBAR(TokenKind.BARBAR, XEXPR),  //  ||
-        PLUS(TokenKind.PLUS, XEXPR1),  //  +
-        SUB(TokenKind.SUB, XEXPR1),  //  -
-        SLASH(TokenKind.SLASH, XEXPR),  //  /
-        BAR(TokenKind.BAR, XEXPR),  //  |
-        CARET(TokenKind.CARET, XEXPR),  //  ^
-        PERCENT(TokenKind.PERCENT, XEXPR),  //  %
+        EQEQ(TokenKind.EQEQ, XEXPR, true),  //  ==
+        LTEQ(TokenKind.LTEQ, XEXPR, true),  //  <=
+        GTEQ(TokenKind.GTEQ, XEXPR, true),  //  >=
+        BANGEQ(TokenKind.BANGEQ, XEXPR, true),  //  !=
+        AMPAMP(TokenKind.AMPAMP, XEXPR, true),  //  &&
+        BARBAR(TokenKind.BARBAR, XEXPR, true),  //  ||
+        PLUS(TokenKind.PLUS, XEXPR1, true),  //  +
+        SUB(TokenKind.SUB, XEXPR1, true),  //  -
+        SLASH(TokenKind.SLASH, XEXPR, true),  //  /
+        BAR(TokenKind.BAR, XEXPR, true),  //  |
+        CARET(TokenKind.CARET, XEXPR, true),  //  ^
+        PERCENT(TokenKind.PERCENT, XEXPR, true),  //  %
         PLUSEQ(TokenKind.PLUSEQ, XEXPR),  //  +=
         SUBEQ(TokenKind.SUBEQ, XEXPR),  //  -=
         STAREQ(TokenKind.STAREQ, XEXPR),  //  *=
@@ -330,6 +330,7 @@
 
         final TokenKind tokenKind;
         final int belongs;
+        final boolean valueOp;
         Function<TK,TK> mapping;
 
         TK(int b) {
@@ -337,8 +338,13 @@
         }
 
         TK(TokenKind tokenKind, int b) {
+            this(tokenKind, b, false);
+        }
+
+        TK(TokenKind tokenKind, int b, boolean valueOp) {
             this.tokenKind = tokenKind;
             this.belongs = b;
+            this.valueOp = valueOp;
             this.mapping = null;
         }
 
@@ -637,6 +643,8 @@
                         return parseExpressionStatement(); // Let this gen the status
                     }
                     return error();
+                case XSTMT1o | XEXPR1o:
+                    return disambiguateStatementVsExpression();
                 default:
                     throw new InternalError("Case not covered " + token.kind.belongs + " in " + token.kind);
             }
@@ -685,6 +693,44 @@
             }
         }
 
+        public Completeness disambiguateStatementVsExpression() {
+            if (token.kind == SWITCH) {
+                nextToken();
+                switch (token.kind) {
+                    case PARENS:
+                        nextToken();
+                        break;
+                    case UNMATCHED:
+                        nextToken();
+                        return Completeness.DEFINITELY_INCOMPLETE;
+                    case EOF:
+                        return Completeness.DEFINITELY_INCOMPLETE;
+                    default:
+                        return error();
+                }
+                switch (token.kind) {
+                    case BRACES:
+                        nextToken();
+                        break;
+                    case UNMATCHED:
+                        nextToken();
+                        return Completeness.DEFINITELY_INCOMPLETE;
+                    case EOF:
+                        return Completeness.DEFINITELY_INCOMPLETE;
+                    default:
+                        return error();
+                }
+                if (token.kind.valueOp) {
+                    return parseExpressionOptionalSemi();
+                } else {
+                    return Completeness.COMPLETE;
+                }
+            } else {
+                throw new InternalError("Unexpected statement/expression not covered " + token.kind.belongs + " in " + token.kind);
+            }
+        }
+
+
         public Completeness disambiguateDeclarationVsExpression() {
             // String folding messes up position information.
             return parseFactory.apply(pt -> {
@@ -699,7 +745,7 @@
                     case LABELED_STATEMENT:
                         if (shouldAbort(IDENTIFIER))  return checkResult;
                         if (shouldAbort(COLON))  return checkResult;
-                    return parseStatement();
+                        return parseStatement();
                     case VARIABLE:
                     case IMPORT:
                     case CLASS:
--- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java	Mon Oct 22 12:13:29 2018 +0200
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java	Mon Oct 22 08:30:39 2018 -0700
@@ -160,7 +160,6 @@
             case WHILE:
             case DO:
             case TRY:
-            case SWITCH:
             case RETURN:
             case THROW:
             case BREAK:
--- a/test/langtools/jdk/jshell/ToolLocalSimpleTest.java	Mon Oct 22 12:13:29 2018 +0200
+++ b/test/langtools/jdk/jshell/ToolLocalSimpleTest.java	Mon Oct 22 08:30:39 2018 -0700
@@ -90,4 +90,14 @@
         // can't set --enable-preview for local, ignore
     }
 
+    @Test
+    public void testSwitchExpression() {
+        // can't set --enable-preview for local, ignore
+    }
+
+    @Test
+    public void testSwitchExpressionCompletion() {
+        // can't set --enable-preview for local, ignore
+    }
+
 }
--- a/test/langtools/jdk/jshell/ToolSimpleTest.java	Mon Oct 22 12:13:29 2018 +0200
+++ b/test/langtools/jdk/jshell/ToolSimpleTest.java	Mon Oct 22 08:30:39 2018 -0700
@@ -76,21 +76,50 @@
 
     @Test
     public void testRawString() {
-         test(false, new String[]{"--enable-preview", "--no-startup"},
-                 (a) -> assertCommand(a, "String s = `abc`", "s ==> \"abc\""),
-                 (a) -> assertCommand(a, "String a = `abc", ""),
-                 (a) -> assertCommand(a, "def`", "a ==> \"abc\\ndef\""),
-                 (a) -> assertCommand(a, "String bj = ``Hi, `Bob` and ```Jim```.``", "bj ==> \"Hi, `Bob` and ```Jim```.\""),
-                 (a) -> assertCommand(a, "String hw = ````````````", ""),
-                 (a) -> assertCommand(a, "Hello, world", ""),
-                 (a) -> assertCommand(a, "````````````;", "hw ==> \"\\nHello, world\\n\""),
-                 (a) -> assertCommand(a, "String uc = `\\u000d\\u000a`", "uc ==> \"\\\\u000d\\\\u000a\""),
-                 (a) -> assertCommand(a, "String es = `\\(.\\)\\1`", "es ==> \"\\\\(.\\\\)\\\\1\""),
-                 (a) -> assertCommand(a, "String end = `abc`+`def`+`ghi`", "end ==> \"abcdefghi\"")
+        test(false, new String[]{"--enable-preview", "--no-startup"},
+                (a) -> assertCommand(a, "String s = `abc`", "s ==> \"abc\""),
+                (a) -> assertCommand(a, "String a = `abc", ""),
+                (a) -> assertCommand(a, "def`", "a ==> \"abc\\ndef\""),
+                (a) -> assertCommand(a, "String bj = ``Hi, `Bob` and ```Jim```.``", "bj ==> \"Hi, `Bob` and ```Jim```.\""),
+                (a) -> assertCommand(a, "String hw = ````````````", ""),
+                (a) -> assertCommand(a, "Hello, world", ""),
+                (a) -> assertCommand(a, "````````````;", "hw ==> \"\\nHello, world\\n\""),
+                (a) -> assertCommand(a, "String uc = `\\u000d\\u000a`", "uc ==> \"\\\\u000d\\\\u000a\""),
+                (a) -> assertCommand(a, "String es = `\\(.\\)\\1`", "es ==> \"\\\\(.\\\\)\\\\1\""),
+                (a) -> assertCommand(a, "String end = `abc`+`def`+`ghi`", "end ==> \"abcdefghi\"")
         );
     }
 
     @Test
+    public void testSwitchExpression() {
+        test(false, new String[]{"--enable-preview", "--no-startup"},
+                (a) -> assertCommand(a, "enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }", "|  created enum Day"),
+                (a) -> assertCommand(a, "Day day = Day.FRIDAY;", "day ==> FRIDAY"),
+                (a) -> assertCommand(a, "switch (day) {", ""),
+                (a) -> assertCommand(a, "case MONDAY, FRIDAY, SUNDAY -> 6;", ""),
+                (a) -> assertCommand(a, "case TUESDAY -> 7;", ""),
+                (a) -> assertCommand(a, "case THURSDAY, SATURDAY -> 8;", ""),
+                (a) -> assertCommand(a, "case WEDNESDAY -> 9;", ""),
+                (a) -> assertCommandOutputContains(a, "}", " ==> 6")
+                );
+    }
+
+    @Test
+    public void testSwitchExpressionCompletion() {
+        test(false, new String[]{"--enable-preview", "--no-startup"},
+                (a) -> assertCommand(a, "enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }", "|  created enum Day"),
+                (a) -> assertCommand(a, "Day day = Day.FRIDAY;", "day ==> FRIDAY"),
+                (a) -> assertCommand(a, "switch (day) {", ""),
+                (a) -> assertCommand(a, "case MONDAY, FRIDAY, SUNDAY -> 6;", ""),
+                (a) -> assertCommand(a, "case TUESDAY -> 7;", ""),
+                (a) -> assertCommand(a, "case THURSDAY, SATURDAY -> 8;", ""),
+                (a) -> assertCommand(a, "case WEDNESDAY -> 9;", ""),
+                (a) -> assertCommand(a, "} +", ""),
+                (a) -> assertCommandOutputContains(a, "1000", " ==> 1006")
+                );
+    }
+
+    @Test
     public void testLessThan() {
         test(
                 (a) -> assertCommand(a, "45", "$1 ==> 45"),