1 /** 
2  * Copyright: Enalye
3  * License: Zlib
4  * Authors: Enalye
5  */
6 module grimoire.stdlib..string;
7 
8 import std..string;
9 import std.conv : to;
10 import grimoire.assembly, grimoire.compiler, grimoire.runtime;
11 
12 package(grimoire.stdlib) void grLoadStdLibString(GrLibrary library) {
13     library.addPrimitive(&_empty, "empty?", [grString], [grBool]);
14     library.addPrimitive(&_unshift, "unshift", [grString, grString], [grString]);
15     library.addPrimitive(&_push, "push", [grString, grString], [grString]);
16     library.addPrimitive(&_shift, "shift", [grString], [grString]);
17     library.addPrimitive(&_pop, "pop", [grString], [grString]);
18     library.addPrimitive(&_shift1, "shift", [grString, grInt], [grString]);
19     library.addPrimitive(&_pop1, "pop", [grString, grInt], [grString]);
20     library.addPrimitive(&_first, "first", [grString], [grString]);
21     library.addPrimitive(&_last, "last", [grString], [grString]);
22     library.addPrimitive(&_remove, "remove", [grString, grInt], [grString]);
23     library.addPrimitive(&_remove2, "remove", [grString, grInt, grInt], [
24             grString
25             ]);
26     library.addPrimitive(&_slice, "slice", [grString, grInt, grInt], [grString]);
27     library.addPrimitive(&_reverse, "reverse", [grString], [grString]);
28     library.addPrimitive(&_insert, "insert", [grString, grInt, grString], [
29             grString
30             ]);
31     library.addPrimitive(&_findFirst, "findFirst", [grString, grString], [grInt]);
32     library.addPrimitive(&_findLast, "findLast", [grString, grString], [grInt]);
33     library.addPrimitive(&_has, "has?", [grString, grString], [grBool]);
34 
35     GrType stringIterType = library.addForeign("StringIter");
36     library.addPrimitive(&_each, "each", [grString], [stringIterType]);
37     library.addPrimitive(&_next, "next", [stringIterType], [grBool, grString]);
38 }
39 
40 private void _empty(GrCall call) {
41     call.setBool(call.getString(0).length == 0);
42 }
43 
44 private void _unshift(GrCall call) {
45     GrString str = call.getString(0);
46     str = call.getString(1) ~ str;
47     call.setString(str);
48 }
49 
50 private void _push(GrCall call) {
51     GrString str = call.getString(0);
52     str ~= call.getString(1);
53     call.setString(str);
54 }
55 
56 private void _shift(GrCall call) {
57     GrString str = call.getString(0);
58     if (!str.length) {
59         call.setString(str);
60         return;
61     }
62     call.setString(str[1 .. $]);
63 }
64 
65 private void _pop(GrCall call) {
66     GrString str = call.getString(0);
67     if (!str.length) {
68         call.setString(str);
69         return;
70     }
71     str.length--;
72     call.setString(str);
73 }
74 
75 private void _shift1(GrCall call) {
76     GrString str = call.getString(0);
77     GrInt size = call.getInt(1);
78     if (size < 0) {
79         call.raise("IndexError");
80         return;
81     }
82     if (str.length < size) {
83         str.length = 0;
84         call.setString(str);
85         return;
86     }
87     if (!str.length) {
88         call.setString(str);
89         return;
90     }
91     call.setString(str[size .. $]);
92 }
93 
94 private void _pop1(GrCall call) {
95     GrString str = call.getString(0);
96     GrInt size = call.getInt(1);
97     if (size < 0) {
98         call.raise("IndexError");
99         return;
100     }
101     if (str.length < size) {
102         str.length = 0;
103         call.setString(str);
104         return;
105     }
106     if (!str.length) {
107         call.setString(str);
108         return;
109     }
110     str.length -= size;
111     call.setString(str);
112 }
113 
114 private void _first(GrCall call) {
115     GrString str = call.getString(0);
116     if (!str.length) {
117         call.raise("IndexError");
118         return;
119     }
120     call.setString(to!GrString(str[0]));
121 }
122 
123 private void _last(GrCall call) {
124     GrString str = call.getString(0);
125     if (!str.length) {
126         call.raise("IndexError");
127         return;
128     }
129     call.setString(to!GrString(str[$ - 1]));
130 }
131 
132 private void _remove(GrCall call) {
133     GrString str = call.getString(0);
134     GrInt index = call.getInt(1);
135     if (index < 0)
136         index = (cast(GrInt) str.length) + index;
137     if (!str.length || index >= str.length || index < 0) {
138         call.setString(str);
139         return;
140     }
141     if (index + 1 == str.length) {
142         str.length--;
143         call.setString(str);
144         return;
145     }
146     if (index == 0) {
147         call.setString(str[1 .. $]);
148         return;
149     }
150     call.setString(str[0 .. index] ~ str[index + 1 .. $]);
151 }
152 
153 private void _remove2(GrCall call) {
154     GrString str = call.getString(0);
155     GrInt index1 = call.getInt(1);
156     GrInt index2 = call.getInt(2);
157     if (index1 < 0)
158         index1 = (cast(GrInt) str.length) + index1;
159     if (index2 < 0)
160         index2 = (cast(GrInt) str.length) + index2;
161 
162     if (index2 < index1) {
163         const GrInt temp = index1;
164         index1 = index2;
165         index2 = temp;
166     }
167 
168     if (!str.length || index1 >= str.length || index2 < 0) {
169         call.setString(str);
170         return;
171     }
172 
173     if (index1 < 0)
174         index1 = 0;
175     if (index2 >= str.length)
176         index2 = (cast(GrInt) str.length) - 1;
177 
178     if (index1 == 0 && (index2 + 1) == str.length) {
179         call.setString("");
180         return;
181     }
182     if (index1 == 0) {
183         call.setString(str[(index2 + 1) .. $]);
184         return;
185     }
186     if ((index2 + 1) == str.length) {
187         call.setString(str[0 .. index1]);
188         return;
189     }
190     call.setString(str[0 .. index1] ~ str[(index2 + 1) .. $]);
191 }
192 
193 private void _slice(GrCall call) {
194     GrString str = call.getString(0);
195     GrInt index1 = call.getInt(1);
196     GrInt index2 = call.getInt(2);
197     if (index1 < 0)
198         index1 = (cast(GrInt) str.length) + index1;
199     if (index2 < 0)
200         index2 = (cast(GrInt) str.length) + index2;
201 
202     if (index2 < index1) {
203         const GrInt temp = index1;
204         index1 = index2;
205         index2 = temp;
206     }
207 
208     if (!str.length || index1 >= str.length || index2 < 0) {
209         call.setString("");
210         return;
211     }
212 
213     if (index1 < 0)
214         index1 = 0;
215     if (index2 >= str.length)
216         index2 = (cast(GrInt) str.length - 1);
217 
218     if (index1 == 0 && (index2 + 1) == str.length) {
219         call.setString(str);
220         return;
221     }
222     call.setString(str[index1 .. index2 + 1]);
223 }
224 
225 private void _reverse(GrCall call) {
226     import std.algorithm.mutation : reverse;
227 
228     call.setString(call.getString(0).dup.reverse);
229 }
230 
231 private void _insert(GrCall call) {
232     GrString str = call.getString(0);
233     GrInt index = call.getInt(1);
234     GrString value = call.getString(2);
235     if (index < 0)
236         index = (cast(GrInt) str.length) + index;
237     if (!str.length || index >= str.length || index < 0) {
238         call.raise("IndexError");
239         return;
240     }
241     if (index + 1 == str.length) {
242         call.setString(str[0 .. index] ~ value ~ str[$ - 1]);
243         return;
244     }
245     if (index == 0) {
246         call.setString(value ~ str);
247         return;
248     }
249     call.setString(str[0 .. index] ~ value ~ str[index .. $]);
250 }
251 
252 private void _findFirst(GrCall call) {
253     GrString str = call.getString(0);
254     GrString value = call.getString(1);
255     call.setInt(cast(GrInt) str.indexOf(value));
256 }
257 
258 private void _findLast(GrCall call) {
259     GrString str = call.getString(0);
260     GrString value = call.getString(1);
261     call.setInt(cast(GrInt) str.lastIndexOf(value));
262 }
263 
264 private void _has(GrCall call) {
265     GrString str = call.getString(0);
266     GrString value = call.getString(1);
267     call.setBool(str.indexOf(value) != -1);
268 }
269 
270 private final class StringIter {
271     GrString value;
272     size_t index;
273 }
274 
275 private void _each(GrCall call) {
276     StringIter iter = new StringIter;
277     iter.value = call.getString(0);
278     call.setForeign(iter);
279 }
280 
281 private void _next(GrCall call) {
282     StringIter iter = call.getForeign!(StringIter)(0);
283     if (!iter) {
284         call.raise("NullError");
285         return;
286     }
287     if (iter.index >= iter.value.length) {
288         call.setBool(false);
289         call.setString("");
290         return;
291     }
292     call.setBool(true);
293     call.setString(to!GrString(iter.value[iter.index]));
294     iter.index++;
295 }