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 jdk.incubator.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         INTLITERAL(Token.Tag.NUMERIC),
 72         LONGLITERAL(Token.Tag.NUMERIC),
 73         FLOATLITERAL(Token.Tag.NUMERIC),
 74         DOUBLELITERAL(Token.Tag.NUMERIC),
 75         CHARLITERAL(Token.Tag.NUMERIC),
 76         STRINGLITERAL(Token.Tag.STRING),
 77         TRUE("true", Token.Tag.NAMED),
 78         FALSE("false", Token.Tag.NAMED),
 79         NULL("null", Token.Tag.NAMED),
 80         UNDERSCORE("_", Token.Tag.NAMED),
 81         ARROW("->"),
 82         LPAREN("("),
 83         RPAREN(")"),
 84         LBRACE("{"),
 85         RBRACE("}"),
 86         LBRACKET("["),
 87         RBRACKET("]"),
 88         COMMA(","),
 89         DOT("."),
 90         EQ("="),
 91         GT(">"),
 92         LT("<"),
 93         QUES("?"),
 94         COLON(":"),
 95         COLCOL("::"),
 96         SEMI(";"),
 97         PLUS("+"),
 98         SUB("-"),
 99         AMP("&"),
100         CARET("^"),
101         MONKEYS_AT("@"),
102         HASH("#"),
103         CUSTOM;
104 
105         public final String name;
106         public final Token.Tag tag;
107 
108         TokenKind() {
109             this(null, Token.Tag.DEFAULT);
110         }
111 
112         TokenKind(String name) {
113             this(name, Token.Tag.DEFAULT);
114         }
115 
116         TokenKind(Token.Tag tag) {
117             this(null, tag);
118         }
119 
120         TokenKind(String name, Token.Tag tag) {
121             this.name = name;
122             this.tag = tag;
123         }
124 
125         public String toString() {
126             return switch (this) {
127                 case IDENTIFIER -> "token.identifier";
128                 case VALUE_IDENTIFIER -> "token.value-identifier";
129                 case CHARLITERAL -> "token.character";
130                 case STRINGLITERAL -> "token.string";
131                 case INTLITERAL -> "token.integer";
132                 case LONGLITERAL -> "token.long-integer";
133                 case FLOATLITERAL -> "token.float";
134                 case DOUBLELITERAL -> "token.double";
135                 case ERROR -> "token.bad-symbol";
136                 case EOF -> "token.end-of-input";
137                 case DOT, COMMA, LPAREN, RPAREN, LBRACKET, RBRACKET, LBRACE, RBRACE -> "'" + name + "'";
138                 default -> name;
139             };
140         }
141 
142         @Override
143         public boolean test(TokenKind that) {
144             return this == that;
145         }
146     }
147 
148     public interface Comment {
149 
150         enum CommentStyle {
151             LINE,
152             BLOCK,
153         }
154 
155         String text();
156 
157         CommentStyle style();
158     }
159 
160     /**
161      * This is the class representing a javac token. Each token has several fields
162      * that are set by the javac lexer (i.e. start/end position, string value, etc).
163      */
164     public static class Token {
165 
166         /**
167          * tags constants
168          **/
169         public enum Tag {
170             DEFAULT,
171             NAMED,
172             STRING,
173             NUMERIC
174         }
175 
176         /**
177          * The token kind
178          */
179         public final TokenKind kind;
180 
181         /**
182          * The start position of this token
183          */
184         public final int pos;
185 
186         /**
187          * The end position of this token
188          */
189         public final int endPos;
190 
191         /**
192          * Comment reader associated with this token
193          */
194         public final List<Comment> comments;
195 
196         Token(TokenKind kind, int pos, int endPos, List<Comment> comments) {
197             this.kind = kind;
198             this.pos = pos;
199             this.endPos = endPos;
200             this.comments = comments == null ? null : List.copyOf(comments);
201             checkKind();
202         }
203 
204         void checkKind() {
205             if (kind.tag != Tag.DEFAULT) {
206                 throw new AssertionError("Bad token kind - expected " + Tag.DEFAULT);
207             }
208         }
209 
210         public String name() {
211             throw new UnsupportedOperationException();
212         }
213 
214         public String stringVal() {
215             throw new UnsupportedOperationException();
216         }
217 
218         public int radix() {
219             throw new UnsupportedOperationException();
220         }
221     }
222 
223     static final class NamedToken extends Token {
224         /**
225          * The name of this token
226          */
227         public final String name;
228 
229         NamedToken(TokenKind kind, int pos, int endPos, String name, List<Comment> comments) {
230             super(kind, pos, endPos, comments);
231             this.name = name;
232         }
233 
234         void checkKind() {
235             if (kind.tag != Tag.NAMED) {
236                 throw new AssertionError("Bad token kind - expected " + Tag.NAMED);
237             }
238         }
239 
240         @Override
241         public String name() {
242             return name;
243         }
244     }
245 
246     static class StringToken extends Token {
247         /**
248          * The string value of this token
249          */
250         public final String stringVal;
251 
252         StringToken(TokenKind kind, int pos, int endPos, String stringVal, List<Comment> comments) {
253             super(kind, pos, endPos, comments);
254             this.stringVal = stringVal;
255         }
256 
257         void checkKind() {
258             if (kind.tag != Tag.STRING) {
259                 throw new AssertionError("Bad token kind - expected " + Tag.STRING);
260             }
261         }
262 
263         @Override
264         public String stringVal() {
265             return stringVal;
266         }
267     }
268 
269     static final class NumericToken extends StringToken {
270         /**
271          * The 'radix' value of this token
272          */
273         public final int radix;
274 
275         NumericToken(TokenKind kind, int pos, int endPos, String stringVal, int radix, List<Comment> comments) {
276             super(kind, pos, endPos, stringVal, comments);
277             this.radix = radix;
278         }
279 
280         void checkKind() {
281             if (kind.tag != Tag.NUMERIC) {
282                 throw new AssertionError("Bad token kind - expected " + Tag.NUMERIC);
283             }
284         }
285 
286         @Override
287         public int radix() {
288             return radix;
289         }
290     }
291 
292     public static final Token DUMMY =
293             new Token(TokenKind.ERROR, 0, 0, null);
294 }