8210923: JShell: support for switch expressions

Reviewed-by: jlahoda
This commit is contained in:
Robert Field 2018-10-22 08:30:39 -07:00
parent b3c1e4f663
commit 749916b897
4 changed files with 119 additions and 35 deletions
src/jdk.jshell/share/classes/jdk/jshell
test/langtools/jdk/jshell

@ -228,15 +228,15 @@ class CompletenessAnalyzer {
// 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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
final TokenKind tokenKind;
final int belongs;
final boolean valueOp;
Function<TK,TK> mapping;
TK(int b) {
@ -337,8 +338,13 @@ class CompletenessAnalyzer {
}
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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
}
}
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 @@ class CompletenessAnalyzer {
case LABELED_STATEMENT:
if (shouldAbort(IDENTIFIER)) return checkResult;
if (shouldAbort(COLON)) return checkResult;
return parseStatement();
return parseStatement();
case VARIABLE:
case IMPORT:
case CLASS:

@ -160,7 +160,6 @@ class ReplParser extends JavacParser {
case WHILE:
case DO:
case TRY:
case SWITCH:
case RETURN:
case THROW:
case BREAK:

@ -90,4 +90,14 @@ public class ToolLocalSimpleTest extends ToolSimpleTest {
// 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
}
}

@ -76,20 +76,49 @@ public class ToolSimpleTest extends ReplToolTesting {
@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(