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 jdk.incubator.code.parser.impl.Tokens.Token;
 29 import jdk.incubator.code.parser.impl.Tokens.TokenKind;
 30 import jdk.incubator.code.type.*;
 31 import jdk.incubator.code.TypeElement;
 32 import jdk.incubator.code.type.RecordTypeRef;
 33 import jdk.incubator.code.type.impl.ConstructorRefImpl;
 34 import jdk.incubator.code.type.impl.FieldRefImpl;
 35 import jdk.incubator.code.type.impl.MethodRefImpl;
 36 import jdk.incubator.code.type.impl.RecordTypeRefImpl;
 37 import java.util.ArrayList;
 38 import java.util.List;
 39 
 40 public final class DescParser {
 41     private DescParser() {}
 42 
 43     /**
 44      * Parse an externalized type element from its serialized textual form.
 45      * @param desc the serialized externalized type element
 46      * @return the externalized type element
 47      */
 48     public static TypeElement.ExternalizedTypeElement parseExTypeElem(String desc) {
 49         Scanner s = Scanner.factory().newScanner(desc);
 50         s.nextToken();
 51         return parseExTypeElem(s);
 52     }
 53 
 54     /**
 55      * Parse a method reference from its serialized textual form.
 56      *
 57      * @param desc the serialized method reference
 58      * @return the method reference
 59      */
 60     public static MethodRef parseMethodRef(String desc) {
 61         Scanner s = Scanner.factory().newScanner(desc);
 62         s.nextToken();
 63         return parseMethodRef(s);
 64     }
 65 
 66     /**
 67      * Parse a constructor reference from its serialized textual form.
 68      *
 69      * @param desc the serialized constructor reference
 70      * @return the constructor reference
 71      */
 72     public static ConstructorRef parseConstructorRef(String desc) {
 73         Scanner s = Scanner.factory().newScanner(desc);
 74         s.nextToken();
 75         return parseConstructorRef(s);
 76     }
 77 
 78     /**
 79      * Parse a field reference from its serialized textual form.
 80      *
 81      * @param desc the serialized field reference
 82      * @return the field reference
 83      */
 84     public static FieldRef parseFieldRef(String desc) {
 85         Scanner s = Scanner.factory().newScanner(desc);
 86         s.nextToken();
 87         return parseFieldRef(s);
 88     }
 89 
 90     /**
 91      * Parse a record type reference from its serialized textual form.
 92      *
 93      * @param desc the serialized record type reference
 94      * @return the record type reference
 95      */
 96     public static RecordTypeRef parseRecordTypeRef(String desc) {
 97         Scanner s = Scanner.factory().newScanner(desc);
 98         s.nextToken();
 99         return parseRecordTypeRef(s);
100     }
101 
102     public static TypeElement.ExternalizedTypeElement parseExTypeElem(Lexer l) {
103         StringBuilder identifier = new StringBuilder();
104         if (l.token().kind == TokenKind.HASH) {
105             // Quoted identifier
106             Token t = l.token();
107             while (t.kind != TokenKind.LT) {
108                 identifier.append(t.kind == TokenKind.IDENTIFIER ? t.name() : t.kind.name);
109                 l.nextToken();
110                 t = l.token();
111             }
112         } else {
113             // type element identifier
114             Tokens.Token t = l.accept(TokenKind.IDENTIFIER,
115                     TokenKind.PLUS, TokenKind.SUB, TokenKind.DOT);
116             identifier.append(t.kind == TokenKind.IDENTIFIER ? t.name() : t.kind.name);
117             if (t.kind == TokenKind.IDENTIFIER) {
118                 // keep going if we see "."
119                 while (l.acceptIf(Tokens.TokenKind.DOT)) {
120                     identifier.append(Tokens.TokenKind.DOT.name);
121                     t = l.accept(Tokens.TokenKind.IDENTIFIER);
122                     identifier.append(t.name());
123                 }
124             }
125         }
126 
127         // Type parameters
128         List<TypeElement.ExternalizedTypeElement> args;
129         if (l.token().kind == Tokens.TokenKind.LT) {
130             args = new ArrayList<>();
131             do {
132                 l.nextToken();
133                 TypeElement.ExternalizedTypeElement arg = parseExTypeElem(l);
134                 args.add(arg);
135             } while (l.token().kind == Tokens.TokenKind.COMMA);
136             l.accept(Tokens.TokenKind.GT);
137         } else {
138             args = List.of();
139         }
140 
141         // Parse array-like syntax []+
142         int dims = 0;
143         while (l.acceptIf(Tokens.TokenKind.LBRACKET)) {
144             l.accept(Tokens.TokenKind.RBRACKET);
145             dims++;
146         }
147 
148         TypeElement.ExternalizedTypeElement td = new TypeElement.ExternalizedTypeElement(identifier.toString(), args);
149         if (dims > 0) {
150             // If array-like then type definition becomes a child with identifier [+
151             return new TypeElement.ExternalizedTypeElement("[".repeat(dims), List.of(td));
152         } else {
153             return td;
154         }
155     }
156 
157     static TypeElement parseTypeElement(Lexer l) {
158         TypeElement.ExternalizedTypeElement typeDesc = parseExTypeElem(l);
159         return CoreTypeFactory.CORE_TYPE_FACTORY.constructType(typeDesc);
160     }
161 
162     static List<TypeElement> parseParameterTypes(Lexer l) {
163         List<TypeElement> ptypes = new ArrayList<>();
164         l.accept(Tokens.TokenKind.LPAREN);
165         if (l.token().kind != Tokens.TokenKind.RPAREN) {
166             ptypes.add(parseTypeElement(l));
167             while (l.acceptIf(Tokens.TokenKind.COMMA)) {
168                 ptypes.add(parseTypeElement(l));
169             }
170         }
171         l.accept(Tokens.TokenKind.RPAREN);
172         return ptypes;
173     }
174 
175     // (T, T, T, T)R
176     static FunctionType parseMethodType(Lexer l) {
177         List<TypeElement> ptypes = parseParameterTypes(l);
178         TypeElement rtype = parseTypeElement(l);
179         return FunctionType.functionType(rtype, ptypes);
180     }
181 
182     static MethodRef parseMethodRef(Lexer l) {
183         TypeElement refType = parseTypeElement(l);
184 
185         l.accept(Tokens.TokenKind.COLCOL);
186 
187         String methodName = l.accept(Tokens.TokenKind.IDENTIFIER).name();
188 
189         FunctionType mtype = parseMethodType(l);
190 
191         return new MethodRefImpl(refType, methodName, mtype);
192     }
193 
194     static ConstructorRef parseConstructorRef(Lexer l) {
195         TypeElement refType = parseTypeElement(l);
196 
197         l.accept(Tokens.TokenKind.COLCOL);
198 
199         // Constructor reference has the special name "<new>"
200         l.accept(Tokens.TokenKind.LT);
201         Tokens.Token t = l.accept(Tokens.TokenKind.IDENTIFIER);
202         if (!t.name().equals("new")) {
203             throw new IllegalArgumentException("Invalid name for constructor reference: " + t.name());
204         }
205         l.accept(Tokens.TokenKind.GT);
206 
207         List<TypeElement> ptypes = parseParameterTypes(l);
208         return new ConstructorRefImpl(FunctionType.functionType(refType, ptypes));
209     }
210 
211     static FieldRef parseFieldRef(Lexer l) {
212         TypeElement refType = parseTypeElement(l);
213 
214         l.accept(Tokens.TokenKind.COLCOL);
215 
216         String fieldName = l.accept(Tokens.TokenKind.IDENTIFIER).name();
217 
218         FunctionType mtype = parseMethodType(l);
219         if (!mtype.parameterTypes().isEmpty()) {
220             throw new IllegalArgumentException();
221         }
222         return new FieldRefImpl(refType, fieldName, mtype.returnType());
223     }
224 
225     static RecordTypeRef parseRecordTypeRef(Lexer l) {
226         List<RecordTypeRef.ComponentRef> components = new ArrayList<>();
227         l.accept(Tokens.TokenKind.LPAREN);
228         if (l.token().kind != Tokens.TokenKind.RPAREN) {
229             do {
230                 TypeElement componentType = parseTypeElement(l);
231                 String componentName = l.accept(Tokens.TokenKind.IDENTIFIER).name();
232 
233                 components.add(new RecordTypeRef.ComponentRef(componentType, componentName));
234             } while(l.acceptIf(Tokens.TokenKind.COMMA));
235         }
236         l.accept(Tokens.TokenKind.RPAREN);
237         TypeElement recordType = parseTypeElement(l);
238         return new RecordTypeRefImpl(recordType, components);
239     }
240 }