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

View file

@ -228,15 +228,15 @@ class CompletenessAnalyzer {
// Declarations and type parameters (thus expressions) // Declarations and type parameters (thus expressions)
EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL), // extends EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL), // extends
COMMA(TokenKind.COMMA, XEXPR|XDECL), // , COMMA(TokenKind.COMMA, XEXPR|XDECL), // ,
AMP(TokenKind.AMP, XEXPR|XDECL), // & AMP(TokenKind.AMP, XEXPR|XDECL, true), // &
GT(TokenKind.GT, XEXPR|XDECL), // > GT(TokenKind.GT, XEXPR|XDECL, true), // >
LT(TokenKind.LT, XEXPR|XDECL1), // < LT(TokenKind.LT, XEXPR|XDECL1, true), // <
LTLT(TokenKind.LTLT, XEXPR|XDECL1), // << LTLT(TokenKind.LTLT, XEXPR|XDECL1, true), // <<
GTGT(TokenKind.GTGT, XEXPR|XDECL), // >> GTGT(TokenKind.GTGT, XEXPR|XDECL, true), // >>
GTGTGT(TokenKind.GTGTGT, XEXPR|XDECL), // >>> GTGTGT(TokenKind.GTGTGT, XEXPR|XDECL, true), // >>>
QUES(TokenKind.QUES, XEXPR|XDECL), // ? QUES(TokenKind.QUES, XEXPR|XDECL, true), // ?
DOT(TokenKind.DOT, XEXPR|XDECL), // . DOT(TokenKind.DOT, XEXPR|XDECL), // .
STAR(TokenKind.STAR, XEXPR), // * (MAPPED: DOTSTAR) STAR(TokenKind.STAR, XEXPR, true), // * (MAPPED: DOTSTAR)
// Statement keywords // Statement keywords
ASSERT(TokenKind.ASSERT, XSTMT1|XSTART), // assert ASSERT(TokenKind.ASSERT, XSTMT1|XSTART), // assert
@ -249,7 +249,7 @@ class CompletenessAnalyzer {
FOR(TokenKind.FOR, XSTMT1|XSTART), // for FOR(TokenKind.FOR, XSTMT1|XSTART), // for
IF(TokenKind.IF, XSTMT1|XSTART), // if IF(TokenKind.IF, XSTMT1|XSTART), // if
RETURN(TokenKind.RETURN, XSTMT1|XTERM|XSTART), // return 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 SYNCHRONIZED(TokenKind.SYNCHRONIZED, XSTMT1|XDECL), // synchronized
THROW(TokenKind.THROW, XSTMT1|XSTART), // throw THROW(TokenKind.THROW, XSTMT1|XSTART), // throw
TRY(TokenKind.TRY, XSTMT1|XSTART), // try TRY(TokenKind.TRY, XSTMT1|XSTART), // try
@ -276,7 +276,7 @@ class CompletenessAnalyzer {
SUBSUB(TokenKind.SUBSUB, XEXPR1|XTERM), // -- SUBSUB(TokenKind.SUBSUB, XEXPR1|XTERM), // --
// Expressions cannot terminate // Expressions cannot terminate
INSTANCEOF(TokenKind.INSTANCEOF, XEXPR), // instanceof INSTANCEOF(TokenKind.INSTANCEOF, XEXPR, true), // instanceof
NEW(TokenKind.NEW, XEXPR1), // new (MAPPED: COLCOLNEW) NEW(TokenKind.NEW, XEXPR1), // new (MAPPED: COLCOLNEW)
SUPER(TokenKind.SUPER, XEXPR1|XDECL), // super -- shouldn't see as rec. But in type parameters SUPER(TokenKind.SUPER, XEXPR1|XDECL), // super -- shouldn't see as rec. But in type parameters
ARROW(TokenKind.ARROW, XEXPR), // -> ARROW(TokenKind.ARROW, XEXPR), // ->
@ -292,18 +292,18 @@ class CompletenessAnalyzer {
BANG(TokenKind.BANG, XEXPR1), // ! BANG(TokenKind.BANG, XEXPR1), // !
TILDE(TokenKind.TILDE, XEXPR1), // ~ TILDE(TokenKind.TILDE, XEXPR1), // ~
COLON(TokenKind.COLON, XEXPR|XTERM), // : COLON(TokenKind.COLON, XEXPR|XTERM), // :
EQEQ(TokenKind.EQEQ, XEXPR), // == EQEQ(TokenKind.EQEQ, XEXPR, true), // ==
LTEQ(TokenKind.LTEQ, XEXPR), // <= LTEQ(TokenKind.LTEQ, XEXPR, true), // <=
GTEQ(TokenKind.GTEQ, XEXPR), // >= GTEQ(TokenKind.GTEQ, XEXPR, true), // >=
BANGEQ(TokenKind.BANGEQ, XEXPR), // != BANGEQ(TokenKind.BANGEQ, XEXPR, true), // !=
AMPAMP(TokenKind.AMPAMP, XEXPR), // && AMPAMP(TokenKind.AMPAMP, XEXPR, true), // &&
BARBAR(TokenKind.BARBAR, XEXPR), // || BARBAR(TokenKind.BARBAR, XEXPR, true), // ||
PLUS(TokenKind.PLUS, XEXPR1), // + PLUS(TokenKind.PLUS, XEXPR1, true), // +
SUB(TokenKind.SUB, XEXPR1), // - SUB(TokenKind.SUB, XEXPR1, true), // -
SLASH(TokenKind.SLASH, XEXPR), // / SLASH(TokenKind.SLASH, XEXPR, true), // /
BAR(TokenKind.BAR, XEXPR), // | BAR(TokenKind.BAR, XEXPR, true), // |
CARET(TokenKind.CARET, XEXPR), // ^ CARET(TokenKind.CARET, XEXPR, true), // ^
PERCENT(TokenKind.PERCENT, XEXPR), // % PERCENT(TokenKind.PERCENT, XEXPR, true), // %
PLUSEQ(TokenKind.PLUSEQ, XEXPR), // += PLUSEQ(TokenKind.PLUSEQ, XEXPR), // +=
SUBEQ(TokenKind.SUBEQ, XEXPR), // -= SUBEQ(TokenKind.SUBEQ, XEXPR), // -=
STAREQ(TokenKind.STAREQ, XEXPR), // *= STAREQ(TokenKind.STAREQ, XEXPR), // *=
@ -330,6 +330,7 @@ class CompletenessAnalyzer {
final TokenKind tokenKind; final TokenKind tokenKind;
final int belongs; final int belongs;
final boolean valueOp;
Function<TK,TK> mapping; Function<TK,TK> mapping;
TK(int b) { TK(int b) {
@ -337,8 +338,13 @@ class CompletenessAnalyzer {
} }
TK(TokenKind tokenKind, int b) { TK(TokenKind tokenKind, int b) {
this(tokenKind, b, false);
}
TK(TokenKind tokenKind, int b, boolean valueOp) {
this.tokenKind = tokenKind; this.tokenKind = tokenKind;
this.belongs = b; this.belongs = b;
this.valueOp = valueOp;
this.mapping = null; this.mapping = null;
} }
@ -637,6 +643,8 @@ class CompletenessAnalyzer {
return parseExpressionStatement(); // Let this gen the status return parseExpressionStatement(); // Let this gen the status
} }
return error(); return error();
case XSTMT1o | XEXPR1o:
return disambiguateStatementVsExpression();
default: default:
throw new InternalError("Case not covered " + token.kind.belongs + " in " + token.kind); 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() { public Completeness disambiguateDeclarationVsExpression() {
// String folding messes up position information. // String folding messes up position information.
return parseFactory.apply(pt -> { return parseFactory.apply(pt -> {
@ -699,7 +745,7 @@ class CompletenessAnalyzer {
case LABELED_STATEMENT: case LABELED_STATEMENT:
if (shouldAbort(IDENTIFIER)) return checkResult; if (shouldAbort(IDENTIFIER)) return checkResult;
if (shouldAbort(COLON)) return checkResult; if (shouldAbort(COLON)) return checkResult;
return parseStatement(); return parseStatement();
case VARIABLE: case VARIABLE:
case IMPORT: case IMPORT:
case CLASS: case CLASS:

View file

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

View file

@ -90,4 +90,14 @@ public class ToolLocalSimpleTest extends ToolSimpleTest {
// can't set --enable-preview for local, ignore // 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
}
} }

View file

@ -76,20 +76,49 @@ public class ToolSimpleTest extends ReplToolTesting {
@Test @Test
public void testRawString() { public void testRawString() {
test(false, new String[]{"--enable-preview", "--no-startup"}, test(false, new String[]{"--enable-preview", "--no-startup"},
(a) -> assertCommand(a, "String s = `abc`", "s ==> \"abc\""), (a) -> assertCommand(a, "String s = `abc`", "s ==> \"abc\""),
(a) -> assertCommand(a, "String a = `abc", ""), (a) -> assertCommand(a, "String a = `abc", ""),
(a) -> assertCommand(a, "def`", "a ==> \"abc\\ndef\""), (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 bj = ``Hi, `Bob` and ```Jim```.``", "bj ==> \"Hi, `Bob` and ```Jim```.\""),
(a) -> assertCommand(a, "String hw = ````````````", ""), (a) -> assertCommand(a, "String hw = ````````````", ""),
(a) -> assertCommand(a, "Hello, world", ""), (a) -> assertCommand(a, "Hello, world", ""),
(a) -> assertCommand(a, "````````````;", "hw ==> \"\\nHello, world\\n\""), (a) -> assertCommand(a, "````````````;", "hw ==> \"\\nHello, world\\n\""),
(a) -> assertCommand(a, "String uc = `\\u000d\\u000a`", "uc ==> \"\\\\u000d\\\\u000a\""), (a) -> assertCommand(a, "String uc = `\\u000d\\u000a`", "uc ==> \"\\\\u000d\\\\u000a\""),
(a) -> assertCommand(a, "String es = `\\(.\\)\\1`", "es ==> \"\\\\(.\\\\)\\\\1\""), (a) -> assertCommand(a, "String es = `\\(.\\)\\1`", "es ==> \"\\\\(.\\\\)\\\\1\""),
(a) -> assertCommand(a, "String end = `abc`+`def`+`ghi`", "end ==> \"abcdefghi\"") (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 @Test
public void testLessThan() { public void testLessThan() {
test( test(