ReplParser.java revision 3828:d30434bde0a8
1/* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.jshell; 27 28import com.sun.tools.javac.code.TypeTag; 29import com.sun.tools.javac.parser.JavacParser; 30import com.sun.tools.javac.parser.ParserFactory; 31import com.sun.tools.javac.parser.Tokens.Comment; 32import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle; 33import com.sun.tools.javac.parser.Tokens.Token; 34import static com.sun.tools.javac.parser.Tokens.TokenKind.CLASS; 35import static com.sun.tools.javac.parser.Tokens.TokenKind.COLON; 36import static com.sun.tools.javac.parser.Tokens.TokenKind.ENUM; 37import static com.sun.tools.javac.parser.Tokens.TokenKind.EOF; 38import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT; 39import static com.sun.tools.javac.parser.Tokens.TokenKind.INTERFACE; 40import static com.sun.tools.javac.parser.Tokens.TokenKind.LPAREN; 41import static com.sun.tools.javac.parser.Tokens.TokenKind.MONKEYS_AT; 42import static com.sun.tools.javac.parser.Tokens.TokenKind.PACKAGE; 43import static com.sun.tools.javac.parser.Tokens.TokenKind.SEMI; 44import static com.sun.tools.javac.parser.Tokens.TokenKind.VOID; 45import com.sun.tools.javac.tree.JCTree; 46import com.sun.tools.javac.tree.JCTree.JCAnnotation; 47import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 48import com.sun.tools.javac.tree.JCTree.JCExpression; 49import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; 50import com.sun.tools.javac.tree.JCTree.JCModifiers; 51import com.sun.tools.javac.tree.JCTree.JCPackageDecl; 52import com.sun.tools.javac.tree.JCTree.JCStatement; 53import com.sun.tools.javac.tree.JCTree.JCTypeParameter; 54import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 55import com.sun.tools.javac.tree.JCTree.Tag; 56import static com.sun.tools.javac.tree.JCTree.Tag.IDENT; 57import com.sun.tools.javac.util.List; 58import com.sun.tools.javac.util.ListBuffer; 59import com.sun.tools.javac.util.Name; 60import com.sun.tools.javac.util.Position; 61 62/** 63 * This is a subclass of JavacParser which overrides one method with a modified 64 * verson of that method designed to allow parsing of one "snippet" of Java 65 * code without the surrounding context of class, method, etc. 66 * Accepts an expression, a statement, an import, or the declaration of a 67 * method, variable, or type (class, interface, ...). 68 */ 69class ReplParser extends JavacParser { 70 71 public ReplParser(ParserFactory fac, 72 com.sun.tools.javac.parser.Lexer S, 73 boolean keepDocComments, 74 boolean keepLineMap, 75 boolean keepEndPositions) { 76 super(fac, S, keepDocComments, keepLineMap, keepEndPositions); 77 } 78 79 /** 80 * As faithful a clone of the overridden method as possible while still 81 * achieving the goal of allowing the parse of a stand-alone snippet. 82 * As a result, some variables are assigned and never used, tests are 83 * always true, loops don't, etc. This is to allow easy transition as the 84 * underlying method changes. 85 * @return a snippet wrapped in a compilation unit 86 */ 87 @Override 88 public JCCompilationUnit parseCompilationUnit() { 89 Token firstToken = token; 90 JCModifiers mods = null; 91 boolean seenImport = false; 92 boolean seenPackage = false; 93 ListBuffer<JCTree> defs = new ListBuffer<>(); 94 if (token.kind == MONKEYS_AT) { 95 mods = modifiersOpt(); 96 } 97 98 boolean firstTypeDecl = true; 99 while (token.kind != EOF) { 100 if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) { 101 // error recovery 102 skip(true, false, false, false); 103 if (token.kind == EOF) { 104 break; 105 } 106 } 107 if (mods == null && token.kind == IMPORT) { 108 seenImport = true; 109 defs.append(importDeclaration()); 110 } else { 111 Comment docComment = token.comment(CommentStyle.JAVADOC); 112 if (firstTypeDecl && !seenImport && !seenPackage) { 113 docComment = firstToken.comment(CommentStyle.JAVADOC); 114 } 115 List<? extends JCTree> udefs = replUnit(mods, docComment); 116 // if (def instanceof JCExpressionStatement) 117 // def = ((JCExpressionStatement)def).expr; 118 for (JCTree def : udefs) { 119 defs.append(def); 120 } 121 mods = null; 122 firstTypeDecl = false; 123 } 124 break; // Remove to process more than one snippet 125 } 126 List<JCTree> rdefs = defs.toList(); 127 class ReplUnit extends JCCompilationUnit { 128 129 public ReplUnit(List<JCTree> defs) { 130 super(defs); 131 } 132 } 133 JCCompilationUnit toplevel = new ReplUnit(rdefs); 134 if (rdefs.isEmpty()) { 135 storeEnd(toplevel, S.prevToken().endPos); 136 } 137 toplevel.lineMap = S.getLineMap(); 138 this.endPosTable.setParser(null); // remove reference to parser 139 toplevel.endPositions = this.endPosTable; 140 return toplevel; 141 } 142 143 @SuppressWarnings("fallthrough") 144 List<? extends JCTree> replUnit(JCModifiers pmods, Comment dc) { 145 switch (token.kind) { 146 case EOF: 147 return List.nil(); 148 case RBRACE: 149 case CASE: 150 case DEFAULT: 151 // These are illegal, fall through to handle as illegal statement 152 case LBRACE: 153 case IF: 154 case FOR: 155 case WHILE: 156 case DO: 157 case TRY: 158 case SWITCH: 159 case RETURN: 160 case THROW: 161 case BREAK: 162 case CONTINUE: 163 case SEMI: 164 case ELSE: 165 case FINALLY: 166 case CATCH: 167 case ASSERT: 168 return List.<JCTree>of(parseStatement()); 169 case SYNCHRONIZED: 170 if (peekToken(LPAREN)) { 171 return List.<JCTree>of(parseStatement()); 172 } 173 //fall-through 174 default: 175 JCModifiers mods = modifiersOpt(pmods); 176 if (token.kind == CLASS 177 || token.kind == INTERFACE 178 || token.kind == ENUM) { 179 return List.<JCTree>of(classOrInterfaceOrEnumDeclaration(mods, dc)); 180 } else { 181 int pos = token.pos; 182 List<JCTypeParameter> typarams = typeParametersOpt(); 183 // if there are type parameters but no modifiers, save the start 184 // position of the method in the modifiers. 185 if (typarams.nonEmpty() && mods.pos == Position.NOPOS) { 186 mods.pos = pos; 187 storeEnd(mods, pos); 188 } 189 List<JCAnnotation> annosAfterParams = annotationsOpt(Tag.ANNOTATION); 190 191 if (annosAfterParams.nonEmpty()) { 192 checkAnnotationsAfterTypeParams(annosAfterParams.head.pos); 193 mods.annotations = mods.annotations.appendList(annosAfterParams); 194 if (mods.pos == Position.NOPOS) { 195 mods.pos = mods.annotations.head.pos; 196 } 197 } 198 199 Token prevToken = token; 200 pos = token.pos; 201 JCExpression t; 202 boolean isVoid = token.kind == VOID; 203 if (isVoid) { 204 t = to(F.at(pos).TypeIdent(TypeTag.VOID)); 205 nextToken(); 206 } else { 207 // return type of method, declared type of variable, or an expression 208 t = term(EXPR | TYPE); 209 } 210 if (token.kind == COLON && t.hasTag(IDENT)) { 211 // labelled statement 212 nextToken(); 213 JCStatement stat = parseStatement(); 214 return List.<JCTree>of(F.at(pos).Labelled(prevToken.name(), stat)); 215 } else if ((isVoid || (lastmode & TYPE) != 0) && LAX_IDENTIFIER.accepts(token.kind)) { 216 // we have "Type Ident", so we can assume it is variable or method declaration 217 pos = token.pos; 218 Name name = ident(); 219 if (token.kind == LPAREN) { 220 // method declaration 221 //mods.flags |= Flags.STATIC; 222 return List.of(methodDeclaratorRest( 223 pos, mods, t, name, typarams, 224 false, isVoid, dc)); 225 } else if (!isVoid && typarams.isEmpty()) { 226 // variable declaration 227 //mods.flags |= Flags.STATIC; 228 List<JCTree> defs 229 = variableDeclaratorsRest(pos, mods, t, name, false, dc, 230 new ListBuffer<JCTree>()).toList(); 231 accept(SEMI); 232 storeEnd(defs.last(), S.prevToken().endPos); 233 return defs; 234 } else { 235 // malformed declaration, return error 236 pos = token.pos; 237 List<JCTree> err = isVoid 238 ? List.of(toP(F.at(pos).MethodDef(mods, name, t, typarams, 239 List.nil(), List.nil(), null, null))) 240 : null; 241 return List.<JCTree>of(syntaxError(token.pos, err, "expected", LPAREN)); 242 } 243 } else if (!typarams.isEmpty()) { 244 // type parameters on non-variable non-method -- error 245 return List.<JCTree>of(syntaxError(token.pos, "illegal.start.of.type")); 246 } else { 247 // expression-statement or expression to evaluate 248 JCExpressionStatement expr = toP(F.at(pos).Exec(t)); 249 return List.<JCTree>of(expr); 250 } 251 252 } 253 } 254 } 255} 256