Tokens.java revision 2571:10fc81ac75b4
1/*
2 * Copyright (c) 1999, 2013, 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 com.sun.tools.javac.parser;
27
28import java.util.Locale;
29
30import com.sun.tools.javac.api.Formattable;
31import com.sun.tools.javac.api.Messages;
32import com.sun.tools.javac.parser.Tokens.Token.Tag;
33import com.sun.tools.javac.util.List;
34import com.sun.tools.javac.util.Name;
35import com.sun.tools.javac.util.Context;
36import com.sun.tools.javac.util.Filter;
37import com.sun.tools.javac.util.ListBuffer;
38import com.sun.tools.javac.util.Names;
39
40/** A class that defines codes/utilities for Java source tokens
41 *  returned from lexical analysis.
42 *
43 *  <p><b>This is NOT part of any supported API.
44 *  If you write code that depends on this, you do so at your own risk.
45 *  This code and its internal interfaces are subject to change or
46 *  deletion without notice.</b>
47 */
48public class Tokens {
49
50    private final Names names;
51
52    /**
53     * Keyword array. Maps name indices to Token.
54     */
55    private final TokenKind[] key;
56
57    /**  The number of the last entered keyword.
58     */
59    private int maxKey = 0;
60
61    /** The names of all tokens.
62     */
63    private Name[] tokenName = new Name[TokenKind.values().length];
64
65    public static final Context.Key<Tokens> tokensKey = new Context.Key<>();
66
67    public static Tokens instance(Context context) {
68        Tokens instance = context.get(tokensKey);
69        if (instance == null)
70            instance = new Tokens(context);
71        return instance;
72    }
73
74    protected Tokens(Context context) {
75        context.put(tokensKey, this);
76        names = Names.instance(context);
77        for (TokenKind t : TokenKind.values()) {
78            if (t.name != null)
79                enterKeyword(t.name, t);
80            else
81                tokenName[t.ordinal()] = null;
82        }
83
84        key = new TokenKind[maxKey+1];
85        for (int i = 0; i <= maxKey; i++) key[i] = TokenKind.IDENTIFIER;
86        for (TokenKind t : TokenKind.values()) {
87            if (t.name != null)
88            key[tokenName[t.ordinal()].getIndex()] = t;
89        }
90    }
91
92    private void enterKeyword(String s, TokenKind token) {
93        Name n = names.fromString(s);
94        tokenName[token.ordinal()] = n;
95        if (n.getIndex() > maxKey) maxKey = n.getIndex();
96    }
97
98    /**
99     * Create a new token given a name; if the name corresponds to a token name,
100     * a new token of the corresponding kind is returned; otherwise, an
101     * identifier token is returned.
102     */
103    TokenKind lookupKind(Name name) {
104        return (name.getIndex() > maxKey) ? TokenKind.IDENTIFIER : key[name.getIndex()];
105    }
106
107    TokenKind lookupKind(String name) {
108        return lookupKind(names.fromString(name));
109    }
110
111    /**
112     * This enum defines all tokens used by the javac scanner. A token is
113     * optionally associated with a name.
114     */
115    public enum TokenKind implements Formattable, Filter<TokenKind> {
116        EOF(),
117        ERROR(),
118        IDENTIFIER(Tag.NAMED),
119        ABSTRACT("abstract"),
120        ASSERT("assert", Tag.NAMED),
121        BOOLEAN("boolean", Tag.NAMED),
122        BREAK("break"),
123        BYTE("byte", Tag.NAMED),
124        CASE("case"),
125        CATCH("catch"),
126        CHAR("char", Tag.NAMED),
127        CLASS("class"),
128        CONST("const"),
129        CONTINUE("continue"),
130        DEFAULT("default"),
131        DO("do"),
132        DOUBLE("double", Tag.NAMED),
133        ELSE("else"),
134        ENUM("enum", Tag.NAMED),
135        EXTENDS("extends"),
136        FINAL("final"),
137        FINALLY("finally"),
138        FLOAT("float", Tag.NAMED),
139        FOR("for"),
140        GOTO("goto"),
141        IF("if"),
142        IMPLEMENTS("implements"),
143        IMPORT("import"),
144        INSTANCEOF("instanceof"),
145        INT("int", Tag.NAMED),
146        INTERFACE("interface"),
147        LONG("long", Tag.NAMED),
148        NATIVE("native"),
149        NEW("new"),
150        PACKAGE("package"),
151        PRIVATE("private"),
152        PROTECTED("protected"),
153        PUBLIC("public"),
154        RETURN("return"),
155        SHORT("short", Tag.NAMED),
156        STATIC("static"),
157        STRICTFP("strictfp"),
158        SUPER("super", Tag.NAMED),
159        SWITCH("switch"),
160        SYNCHRONIZED("synchronized"),
161        THIS("this", Tag.NAMED),
162        THROW("throw"),
163        THROWS("throws"),
164        TRANSIENT("transient"),
165        TRY("try"),
166        VOID("void", Tag.NAMED),
167        VOLATILE("volatile"),
168        WHILE("while"),
169        INTLITERAL(Tag.NUMERIC),
170        LONGLITERAL(Tag.NUMERIC),
171        FLOATLITERAL(Tag.NUMERIC),
172        DOUBLELITERAL(Tag.NUMERIC),
173        CHARLITERAL(Tag.NUMERIC),
174        STRINGLITERAL(Tag.STRING),
175        TRUE("true", Tag.NAMED),
176        FALSE("false", Tag.NAMED),
177        NULL("null", Tag.NAMED),
178        UNDERSCORE("_", Tag.NAMED),
179        ARROW("->"),
180        COLCOL("::"),
181        LPAREN("("),
182        RPAREN(")"),
183        LBRACE("{"),
184        RBRACE("}"),
185        LBRACKET("["),
186        RBRACKET("]"),
187        SEMI(";"),
188        COMMA(","),
189        DOT("."),
190        ELLIPSIS("..."),
191        EQ("="),
192        GT(">"),
193        LT("<"),
194        BANG("!"),
195        TILDE("~"),
196        QUES("?"),
197        COLON(":"),
198        EQEQ("=="),
199        LTEQ("<="),
200        GTEQ(">="),
201        BANGEQ("!="),
202        AMPAMP("&&"),
203        BARBAR("||"),
204        PLUSPLUS("++"),
205        SUBSUB("--"),
206        PLUS("+"),
207        SUB("-"),
208        STAR("*"),
209        SLASH("/"),
210        AMP("&"),
211        BAR("|"),
212        CARET("^"),
213        PERCENT("%"),
214        LTLT("<<"),
215        GTGT(">>"),
216        GTGTGT(">>>"),
217        PLUSEQ("+="),
218        SUBEQ("-="),
219        STAREQ("*="),
220        SLASHEQ("/="),
221        AMPEQ("&="),
222        BAREQ("|="),
223        CARETEQ("^="),
224        PERCENTEQ("%="),
225        LTLTEQ("<<="),
226        GTGTEQ(">>="),
227        GTGTGTEQ(">>>="),
228        MONKEYS_AT("@"),
229        CUSTOM;
230
231        public final String name;
232        public final Tag tag;
233
234        TokenKind() {
235            this(null, Tag.DEFAULT);
236        }
237
238        TokenKind(String name) {
239            this(name, Tag.DEFAULT);
240        }
241
242        TokenKind(Tag tag) {
243            this(null, tag);
244        }
245
246        TokenKind(String name, Tag tag) {
247            this.name = name;
248            this.tag = tag;
249        }
250
251        public String toString() {
252            switch (this) {
253            case IDENTIFIER:
254                return "token.identifier";
255            case CHARLITERAL:
256                return "token.character";
257            case STRINGLITERAL:
258                return "token.string";
259            case INTLITERAL:
260                return "token.integer";
261            case LONGLITERAL:
262                return "token.long-integer";
263            case FLOATLITERAL:
264                return "token.float";
265            case DOUBLELITERAL:
266                return "token.double";
267            case ERROR:
268                return "token.bad-symbol";
269            case EOF:
270                return "token.end-of-input";
271            case DOT: case COMMA: case SEMI: case LPAREN: case RPAREN:
272            case LBRACKET: case RBRACKET: case LBRACE: case RBRACE:
273                return "'" + name + "'";
274            default:
275                return name;
276            }
277        }
278
279        public String getKind() {
280            return "Token";
281        }
282
283        public String toString(Locale locale, Messages messages) {
284            return name != null ? toString() : messages.getLocalizedString(locale, "compiler.misc." + toString());
285        }
286
287        @Override
288        public boolean accepts(TokenKind that) {
289            return this == that;
290        }
291    }
292
293    public interface Comment {
294
295        enum CommentStyle {
296            LINE,
297            BLOCK,
298            JAVADOC,
299        }
300
301        String getText();
302        int getSourcePos(int index);
303        CommentStyle getStyle();
304        boolean isDeprecated();
305    }
306
307    /**
308     * This is the class representing a javac token. Each token has several fields
309     * that are set by the javac lexer (i.e. start/end position, string value, etc).
310     */
311    public static class Token {
312
313        /** tags constants **/
314        enum Tag {
315            DEFAULT,
316            NAMED,
317            STRING,
318            NUMERIC
319        }
320
321        /** The token kind */
322        public final TokenKind kind;
323
324        /** The start position of this token */
325        public final int pos;
326
327        /** The end position of this token */
328        public final int endPos;
329
330        /** Comment reader associated with this token */
331        public final List<Comment> comments;
332
333        Token(TokenKind kind, int pos, int endPos, List<Comment> comments) {
334            this.kind = kind;
335            this.pos = pos;
336            this.endPos = endPos;
337            this.comments = comments;
338            checkKind();
339        }
340
341        Token[] split(Tokens tokens) {
342            if (kind.name.length() < 2 || kind.tag != Tag.DEFAULT) {
343                throw new AssertionError("Cant split" + kind);
344            }
345
346            TokenKind t1 = tokens.lookupKind(kind.name.substring(0, 1));
347            TokenKind t2 = tokens.lookupKind(kind.name.substring(1));
348
349            if (t1 == null || t2 == null) {
350                throw new AssertionError("Cant split - bad subtokens");
351            }
352            return new Token[] {
353                new Token(t1, pos, pos + t1.name.length(), comments),
354                new Token(t2, pos + t1.name.length(), endPos, null)
355            };
356        }
357
358        protected void checkKind() {
359            if (kind.tag != Tag.DEFAULT) {
360                throw new AssertionError("Bad token kind - expected " + Tag.STRING);
361            }
362        }
363
364        public Name name() {
365            throw new UnsupportedOperationException();
366        }
367
368        public String stringVal() {
369            throw new UnsupportedOperationException();
370        }
371
372        public int radix() {
373            throw new UnsupportedOperationException();
374        }
375
376        /**
377         * Preserve classic semantics - if multiple javadocs are found on the token
378         * the last one is returned
379         */
380        public Comment comment(Comment.CommentStyle style) {
381            List<Comment> comments = getComments(Comment.CommentStyle.JAVADOC);
382            return comments.isEmpty() ?
383                    null :
384                    comments.head;
385        }
386
387        /**
388         * Preserve classic semantics - deprecated should be set if at least one
389         * javadoc comment attached to this token contains the '@deprecated' string
390         */
391        public boolean deprecatedFlag() {
392            for (Comment c : getComments(Comment.CommentStyle.JAVADOC)) {
393                if (c.isDeprecated()) {
394                    return true;
395                }
396            }
397            return false;
398        }
399
400        private List<Comment> getComments(Comment.CommentStyle style) {
401            if (comments == null) {
402                return List.nil();
403            } else {
404                ListBuffer<Comment> buf = new ListBuffer<>();
405                for (Comment c : comments) {
406                    if (c.getStyle() == style) {
407                        buf.add(c);
408                    }
409                }
410                return buf.toList();
411            }
412        }
413    }
414
415    final static class NamedToken extends Token {
416        /** The name of this token */
417        public final Name name;
418
419        public NamedToken(TokenKind kind, int pos, int endPos, Name name, List<Comment> comments) {
420            super(kind, pos, endPos, comments);
421            this.name = name;
422        }
423
424        protected void checkKind() {
425            if (kind.tag != Tag.NAMED) {
426                throw new AssertionError("Bad token kind - expected " + Tag.NAMED);
427            }
428        }
429
430        @Override
431        public Name name() {
432            return name;
433        }
434    }
435
436    static class StringToken extends Token {
437        /** The string value of this token */
438        public final String stringVal;
439
440        public StringToken(TokenKind kind, int pos, int endPos, String stringVal, List<Comment> comments) {
441            super(kind, pos, endPos, comments);
442            this.stringVal = stringVal;
443        }
444
445        protected void checkKind() {
446            if (kind.tag != Tag.STRING) {
447                throw new AssertionError("Bad token kind - expected " + Tag.STRING);
448            }
449        }
450
451        @Override
452        public String stringVal() {
453            return stringVal;
454        }
455    }
456
457    final static class NumericToken extends StringToken {
458        /** The 'radix' value of this token */
459        public final int radix;
460
461        public NumericToken(TokenKind kind, int pos, int endPos, String stringVal, int radix, List<Comment> comments) {
462            super(kind, pos, endPos, stringVal, comments);
463            this.radix = radix;
464        }
465
466        protected void checkKind() {
467            if (kind.tag != Tag.NUMERIC) {
468                throw new AssertionError("Bad token kind - expected " + Tag.NUMERIC);
469            }
470        }
471
472        @Override
473        public int radix() {
474            return radix;
475        }
476    }
477
478    public static final Token DUMMY =
479                new Token(TokenKind.ERROR, 0, 0, null);
480}
481