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 }