ReplParser.java revision 3062:15bdc18525ff
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 */ 25package jdk.jshell; 26 27import com.sun.tools.javac.code.TypeTag; 28import com.sun.tools.javac.parser.JavacParser; 29import com.sun.tools.javac.parser.ParserFactory; 30import com.sun.tools.javac.parser.Tokens.Comment; 31import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle; 32import com.sun.tools.javac.parser.Tokens.Token; 33import static com.sun.tools.javac.parser.Tokens.TokenKind.CLASS; 34import static com.sun.tools.javac.parser.Tokens.TokenKind.COLON; 35import static com.sun.tools.javac.parser.Tokens.TokenKind.ENUM; 36import static com.sun.tools.javac.parser.Tokens.TokenKind.EOF; 37import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT; 38import static com.sun.tools.javac.parser.Tokens.TokenKind.INTERFACE; 39import static com.sun.tools.javac.parser.Tokens.TokenKind.LPAREN; 40import static com.sun.tools.javac.parser.Tokens.TokenKind.MONKEYS_AT; 41import static com.sun.tools.javac.parser.Tokens.TokenKind.PACKAGE; 42import static com.sun.tools.javac.parser.Tokens.TokenKind.SEMI; 43import static com.sun.tools.javac.parser.Tokens.TokenKind.VOID; 44import com.sun.tools.javac.tree.JCTree; 45import com.sun.tools.javac.tree.JCTree.JCAnnotation; 46import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 47import com.sun.tools.javac.tree.JCTree.JCExpression; 48import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; 49import com.sun.tools.javac.tree.JCTree.JCModifiers; 50import com.sun.tools.javac.tree.JCTree.JCPackageDecl; 51import com.sun.tools.javac.tree.JCTree.JCStatement; 52import com.sun.tools.javac.tree.JCTree.JCTypeParameter; 53import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 54import com.sun.tools.javac.tree.JCTree.Tag; 55import static com.sun.tools.javac.tree.JCTree.Tag.IDENT; 56import com.sun.tools.javac.util.List; 57import com.sun.tools.javac.util.ListBuffer; 58import com.sun.tools.javac.util.Name; 59import com.sun.tools.javac.util.Position; 60 61/** 62 * This is a subclass of JavacParser which overrides one method with a modified 63 * verson of that method designed to allow parsing of one "snippet" of Java 64 * code without the surrounding context of class, method, etc. 65 * Accepts an expression, a statement, an import, or the declaration of a 66 * method, variable, or type (class, interface, ...). 67 */ 68class ReplParser extends JavacParser { 69 70 public ReplParser(ParserFactory fac, 71 com.sun.tools.javac.parser.Lexer S, 72 boolean keepDocComments, 73 boolean keepLineMap, 74 boolean keepEndPositions) { 75 super(fac, S, keepDocComments, keepLineMap, keepEndPositions); 76 } 77 78 /** 79 * As faithful a clone of the overridden method as possible while still 80 * achieving the goal of allowing the parse of a stand-alone snippet. 81 * As a result, some variables are assigned and never used, tests are 82 * always true, loops don't, etc. This is to allow easy transition as the 83 * underlying method changes. 84 * @return a snippet wrapped in a compilation unit 85 */ 86 @Override 87 public JCCompilationUnit parseCompilationUnit() { 88 Token firstToken = token; 89 JCModifiers mods = null; 90 boolean seenImport = false; 91 boolean seenPackage = false; 92 ListBuffer<JCTree> defs = new ListBuffer<>(); 93 if (token.kind == MONKEYS_AT) { 94 mods = modifiersOpt(); 95 } 96 97 if (token.kind == PACKAGE) { 98 int packagePos = token.pos; 99 List<JCAnnotation> annotations = List.nil(); 100 seenPackage = true; 101 if (mods != null) { 102 checkNoMods(mods.flags); 103 annotations = mods.annotations; 104 mods = null; 105 } 106 nextToken(); 107 JCExpression pid = qualident(false); 108 accept(SEMI); 109 JCPackageDecl pd = F.at(packagePos).PackageDecl(annotations, pid); 110 attach(pd, firstToken.comment(CommentStyle.JAVADOC)); 111 storeEnd(pd, token.pos); 112 defs.append(pd); 113 } 114 115 boolean firstTypeDecl = true; 116 while (token.kind != EOF) { 117 if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) { 118 // error recovery 119 skip(true, false, false, false); 120 if (token.kind == EOF) { 121 break; 122 } 123 } 124 if (mods == null && token.kind == IMPORT) { 125 seenImport = true; 126 defs.append(importDeclaration()); 127 } else { 128 Comment docComment = token.comment(CommentStyle.JAVADOC); 129 if (firstTypeDecl && !seenImport && !seenPackage) { 130 docComment = firstToken.comment(CommentStyle.JAVADOC); 131 } 132 List<? extends JCTree> udefs = replUnit(mods, docComment); 133 // if (def instanceof JCExpressionStatement) 134 // def = ((JCExpressionStatement)def).expr; 135 for (JCTree def : udefs) { 136 defs.append(def); 137 } 138 mods = null; 139 firstTypeDecl = false; 140 } 141 break; // Remove to process more than one snippet 142 } 143 List<JCTree> rdefs = defs.toList(); 144 class ReplUnit extends JCCompilationUnit { 145 146 public ReplUnit(List<JCTree> defs) { 147 super(defs); 148 } 149 } 150 JCCompilationUnit toplevel = new ReplUnit(rdefs); 151 if (rdefs.isEmpty()) { 152 storeEnd(toplevel, S.prevToken().endPos); 153 } 154 toplevel.lineMap = S.getLineMap(); 155 this.endPosTable.setParser(null); // remove reference to parser 156 toplevel.endPositions = this.endPosTable; 157 return toplevel; 158 } 159 160 @SuppressWarnings("fallthrough") 161 List<? extends JCTree> replUnit(JCModifiers pmods, Comment dc) { 162 switch (token.kind) { 163 case EOF: 164 return List.nil(); 165 case RBRACE: 166 case CASE: 167 case DEFAULT: 168 // These are illegal, fall through to handle as illegal statement 169 case LBRACE: 170 case IF: 171 case FOR: 172 case WHILE: 173 case DO: 174 case TRY: 175 case SWITCH: 176 case RETURN: 177 case THROW: 178 case BREAK: 179 case CONTINUE: 180 case SEMI: 181 case ELSE: 182 case FINALLY: 183 case CATCH: 184 case ASSERT: 185 return List.<JCTree>of(parseStatement()); 186 case SYNCHRONIZED: 187 if (peekToken(LPAREN)) { 188 return List.<JCTree>of(parseStatement()); 189 } 190 //fall-through 191 default: 192 JCModifiers mods = modifiersOpt(pmods); 193 if (token.kind == CLASS 194 || token.kind == INTERFACE 195 || token.kind == ENUM) { 196 return List.<JCTree>of(classOrInterfaceOrEnumDeclaration(mods, dc)); 197 } else { 198 int pos = token.pos; 199 List<JCTypeParameter> typarams = typeParametersOpt(); 200 // if there are type parameters but no modifiers, save the start 201 // position of the method in the modifiers. 202 if (typarams.nonEmpty() && mods.pos == Position.NOPOS) { 203 mods.pos = pos; 204 storeEnd(mods, pos); 205 } 206 List<JCAnnotation> annosAfterParams = annotationsOpt(Tag.ANNOTATION); 207 208 if (annosAfterParams.nonEmpty()) { 209 checkAnnotationsAfterTypeParams(annosAfterParams.head.pos); 210 mods.annotations = mods.annotations.appendList(annosAfterParams); 211 if (mods.pos == Position.NOPOS) { 212 mods.pos = mods.annotations.head.pos; 213 } 214 } 215 216 Token prevToken = token; 217 pos = token.pos; 218 JCExpression t; 219 boolean isVoid = token.kind == VOID; 220 if (isVoid) { 221 t = to(F.at(pos).TypeIdent(TypeTag.VOID)); 222 nextToken(); 223 } else { 224 // return type of method, declared type of variable, or an expression 225 t = term(EXPR | TYPE); 226 } 227 if (token.kind == COLON && t.hasTag(IDENT)) { 228 // labelled statement 229 nextToken(); 230 JCStatement stat = parseStatement(); 231 return List.<JCTree>of(F.at(pos).Labelled(prevToken.name(), stat)); 232 } else if ((isVoid || (lastmode & TYPE) != 0) && LAX_IDENTIFIER.accepts(token.kind)) { 233 // we have "Type Ident", so we can assume it is variable or method declaration 234 pos = token.pos; 235 Name name = ident(); 236 if (token.kind == LPAREN) { 237 // method declaration 238 //mods.flags |= Flags.STATIC; 239 return List.of(methodDeclaratorRest( 240 pos, mods, t, name, typarams, 241 false, isVoid, dc)); 242 } else if (!isVoid && typarams.isEmpty()) { 243 // variable declaration 244 //mods.flags |= Flags.STATIC; 245 List<JCTree> defs 246 = variableDeclaratorsRest(pos, mods, t, name, false, dc, 247 new ListBuffer<JCTree>()).toList(); 248 accept(SEMI); 249 storeEnd(defs.last(), S.prevToken().endPos); 250 return defs; 251 } else { 252 // malformed declaration, return error 253 pos = token.pos; 254 List<JCTree> err = isVoid 255 ? List.<JCTree>of(toP(F.at(pos).MethodDef(mods, name, t, typarams, 256 List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null))) 257 : null; 258 return List.<JCTree>of(syntaxError(token.pos, err, "expected", LPAREN)); 259 } 260 } else if (!typarams.isEmpty()) { 261 // type parameters on non-variable non-method -- error 262 return List.<JCTree>of(syntaxError(token.pos, "illegal.start.of.type")); 263 } else { 264 // expression-statement or expression to evaluate 265 JCExpressionStatement expr = toP(F.at(pos).Exec(t)); 266 return List.<JCTree>of(expr); 267 } 268 269 } 270 } 271 } 272} 273