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