diff -r ee3fd0a4120d src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java --- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java Mon Nov 20 16:02:05 2017 +0530 +++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java Tue Nov 21 12:49:36 2017 -0800 @@ -31,14 +31,25 @@ import com.sun.tools.javac.parser.Tokens.Comment; import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle; import com.sun.tools.javac.parser.Tokens.Token; +import com.sun.tools.javac.parser.Tokens.TokenKind; import static com.sun.tools.javac.parser.Tokens.TokenKind.CLASS; import static com.sun.tools.javac.parser.Tokens.TokenKind.COLON; +import static com.sun.tools.javac.parser.Tokens.TokenKind.COMMA; +import static com.sun.tools.javac.parser.Tokens.TokenKind.DOT; import static com.sun.tools.javac.parser.Tokens.TokenKind.ENUM; import static com.sun.tools.javac.parser.Tokens.TokenKind.EOF; +import static com.sun.tools.javac.parser.Tokens.TokenKind.GT; +import static com.sun.tools.javac.parser.Tokens.TokenKind.GTGT; +import static com.sun.tools.javac.parser.Tokens.TokenKind.GTGTGT; +import static com.sun.tools.javac.parser.Tokens.TokenKind.IDENTIFIER; import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT; import static com.sun.tools.javac.parser.Tokens.TokenKind.INTERFACE; +import static com.sun.tools.javac.parser.Tokens.TokenKind.LBRACKET; import static com.sun.tools.javac.parser.Tokens.TokenKind.LPAREN; +import static com.sun.tools.javac.parser.Tokens.TokenKind.LT; import static com.sun.tools.javac.parser.Tokens.TokenKind.MONKEYS_AT; +import static com.sun.tools.javac.parser.Tokens.TokenKind.QUES; +import static com.sun.tools.javac.parser.Tokens.TokenKind.RBRACKET; import static com.sun.tools.javac.parser.Tokens.TokenKind.SEMI; import static com.sun.tools.javac.parser.Tokens.TokenKind.VOID; import com.sun.tools.javac.tree.JCTree; @@ -51,6 +62,7 @@ import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.Tag; import static com.sun.tools.javac.tree.JCTree.Tag.IDENT; +import com.sun.tools.javac.util.Filter; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; @@ -142,8 +154,7 @@ return toplevel; } - @SuppressWarnings("fallthrough") - List replUnit(JCModifiers pmods, Comment dc) { + List replUnit(JCModifiers pmods, Comment dc) { switch (token.kind) { case EOF: return List.nil(); @@ -172,89 +183,171 @@ if (peekToken(LPAREN)) { return List.of(parseStatement()); } - //fall-through + return replDefault(TYPE, pmods, dc); + case IDENTIFIER: + case BOOLEAN: + case BYTE: + case CHAR: + case DOUBLE: + case FLOAT: + case INT: + case LONG: + case SHORT: + return replDefault(isType(1, false) ? TYPE : EXPR, pmods, dc); default: - JCModifiers mods = modifiersOpt(pmods); - if (token.kind == CLASS - || token.kind == INTERFACE - || token.kind == ENUM) { - return List.of(classOrInterfaceOrEnumDeclaration(mods, dc)); + return replDefault(EXPR | TYPE, pmods, dc); + } + } + + private List replDefault(int mode, JCModifiers pmods, Comment dc) { + JCModifiers mods = modifiersOpt(pmods); + if (token.kind == CLASS || + token.kind == INTERFACE || + token.kind == ENUM) { + return List.of(classOrInterfaceOrEnumDeclaration(mods, dc)); + } else { + int pos = token.pos; + List typarams = typeParametersOpt(); + // if there are type parameters but no modifiers, save the start + // position of the method in the modifiers. + if (typarams.nonEmpty() && mods.pos == Position.NOPOS) { + mods.pos = pos; + storeEnd(mods, pos); + } + List annosAfterParams = annotationsOpt(Tag.ANNOTATION); + + if (annosAfterParams.nonEmpty()) { + checkAnnotationsAfterTypeParams(annosAfterParams.head.pos); + mods.annotations = mods.annotations.appendList(annosAfterParams); + if (mods.pos == Position.NOPOS) { + mods.pos = mods.annotations.head.pos; + } + } + + Token prevToken = token; + pos = token.pos; + JCExpression t; + boolean isVoid = token.kind == VOID; + if (isVoid) { + t = to(F.at(pos).TypeIdent(TypeTag.VOID)); + nextToken(); + } else { + // return type of method, declared type of variable, or an expression + t = term(mode); + } + if (token.kind == COLON && t.hasTag(IDENT)) { + // labelled statement + nextToken(); + JCStatement stat = parseStatement(); + return List.of(F.at(pos).Labelled(prevToken.name(), stat)); + } else if ((isVoid || (lastmode & TYPE) != 0) && LAX_IDENTIFIER.accepts(token.kind)) { + // we have "Type Ident", so we can assume it is variable or method declaration + pos = token.pos; + Name name = ident(); + if (token.kind == LPAREN) { + // method declaration + //mods.flags |= Flags.STATIC; + return List.of(methodDeclaratorRest( + pos, mods, t, name, typarams, + false, isVoid, dc)); + } else if (!isVoid && typarams.isEmpty()) { + // variable declaration + //mods.flags |= Flags.STATIC; + List defs + = variableDeclaratorsRest(pos, mods, t, name, false, dc, + new ListBuffer(), true).toList(); + accept(SEMI); + storeEnd(defs.last(), S.prevToken().endPos); + return defs; } else { - int pos = token.pos; - List typarams = typeParametersOpt(); - // if there are type parameters but no modifiers, save the start - // position of the method in the modifiers. - if (typarams.nonEmpty() && mods.pos == Position.NOPOS) { - mods.pos = pos; - storeEnd(mods, pos); - } - List annosAfterParams = annotationsOpt(Tag.ANNOTATION); + // malformed declaration, return error + pos = token.pos; + List err = isVoid + ? List.of(toP(F.at(pos).MethodDef(mods, name, t, typarams, + List.nil(), List.nil(), null, null))) + : null; + return List.of(syntaxError(token.pos, err, "expected", LPAREN)); + } + } else if (!typarams.isEmpty()) { + // type parameters on non-variable non-method -- error + return List.of(syntaxError(token.pos, "illegal.start.of.type")); + } else { + // expression-statement or expression to evaluate + JCExpressionStatement expr = toP(F.at(pos).Exec(t)); + return List.of(expr); + } - if (annosAfterParams.nonEmpty()) { - checkAnnotationsAfterTypeParams(annosAfterParams.head.pos); - mods.annotations = mods.annotations.appendList(annosAfterParams); - if (mods.pos == Position.NOPOS) { - mods.pos = mods.annotations.head.pos; - } - } + } + } - Token prevToken = token; - pos = token.pos; - JCExpression t; - boolean isVoid = token.kind == VOID; - if (isVoid) { - t = to(F.at(pos).TypeIdent(TypeTag.VOID)); - nextToken(); - } else { - // return type of method, declared type of variable, or an expression - // unless expression is being forced - t = term(forceExpression - ? EXPR - : EXPR | TYPE); - } - if (token.kind == COLON && t.hasTag(IDENT)) { - // labelled statement - nextToken(); - JCStatement stat = parseStatement(); - return List.of(F.at(pos).Labelled(prevToken.name(), stat)); - } else if ((isVoid || (lastmode & TYPE) != 0) && LAX_IDENTIFIER.accepts(token.kind)) { - // we have "Type Ident", so we can assume it is variable or method declaration - pos = token.pos; - Name name = ident(); - if (token.kind == LPAREN) { - // method declaration - //mods.flags |= Flags.STATIC; - return List.of(methodDeclaratorRest( - pos, mods, t, name, typarams, - false, isVoid, dc)); - } else if (!isVoid && typarams.isEmpty()) { - // variable declaration - //mods.flags |= Flags.STATIC; - List defs - = variableDeclaratorsRest(pos, mods, t, name, false, dc, - new ListBuffer(), true).toList(); - accept(SEMI); - storeEnd(defs.last(), S.prevToken().endPos); - return defs; - } else { - // malformed declaration, return error - pos = token.pos; - List err = isVoid - ? List.of(toP(F.at(pos).MethodDef(mods, name, t, typarams, - List.nil(), List.nil(), null, null))) - : null; - return List.of(syntaxError(token.pos, err, "expected", LPAREN)); - } - } else if (!typarams.isEmpty()) { - // type parameters on non-variable non-method -- error - return List.of(syntaxError(token.pos, "illegal.start.of.type")); - } else { - // expression-statement or expression to evaluate - JCExpressionStatement expr = toP(F.at(pos).Exec(t)); - return List.of(expr); - } - + // Determine if the lookahead contains a type. + // Precondition: the Snippet starts with an identifier or primitive type name. + // Assume: that the type starts a variable or method declaration. + // Based on UnannType and RefenceType in the Java Language Specification + private boolean isType(int lookahead, boolean inTypeArguments) { + switch (S.token(lookahead).kind) { + // Annotated ArrayType : Dims + // id @ ... -- is not a valid expressions + case MONKEYS_AT: + return true; + // UnannArrayType : Dims + // id [ ] -- is not a valid expression, unless foloowed by .class + case LBRACKET: + return S.token(lookahead + 1).kind == RBRACKET && + S.token(lookahead + 2).kind != DOT; + // TypeArguments -- id < id , and id < id > are not valid expression starts + case COMMA: + case GT: + case GTGT: + case GTGTGT: + return inTypeArguments; + // Type ended, beginning of MethodDeclarator or FieldDeclarator + // type id -- is declarator, not expression + case IDENTIFIER: + return true; + // [Unann]ClassOrInterfaceType . {Annotation} Identifier [TypeArguments] + case DOT: + switch (S.token(lookahead + 1).kind) { + // UnannClassOrInterfaceType . Annotation ... + // id . @ ... + case MONKEYS_AT: + return true; + // [Unann]ClassOrInterfaceType . Identifier [TypeArguments] + case IDENTIFIER: + // move past id . id + return isType(lookahead + 2, inTypeArguments); + default: + return false; } + // Identifier TypeArguments + // id < ... -- has an ambiguity between less-than and a type with type arguments + case LT: + switch (S.token(lookahead + 1).kind) { + // Identifier < Wildcard ... + // neither id < ? ... nor id < @ ... are valid expressions + case MONKEYS_AT: + case QUES: + return true; + // id < id ... -- if it is type, the next token will be + // a comma (separating type arguments) or a > to close the type argument, + // or it will be a ReferenceType (tail recursion)) + case IDENTIFIER: + case BOOLEAN: + case BYTE: + case CHAR: + case DOUBLE: + case FLOAT: + case INT: + case LONG: + case SHORT: + // Look for ReferenceType within TypeArgument + // move past id < id + return isType(lookahead + 2, true); + default: + return false; + } + default: + return false; } } } diff -r ee3fd0a4120d src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java --- a/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java Mon Nov 20 16:02:05 2017 +0530 +++ b/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java Tue Nov 21 12:49:36 2017 -0800 @@ -225,7 +225,7 @@ // Parse a snippet and return our parse task handler Z parse(final String source, Worker worker) { return parse(source, false, pt -> { - if (!pt.units().isEmpty() + if (false && !pt.units().isEmpty() && pt.units().get(0).getKind() == Kind.EXPRESSION_STATEMENT && pt.getDiagnostics().hasOtherThanNotStatementErrors()) { // It failed, it may be an expression being incorrectly