1 /** 
2  * Copyright: Enalye
3  * License: Zlib
4  * Authors: Enalye
5  */
6 module grimoire.compiler.library;
7 
8 import std.traits;
9 import std.conv : to;
10 import grimoire.runtime;
11 import grimoire.compiler.primitive;
12 import grimoire.compiler.type;
13 import grimoire.compiler.mangle;
14 import grimoire.compiler.pretty;
15 
16 /**
17 Contains type information and D linked functions.
18 */
19 class GrLibrary {
20     package(grimoire) {
21         /// Opaque pointer types. \
22         /// They're pointer only defined by a name. \
23         /// Can only be used with primitives.
24         GrAbstractForeignDefinition[] _abstractForeignDefinitions;
25         /// Type aliases
26         GrTypeAliasDefinition[] _aliasDefinitions;
27         /// Enum types.
28         GrEnumDefinition[] _enumDefinitions;
29         /// Object types.
30         GrClassDefinition[] _abstractClassDefinitions;
31         /// Variable types
32         GrVariableDefinition[] _variableDefinitions;
33 
34         /// All primitives, used for both the compiler and the runtime.
35         GrPrimitive[] _abstractPrimitives;
36 
37         /// All the primitive callbacks.
38         GrCallback[] _callbacks;
39     }
40 
41     /// Define a variable
42     void addVariable(string name, GrType type, bool isConstant) {
43         GrVariableDefinition variable = new GrVariableDefinition;
44         variable.name = name;
45         variable.type = type;
46         variable.isConstant = isConstant;
47         _variableDefinitions ~= variable;
48     }
49 
50     /// Define a variable with a default value
51     void addVariable(T)(string name, GrType type, T defaultValue, bool isConstant) {
52         GrVariableDefinition variable = new GrVariableDefinition;
53         variable.name = name;
54         variable.type = type;
55         variable.isConstant = isConstant;
56 
57         final switch (type.baseType) with (GrBaseType) {
58         case bool_:
59         case int_:
60         case enum_:
61         case float_:
62         case string_:
63             break;
64         case class_:
65         case chan:
66         case function_:
67         case task:
68         case array_:
69         case foreign:
70         case void_:
71         case null_:
72         case internalTuple:
73         case reference:
74             throw new Exception(
75                     "can't initialize library variable of type `" ~ grGetPrettyType(type) ~ "`");
76         }
77         static if (isIntegral!T) {
78             if (type.baseType != GrBaseType.int_ && type.baseType != GrBaseType.enum_)
79                 throw new Exception(
80                         "the default value of `" ~ name ~ "` doesn't match the type of  `" ~ grGetPrettyType(
81                         type) ~ "`");
82             variable.ivalue = cast(int) defaultValue;
83         }
84         else static if (is(T == bool)) {
85             if (type.baseType != GrBaseType.bool_)
86                 throw new Exception(
87                         "the default value of `" ~ name ~ "` doesn't match the type of  `" ~ grGetPrettyType(
88                         type) ~ "`");
89             variable.ivalue = defaultValue ? 1 : 0;
90         }
91         else static if (isFloatingPoint!T) {
92             if (type.baseType != GrBaseType.float_)
93                 throw new Exception(
94                         "the default value of `" ~ name ~ "` doesn't match the type of  `" ~ grGetPrettyType(
95                         type) ~ "`");
96             variable.fvalue = cast(float) defaultValue;
97         }
98         static if (is(T == string)) {
99             if (type.baseType != GrBaseType.string_)
100                 throw new Exception(
101                         "the default value of `" ~ name ~ "` doesn't match the type of  `" ~ grGetPrettyType(
102                         type) ~ "`");
103             variable.svalue = defaultValue;
104         }
105         variable.isInitialized = true;
106         _variableDefinitions ~= variable;
107     }
108 
109     /// Define an enumeration
110     GrType addEnum(string name, string[] fields) {
111         GrEnumDefinition enum_ = new GrEnumDefinition;
112         enum_.name = name;
113         enum_.fields = fields;
114         enum_.isPublic = true;
115         _enumDefinitions ~= enum_;
116 
117         GrType type = GrBaseType.enum_;
118         type.mangledType = name;
119         return type;
120     }
121 
122     /// Define a class type.
123     GrType addClass(string name, string[] fields, GrType[] signature,
124             string[] templateVariables = [], string parent = "",
125             GrType[] parentTemplateSignature = []) {
126         if (fields.length != signature.length)
127             throw new Exception("class signature mismatch");
128         GrClassDefinition class_ = new GrClassDefinition;
129         class_.name = name;
130         class_.parent = parent;
131         class_.signature = signature;
132         class_.fields = fields;
133         class_.templateVariables = templateVariables;
134         class_.parentTemplateSignature = parentTemplateSignature;
135         class_.isPublic = true;
136         class_.isParsed = true;
137         _abstractClassDefinitions ~= class_;
138 
139         class_.fieldsInfo.length = fields.length;
140         for (int i; i < class_.fieldsInfo.length; ++i) {
141             class_.fieldsInfo[i].fileId = 0;
142             class_.fieldsInfo[i].isPublic = true;
143             class_.fieldsInfo[i].position = 0;
144         }
145 
146         GrType type = GrBaseType.class_;
147         type.mangledType = name;
148         type.isAbstract = class_.templateVariables.length > 0;
149         return type;
150     }
151 
152     /// Define a type alias
153     GrType addTypeAlias(string name, GrType type) {
154         GrTypeAliasDefinition typeAlias = new GrTypeAliasDefinition;
155         typeAlias.name = name;
156         typeAlias.type = type;
157         typeAlias.isPublic = true;
158         _aliasDefinitions ~= typeAlias;
159         return type;
160     }
161 
162     /// Define an opaque pointer type.
163     GrType addForeign(string name, string[] templateVariables = [],
164             string parent = "", GrType[] parentTemplateSignature = []) {
165         if (name == parent)
166             throw new Exception("`" ~ name ~ "` can't be its own parent");
167         GrAbstractForeignDefinition foreign = new GrAbstractForeignDefinition;
168         foreign.name = name;
169         foreign.templateVariables = templateVariables;
170         foreign.parent = parent;
171         foreign.parentTemplateSignature = parentTemplateSignature;
172         _abstractForeignDefinitions ~= foreign;
173 
174         GrType type = GrBaseType.foreign;
175         type.mangledType = name;
176         type.isAbstract = foreign.templateVariables.length > 0;
177         return type;
178     }
179 
180     /// Define a new primitive.
181     GrPrimitive addPrimitive(GrCallback callback, string name,
182             GrType[] inSignature = [], GrType[] outSignature = []) {
183         bool isAbstract;
184         foreach (GrType type; inSignature) {
185             if (type.isAbstract)
186                 throw new Exception("`" ~ grGetPrettyFunction(name, inSignature,
187                         outSignature) ~ "` can't use type `" ~ grGetPrettyType(
188                         type) ~ "` as it is abstract");
189             if (type.isAny) {
190                 isAbstract = true;
191                 break;
192             }
193         }
194         foreach (GrType type; outSignature) {
195             if (type.isAbstract)
196                 throw new Exception("`" ~ grGetPrettyFunction(name, inSignature,
197                         outSignature) ~ "` can't use type `" ~ grGetPrettyType(
198                         type) ~ "` as it is abstract");
199         }
200 
201         GrPrimitive primitive = new GrPrimitive;
202         primitive.inSignature = inSignature;
203         primitive.outSignature = outSignature;
204         primitive.name = name;
205         primitive.callbackId = cast(int) _callbacks.length;
206 
207         _callbacks ~= callback;
208 
209         _abstractPrimitives ~= primitive;
210         return primitive;
211     }
212 
213     /// Type of operator overloading
214     enum Operator {
215         add,
216         substract,
217         multiply,
218         divide,
219         concatenate,
220         remainder,
221         power,
222         equal,
223         doubleEqual,
224         threeWayComparison,
225         notEqual,
226         greaterOrEqual,
227         greater,
228         lesserOrEqual,
229         lesser,
230         leftShift,
231         rightShift,
232         and,
233         or,
234         xor,
235         not,
236     }
237 
238     /**
239     An operator is a function that replace a binary or unary grimoire operator such as `+`, `==`, etc
240     The name of the function must be that of the operator like "+", "-", "or", etc.
241     */
242     GrPrimitive addOperator(GrCallback callback, Operator operator,
243             GrType[] inSignature, GrType outType) {
244         string name;
245         final switch (operator) with (Operator) {
246         case add:
247             name = "+";
248             break;
249         case substract:
250             name = "-";
251             break;
252         case multiply:
253             name = "*";
254             break;
255         case divide:
256             name = "/";
257             break;
258         case concatenate:
259             name = "~";
260             break;
261         case remainder:
262             name = "%";
263             break;
264         case power:
265             name = "^";
266             break;
267         case equal:
268             name = "==";
269             break;
270         case doubleEqual:
271             name = "===";
272             break;
273         case threeWayComparison:
274             name = "<=>";
275             break;
276         case notEqual:
277             name = "!=";
278             break;
279         case greaterOrEqual:
280             name = ">=";
281             break;
282         case greater:
283             name = ">";
284             break;
285         case lesserOrEqual:
286             name = "<=";
287             break;
288         case lesser:
289             name = "<";
290             break;
291         case leftShift:
292             name = "<<";
293             break;
294         case rightShift:
295             name = ">>";
296             break;
297         case and:
298             name = "and";
299             break;
300         case or:
301             name = "or";
302             break;
303         case xor:
304             name = "xor";
305             break;
306         case not:
307             name = "not";
308             break;
309         }
310         return addOperator(callback, name, inSignature, outType);
311     }
312     /// Ditto
313     GrPrimitive addOperator(GrCallback callback, string name, GrType[] inSignature, GrType outType) {
314         if (inSignature.length > 2uL)
315             throw new Exception(
316                     "The operator `" ~ name ~ "` cannot take more than 2 parameters: " ~ grGetPrettyFunctionCall("",
317                     inSignature));
318         return addPrimitive(callback, "@op_" ~ name, inSignature, [outType]);
319     }
320 
321     /**
322     A cast operator allows to convert from one type to another.
323     It have to have only one parameter and return the casted value.
324     */
325     GrPrimitive addCast(GrCallback callback, GrType srcType, GrType dstType, bool isExplicit = false) {
326         auto primitive = addPrimitive(callback, "@as", [srcType, dstType], [
327                 dstType
328                 ]);
329         primitive.isExplicit = isExplicit;
330         return primitive;
331     }
332 
333     private string getPrettyPrimitive(GrPrimitive primitive) {
334         import std.conv : to;
335 
336         string result = primitive.name;
337         auto nbParameters = primitive.inSignature.length;
338         if (primitive.name == "@as")
339             nbParameters = 1;
340         result ~= "(";
341         for (int i; i < nbParameters; i++) {
342             result ~= grGetPrettyType(primitive.inSignature[i]);
343             if ((i + 2) <= nbParameters)
344                 result ~= ", ";
345         }
346         result ~= ")";
347         for (int i; i < primitive.outSignature.length; i++) {
348             result ~= i ? ", " : " ";
349             result ~= grGetPrettyType(primitive.outSignature[i]);
350         }
351         return result;
352     }
353 }