1 /** 
2  * Copyright: Enalye
3  * License: Zlib
4  * Authors: Enalye
5  */
6 module grimoire.stdlib.map;
7 
8 import std.typecons : Tuple, tuple;
9 import std.conv : to;
10 import grimoire.assembly, grimoire.compiler, grimoire.runtime;
11 import grimoire.stdlib.util;
12 
13 /// Hashmap
14 private final class Map(T) {
15     /// Payload
16     T[GrString] data;
17 
18     /// Ctor
19     this(GrString[] keys, T[] values) {
20         for (size_t i; i < keys.length; ++i) {
21             data[keys[i]] = values[i];
22         }
23     }
24 
25     this(Map!T map) {
26         data = map.data.dup;
27     }
28 }
29 
30 private {
31     alias IntMap = Map!(GrInt);
32     alias FloatMap = Map!(GrFloat);
33     alias StringMap = Map!(GrString);
34     alias ObjectMap = Map!(GrPtr);
35 }
36 
37 /// Iterator
38 private final class MapIter(T) {
39     Tuple!(GrString, T)[] pairs;
40     size_t index;
41 }
42 
43 package(grimoire.stdlib) void grLoadStdLibMap(GrLibrary library) {
44     library.addForeign("Map", ["T"]);
45     library.addForeign("MapIter", ["T"]);
46 
47     static foreach (t; ["Int", "Float", "String", "Object"]) {
48         mixin("GrType any" ~ t ~ "Map = grAny(\"M\", (type, data) {
49                 if (type.baseType != GrBaseType.foreign)
50                     return false;
51                 auto subType = grUnmangleComposite(type.mangledType);
52                 if(subType.name != \"Map\")
53                     return false;
54                 if(subType.signature.length != 1)
55                     return false;
56                 data.set(\"T\", subType.signature[0]);
57                 data.set(\"A\", grArray(subType.signature[0]));
58                 return grIsKindOf" ~ t ~ "(subType.signature[0].baseType);
59             });
60 
61             GrType any" ~ t
62                 ~ "Array = grAny(\"A\", (type, data) {
63                 if (type.baseType != GrBaseType.array_)
64                     return false;
65                 const GrType subType = grUnmangle(type.mangledType);
66                 data.set(\"M\", grGetForeignType(\"Map\", [subType]));
67                 return grIsKindOf" ~ t ~ "(subType.baseType);
68             });
69 
70             library.addPrimitive(&_make_!\"" ~ t
71                 ~ "\", \"Map\", [grStringArray, any" ~ t ~ "Array], [grAny(\"M\")]);
72 
73             library.addPrimitive(&_copy_!\"" ~ t
74                 ~ "\", \"copy\", [any" ~ t ~ "Map], [grAny(\"M\")]);
75 
76             library.addPrimitive(&_size_!\"" ~ t ~ "\", \"size\", [any"
77                 ~ t ~ "Map], [grInt]);
78 
79             library.addPrimitive(&_empty_!\"" ~ t ~ "\", \"empty?\", [
80                 any" ~ t ~ "Map
81             ], [grBool]);
82 
83             library.addPrimitive(&_clear_!\"" ~ t ~ "\", \"clear\", [
84                 any" ~ t ~ "Map
85             ], [grAny(\"M\")]);
86 
87             library.addPrimitive(&_set_!\"" ~ t
88                 ~ "\", \"set\", [any" ~ t ~ "Map, grString, grAny(\"T\")]);
89 
90             library.addPrimitive(&_get_!\"" ~ t
91                 ~ "\", \"get\", [any" ~ t ~ "Map, grString], [grAny(\"T\")]);
92 
93             library.addPrimitive(&_has_!\""
94                 ~ t ~ "\", \"has?\", [any" ~ t ~ "Map, grString], [grBool]);
95 
96             library.addPrimitive(&_remove_!\"" ~ t
97                 ~ "\", \"remove\", [any" ~ t ~ "Map, grString]);
98 
99             library.addPrimitive(&_byKeys_!\"" ~ t ~ "\", \"byKeys\", [any" ~ t
100                 ~ "Map], [grStringArray]);
101 
102             library.addPrimitive(&_byValues_!\"" ~ t ~ "\", \"byValues\", [any" ~ t ~ "Map], [any"
103                 ~ t ~ "Array]);
104 
105             library.addPrimitive(&_each_!\"" ~ t ~ "\", \"each\", [
106                     grAny(\"A\", (type, data) {
107                 if (type.baseType != GrBaseType.foreign)
108                     return false;
109                 auto subType = grUnmangleComposite(type.mangledType);
110                 if(subType.name != \"Map\")
111                     return false;
112                 if(subType.signature.length != 1)
113                     return false;
114                 data.set(\"R\", grGetForeignType(\"MapIter\", subType.signature));
115                 return grIsKindOf" ~ t ~ "(subType.signature[0].baseType);
116             })
117                 ], [grAny(\"R\")]);
118 
119             library.addPrimitive(&_next_!\""
120                 ~ t ~ "\", \"next\", [
121                     grAny(\"R\", (type, data) {
122                 if (type.baseType != GrBaseType.foreign)
123                     return false;
124                 auto result = grUnmangleComposite(type.mangledType);
125                 if(result.signature.length != 1 || result.name != \"MapIter\")
126                     return false;
127                 data.set(\"T\", grGetClassType(\"Pair\", [grString, result.signature[0]]));
128                 return grIsKindOf" ~ t ~ "(result.signature[0].baseType);
129                     })
130                 ], [grBool, grAny(\"T\")]);
131             ");
132     }
133 
134     GrType boolMap = grGetForeignType("Map", [grBool]);
135     library.addPrimitive(&_print_!("bool", false), "print", [boolMap]);
136     library.addPrimitive(&_print_!("bool", true), "printl", [boolMap]);
137 
138     GrType intMap = grGetForeignType("Map", [grInt]);
139     library.addPrimitive(&_print_!("int", false), "print", [intMap]);
140     library.addPrimitive(&_print_!("int", true), "printl", [intMap]);
141 
142     GrType floatMap = grGetForeignType("Map", [grFloat]);
143     library.addPrimitive(&_print_!("float", false), "print", [floatMap]);
144     library.addPrimitive(&_print_!("float", true), "printl", [floatMap]);
145 
146     GrType stringMap = grGetForeignType("Map", [grString]);
147     library.addPrimitive(&_print_!("string", false), "print", [stringMap]);
148     library.addPrimitive(&_print_!("string", true), "printl", [stringMap]);
149 }
150 
151 private void _make_(string t)(GrCall call) {
152     mixin(t ~ "Map map = new " ~ t ~ "Map(call.getStringArray(0).data, call.get"
153             ~ t ~ "Array(1).data);");
154     call.setForeign(map);
155 }
156 
157 private void _copy_(string t)(GrCall call) {
158     mixin(t ~ "Map map = call.getForeign!" ~ t ~ "Map(0);");
159     if (!map) {
160         call.raise("NullError");
161         return;
162     }
163     mixin("call.setForeign!" ~ t ~ "Map(new " ~ t ~ "Map(map));");
164 }
165 
166 private void _size_(string t)(GrCall call) {
167     mixin(t ~ "Map map = call.getForeign!" ~ t ~ "Map(0);");
168     if (!map) {
169         call.raise("NullError");
170         return;
171     }
172     call.setInt(cast(GrInt) map.data.length);
173 }
174 
175 private void _empty_(string t)(GrCall call) {
176     mixin("const " ~ t ~ "Map map = call.getForeign!" ~ t ~ "Map(0);");
177     if (!map) {
178         call.raise("NullError");
179         return;
180     }
181     call.setBool(map.data.length == 0);
182 }
183 
184 private void _clear_(string t)(GrCall call) {
185     mixin(t ~ "Map map = call.getForeign!" ~ t ~ "Map(0);");
186     if (!map) {
187         call.raise("NullError");
188         return;
189     }
190     map.data.clear();
191     mixin("call.setForeign!" ~ t ~ "Map(map);");
192 }
193 
194 private void _set_(string t)(GrCall call) {
195     mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);");
196     if (!map) {
197         call.raise("NullError");
198         return;
199     }
200     static if (t == "Object") {
201         map.data[call.getString(1)] = call.getPtr(2);
202     }
203     else {
204         mixin("map.data[call.getString(1)] = call.get" ~ t ~ "(2);");
205     }
206 }
207 
208 private void _get_(string t)(GrCall call) {
209     mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);");
210     if (!map) {
211         call.raise("NullError");
212         return;
213     }
214     static if (t == "Object") {
215         call.setPtr(map.data[call.getString(1)]);
216     }
217     else {
218         mixin("call.set" ~ t ~ "(map.data[call.getString(1)]);");
219     }
220 }
221 
222 private void _has_(string t)(GrCall call) {
223     mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);");
224     if (!map) {
225         call.raise("NullError");
226         return;
227     }
228     call.setBool((call.getString(1) in map.data) !is null);
229 }
230 
231 private void _remove_(string t)(GrCall call) {
232     mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);");
233     if (!map) {
234         call.raise("NullError");
235         return;
236     }
237     map.data.remove(call.getString(1));
238 }
239 
240 private void _byKeys_(string t)(GrCall call) {
241     mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);");
242     if (!map) {
243         call.raise("NullError");
244         return;
245     }
246     GrStringArray ary = new GrStringArray;
247     ary.data = map.data.keys;
248     call.setStringArray(ary);
249 }
250 
251 private void _byValues_(string t)(GrCall call) {
252     mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);");
253     if (!map) {
254         call.raise("NullError");
255         return;
256     }
257     mixin("Gr" ~ t ~ "Array ary = new Gr" ~ t ~ "Array;");
258     ary.data = map.data.values;
259     mixin("call.set" ~ t ~ "Array(ary);");
260 }
261 
262 private void _printb_(string t)(GrCall call) {
263     Map map = call.getForeign!(IntMap)(0);
264     if (!map) {
265         call.raise("NullError");
266         return;
267     }
268     GrString result = "{";
269     bool isFirst = true;
270     foreach (key, value; map.data) {
271         if (isFirst) {
272             isFirst = false;
273         }
274         else {
275             result ~= ", ";
276         }
277         result ~= "\"" ~ key ~ "\"=>" ~ to!string(cast(GrBool) value);
278     }
279     result ~= "}";
280     _stdOut(result);
281 }
282 
283 private void _printlb_(string t)(GrCall call) {
284     Map map = call.getForeign!(IntMap)(0);
285     if (!map) {
286         call.raise("NullError");
287         return;
288     }
289     GrString result = "{";
290     bool isFirst = true;
291     foreach (key, value; map.data) {
292         if (isFirst) {
293             isFirst = false;
294         }
295         else {
296             result ~= ", ";
297         }
298         result ~= "\"" ~ key ~ "\"=>" ~ to!string(cast(GrBool) value);
299     }
300     result ~= "}\n";
301     _stdOut(result);
302 }
303 
304 private void _each_(string t)(GrCall call) {
305     mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);");
306     if (!map) {
307         call.raise("NullError");
308         return;
309     }
310     static if (t == "Int") {
311         MapIter!(GrInt) iter = new MapIter!(GrInt);
312     }
313     else static if (t == "Float") {
314         MapIter!(GrFloat) iter = new MapIter!(GrFloat);
315     }
316     else static if (t == "String") {
317         MapIter!(GrString) iter = new MapIter!(GrString);
318     }
319     else static if (t == "Object") {
320         MapIter!(GrPtr) iter = new MapIter!(GrPtr);
321     }
322     foreach (pair; map.data.byKeyValue()) {
323         iter.pairs ~= tuple(pair.key, pair.value);
324     }
325     call.setForeign(iter);
326 }
327 
328 private void _next_(string t)(GrCall call) {
329     static if (t == "Int") {
330         MapIter!(GrInt) iter = call.getForeign!(MapIter!(GrInt))(0);
331     }
332     else static if (t == "Float") {
333         MapIter!(GrFloat) iter = call.getForeign!(MapIter!(GrFloat))(0);
334     }
335     else static if (t == "String") {
336         MapIter!(GrString) iter = call.getForeign!(MapIter!(GrString))(0);
337     }
338     else static if (t == "Object") {
339         MapIter!(GrPtr) iter = call.getForeign!(MapIter!(GrPtr))(0);
340     }
341     if (!iter) {
342         call.raise("NullError");
343         return;
344     }
345     if (iter.index >= iter.pairs.length) {
346         call.setBool(false);
347         call.setPtr(null);
348         return;
349     }
350     call.setBool(true);
351     static if (t == "Int") {
352         GrObject obj = new GrObject(["first", "second"]);
353         obj.setString("first", iter.pairs[iter.index][0]);
354         obj.setInt("second", iter.pairs[iter.index][1]);
355         call.setObject(obj);
356     }
357     else static if (t == "Float") {
358         GrObject obj = new GrObject(["first", "second"]);
359         obj.setString("first", iter.pairs[iter.index][0]);
360         obj.setFloat("second", iter.pairs[iter.index][1]);
361         call.setObject(obj);
362     }
363     else static if (t == "String") {
364         GrObject obj = new GrObject(["first", "second"]);
365         obj.setString("first", iter.pairs[iter.index][0]);
366         obj.setString("second", iter.pairs[iter.index][1]);
367         call.setObject(obj);
368     }
369     else static if (t == "Object") {
370         GrObject obj = new GrObject(["first", "second"]);
371         obj.setString("first", iter.pairs[iter.index][0]);
372         obj.setPtr("second", iter.pairs[iter.index][1]);
373         call.setObject(obj);
374     }
375     iter.index++;
376 }
377 
378 private void _print_(string t, bool newLine)(GrCall call) {
379     static if (t == "bool" || t == "int") {
380         IntMap map = call.getForeign!(IntMap)(0);
381     }
382     else static if (t == "float") {
383         FloatMap map = call.getForeign!(FloatMap)(0);
384     }
385     else static if (t == "string") {
386         StringMap map = call.getForeign!(StringMap)(0);
387     }
388     if (!map) {
389         call.raise("NullError");
390         return;
391     }
392     GrString result = "{";
393     bool isFirst = true;
394     foreach (key, value; map.data) {
395         if (isFirst) {
396             isFirst = false;
397         }
398         else {
399             result ~= ", ";
400         }
401         result ~= "\"" ~ key ~ "\"=>";
402         static if (t == "string") {
403             result ~= "\"" ~ to!string(value) ~ "\"";
404         }
405         else static if (t == "bool") {
406             result ~= to!string(cast(bool) value);
407         }
408         else {
409             result ~= to!string(value);
410         }
411     }
412     result ~= newLine ? "}\n" : "}";
413     _stdOut(result);
414 }