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 }