aoqi@0: /* aoqi@0: * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.javac.parser; aoqi@0: aoqi@0: import java.util.Locale; aoqi@0: aoqi@0: import com.sun.tools.javac.api.Formattable; aoqi@0: import com.sun.tools.javac.api.Messages; aoqi@0: import com.sun.tools.javac.parser.Tokens.Token.Tag; aoqi@0: import com.sun.tools.javac.util.List; aoqi@0: import com.sun.tools.javac.util.Name; aoqi@0: import com.sun.tools.javac.util.Context; aoqi@0: import com.sun.tools.javac.util.Filter; aoqi@0: import com.sun.tools.javac.util.ListBuffer; aoqi@0: import com.sun.tools.javac.util.Names; aoqi@0: aoqi@0: /** A class that defines codes/utilities for Java source tokens aoqi@0: * returned from lexical analysis. aoqi@0: * aoqi@0: *

This is NOT part of any supported API. aoqi@0: * If you write code that depends on this, you do so at your own risk. aoqi@0: * This code and its internal interfaces are subject to change or aoqi@0: * deletion without notice. aoqi@0: */ aoqi@0: public class Tokens { aoqi@0: aoqi@0: private final Names names; aoqi@0: aoqi@0: /** aoqi@0: * Keyword array. Maps name indices to Token. aoqi@0: */ aoqi@0: private final TokenKind[] key; aoqi@0: aoqi@0: /** The number of the last entered keyword. aoqi@0: */ aoqi@0: private int maxKey = 0; aoqi@0: aoqi@0: /** The names of all tokens. aoqi@0: */ aoqi@0: private Name[] tokenName = new Name[TokenKind.values().length]; aoqi@0: aoqi@0: public static final Context.Key tokensKey = aoqi@0: new Context.Key(); aoqi@0: aoqi@0: public static Tokens instance(Context context) { aoqi@0: Tokens instance = context.get(tokensKey); aoqi@0: if (instance == null) aoqi@0: instance = new Tokens(context); aoqi@0: return instance; aoqi@0: } aoqi@0: aoqi@0: protected Tokens(Context context) { aoqi@0: context.put(tokensKey, this); aoqi@0: names = Names.instance(context); aoqi@0: for (TokenKind t : TokenKind.values()) { aoqi@0: if (t.name != null) aoqi@0: enterKeyword(t.name, t); aoqi@0: else aoqi@0: tokenName[t.ordinal()] = null; aoqi@0: } aoqi@0: aoqi@0: key = new TokenKind[maxKey+1]; aoqi@0: for (int i = 0; i <= maxKey; i++) key[i] = TokenKind.IDENTIFIER; aoqi@0: for (TokenKind t : TokenKind.values()) { aoqi@0: if (t.name != null) aoqi@0: key[tokenName[t.ordinal()].getIndex()] = t; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private void enterKeyword(String s, TokenKind token) { aoqi@0: Name n = names.fromString(s); aoqi@0: tokenName[token.ordinal()] = n; aoqi@0: if (n.getIndex() > maxKey) maxKey = n.getIndex(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Create a new token given a name; if the name corresponds to a token name, aoqi@0: * a new token of the corresponding kind is returned; otherwise, an aoqi@0: * identifier token is returned. aoqi@0: */ aoqi@0: TokenKind lookupKind(Name name) { aoqi@0: return (name.getIndex() > maxKey) ? TokenKind.IDENTIFIER : key[name.getIndex()]; aoqi@0: } aoqi@0: aoqi@0: TokenKind lookupKind(String name) { aoqi@0: return lookupKind(names.fromString(name)); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * This enum defines all tokens used by the javac scanner. A token is aoqi@0: * optionally associated with a name. aoqi@0: */ aoqi@0: public enum TokenKind implements Formattable, Filter { aoqi@0: EOF(), aoqi@0: ERROR(), aoqi@0: IDENTIFIER(Tag.NAMED), aoqi@0: ABSTRACT("abstract"), aoqi@0: ASSERT("assert", Tag.NAMED), aoqi@0: BOOLEAN("boolean", Tag.NAMED), aoqi@0: BREAK("break"), aoqi@0: BYTE("byte", Tag.NAMED), aoqi@0: CASE("case"), aoqi@0: CATCH("catch"), aoqi@0: CHAR("char", Tag.NAMED), aoqi@0: CLASS("class"), aoqi@0: CONST("const"), aoqi@0: CONTINUE("continue"), aoqi@0: DEFAULT("default"), aoqi@0: DO("do"), aoqi@0: DOUBLE("double", Tag.NAMED), aoqi@0: ELSE("else"), aoqi@0: ENUM("enum", Tag.NAMED), aoqi@0: EXTENDS("extends"), aoqi@0: FINAL("final"), aoqi@0: FINALLY("finally"), aoqi@0: FLOAT("float", Tag.NAMED), aoqi@0: FOR("for"), aoqi@0: GOTO("goto"), aoqi@0: IF("if"), aoqi@0: IMPLEMENTS("implements"), aoqi@0: IMPORT("import"), aoqi@0: INSTANCEOF("instanceof"), aoqi@0: INT("int", Tag.NAMED), aoqi@0: INTERFACE("interface"), aoqi@0: LONG("long", Tag.NAMED), aoqi@0: NATIVE("native"), aoqi@0: NEW("new"), aoqi@0: PACKAGE("package"), aoqi@0: PRIVATE("private"), aoqi@0: PROTECTED("protected"), aoqi@0: PUBLIC("public"), aoqi@0: RETURN("return"), aoqi@0: SHORT("short", Tag.NAMED), aoqi@0: STATIC("static"), aoqi@0: STRICTFP("strictfp"), aoqi@0: SUPER("super", Tag.NAMED), aoqi@0: SWITCH("switch"), aoqi@0: SYNCHRONIZED("synchronized"), aoqi@0: THIS("this", Tag.NAMED), aoqi@0: THROW("throw"), aoqi@0: THROWS("throws"), aoqi@0: TRANSIENT("transient"), aoqi@0: TRY("try"), aoqi@0: VOID("void", Tag.NAMED), aoqi@0: VOLATILE("volatile"), aoqi@0: WHILE("while"), aoqi@0: INTLITERAL(Tag.NUMERIC), aoqi@0: LONGLITERAL(Tag.NUMERIC), aoqi@0: FLOATLITERAL(Tag.NUMERIC), aoqi@0: DOUBLELITERAL(Tag.NUMERIC), aoqi@0: CHARLITERAL(Tag.NUMERIC), aoqi@0: STRINGLITERAL(Tag.STRING), aoqi@0: TRUE("true", Tag.NAMED), aoqi@0: FALSE("false", Tag.NAMED), aoqi@0: NULL("null", Tag.NAMED), aoqi@0: UNDERSCORE("_", Tag.NAMED), aoqi@0: ARROW("->"), aoqi@0: COLCOL("::"), aoqi@0: LPAREN("("), aoqi@0: RPAREN(")"), aoqi@0: LBRACE("{"), aoqi@0: RBRACE("}"), aoqi@0: LBRACKET("["), aoqi@0: RBRACKET("]"), aoqi@0: SEMI(";"), aoqi@0: COMMA(","), aoqi@0: DOT("."), aoqi@0: ELLIPSIS("..."), aoqi@0: EQ("="), aoqi@0: GT(">"), aoqi@0: LT("<"), aoqi@0: BANG("!"), aoqi@0: TILDE("~"), aoqi@0: QUES("?"), aoqi@0: COLON(":"), aoqi@0: EQEQ("=="), aoqi@0: LTEQ("<="), aoqi@0: GTEQ(">="), aoqi@0: BANGEQ("!="), aoqi@0: AMPAMP("&&"), aoqi@0: BARBAR("||"), aoqi@0: PLUSPLUS("++"), aoqi@0: SUBSUB("--"), aoqi@0: PLUS("+"), aoqi@0: SUB("-"), aoqi@0: STAR("*"), aoqi@0: SLASH("/"), aoqi@0: AMP("&"), aoqi@0: BAR("|"), aoqi@0: CARET("^"), aoqi@0: PERCENT("%"), aoqi@0: LTLT("<<"), aoqi@0: GTGT(">>"), aoqi@0: GTGTGT(">>>"), aoqi@0: PLUSEQ("+="), aoqi@0: SUBEQ("-="), aoqi@0: STAREQ("*="), aoqi@0: SLASHEQ("/="), aoqi@0: AMPEQ("&="), aoqi@0: BAREQ("|="), aoqi@0: CARETEQ("^="), aoqi@0: PERCENTEQ("%="), aoqi@0: LTLTEQ("<<="), aoqi@0: GTGTEQ(">>="), aoqi@0: GTGTGTEQ(">>>="), aoqi@0: MONKEYS_AT("@"), aoqi@0: CUSTOM; aoqi@0: aoqi@0: public final String name; aoqi@0: public final Tag tag; aoqi@0: aoqi@0: TokenKind() { aoqi@0: this(null, Tag.DEFAULT); aoqi@0: } aoqi@0: aoqi@0: TokenKind(String name) { aoqi@0: this(name, Tag.DEFAULT); aoqi@0: } aoqi@0: aoqi@0: TokenKind(Tag tag) { aoqi@0: this(null, tag); aoqi@0: } aoqi@0: aoqi@0: TokenKind(String name, Tag tag) { aoqi@0: this.name = name; aoqi@0: this.tag = tag; aoqi@0: } aoqi@0: aoqi@0: public String toString() { aoqi@0: switch (this) { aoqi@0: case IDENTIFIER: aoqi@0: return "token.identifier"; aoqi@0: case CHARLITERAL: aoqi@0: return "token.character"; aoqi@0: case STRINGLITERAL: aoqi@0: return "token.string"; aoqi@0: case INTLITERAL: aoqi@0: return "token.integer"; aoqi@0: case LONGLITERAL: aoqi@0: return "token.long-integer"; aoqi@0: case FLOATLITERAL: aoqi@0: return "token.float"; aoqi@0: case DOUBLELITERAL: aoqi@0: return "token.double"; aoqi@0: case ERROR: aoqi@0: return "token.bad-symbol"; aoqi@0: case EOF: aoqi@0: return "token.end-of-input"; aoqi@0: case DOT: case COMMA: case SEMI: case LPAREN: case RPAREN: aoqi@0: case LBRACKET: case RBRACKET: case LBRACE: case RBRACE: aoqi@0: return "'" + name + "'"; aoqi@0: default: aoqi@0: return name; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public String getKind() { aoqi@0: return "Token"; aoqi@0: } aoqi@0: aoqi@0: public String toString(Locale locale, Messages messages) { aoqi@0: return name != null ? toString() : messages.getLocalizedString(locale, "compiler.misc." + toString()); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public boolean accepts(TokenKind that) { aoqi@0: return this == that; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public interface Comment { aoqi@0: aoqi@0: enum CommentStyle { aoqi@0: LINE, aoqi@0: BLOCK, aoqi@0: JAVADOC, aoqi@0: } aoqi@0: aoqi@0: String getText(); aoqi@0: int getSourcePos(int index); aoqi@0: CommentStyle getStyle(); aoqi@0: boolean isDeprecated(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * This is the class representing a javac token. Each token has several fields aoqi@0: * that are set by the javac lexer (i.e. start/end position, string value, etc). aoqi@0: */ aoqi@0: public static class Token { aoqi@0: aoqi@0: /** tags constants **/ aoqi@0: enum Tag { aoqi@0: DEFAULT, aoqi@0: NAMED, aoqi@0: STRING, aoqi@0: NUMERIC; aoqi@0: } aoqi@0: aoqi@0: /** The token kind */ aoqi@0: public final TokenKind kind; aoqi@0: aoqi@0: /** The start position of this token */ aoqi@0: public final int pos; aoqi@0: aoqi@0: /** The end position of this token */ aoqi@0: public final int endPos; aoqi@0: aoqi@0: /** Comment reader associated with this token */ aoqi@0: public final List comments; aoqi@0: aoqi@0: Token(TokenKind kind, int pos, int endPos, List comments) { aoqi@0: this.kind = kind; aoqi@0: this.pos = pos; aoqi@0: this.endPos = endPos; aoqi@0: this.comments = comments; aoqi@0: checkKind(); aoqi@0: } aoqi@0: aoqi@0: Token[] split(Tokens tokens) { aoqi@0: if (kind.name.length() < 2 || kind.tag != Tag.DEFAULT) { aoqi@0: throw new AssertionError("Cant split" + kind); aoqi@0: } aoqi@0: aoqi@0: TokenKind t1 = tokens.lookupKind(kind.name.substring(0, 1)); aoqi@0: TokenKind t2 = tokens.lookupKind(kind.name.substring(1)); aoqi@0: aoqi@0: if (t1 == null || t2 == null) { aoqi@0: throw new AssertionError("Cant split - bad subtokens"); aoqi@0: } aoqi@0: return new Token[] { aoqi@0: new Token(t1, pos, pos + t1.name.length(), comments), aoqi@0: new Token(t2, pos + t1.name.length(), endPos, null) aoqi@0: }; aoqi@0: } aoqi@0: aoqi@0: protected void checkKind() { aoqi@0: if (kind.tag != Tag.DEFAULT) { aoqi@0: throw new AssertionError("Bad token kind - expected " + Tag.STRING); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public Name name() { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: public String stringVal() { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: public int radix() { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Preserve classic semantics - if multiple javadocs are found on the token aoqi@0: * the last one is returned aoqi@0: */ aoqi@0: public Comment comment(Comment.CommentStyle style) { aoqi@0: List comments = getComments(Comment.CommentStyle.JAVADOC); aoqi@0: return comments.isEmpty() ? aoqi@0: null : aoqi@0: comments.head; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Preserve classic semantics - deprecated should be set if at least one aoqi@0: * javadoc comment attached to this token contains the '@deprecated' string aoqi@0: */ aoqi@0: public boolean deprecatedFlag() { aoqi@0: for (Comment c : getComments(Comment.CommentStyle.JAVADOC)) { aoqi@0: if (c.isDeprecated()) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: private List getComments(Comment.CommentStyle style) { aoqi@0: if (comments == null) { aoqi@0: return List.nil(); aoqi@0: } else { aoqi@0: ListBuffer buf = new ListBuffer<>(); aoqi@0: for (Comment c : comments) { aoqi@0: if (c.getStyle() == style) { aoqi@0: buf.add(c); aoqi@0: } aoqi@0: } aoqi@0: return buf.toList(); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: final static class NamedToken extends Token { aoqi@0: /** The name of this token */ aoqi@0: public final Name name; aoqi@0: aoqi@0: public NamedToken(TokenKind kind, int pos, int endPos, Name name, List comments) { aoqi@0: super(kind, pos, endPos, comments); aoqi@0: this.name = name; aoqi@0: } aoqi@0: aoqi@0: protected void checkKind() { aoqi@0: if (kind.tag != Tag.NAMED) { aoqi@0: throw new AssertionError("Bad token kind - expected " + Tag.NAMED); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public Name name() { aoqi@0: return name; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: static class StringToken extends Token { aoqi@0: /** The string value of this token */ aoqi@0: public final String stringVal; aoqi@0: aoqi@0: public StringToken(TokenKind kind, int pos, int endPos, String stringVal, List comments) { aoqi@0: super(kind, pos, endPos, comments); aoqi@0: this.stringVal = stringVal; aoqi@0: } aoqi@0: aoqi@0: protected void checkKind() { aoqi@0: if (kind.tag != Tag.STRING) { aoqi@0: throw new AssertionError("Bad token kind - expected " + Tag.STRING); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String stringVal() { aoqi@0: return stringVal; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: final static class NumericToken extends StringToken { aoqi@0: /** The 'radix' value of this token */ aoqi@0: public final int radix; aoqi@0: aoqi@0: public NumericToken(TokenKind kind, int pos, int endPos, String stringVal, int radix, List comments) { aoqi@0: super(kind, pos, endPos, stringVal, comments); aoqi@0: this.radix = radix; aoqi@0: } aoqi@0: aoqi@0: protected void checkKind() { aoqi@0: if (kind.tag != Tag.NUMERIC) { aoqi@0: throw new AssertionError("Bad token kind - expected " + Tag.NUMERIC); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public int radix() { aoqi@0: return radix; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public static final Token DUMMY = aoqi@0: new Token(TokenKind.ERROR, 0, 0, null); aoqi@0: }