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