1 /** 2 * Copyright: Enalye 3 * License: Zlib 4 * Authors: Enalye 5 */ 6 module grimoire.compiler.data; 7 8 import std.conv : to; 9 import grimoire.runtime; 10 import grimoire.compiler.primitive; 11 import grimoire.compiler.type; 12 import grimoire.compiler.mangle; 13 import grimoire.compiler.library; 14 import grimoire.compiler.pretty; 15 16 /** 17 Contains type information and D linked functions. \ 18 Must be the same between the compilation and the runtime. 19 ___ 20 Only use the *add*X() functions ***before*** compilation happen, 21 else they won't be linked. 22 */ 23 class GrData { 24 package(grimoire) { 25 /// Opaque pointer types. \ 26 /// They're pointer only defined by a name. \ 27 /// Can only be used with primitives. 28 GrForeignDefinition[] _foreignDefinitions; 29 /// Abstract foreign types. 30 GrAbstractForeignDefinition[] _abstractForeignDefinitions; 31 /// Type aliases 32 GrTypeAliasDefinition[] _aliasDefinitions, _templateAliasDefinitions; 33 /// Enum types. 34 GrEnumDefinition[] _enumDefinitions; 35 /// Object types. 36 GrClassDefinition[] _classDefinitions; 37 /// Abstract object types. 38 GrClassDefinition[] _abstractClassDefinitions; 39 /// Variable types 40 GrVariableDefinition[] _variableDefinitions; 41 42 /// All primitives, used for both the compiler and the runtime. 43 GrPrimitive[] _primitives, _abstractPrimitives; 44 45 /// Used to validate special primitives. 46 GrAnyData _anyData; 47 48 GrCallback[] _callbacks; 49 } 50 51 /// Add types and primitives defined in the library 52 void addLibrary(GrLibrary library) { 53 _abstractForeignDefinitions ~= library._abstractForeignDefinitions; 54 _aliasDefinitions ~= library._aliasDefinitions; 55 _abstractClassDefinitions ~= library._abstractClassDefinitions; 56 _variableDefinitions ~= library._variableDefinitions; 57 foreach (GrEnumDefinition enum_; library._enumDefinitions) { 58 enum_.index = _enumDefinitions.length; 59 _enumDefinitions ~= enum_; 60 } 61 const uint libStartIndex = cast(uint) _callbacks.length; 62 foreach (GrPrimitive primitive; library._abstractPrimitives) { 63 GrPrimitive prim = new GrPrimitive(primitive); 64 prim.callbackId += libStartIndex; 65 _abstractPrimitives ~= prim; 66 } 67 _callbacks ~= library._callbacks; 68 } 69 70 /// Primitive global constants, call registerIntConstant at the start of the parser. \ 71 /// Not used for now. 72 GrType addIntConstant(string name, int value) { 73 if (value + 1 > value) 74 assert(false, "TODO: Implement later"); 75 return grVoid; 76 } 77 78 /// Is a type already declared in this file 79 package bool isTypeDeclared(string name, uint fileId, bool isPublic) { 80 if (isEnum(name, fileId, isPublic)) 81 return true; 82 if (isClass(name, fileId, isPublic)) 83 return true; 84 if (isTypeAlias(name, fileId, isPublic)) 85 return true; 86 if (isForeign(name)) 87 return true; 88 return false; 89 } 90 91 /// Is a type already declared in this file 92 private bool isTypeDeclared(string name) { 93 if (isEnum(name)) 94 return true; 95 if (isClass(name)) 96 return true; 97 if (isTypeAlias(name)) 98 return true; 99 if (isForeign(name)) 100 return true; 101 return false; 102 } 103 104 /// Define an enum type. 105 package GrType addEnum(string name, string[] fields, uint fileId, bool isPublic) { 106 GrEnumDefinition enumDef = new GrEnumDefinition; 107 enumDef.name = name; 108 enumDef.fields = fields; 109 enumDef.index = _enumDefinitions.length; 110 enumDef.fileId = fileId; 111 enumDef.isPublic = isPublic; 112 _enumDefinitions ~= enumDef; 113 114 GrType stType = GrBaseType.enum_; 115 stType.mangledType = name; 116 return stType; 117 } 118 119 package void registerClass(string name, uint fileId, bool isPublic, 120 string[] templateVariables, uint position) { 121 GrClassDefinition class_ = new GrClassDefinition; 122 class_.name = name; 123 class_.position = position; 124 class_.fileId = fileId; 125 class_.isPublic = isPublic; 126 class_.templateVariables = templateVariables; 127 _abstractClassDefinitions ~= class_; 128 } 129 130 /// Define an alias of another type. 131 package GrType addTypeAlias(string name, GrType type, uint fileId, bool isPublic) { 132 GrTypeAliasDefinition typeAlias = new GrTypeAliasDefinition; 133 typeAlias.name = name; 134 typeAlias.type = type; 135 typeAlias.fileId = fileId; 136 typeAlias.isPublic = isPublic; 137 _aliasDefinitions ~= typeAlias; 138 return type; 139 } 140 141 /// Define an alias of another type. 142 package GrType addTemplateAlias(string name, GrType type, uint fileId, bool isPublic) { 143 GrTypeAliasDefinition typeAlias = new GrTypeAliasDefinition; 144 typeAlias.name = name; 145 typeAlias.type = type; 146 typeAlias.fileId = fileId; 147 typeAlias.isPublic = isPublic; 148 _templateAliasDefinitions ~= typeAlias; 149 return type; 150 } 151 152 package void clearTemplateAliases() { 153 _templateAliasDefinitions.length = 0; 154 } 155 156 /// Is the enum defined ? 157 package bool isEnum(string name, uint fileId, bool isPublic) { 158 foreach (enumType; _enumDefinitions) { 159 if (enumType.name == name && (enumType.fileId == fileId || enumType.isPublic || isPublic)) 160 return true; 161 } 162 return false; 163 } 164 165 /// Ditto 166 private bool isEnum(string name) { 167 foreach (enumType; _enumDefinitions) { 168 if (enumType.name == name) 169 return true; 170 } 171 return false; 172 } 173 174 /// Is the class defined ? 175 package bool isClass(string name, uint fileId, bool isPublic) { 176 foreach (class_; _abstractClassDefinitions) { 177 if (class_.name == name && (class_.fileId == fileId || class_.isPublic || isPublic)) 178 return true; 179 } 180 return false; 181 } 182 183 /// Ditto 184 private bool isClass(string name) { 185 foreach (class_; _abstractClassDefinitions) { 186 if (class_.name == name) 187 return true; 188 } 189 return false; 190 } 191 192 /// Is the type alias defined ? 193 package bool isTypeAlias(string name, uint fileId, bool isPublic) { 194 foreach (typeAlias; _templateAliasDefinitions) { 195 if (typeAlias.name == name && (typeAlias.fileId == fileId 196 || typeAlias.isPublic || isPublic)) 197 return true; 198 } 199 foreach (typeAlias; _aliasDefinitions) { 200 if (typeAlias.name == name && (typeAlias.fileId == fileId 201 || typeAlias.isPublic || isPublic)) 202 return true; 203 } 204 return false; 205 } 206 207 /// Ditto 208 private bool isTypeAlias(string name) { 209 foreach (typeAlias; _aliasDefinitions) { 210 if (typeAlias.name == name) 211 return true; 212 } 213 return false; 214 } 215 216 /// Is the user-type defined ? 217 package bool isForeign(string name) { 218 foreach (foreign; _abstractForeignDefinitions) { 219 if (foreign.name == name) 220 return true; 221 } 222 return false; 223 } 224 225 /// Return the user-type definition. 226 GrForeignDefinition getForeign(string mangledName) { 227 import std.algorithm.searching : findSplitBefore; 228 229 foreach (foreign; _foreignDefinitions) { 230 if (foreign.name == mangledName) 231 return foreign; 232 } 233 234 const mangledTuple = findSplitBefore(mangledName, "$"); 235 string name = mangledTuple[0]; 236 GrType[] templateTypes = grUnmangleSignature(mangledTuple[1]); 237 foreach (foreign; _abstractForeignDefinitions) { 238 if (foreign.name == name && foreign.templateVariables.length == templateTypes.length) { 239 GrForeignDefinition generatedForeign = new GrForeignDefinition; 240 generatedForeign.name = mangledName; 241 generatedForeign.parent = foreign.parent; 242 243 _anyData = new GrAnyData; 244 for (int i; i < foreign.templateVariables.length; ++i) { 245 _anyData.set(foreign.templateVariables[i], templateTypes[i]); 246 } 247 248 GrType[] parentTemplateSignature = foreign.parentTemplateSignature; 249 for (int i; i < parentTemplateSignature.length; ++i) { 250 if (parentTemplateSignature[i].isAny) { 251 parentTemplateSignature[i] = _anyData.get( 252 parentTemplateSignature[i].mangledType); 253 } 254 } 255 generatedForeign.parent = grMangleComposite(generatedForeign.parent, 256 parentTemplateSignature); 257 258 _foreignDefinitions ~= generatedForeign; 259 return generatedForeign; 260 } 261 } 262 return null; 263 } 264 265 /// Return the enum definition. 266 GrEnumDefinition getEnum(string name, uint fileId) { 267 import std.conv : to; 268 269 foreach (enumType; _enumDefinitions) { 270 if (enumType.name == name && (enumType.fileId == fileId || enumType.isPublic)) 271 return enumType; 272 } 273 return null; 274 } 275 276 /// Return the class definition. 277 package GrClassDefinition getClass(string mangledName, uint fileId, bool isPublic = false) { 278 import std.algorithm.searching : findSplitBefore; 279 280 foreach (class_; _classDefinitions) { 281 if (class_.name == mangledName && (class_.fileId == fileId || class_.isPublic 282 || isPublic)) 283 return class_; 284 } 285 const mangledTuple = findSplitBefore(mangledName, "$"); 286 string name = mangledTuple[0]; 287 GrType[] templateTypes = grUnmangleSignature(mangledTuple[1]); 288 foreach (class_; _abstractClassDefinitions) { 289 if (class_.name == name && class_.templateVariables.length == templateTypes.length 290 && (class_.fileId == fileId || class_.isPublic || isPublic)) { 291 GrClassDefinition generatedClass = new GrClassDefinition; 292 generatedClass.name = mangledName; 293 generatedClass.parent = class_.parent; 294 generatedClass.signature = class_.signature; 295 generatedClass.fields = class_.fields; 296 generatedClass.templateVariables = class_.templateVariables; 297 generatedClass.templateTypes = templateTypes; 298 generatedClass.position = class_.position; 299 generatedClass.isParsed = class_.isParsed; 300 generatedClass.isPublic = class_.isPublic; 301 generatedClass.fileId = class_.fileId; 302 generatedClass.fieldsInfo = class_.fieldsInfo; 303 generatedClass.index = _classDefinitions.length; 304 305 _anyData = new GrAnyData; 306 for (int i; i < generatedClass.templateVariables.length; ++i) { 307 _anyData.set(generatedClass.templateVariables[i], 308 generatedClass.templateTypes[i]); 309 } 310 311 for (int i; i < generatedClass.signature.length; ++i) { 312 if (generatedClass.signature[i].isAny) { 313 generatedClass.signature[i] = _anyData.get( 314 generatedClass.signature[i].mangledType); 315 if (generatedClass.signature[i].baseType == GrBaseType.void_) 316 return null; 317 } 318 } 319 320 GrType[] parentTemplateSignature = class_.parentTemplateSignature; 321 for (int i; i < parentTemplateSignature.length; ++i) { 322 if (parentTemplateSignature[i].isAny) { 323 parentTemplateSignature[i] = _anyData.get( 324 parentTemplateSignature[i].mangledType); 325 } 326 } 327 generatedClass.parent = grMangleComposite(generatedClass.parent, 328 parentTemplateSignature); 329 330 _classDefinitions ~= generatedClass; 331 return generatedClass; 332 } 333 } 334 return null; 335 } 336 337 /// Return the type alias definition. 338 GrTypeAliasDefinition getTypeAlias(string name, uint fileId) { 339 foreach (typeAlias; _templateAliasDefinitions) { 340 if (typeAlias.name == name && (typeAlias.fileId == fileId || typeAlias.isPublic)) 341 return typeAlias; 342 } 343 foreach (typeAlias; _aliasDefinitions) { 344 if (typeAlias.name == name && (typeAlias.fileId == fileId || typeAlias.isPublic)) 345 return typeAlias; 346 } 347 return null; 348 } 349 350 /** 351 Is the primitive already declared ? 352 */ 353 bool isPrimitiveDeclared(string mangledName) { 354 foreach (primitive; _primitives) { 355 if (primitive.mangledName == mangledName) 356 return true; 357 } 358 return false; 359 } 360 361 /** 362 Returns the declared primitive definition. 363 */ 364 GrPrimitive getPrimitive(string mangledName) { 365 import std.conv : to; 366 367 foreach (primitive; _primitives) { 368 if (primitive.mangledName == mangledName) 369 return primitive; 370 } 371 return null; 372 } 373 374 /// Ditto 375 package GrPrimitive getPrimitive(string name, GrType[] signature) { 376 const string mangledName = grMangleComposite(name, signature); 377 foreach (GrPrimitive primitive; _primitives) { 378 if (primitive.name == name) { 379 if (primitive.mangledName == mangledName) 380 return primitive; 381 } 382 } 383 foreach (GrPrimitive primitive; _primitives) { 384 if (primitive.name == name) { 385 if (isSignatureCompatible(signature, primitive.inSignature, 0, true)) 386 return primitive; 387 } 388 } 389 foreach (GrPrimitive primitive; _abstractPrimitives) { 390 if (primitive.name == name) { 391 _anyData = new GrAnyData; 392 if (isSignatureCompatible(signature, primitive.inSignature, 0, true)) { 393 GrPrimitive reifiedPrimitive = reifyPrimitive(primitive, signature); 394 if (!reifiedPrimitive) 395 continue; 396 return reifiedPrimitive; 397 } 398 } 399 } 400 return null; 401 } 402 403 package GrPrimitive reifyPrimitive(GrPrimitive templatePrimitive, GrType[] signature) { 404 // We assume the signature was already validated with `isSignatureCompatible` to be fully compatible with the primitive 405 GrPrimitive primitive = new GrPrimitive(templatePrimitive); 406 for (int i; i < primitive.inSignature.length; ++i) { 407 if (primitive.inSignature[i].isAny) { 408 primitive.inSignature[i] = _anyData.get(primitive.inSignature[i].mangledType); 409 if (primitive.inSignature[i].baseType == GrBaseType.void_) 410 throw new Exception("`" ~ getPrettyPrimitive(primitive) ~ "` can't be reified"); 411 } 412 checkUnknownClasses(primitive.inSignature[i]); 413 } 414 for (int i; i < primitive.outSignature.length; ++i) { 415 if (primitive.outSignature[i].isAny) { 416 primitive.outSignature[i] = _anyData.get(primitive.outSignature[i].mangledType); 417 if (primitive.outSignature[i].baseType == GrBaseType.void_) 418 throw new Exception("`" ~ getPrettyPrimitive(primitive) ~ "` can't be reified"); 419 } 420 checkUnknownClasses(primitive.outSignature[i]); 421 } 422 primitive.mangledName = grMangleComposite(primitive.name, primitive.inSignature); 423 primitive.index = cast(uint) _primitives.length; 424 if (isPrimitiveDeclared(primitive.mangledName)) 425 throw new Exception("`" ~ getPrettyPrimitive(primitive) ~ "` is already declared"); 426 _primitives ~= primitive; 427 return primitive; 428 } 429 430 // Forcing the classes to be reified they aren't already 431 private void checkUnknownClasses(GrType type) { 432 switch (type.baseType) with (GrBaseType) { 433 case class_: 434 GrClassDefinition classDef = getClass(type.mangledType, 0, true); 435 if (!classDef) 436 throw new Exception("undefined class `" ~ type.mangledType ~ "`"); 437 foreach (GrType fieldType; classDef.signature) 438 checkUnknownClasses(fieldType); 439 break; 440 case array_: 441 case chan: 442 GrType subType = grUnmangle(type.mangledType); 443 checkUnknownClasses(subType); 444 break; 445 case function_: 446 foreach (GrType inType; grUnmangleSignature(type.mangledType)) 447 checkUnknownClasses(inType); 448 foreach (GrType outType; grUnmangleSignature(type.mangledReturnType)) 449 checkUnknownClasses(outType); 450 break; 451 case task: 452 foreach (GrType inType; grUnmangleSignature(type.mangledType)) 453 checkUnknownClasses(inType); 454 break; 455 default: 456 return; 457 } 458 } 459 460 /// Check if the first signature match or can be upgraded (by inheritance) to the second one. 461 package bool isSignatureCompatible(GrType[] first, GrType[] second, 462 uint fileId, bool isPublic = false) { 463 if (first.length != second.length) 464 return false; 465 __signatureLoop: for (int i; i < first.length; ++i) { 466 if (second[i].isAny) { 467 const GrType registeredType = _anyData.get(second[i].mangledType); 468 if (registeredType.baseType == GrBaseType.void_) { 469 _anyData.set(second[i].mangledType, first[i]); 470 } 471 else { 472 if (registeredType != first[i]) 473 return false; 474 } 475 if (!second[i].predicate) 476 return false; 477 if (!second[i].predicate(first[i], _anyData)) 478 return false; 479 continue; 480 } 481 if (first[i].baseType == GrBaseType.null_ 482 && (second[i].baseType == GrBaseType.foreign 483 || second[i].baseType == GrBaseType.class_)) 484 continue; 485 if (first[i].baseType == GrBaseType.foreign && second[i].baseType == GrBaseType.foreign) { 486 for (;;) { 487 if (first[i] == second[i]) 488 continue __signatureLoop; 489 const GrForeignDefinition foreignType = getForeign(first[i].mangledType); 490 if (!foreignType.parent.length) 491 return false; 492 first[i].mangledType = foreignType.parent; 493 } 494 } 495 else if (first[i].baseType == GrBaseType.class_ 496 && second[i].baseType == GrBaseType.class_) { 497 for (;;) { 498 if (first[i] == second[i]) 499 continue __signatureLoop; 500 const GrClassDefinition classType = getClass(first[i].mangledType, 501 fileId, isPublic); 502 if (!classType.parent.length) 503 return false; 504 first[i].mangledType = classType.parent; 505 } 506 } 507 else if (first[i] != second[i]) { 508 return false; 509 } 510 } 511 return true; 512 } 513 514 /** 515 Prettify a primitive signature. 516 */ 517 private string getPrimitiveDisplayById(uint id) { 518 if (id >= _primitives.length) 519 throw new Exception("Invalid primitive id"); 520 return getPrettyPrimitive(_primitives[id]); 521 } 522 523 private string getPrettyPrimitive(GrPrimitive primitive) { 524 import std.conv : to; 525 526 string result = primitive.name; 527 auto nbParameters = primitive.inSignature.length; 528 if (primitive.name == "@as") 529 nbParameters = 1; 530 result ~= "("; 531 for (int i; i < nbParameters; i++) { 532 result ~= grGetPrettyType(primitive.inSignature[i]); 533 if ((i + 2) <= nbParameters) 534 result ~= ", "; 535 } 536 result ~= ")"; 537 for (int i; i < primitive.outSignature.length; i++) { 538 result ~= i ? ", " : " "; 539 result ~= grGetPrettyType(primitive.outSignature[i]); 540 } 541 return result; 542 } 543 }