1 /** 
2  * Copyright: Enalye
3  * License: Zlib
4  * Authors: Enalye
5  */
6 module grimoire.stdlib.array;
7 
8 import std.range;
9 import grimoire.assembly, grimoire.compiler, grimoire.runtime;
10 
11 package(grimoire.stdlib) void grLoadStdLibArray(GrLibrary library) {
12     library.addForeign("ArrayIter", ["T"]);
13 
14     static foreach (t; ["Int", "Float", "String", "Object"]) {
15         mixin("GrType any" ~ t ~ "Array = grAny(\"A\", (type, data) {
16                 if (type.baseType != GrBaseType.array_)
17                     return false;
18                 const GrType subType = grUnmangle(type.mangledType);
19                 data.set(\"T\", subType);
20                 return grIsKindOf" ~ t ~ "(subType.baseType);
21             });
22             library.addPrimitive(&_copy_!\"" ~ t ~ "\", \"copy\", [any"
23                 ~ t ~ "Array], [grAny(\"A\")]);
24             library.addPrimitive(&_size_!\"" ~ t ~ "\", \"size\", [any" ~ t ~ "Array], [grInt]);
25             library.addPrimitive(&_resize_!\"" ~ t ~ "\", \"resize\", [
26                 any"
27                 ~ t ~ "Array, grInt
28             ], [grAny(\"A\")]);
29             library.addPrimitive(&_empty_!\"" ~ t ~ "\", \"empty?\", [
30                 any" ~ t ~ "Array
31             ], [grBool]);
32             library.addPrimitive(&_fill_!\"" ~ t ~ "\", \"fill\", [
33                 any" ~ t
34                 ~ "Array, grAny(\"T\")
35             ], [grAny(\"A\")]);
36             library.addPrimitive(&_clear_!\"" ~ t ~ "\", \"clear\", [
37                 any" ~ t
38                 ~ "Array
39             ], [grAny(\"A\")]);
40             library.addPrimitive(&_unshift_!\"" ~ t ~ "\", \"unshift\", [
41                     any" ~ t ~ "Array, grAny(\"T\")
42                 ], [grAny(\"A\")]);
43             library.addPrimitive(&_push_!\"" ~ t
44                 ~ "\", \"push\", [
45                     any" ~ t ~ "Array, grAny(\"T\")
46                 ], [grAny(\"A\")]);
47             library.addPrimitive(&_shift_!\"" ~ t ~ "\", \"shift\", [
48                     any" ~ t ~ "Array
49                 ], [grAny(\"T\")]);
50             library.addPrimitive(&_pop_!\"" ~ t
51                 ~ "\", \"pop\", [
52                     any" ~ t ~ "Array
53                 ], [grAny(\"T\")]);
54             library.addPrimitive(&_shift1_!\"" ~ t ~ "\", \"shift\", [
55                     any" ~ t ~ "Array, grInt
56                 ], [grAny(\"A\")]);
57             library.addPrimitive(&_pop1_!\"" ~ t ~ "\", \"pop\", [
58                     any" ~ t
59                 ~ "Array, grInt
60                 ], [grAny(\"A\")]);
61             library.addPrimitive(&_first_!\"" ~ t ~ "\", \"first\", [
62                 any" ~ t ~ "Array
63             ], [grAny(\"T\")]);
64             library.addPrimitive(&_last_!\"" ~ t ~ "\", \"last\", [
65                     any"
66                 ~ t ~ "Array
67                 ], [grAny(\"T\")]);
68             library.addPrimitive(&_remove_!\"" ~ t ~ "\", \"remove\", [
69                     any" ~ t ~ "Array, grInt
70                 ], [grAny(\"A\")]);
71             library.addPrimitive(&_remove2_!\"" ~ t ~ "\", \"remove\", [
72                     any" ~ t ~ "Array, grInt, grInt
73                 ], [grAny(\"A\")]);
74             library.addPrimitive(&_slice_!\""
75                 ~ t ~ "\", \"slice!\", [
76                     any" ~ t ~ "Array, grInt, grInt
77                 ], [grAny(\"A\")]);
78             library.addPrimitive(&_slice_copy_!\"" ~ t ~ "\", \"slice\", [
79                     any" ~ t ~ "Array, grInt, grInt
80                 ], [grAny(\"A\")]);
81             library.addPrimitive(&_reverse_!\"" ~ t
82                 ~ "\", \"reverse\", [
83                     any" ~ t ~ "Array
84                 ], [grAny(\"A\")]);
85             library.addPrimitive(&_insert_!\"" ~ t ~ "\", \"insert\", [
86                     any" ~ t ~ "Array, grInt, grAny(\"T\")
87                 ], [grAny(\"A\")]);
88             library.addPrimitive(&_each_!\"" ~ t
89                 ~ "\", \"each\", [
90                     grAny(\"A\", (type, data) {
91                 if (type.baseType != GrBaseType.array_)
92                     return false;
93                 const GrType subType = grUnmangle(type.mangledType);
94                 data.set(\"R\", grGetForeignType(\"ArrayIter\", [subType]));
95                 return grIsKindOf" ~ t ~ "(subType.baseType);
96             })
97                 ], [grAny(\"R\")]);
98             library.addPrimitive(&_next_!\""
99                 ~ t ~ "\", \"next\", [
100                     grAny(\"R\", (type, data) {
101                 if (type.baseType != GrBaseType.foreign)
102                     return false;
103                 auto result = grUnmangleComposite(type.mangledType);
104                 if(result.signature.length != 1 || result.name != \"ArrayIter\")
105                     return false;
106                 data.set(\"T\", result.signature[0]);
107                 return grIsKindOf" ~ t ~ "(result.signature[0].baseType);
108                     })
109                 ], [grBool, grAny(\"T\")]);
110             ");
111 
112         static if (t != "Object") {
113             mixin("
114             library.addPrimitive(&_sort_!\"" ~ t ~ "\", \"sort\", [
115                     any" ~ t ~ "Array
116                 ], [grAny(\"A\")]);
117             library.addPrimitive(&_findFirst_!\"" ~ t ~ "\", \"findFirst\", [
118                     any" ~ t ~ "Array, grAny(\"T\")
119                 ], [grInt]);
120             library.addPrimitive(&_findLast_!\"" ~ t
121                     ~ "\", \"findLast\", [
122                     any" ~ t ~ "Array, grAny(\"T\")
123                 ], [grInt]);
124             library.addPrimitive(&_findLast_!\"" ~ t ~ "\", \"findLast\", [
125                     any" ~ t
126                     ~ "Array, grAny(\"T\")
127                 ], [grInt]);
128             library.addPrimitive(&_has_!\"" ~ t
129                     ~ "\", \"has?\", [
130                     any" ~ t ~ "Array, grAny(\"T\")
131                 ], [grBool]);
132                 ");
133         }
134     }
135 }
136 
137 private void _copy_(string t)(GrCall call) {
138     mixin("Gr" ~ t ~ "Array copy = new Gr" ~ t ~ "Array;
139         copy.data = call.get"
140             ~ t ~ "Array(0).data.dup;
141         call.set" ~ t ~ "Array(copy);");
142 }
143 
144 private void _size_(string t)(GrCall call) {
145     mixin("call.setInt(cast(GrInt) call.get" ~ t ~ "Array(0).data.length);");
146 }
147 
148 private void _resize_(string t)(GrCall call) {
149     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
150     const GrInt size = call.getInt(1);
151     if (size < 0) {
152         call.raise("ArgumentError");
153         return;
154     }
155     static if (t == "Float") {
156         if (size > array.data.length) {
157             GrInt index = cast(GrInt) array.data.length;
158             array.data.length = size;
159             for (; index < array.data.length; ++index)
160                 array.data[index] = 0f;
161         }
162         else {
163             array.data.length = size;
164         }
165     }
166     else {
167         array.data.length = size;
168     }
169     mixin("call.set" ~ t ~ "Array(array);");
170 }
171 
172 private void _empty_(string t)(GrCall call) {
173     mixin("const Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
174     call.setBool(array.data.empty);
175 }
176 
177 private void _fill_(string t)(GrCall call) {
178     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
179     static if (t == "Object") {
180         GrPtr value = call.getPtr(1);
181     }
182     else {
183         mixin("auto value = call.get" ~ t ~ "(1);");
184     }
185     for (size_t index; index < array.data.length; ++index)
186         array.data[index] = value;
187     mixin("call.set" ~ t ~ "Array(array);");
188 }
189 
190 private void _clear_(string t)(GrCall call) {
191     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
192     array.data.length = 0;
193     mixin("call.set" ~ t ~ "Array(array);");
194 }
195 
196 private void _unshift_(string t)(GrCall call) {
197     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
198     static if (t == "Object") {
199         array.data = call.getPtr(1) ~ array.data;
200     }
201     else {
202         mixin("array.data = call.get" ~ t ~ "(1) ~ array.data;");
203     }
204     mixin("call.set" ~ t ~ "Array(array);");
205 }
206 
207 private void _push_(string t)(GrCall call) {
208     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
209     static if (t == "Object") {
210         array.data ~= call.getPtr(1);
211     }
212     else {
213         mixin("array.data ~= call.get" ~ t ~ "(1);");
214     }
215     mixin("call.set" ~ t ~ "Array(array);");
216 }
217 
218 private void _shift_(string t)(GrCall call) {
219     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
220     if (!array.data.length) {
221         call.raise("IndexError");
222         return;
223     }
224     static if (t == "Object") {
225         call.setPtr(array.data[0]);
226     }
227     else {
228         mixin("call.set" ~ t ~ "(array.data[0]);");
229     }
230     array.data = array.data[1 .. $];
231 }
232 
233 private void _pop_(string t)(GrCall call) {
234     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
235     if (!array.data.length) {
236         call.raise("IndexError");
237         return;
238     }
239     static if (t == "Object") {
240         call.setPtr(array.data[$ - 1]);
241     }
242     else {
243         mixin("call.set" ~ t ~ "(array.data[$ - 1]);");
244     }
245     array.data.length--;
246 }
247 
248 private void _shift1_(string t)(GrCall call) {
249     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
250     GrInt size = call.getInt(1);
251     if (size < 0) {
252         call.raise("IndexError");
253         return;
254     }
255     if (array.data.length < size) {
256         size = cast(GrInt) array.data.length;
257     }
258     mixin("Gr" ~ t ~ "Array copy = new Gr" ~ t ~ "Array;");
259     copy.data = array.data[0 .. size];
260     array.data = array.data[size .. $];
261     mixin("call.set" ~ t ~ "Array(copy);");
262 }
263 
264 private void _pop1_(string t)(GrCall call) {
265     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
266     GrInt size = call.getInt(1);
267     if (size < 0) {
268         call.raise("IndexError");
269         return;
270     }
271     if (array.data.length < size) {
272         size = cast(GrInt) array.data.length;
273     }
274     mixin("Gr" ~ t ~ "Array copy = new Gr" ~ t ~ "Array;");
275     copy.data = array.data[$ - size .. $];
276     array.data.length -= size;
277     mixin("call.set" ~ t ~ "Array(copy);");
278 }
279 
280 private void _first_(string t)(GrCall call) {
281     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
282     if (!array.data.length) {
283         call.raise("IndexError");
284         return;
285     }
286     static if (t == "Object") {
287         mixin("call.setPtr(array.data[0]);");
288     }
289     else {
290         mixin("call.set" ~ t ~ "(array.data[0]);");
291     }
292 }
293 
294 private void _last_(string t)(GrCall call) {
295     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
296     if (!array.data.length) {
297         call.raise("IndexError");
298         return;
299     }
300     static if (t == "Object") {
301         mixin("call.setPtr(array.data[$ - 1]);");
302     }
303     else {
304         mixin("call.set" ~ t ~ "(array.data[$ - 1]);");
305     }
306 }
307 
308 private void _remove_(string t)(GrCall call) {
309     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
310     GrInt index = call.getInt(1);
311     if (index < 0)
312         index = (cast(GrInt) array.data.length) + index;
313     if (!array.data.length || index >= array.data.length || index < 0) {
314         mixin("call.set" ~ t ~ "Array(array);");
315         return;
316     }
317     if (index + 1 == array.data.length) {
318         array.data.length--;
319         mixin("call.set" ~ t ~ "Array(array);");
320         return;
321     }
322     if (index == 0) {
323         array.data = array.data[1 .. $];
324         mixin("call.set" ~ t ~ "Array(array);");
325         return;
326     }
327     array.data = array.data[0 .. index] ~ array.data[index + 1 .. $];
328     mixin("call.set" ~ t ~ "Array(array);");
329 }
330 
331 private void _remove2_(string t)(GrCall call) {
332     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
333     GrInt index1 = call.getInt(1);
334     GrInt index2 = call.getInt(2);
335     if (index1 < 0)
336         index1 = (cast(GrInt) array.data.length) + index1;
337     if (index2 < 0)
338         index2 = (cast(GrInt) array.data.length) + index2;
339 
340     if (index2 < index1) {
341         const GrInt temp = index1;
342         index1 = index2;
343         index2 = temp;
344     }
345 
346     if (!array.data.length || index1 >= array.data.length || index2 < 0) {
347         mixin("call.set" ~ t ~ "Array(array);");
348         return;
349     }
350 
351     if (index1 < 0)
352         index1 = 0;
353     if (index2 >= array.data.length)
354         index2 = (cast(GrInt) array.data.length) - 1;
355 
356     if (index1 == 0 && (index2 + 1) == array.data.length) {
357         array.data.length = 0;
358         mixin("call.set" ~ t ~ "Array(array);");
359         return;
360     }
361     if (index1 == 0) {
362         array.data = array.data[(index2 + 1) .. $];
363         mixin("call.set" ~ t ~ "Array(array);");
364         return;
365     }
366     if ((index2 + 1) == array.data.length) {
367         array.data = array.data[0 .. index1];
368         mixin("call.set" ~ t ~ "Array(array);");
369         return;
370     }
371     array.data = array.data[0 .. index1] ~ array.data[(index2 + 1) .. $];
372     mixin("call.set" ~ t ~ "Array(array);");
373 }
374 
375 private void _slice_(string t)(GrCall call) {
376     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
377     GrInt index1 = call.getInt(1);
378     GrInt index2 = call.getInt(2);
379     if (index1 < 0)
380         index1 = (cast(GrInt) array.data.length) + index1;
381     if (index2 < 0)
382         index2 = (cast(GrInt) array.data.length) + index2;
383 
384     if (index2 < index1) {
385         const GrInt temp = index1;
386         index1 = index2;
387         index2 = temp;
388     }
389 
390     if (!array.data.length || index1 >= array.data.length || index2 < 0) {
391         array.data.length = 0;
392         mixin("call.set" ~ t ~ "Array(array);");
393         return;
394     }
395 
396     if (index1 < 0)
397         index1 = 0;
398     if (index2 >= array.data.length)
399         index2 = (cast(GrInt) array.data.length - 1);
400 
401     if (index1 == 0 && (index2 + 1) == array.data.length) {
402         mixin("call.set" ~ t ~ "Array(array);");
403         return;
404     }
405     array.data = array.data[index1 .. index2 + 1];
406     mixin("call.set" ~ t ~ "Array(array);");
407 }
408 
409 private void _slice_copy_(string t)(GrCall call) {
410     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
411     mixin("Gr" ~ t ~ "Array copy = new Gr" ~ t ~ "Array;");
412     GrInt index1 = call.getInt(1);
413     GrInt index2 = call.getInt(2);
414     if (index1 < 0)
415         index1 = (cast(GrInt) array.data.length) + index1;
416     if (index2 < 0)
417         index2 = (cast(GrInt) array.data.length) + index2;
418 
419     if (index2 < index1) {
420         const GrInt temp = index1;
421         index1 = index2;
422         index2 = temp;
423     }
424 
425     if (!array.data.length || index1 >= array.data.length || index2 < 0) {
426         mixin("call.set" ~ t ~ "Array(copy);");
427         return;
428     }
429 
430     if (index1 < 0)
431         index1 = 0;
432     if (index2 >= array.data.length)
433         index2 = (cast(GrInt) array.data.length - 1);
434 
435     if (index1 == 0 && (index2 + 1) == array.data.length) {
436         copy.data = array.data;
437     }
438     else {
439         copy.data = array.data[index1 .. index2 + 1];
440     }
441     mixin("call.set" ~ t ~ "Array(copy);");
442 }
443 
444 private void _reverse_(string t)(GrCall call) {
445     import std.algorithm.mutation : reverse;
446 
447     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
448     array.data = array.data.reverse;
449     mixin("call.set" ~ t ~ "Array(array);");
450 }
451 
452 private void _insert_(string t)(GrCall call) {
453     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
454     GrInt index = call.getInt(1);
455     static if (t == "Object") {
456         GrPtr value = call.getPtr(2);
457     }
458     else {
459         mixin("auto value = call.get" ~ t ~ "(2);");
460     }
461     if (index < 0)
462         index = (cast(GrInt) array.data.length) + index;
463     if (!array.data.length || index >= array.data.length || index < 0) {
464         call.raise("IndexError");
465         return;
466     }
467     if (index + 1 == array.data.length) {
468         array.data = array.data[0 .. index] ~ value ~ array.data[$ - 1];
469         mixin("call.set" ~ t ~ "Array(array);");
470         return;
471     }
472     if (index == 0) {
473         array.data = value ~ array.data;
474         mixin("call.set" ~ t ~ "Array(array);");
475         return;
476     }
477     array.data = array.data[0 .. index] ~ value ~ array.data[index .. $];
478     mixin("call.set" ~ t ~ "Array(array);");
479 }
480 
481 private void _sort_(string t)(GrCall call) {
482     import std.algorithm.sorting : sort;
483 
484     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
485     array.data.sort();
486     mixin("call.set" ~ t ~ "Array(array);");
487 }
488 
489 private void _findFirst_(string t)(GrCall call) {
490     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
491     mixin("auto value = call.get" ~ t ~ "(1);");
492     for (GrInt index; index < array.data.length; ++index) {
493         if (array.data[index] == value) {
494             call.setInt(index);
495             return;
496         }
497     }
498     call.setInt(-1);
499 }
500 
501 private void _findLast_(string t)(GrCall call) {
502     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
503     mixin("auto value = call.get" ~ t ~ "(1);");
504     for (GrInt index = (cast(GrInt) array.data.length) - 1; index > 0; --index) {
505         if (array.data[index] == value) {
506             call.setInt(index);
507             return;
508         }
509     }
510     call.setInt(-1);
511 }
512 
513 private void _has_(string t)(GrCall call) {
514     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
515     mixin("auto value = call.get" ~ t ~ "(1);");
516     for (GrInt index; index < array.data.length; ++index) {
517         if (array.data[index] == value) {
518             call.setBool(true);
519             return;
520         }
521     }
522     call.setBool(false);
523 }
524 
525 private final class ArrayIter(T) {
526     T[] array;
527     size_t index;
528 }
529 
530 private void _each_(string t)(GrCall call) {
531     mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);");
532     static if (t == "Int") {
533         ArrayIter!(GrInt) iter = new ArrayIter!(GrInt);
534     }
535     else static if (t == "Float") {
536         ArrayIter!(GrFloat) iter = new ArrayIter!(GrFloat);
537     }
538     else static if (t == "String") {
539         ArrayIter!(GrString) iter = new ArrayIter!(GrString);
540     }
541     else static if (t == "Object") {
542         ArrayIter!(GrPtr) iter = new ArrayIter!(GrPtr);
543     }
544     iter.array = array.data.dup;
545     call.setForeign(iter);
546 }
547 
548 private void _next_(string t)(GrCall call) {
549     static if (t == "Int") {
550         ArrayIter!(GrInt) iter = call.getForeign!(ArrayIter!(GrInt))(0);
551     }
552     else static if (t == "Float") {
553         ArrayIter!(GrFloat) iter = call.getForeign!(ArrayIter!(GrFloat))(0);
554     }
555     else static if (t == "String") {
556         ArrayIter!(GrString) iter = call.getForeign!(ArrayIter!(GrString))(0);
557     }
558     else static if (t == "Object") {
559         ArrayIter!(GrPtr) iter = call.getForeign!(ArrayIter!(GrPtr))(0);
560     }
561     if (!iter) {
562         call.raise("NullError");
563         return;
564     }
565     if (iter.index >= iter.array.length) {
566         call.setBool(false);
567         static if (t == "Int") {
568             call.setInt(0);
569         }
570         else static if (t == "Float") {
571             call.setFloat(0f);
572         }
573         else static if (t == "String") {
574             call.setString("");
575         }
576         else static if (t == "Object") {
577             call.setPtr(null);
578         }
579         return;
580     }
581     call.setBool(true);
582     static if (t == "Int") {
583         call.setInt(iter.array[iter.index]);
584     }
585     else static if (t == "Float") {
586         call.setFloat(iter.array[iter.index]);
587     }
588     else static if (t == "String") {
589         call.setString(iter.array[iter.index]);
590     }
591     else static if (t == "Object") {
592         call.setPtr(iter.array[iter.index]);
593     }
594     iter.index++;
595 }