1 /*
  2  * Copyright (c) 2024, 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 
 26 package java.lang.reflect.code.parser.impl;
 27 
 28 import java.util.HashMap;
 29 import java.util.List;
 30 import java.util.Map;
 31 import java.util.function.Predicate;
 32 
 33 /**
 34  * A class that defines codes/utilities for Java source tokens
 35  * returned from lexical analysis.
 36  *
 37  * <p><b>This is NOT part of any supported API.
 38  * If you write code that depends on this, you do so at your own risk.
 39  * This code and its internal interfaces are subject to change or
 40  * deletion without notice.</b>
 41  */
 42 public final class Tokens {
 43 
 44     /**
 45      * Keyword array. Maps name indices to Token.
 46      */
 47     private final Map<String, TokenKind> keywords = new HashMap<>();
 48 
 49     Tokens() {
 50         for (TokenKind t : TokenKind.values()) {
 51             if (t.name != null) {
 52                 keywords.put(t.name, t);
 53             }
 54         }
 55     }
 56 
 57     TokenKind lookupKind(String name, TokenKind identifier) {
 58         TokenKind t = keywords.get(name);
 59         return (t != null) ? t : identifier;
 60     }
 61 
 62     /**
 63      * This enum defines all tokens used by the javac scanner. A token is
 64      * optionally associated with a name.
 65      */
 66     public enum TokenKind implements Predicate<TokenKind> {
 67         EOF(),
 68         ERROR(),
 69         IDENTIFIER(Token.Tag.NAMED),
 70         VALUE_IDENTIFIER(Token.Tag.NAMED),
 71         EXTENDS("extends", Token.Tag.NAMED),
 72         SUPER("super", Token.Tag.NAMED),
 73         INTLITERAL(Token.Tag.NUMERIC),
 74         LONGLITERAL(Token.Tag.NUMERIC),
 75         FLOATLITERAL(Token.Tag.NUMERIC),
 76         DOUBLELITERAL(Token.Tag.NUMERIC),
 77         CHARLITERAL(Token.Tag.NUMERIC),
 78         STRINGLITERAL(Token.Tag.STRING),
 79         TRUE("true", Token.Tag.NAMED),
 80         FALSE("false", Token.Tag.NAMED),
 81         NULL("null", Token.Tag.NAMED),
 82         UNDERSCORE("_", Token.Tag.NAMED),
 83         ARROW("->"),
 84         LPAREN("("),
 85         RPAREN(")"),
 86         LBRACE("{"),
 87         RBRACE("}"),
 88         LBRACKET("["),
 89         RBRACKET("]"),
 90         COMMA(","),
 91         DOT("."),
 92         EQ("="),
 93         GT(">"),
 94         LT("<"),
 95         QUES("?"),
 96         COLON(":"),
 97         COLCOL("::"),
 98         SEMI(";"),
 99         PLUS("+"),
100         SUB("-"),
101         AMP("&"),
102         CARET("^"),
103         MONKEYS_AT("@"),
104         HASH("#"),
105         CUSTOM;
106 
107         public final String name;
108         public final Token.Tag tag;
109 
110         TokenKind() {
111             this(null, Token.Tag.DEFAULT);
112         }
113 
114         TokenKind(String name) {
115             this(name, Token.Tag.DEFAULT);
116         }
117 
118         TokenKind(Token.Tag tag) {
119             this(null, tag);
120         }
121 
122         TokenKind(String name, Token.Tag tag) {
123             this.name = name;
124             this.tag = tag;
125         }
126 
127         public String toString() {
128             return switch (this) {
129                 case IDENTIFIER -> "token.identifier";
130                 case VALUE_IDENTIFIER -> "token.value-identifier";
131                 case CHARLITERAL -> "token.character";
132                 case STRINGLITERAL -> "token.string";
133                 case INTLITERAL -> "token.integer";
134                 case LONGLITERAL -> "token.long-integer";
135                 case FLOATLITERAL -> "token.float";
136                 case DOUBLELITERAL -> "token.double";
137                 case ERROR -> "token.bad-symbol";
138                 case EOF -> "token.end-of-input";
139                 case DOT, COMMA, LPAREN, RPAREN, LBRACKET, RBRACKET, LBRACE, RBRACE -> "'" + name + "'";
140                 default -> name;
141             };
142         }
143 
144         @Override
145         public boolean test(TokenKind that) {
146             return this == that;
147         }
148     }
149 
150     public interface Comment {
151 
152         enum CommentStyle {
153             LINE,
154             BLOCK,
155         }
156 
157         String text();
158 
159         CommentStyle style();
160     }
161 
162     /**
163      * This is the class representing a javac token. Each token has several fields
164      * that are set by the javac lexer (i.e. start/end position, string value, etc).
165      */
166     public static class Token {
167 
168         /**
169          * tags constants
170          **/
171         public enum Tag {
172             DEFAULT,
173             NAMED,
174             STRING,
175             NUMERIC
176         }
177 
178         /**
179          * The token kind
180          */
181         public final TokenKind kind;
182 
183         /**
184          * The start position of this token
185          */
186         public final int pos;
187 
188         /**
189          * The end position of this token
190          */
191         public final int endPos;
192 
193         /**
194          * Comment reader associated with this token
195          */
196         public final List<Comment> comments;
197 
198         Token(TokenKind kind, int pos, int endPos, List<Comment> comments) {
199             this.kind = kind;
200             this.pos = pos;
201             this.endPos = endPos;
202             this.comments = comments == null ? null : List.copyOf(comments);
203             checkKind();
204         }
205 
206         void checkKind() {
207             if (kind.tag != Tag.DEFAULT) {
208                 throw new AssertionError("Bad token kind - expected " + Tag.DEFAULT);
209             }
210         }
211 
212         public String name() {
213             throw new UnsupportedOperationException();
214         }
215 
216         public String stringVal() {
217             throw new UnsupportedOperationException();
218         }
219 
220         public int radix() {
221             throw new UnsupportedOperationException();
222         }
223     }
224 
225     static final class NamedToken extends Token {
226         /**
227          * The name of this token
228          */
229         public final String name;
230 
231         NamedToken(TokenKind kind, int pos, int endPos, String name, List<Comment> comments) {
232             super(kind, pos, endPos, comments);
233             this.name = name;
234         }
235 
236         void checkKind() {
237             if (kind.tag != Tag.NAMED) {
238                 throw new AssertionError("Bad token kind - expected " + Tag.NAMED);
239             }
240         }
241 
242         @Override
243         public String name() {
244             return name;
245         }
246     }
247 
248     static class StringToken extends Token {
249         /**
250          * The string value of this token
251          */
252         public final String stringVal;
253 
254         StringToken(TokenKind kind, int pos, int endPos, String stringVal, List<Comment> comments) {
255             super(kind, pos, endPos, comments);
256             this.stringVal = stringVal;
257         }
258 
259         void checkKind() {
260             if (kind.tag != Tag.STRING) {
261                 throw new AssertionError("Bad token kind - expected " + Tag.STRING);
262             }
263         }
264 
265         @Override
266         public String stringVal() {
267             return stringVal;
268         }
269     }
270 
271     static final class NumericToken extends StringToken {
272         /**
273          * The 'radix' value of this token
274          */
275         public final int radix;
276 
277         NumericToken(TokenKind kind, int pos, int endPos, String stringVal, int radix, List<Comment> comments) {
278             super(kind, pos, endPos, stringVal, comments);
279             this.radix = radix;
280         }
281 
282         void checkKind() {
283             if (kind.tag != Tag.NUMERIC) {
284                 throw new AssertionError("Bad token kind - expected " + Tag.NUMERIC);
285             }
286         }
287 
288         @Override
289         public int radix() {
290             return radix;
291         }
292     }
293 
294     public static final Token DUMMY =
295             new Token(TokenKind.ERROR, 0, 0, null);
296 }