/* * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.javac.parser; import java.util.Locale; import com.sun.tools.javac.api.Formattable; import com.sun.tools.javac.api.Messages; import com.sun.tools.javac.parser.Tokens.Token.Tag; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Filter; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Names; /** A class that defines codes/utilities for Java source tokens * returned from lexical analysis. * * <p><b>This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice.</b> */ public class Tokens { private final Names names; /** * Keyword array. Maps name indices to Token. */ private final TokenKind[] key; /** The number of the last entered keyword. */ private int maxKey = 0; /** The names of all tokens. */ private Name[] tokenName = new Name[TokenKind.values().length]; public static final Context.Key<Tokens> tokensKey = new Context.Key<>(); public static Tokens instance(Context context) { Tokens instance = context.get(tokensKey); if (instance == null) instance = new Tokens(context); return instance; } protected Tokens(Context context) { context.put(tokensKey, this); names = Names.instance(context); for (TokenKind t : TokenKind.values()) { if (t.name != null) enterKeyword(t.name, t); else tokenName[t.ordinal()] = null; } key = new TokenKind[maxKey+1]; for (int i = 0; i <= maxKey; i++) key[i] = TokenKind.IDENTIFIER; for (TokenKind t : TokenKind.values()) { if (t.name != null) key[tokenName[t.ordinal()].getIndex()] = t; } } private void enterKeyword(String s, TokenKind token) { Name n = names.fromString(s); tokenName[token.ordinal()] = n; if (n.getIndex() > maxKey) maxKey = n.getIndex(); } /** * Create a new token given a name; if the name corresponds to a token name, * a new token of the corresponding kind is returned; otherwise, an * identifier token is returned. */ TokenKind lookupKind(Name name) { return (name.getIndex() > maxKey) ? TokenKind.IDENTIFIER : key[name.getIndex()]; } TokenKind lookupKind(String name) { return lookupKind(names.fromString(name)); } /** * This enum defines all tokens used by the javac scanner. A token is * optionally associated with a name. */ public enum TokenKind implements Formattable, Filter<TokenKind> { EOF(), ERROR(), IDENTIFIER(Tag.NAMED), ABSTRACT("abstract"), ASSERT("assert", Tag.NAMED), BOOLEAN("boolean", Tag.NAMED), BREAK("break"), BYTE("byte", Tag.NAMED), CASE("case"), CATCH("catch"), CHAR("char", Tag.NAMED), CLASS("class"), CONST("const"), CONTINUE("continue"), DEFAULT("default"), DO("do"), DOUBLE("double", Tag.NAMED), ELSE("else"), ENUM("enum", Tag.NAMED), EXTENDS("extends"), FINAL("final"), FINALLY("finally"), FLOAT("float", Tag.NAMED), FOR("for"), GOTO("goto"), IF("if"), IMPLEMENTS("implements"), IMPORT("import"), INSTANCEOF("instanceof"), INT("int", Tag.NAMED), INTERFACE("interface"), LONG("long", Tag.NAMED), NATIVE("native"), NEW("new"), PACKAGE("package"), PRIVATE("private"), PROTECTED("protected"), PUBLIC("public"), RETURN("return"), SHORT("short", Tag.NAMED), STATIC("static"), STRICTFP("strictfp"), SUPER("super", Tag.NAMED), SWITCH("switch"), SYNCHRONIZED("synchronized"), THIS("this", Tag.NAMED), THROW("throw"), THROWS("throws"), TRANSIENT("transient"), TRY("try"), VOID("void", Tag.NAMED), VOLATILE("volatile"), WHILE("while"), INTLITERAL(Tag.NUMERIC), LONGLITERAL(Tag.NUMERIC), FLOATLITERAL(Tag.NUMERIC), DOUBLELITERAL(Tag.NUMERIC), CHARLITERAL(Tag.NUMERIC), STRINGLITERAL(Tag.STRING), TRUE("true", Tag.NAMED), FALSE("false", Tag.NAMED), NULL("null", Tag.NAMED), UNDERSCORE("_", Tag.NAMED), ARROW("->"), COLCOL("::"), LPAREN("("), RPAREN(")"), LBRACE("{"), RBRACE("}"), LBRACKET("["), RBRACKET("]"), SEMI(";"), COMMA(","), DOT("."), ELLIPSIS("..."), EQ("="), GT(">"), LT("<"), BANG("!"), TILDE("~"), QUES("?"), COLON(":"), EQEQ("=="), LTEQ("<="), GTEQ(">="), BANGEQ("!="), AMPAMP("&&"), BARBAR("||"), PLUSPLUS("++"), SUBSUB("--"), PLUS("+"), SUB("-"), STAR("*"), SLASH("/"), AMP("&"), BAR("|"), CARET("^"), PERCENT("%"), LTLT("<<"), GTGT(">>"), GTGTGT(">>>"), PLUSEQ("+="), SUBEQ("-="), STAREQ("*="), SLASHEQ("/="), AMPEQ("&="), BAREQ("|="), CARETEQ("^="), PERCENTEQ("%="), LTLTEQ("<<="), GTGTEQ(">>="), GTGTGTEQ(">>>="), MONKEYS_AT("@"), CUSTOM; public final String name; public final Tag tag; TokenKind() { this(null, Tag.DEFAULT); } TokenKind(String name) { this(name, Tag.DEFAULT); } TokenKind(Tag tag) { this(null, tag); } TokenKind(String name, Tag tag) { this.name = name; this.tag = tag; } public String toString() { switch (this) { case IDENTIFIER: return "token.identifier"; case CHARLITERAL: return "token.character"; case STRINGLITERAL: return "token.string"; case INTLITERAL: return "token.integer"; case LONGLITERAL: return "token.long-integer"; case FLOATLITERAL: return "token.float"; case DOUBLELITERAL: return "token.double"; case ERROR: return "token.bad-symbol"; case EOF: return "token.end-of-input"; case DOT: case COMMA: case SEMI: case LPAREN: case RPAREN: case LBRACKET: case RBRACKET: case LBRACE: case RBRACE: return "'" + name + "'"; default: return name; } } public String getKind() { return "Token"; } public String toString(Locale locale, Messages messages) { return name != null ? toString() : messages.getLocalizedString(locale, "compiler.misc." + toString()); } @Override public boolean accepts(TokenKind that) { return this == that; } } public interface Comment { enum CommentStyle { LINE, BLOCK, JAVADOC, } String getText(); int getSourcePos(int index); CommentStyle getStyle(); boolean isDeprecated(); } /** * This is the class representing a javac token. Each token has several fields * that are set by the javac lexer (i.e. start/end position, string value, etc). */ public static class Token { /** tags constants **/ enum Tag { DEFAULT, NAMED, STRING, NUMERIC } /** The token kind */ public final TokenKind kind; /** The start position of this token */ public final int pos; /** The end position of this token */ public final int endPos; /** Comment reader associated with this token */ public final List<Comment> comments; Token(TokenKind kind, int pos, int endPos, List<Comment> comments) { this.kind = kind; this.pos = pos; this.endPos = endPos; this.comments = comments; checkKind(); } Token[] split(Tokens tokens) { if (kind.name.length() < 2 || kind.tag != Tag.DEFAULT) { throw new AssertionError("Cant split" + kind); } TokenKind t1 = tokens.lookupKind(kind.name.substring(0, 1)); TokenKind t2 = tokens.lookupKind(kind.name.substring(1)); if (t1 == null || t2 == null) { throw new AssertionError("Cant split - bad subtokens"); } return new Token[] { new Token(t1, pos, pos + t1.name.length(), comments), new Token(t2, pos + t1.name.length(), endPos, null) }; } protected void checkKind() { if (kind.tag != Tag.DEFAULT) { throw new AssertionError("Bad token kind - expected " + Tag.STRING); } } public Name name() { throw new UnsupportedOperationException(); } public String stringVal() { throw new UnsupportedOperationException(); } public int radix() { throw new UnsupportedOperationException(); } /** * Preserve classic semantics - if multiple javadocs are found on the token * the last one is returned */ public Comment comment(Comment.CommentStyle style) { List<Comment> comments = getComments(Comment.CommentStyle.JAVADOC); return comments.isEmpty() ? null : comments.head; } /** * Preserve classic semantics - deprecated should be set if at least one * javadoc comment attached to this token contains the '@deprecated' string */ public boolean deprecatedFlag() { for (Comment c : getComments(Comment.CommentStyle.JAVADOC)) { if (c.isDeprecated()) { return true; } } return false; } private List<Comment> getComments(Comment.CommentStyle style) { if (comments == null) { return List.nil(); } else { ListBuffer<Comment> buf = new ListBuffer<>(); for (Comment c : comments) { if (c.getStyle() == style) { buf.add(c); } } return buf.toList(); } } } final static class NamedToken extends Token { /** The name of this token */ public final Name name; public NamedToken(TokenKind kind, int pos, int endPos, Name name, List<Comment> comments) { super(kind, pos, endPos, comments); this.name = name; } protected void checkKind() { if (kind.tag != Tag.NAMED) { throw new AssertionError("Bad token kind - expected " + Tag.NAMED); } } @Override public Name name() { return name; } } static class StringToken extends Token { /** The string value of this token */ public final String stringVal; public StringToken(TokenKind kind, int pos, int endPos, String stringVal, List<Comment> comments) { super(kind, pos, endPos, comments); this.stringVal = stringVal; } protected void checkKind() { if (kind.tag != Tag.STRING) { throw new AssertionError("Bad token kind - expected " + Tag.STRING); } } @Override public String stringVal() { return stringVal; } } final static class NumericToken extends StringToken { /** The 'radix' value of this token */ public final int radix; public NumericToken(TokenKind kind, int pos, int endPos, String stringVal, int radix, List<Comment> comments) { super(kind, pos, endPos, stringVal, comments); this.radix = radix; } protected void checkKind() { if (kind.tag != Tag.NUMERIC) { throw new AssertionError("Bad token kind - expected " + Tag.NUMERIC); } } @Override public int radix() { return radix; } } public static final Token DUMMY = new Token(TokenKind.ERROR, 0, 0, null); }