/*
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jshell;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.parser.JavacParser;
import com.sun.tools.javac.parser.ParserFactory;
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 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.ENUM;
import static com.sun.tools.javac.parser.Tokens.TokenKind.EOF;
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.LPAREN;
import static com.sun.tools.javac.parser.Tokens.TokenKind.MONKEYS_AT;
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;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCStatement;
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.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Position;
This is a subclass of JavacParser which overrides one method with a modified
verson of that method designed to allow parsing of one "snippet" of Java
code without the surrounding context of class, method, etc.
Accepts an expression, a statement, an import, or the declaration of a
method, variable, or type (class, interface, ...).
/**
* This is a subclass of JavacParser which overrides one method with a modified
* verson of that method designed to allow parsing of one "snippet" of Java
* code without the surrounding context of class, method, etc.
* Accepts an expression, a statement, an import, or the declaration of a
* method, variable, or type (class, interface, ...).
*/
class ReplParser extends JavacParser {
// force starting in expression mode
private final boolean forceExpression;
public ReplParser(ParserFactory fac,
com.sun.tools.javac.parser.Lexer S,
boolean keepDocComments,
boolean keepLineMap,
boolean keepEndPositions,
boolean forceExpression) {
super(fac, S, keepDocComments, keepLineMap, keepEndPositions);
this.forceExpression = forceExpression;
}
As faithful a clone of the overridden method as possible while still
achieving the goal of allowing the parse of a stand-alone snippet.
As a result, some variables are assigned and never used, tests are
always true, loops don't, etc. This is to allow easy transition as the
underlying method changes.
Returns: a snippet wrapped in a compilation unit
/**
* As faithful a clone of the overridden method as possible while still
* achieving the goal of allowing the parse of a stand-alone snippet.
* As a result, some variables are assigned and never used, tests are
* always true, loops don't, etc. This is to allow easy transition as the
* underlying method changes.
* @return a snippet wrapped in a compilation unit
*/
@Override
public JCCompilationUnit parseCompilationUnit() {
Token firstToken = token;
JCModifiers mods = null;
boolean seenImport = false;
boolean seenPackage = false;
ListBuffer<JCTree> defs = new ListBuffer<>();
if (token.kind == MONKEYS_AT) {
mods = modifiersOpt();
}
boolean firstTypeDecl = true;
while (token.kind != EOF) {
if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) {
// error recovery
skip(true, false, false, false);
if (token.kind == EOF) {
break;
}
}
if (mods == null && token.kind == IMPORT) {
seenImport = true;
defs.append(importDeclaration());
} else {
Comment docComment = token.comment(CommentStyle.JAVADOC);
if (firstTypeDecl && !seenImport && !seenPackage) {
docComment = firstToken.comment(CommentStyle.JAVADOC);
}
List<? extends JCTree> udefs = replUnit(mods, docComment);
// if (def instanceof JCExpressionStatement)
// def = ((JCExpressionStatement)def).expr;
for (JCTree def : udefs) {
defs.append(def);
}
mods = null;
firstTypeDecl = false;
}
break; // Remove to process more than one snippet
}
List<JCTree> rdefs = defs.toList();
class ReplUnit extends JCCompilationUnit {
public ReplUnit(List<JCTree> defs) {
super(defs);
}
}
JCCompilationUnit toplevel = new ReplUnit(rdefs);
if (rdefs.isEmpty()) {
storeEnd(toplevel, S.prevToken().endPos);
}
toplevel.lineMap = S.getLineMap();
this.endPosTable.setParser(null); // remove reference to parser
toplevel.endPositions = this.endPosTable;
return toplevel;
}
@SuppressWarnings("fallthrough")
List<? extends JCTree> replUnit(JCModifiers pmods, Comment dc) {
switch (token.kind) {
case EOF:
return List.nil();
case RBRACE:
case CASE:
case DEFAULT:
// These are illegal, fall through to handle as illegal statement
case LBRACE:
case IF:
case FOR:
case WHILE:
case DO:
case TRY:
case SWITCH:
case RETURN:
case THROW:
case BREAK:
case CONTINUE:
case SEMI:
case ELSE:
case FINALLY:
case CATCH:
case ASSERT:
return List.<JCTree>of(parseStatement());
case SYNCHRONIZED:
if (peekToken(LPAREN)) {
return List.<JCTree>of(parseStatement());
}
//fall-through
default:
JCModifiers mods = modifiersOpt(pmods);
if (token.kind == CLASS
|| token.kind == INTERFACE
|| token.kind == ENUM) {
return List.<JCTree>of(classOrInterfaceOrEnumDeclaration(mods, dc));
} else {
int pos = token.pos;
List<JCTypeParameter> 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<JCAnnotation> 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
// 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.<JCTree>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<JCTree> defs
= variableDeclaratorsRest(pos, mods, t, name, false, dc,
new ListBuffer<JCTree>()).toList();
accept(SEMI);
storeEnd(defs.last(), S.prevToken().endPos);
return defs;
} else {
// malformed declaration, return error
pos = token.pos;
List<JCTree> err = isVoid
? List.of(toP(F.at(pos).MethodDef(mods, name, t, typarams,
List.nil(), List.nil(), null, null)))
: null;
return List.<JCTree>of(syntaxError(token.pos, err, "expected", LPAREN));
}
} else if (!typarams.isEmpty()) {
// type parameters on non-variable non-method -- error
return List.<JCTree>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.<JCTree>of(expr);
}
}
}
}
}