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