1 // SemiTwist Library
2 // Written in the D programming language.
3 
4 module semitwist.util.ctfe;
5 
6 import std.ascii;
7 import std.algorithm;
8 import std.array;
9 import std.range;
10 import std.stdio;
11 import std..string;
12 import std.traits;
13 import std.uni;
14 
15 import semitwist.util.all;
16 
17 T[] ctfe_pad(T)(T[] str, int length, T[] padChar=" ")
18 {
19 	return ctfe_pad(str, length, true, padChar);
20 }
21 T[] ctfe_pad(T)(T[] str, int length, bool padLeft, T[] padChar=" ")
22 {
23 	if(str.length < length)
24 	{
25 		auto paddingSize = min(length - str.length, int.max);
26 		auto padding = ctfe_repeat!(T)(padChar, paddingSize);
27 		
28 		if(padLeft)
29 			str = padding ~ str;
30 		else
31 			str = str ~ padding;
32 	}
33 	
34 	return str;
35 }
36 
37 /*T[] ctfe_repeat(T)(T chr, int count)
38 {
39 	return ctfe_repeat("" ~ chr, count);
40 }*/
41 T[] ctfe_repeat(T)(T[] str, int count)
42 {
43 	T[] ret = "";
44 	
45 	for(int i=0; i < count; i++)
46 		ret ~= str;
47 		
48 	return ret;
49 }
50 
51 //size_t ctfe_find(T, TElem)(T collection, TElem elem, size_t start=0) if(isSomeString!T && is(unqual!T:TElem[]))
52 size_t ctfe_find(T)(const(T)[] collection, const(T) elem, size_t start=0)
53 {
54 	for(size_t i=start; i<collection.length; i++)
55 	{
56 		if(collection[i] == elem)
57 			return i;
58 	}
59 	return collection.length;
60 }
61 
62 size_t ctfe_find(T)(const(T)[] haystack, const(T)[] needle, size_t start=0) //if(isSomeString!T)
63 {
64 	if(haystack.length < needle.length)
65 		return haystack.length;
66 
67 	for(size_t i=start; i <= haystack.length-needle.length; i++)
68 	{
69 		if(haystack[i..i+needle.length] == needle)
70 			return i;
71 	}
72 	return haystack.length;
73 }
74 
75 T ctfe_join(T)(T[] strs, T delim) if(isSomeString!T)
76 {
77 	T value = "";
78 	
79 	foreach(i, str; strs)
80 		value ~= (i==0?"":delim) ~ str;
81 	
82 	return value;
83 }
84 
85 T ctfe_substitute(T)(T str, T match, T replace) if(isSomeString!T)
86 {
87 	T value = "";
88 	
89 	if(str.length < match.length)
90 		return str;
91 	
92 	while(str.length >= match.length)
93 	{
94 		if(str[0..match.length] == match)
95 		{
96 			value ~= replace;
97 			str = str[match.length .. $];
98 		}
99 		else
100 		{
101 			// Can't do "value ~= str[0];" because of DMD Issue #5722
102 			//TODO: Change back to "value ~= str[0];" when DMD Issue #5722 is fixed
103 			value ~= [ str[0] ];
104 
105 			str = str[1 .. $];
106 		}
107 	}
108 	value ~= str;
109 	return value;
110 }
111 
112 T[] ctfe_split(T)(T str, T delim) if(isSomeString!T)
113 {
114 	T[] arr;
115 	auto currStr = str;
116 	size_t index;
117 	while((index=ctfe_find(currStr, delim)) < currStr.length)
118 	{
119 		arr ~= currStr[0..index];
120 		currStr = currStr[index+delim.length..$];
121 	}
122 	arr ~= currStr;
123 	return arr;
124 }
125 
126 
127 /// ctfe_subMapJoin("Hi WHO. ", "WHO", ["Joey", "Q", "Sue"])
128 /// --> "Hi Joey. Hi Q. Hi Sue. "
129 T ctfe_subMapJoin(T)(T str, T match, T[] replacements) if(isSomeString!T)
130 {
131 	T value = "";
132 	foreach(T replace; replacements)
133 		value ~= ctfe_substitute(str, match, replace);
134 
135 	return value;
136 }
137 
138 bool ctfe_iswhite(dchar ch)
139 {
140 	foreach(i; 0..whitespace.length)
141 	if(ch == whitespace[i])
142 		return true;
143 
144 	return false;
145 }
146 
147 T ctfe_to(T)(string str) if(is(T==uint))
148 {
149 	T val = 0;
150 	foreach(i; 0..str.length)
151 	{
152 		auto newVal = cast(ubyte)str[i];
153 		if(newVal < cast(ubyte)'0' || newVal > cast(ubyte)'9')
154 			throw new Exception("Invalid character");
155 		
156 		val *= 10;
157 		val += newVal - cast(ubyte)'0';
158 	}
159 	
160 	return val;
161 }
162 
163 T ctfe_strip(T)(T str) if(isSomeString!T)
164 {
165 	if(!__ctfe)
166 		return strip(str);
167 	
168 	return str.ctfe_stripl().ctfe_stripr();
169 }
170 
171 T ctfe_stripl(T)(T str) if(isSomeString!T)
172 {
173 	if(!__ctfe)
174 		return stripLeft(str);
175 	
176 	alias ElementEncodingType!T TChar;
177 	
178 	size_t startIndex = str.length;
179 	
180 	foreach(i, TChar ch; str)
181 	if(!ctfe_isWhite(cast(dchar)ch))
182 	{
183 		startIndex = i;
184 		break;
185 	}
186 	
187 	return str[startIndex..$];
188 }
189 
190 T ctfe_stripr(T)(T str) if(isSomeString!T)
191 {
192 	if(!__ctfe)
193 		return stripRight(str);
194 	
195 	alias ElementEncodingType!T TChar;
196 	
197 	size_t endIndex = 0;
198 	
199 	foreach_reverse(i, TChar ch; str)
200 	if(!ctfe_isWhite(cast(dchar)ch))
201 	{
202 		endIndex = i+1;
203 		break;
204 	}
205 	
206 	return str[0..endIndex];
207 }
208 
209 bool ctfe_isWhite(dchar ch)
210 {
211 	return
212 		ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t' ||
213 		ch == '\v' || ch == '\f' ||
214 		ch == '\u2028' || ch == '\u2029' ||
215 		ch == '\u0085' || ch == '\u00A0' || ch == '\u1680' || ch == '\u180E' ||
216 		(ch >= '\u2000' && ch <= '\u200A') ||
217 		ch == '\u202F' || ch == '\u205F' || ch == '\u3000';
218 }
219 
220 mixin(unittestSemiTwistDLib(q{
221 
222 	// ctfe_find ---------------------------
223 	mixin(deferEnsure!(q{ ctfe_find("abcde", 'd' ) }, q{ _==3 }));
224 	mixin(deferEnsure!(q{ ctfe_find("abcde", 'X' ) }, q{ _==5 }));
225 	mixin(deferEnsure!(q{ ctfe_find("abcde", "d" ) }, q{ _==3 }));
226 	mixin(deferEnsure!(q{ ctfe_find("abcde", "cd") }, q{ _==2 }));
227 	mixin(deferEnsure!(q{ ctfe_find("abcde", "cX") }, q{ _==5 }));
228 
229 	mixin(deferEnsure!(q{ ctfe_find("cdbcde", 'd' , 2) }, q{ _==4 }));
230 	mixin(deferEnsure!(q{ ctfe_find("cdbcde", "d" , 2) }, q{ _==4 }));
231 	mixin(deferEnsure!(q{ ctfe_find("cdbcde", "cd", 1) }, q{ _==3 }));
232 	mixin(deferEnsure!(q{ ctfe_find("cXbcde", "cX", 1) }, q{ _==6 }));
233 
234 	mixin(deferEnsure!(q{ ctfe_find("abc", 'a')     }, q{ _==0 }));
235 	mixin(deferEnsure!(q{ ctfe_find("abc", 'c')     }, q{ _==2 }));
236 	mixin(deferEnsure!(q{ ctfe_find("abc", "a")     }, q{ _==0 }));
237 	mixin(deferEnsure!(q{ ctfe_find("abc", "c")     }, q{ _==2 }));
238 	mixin(deferEnsure!(q{ ctfe_find("aabbcc", "aa") }, q{ _==0 }));
239 	mixin(deferEnsure!(q{ ctfe_find("aabbcc", "cc") }, q{ _==4 }));
240 
241 	mixin(deferEnsure!(q{ ctfe_find("abc", "abcde") }, q{ _==3 }));
242 
243 	// ctfe_split ---------------------------
244 	mixin(deferEnsure!(q{ ctfe_split("a--b-b--ccc---d----e--", "--") }, q{ _==["a","b-b","ccc","-d","","e",""] }));
245 	mixin(deferEnsure!(q{ ctfe_split("-Xa", "-X") }, q{ _==["","a"] }));
246 
247 	// ctfe_iswhite ---------------------------
248 	mixin(deferEnsure!(q{ ctfe_iswhite(' ')  }, q{ _==true  }));
249 	mixin(deferEnsure!(q{ ctfe_iswhite('\t') }, q{ _==true  }));
250 	mixin(deferEnsure!(q{ ctfe_iswhite('\r') }, q{ _==true  }));
251 	mixin(deferEnsure!(q{ ctfe_iswhite('\n') }, q{ _==true  }));
252 	mixin(deferEnsure!(q{ ctfe_iswhite('X')  }, q{ _==false }));
253 
254 	// ctfe_join ---------------------------
255 	mixin(deferEnsure!(q{ ctfe_join([""," ","","A","","BC","","D"," ",""], "\n") }, q{ _=="\n \n\nA\n\nBC\n\nD\n \n" }));
256 	//mixin(traceVal!(q{ "\n"~ctfe_join([""," ","","A","","BC","","D"," ",""], "\n").escapeDDQS() }));
257 
258 	// ctfe_substitute ---------------------------
259 	enum ctfe_substitute_test_1 = ctfe_substitute("hello", "X", "R");
260 	mixin(deferEnsure!(q{ ctfe_substitute_test_1 }, q{ _=="hello" }));
261 	
262 	enum ctfe_substitute_test_2 = ctfe_substitute("hello", "e", "e");
263 	mixin(deferEnsure!(q{ ctfe_substitute_test_2 }, q{ _=="hello" }));
264 
265 	enum ctfe_substitute_test_3 = ctfe_substitute("hello", "e", "X");
266 	mixin(deferEnsure!(q{ ctfe_substitute_test_3 }, q{ _=="hXllo" }));
267 	
268 	enum ctfe_substitute_test_4 = ctfe_substitute("日本語", "X", "R");
269 	mixin(deferEnsure!(q{ ctfe_substitute_test_4 }, q{ _=="日本語" }));
270 	
271 	enum ctfe_substitute_test_5 = ctfe_substitute("こんにちわ", "X", "R");
272 	mixin(deferEnsure!(q{ ctfe_substitute_test_5 }, q{ _=="こんにちわ" }));
273 	
274 	enum ctfe_substitute_test_6 = ctfe_substitute("こんにちわ", "にち", "ばん");
275 	mixin(deferEnsure!(q{ ctfe_substitute_test_6 }, q{ _=="こんばんわ" }));
276 	
277 	enum wstring ctfe_substitute_test_7 = ctfe_substitute("こんにちわ"w, "にち"w, "ばん"w);
278 	mixin(deferEnsure!(q{ ctfe_substitute_test_7 }, q{ _=="こんばんわ"w }));
279 	
280 	enum dstring ctfe_substitute_test_8 = ctfe_substitute("こんにちわ"d, "にち"d, "ばん"d);
281 	mixin(deferEnsure!(q{ ctfe_substitute_test_8 }, q{ _=="こんばんわ"d }));
282 	
283 	// ctfe_pad ---------------------------
284 	enum ctfe_pad_test_1 = ctfe_pad("Hi", 5);
285 	mixin(deferEnsure!(`ctfe_pad_test_1`, `_ == "   Hi"`));
286 
287 	enum ctfe_pad_test_2 = ctfe_pad("Hi", 5, "-");
288 	mixin(deferEnsure!(`ctfe_pad_test_2`, `_ == "---Hi"`));
289 
290 	enum ctfe_pad_test_3 = ctfe_pad("Hi", 1, "-");
291 	mixin(deferEnsure!(`ctfe_pad_test_3`, `_ == "Hi"`));
292 
293 	enum ctfe_pad_test_4 = ctfe_pad("Hi", 4, false);
294 	mixin(deferEnsure!(`ctfe_pad_test_4`, `_ == "Hi  "`));
295 
296 	enum ctfe_pad_test_5 = ctfe_pad("Hi", 1, false);
297 	mixin(deferEnsure!(`ctfe_pad_test_5`, `_ == "Hi"`));
298 
299 	enum ctfe_pad_test_6 = ctfe_pad("Hi", 5, false, "+");
300 	mixin(deferEnsure!(`ctfe_pad_test_6`, `_ == "Hi+++"`));
301 
302 	enum wstring ctfe_pad_test_7 = ctfe_pad("Hi"w, 5);
303 	mixin(deferEnsure!(`ctfe_pad_test_7`, `_ == "   Hi"w`));
304 
305 	enum dstring ctfe_pad_test_8 = ctfe_pad("Hi"d, 5);
306 	mixin(deferEnsure!(`ctfe_pad_test_8`, `_ == "   Hi"d`));
307 
308 /+
309 	// Fails right now
310 	enum ctfe_pad_test_9 = ctfe_pad("日本語", 5, "五");
311 	mixin(deferEnsure!(`ctfe_pad_test_9`, `_ == "五五日本語"`));
312 +/
313 
314 	// ctfe_repeat ---------------------------
315 	enum ctfe_repeat_test_aneg1 = ctfe_repeat("a", -1);
316 	mixin(deferEnsure!(`ctfe_repeat_test_aneg1`, `_ == ""`));
317 
318 	enum ctfe_repeat_test_a2 = ctfe_repeat("a", 2);
319 	mixin(deferEnsure!(`ctfe_repeat_test_a2`, `_ == "aa"`));
320 
321 	enum ctfe_repeat_test_Ab5 = ctfe_repeat("Ab", 5);
322 	mixin(deferEnsure!(`ctfe_repeat_test_Ab5`, `_ == "AbAbAbAbAb"`));
323 
324 	enum ctfe_repeat_test_Ab0 = ctfe_repeat("Ab", 0);
325 	mixin(deferEnsure!(`ctfe_repeat_test_Ab0`, `_ == ""`));
326 
327 	enum wstring ctfe_repeat_test_a4w = ctfe_repeat("a"w, 4);
328 	mixin(deferEnsure!(`ctfe_repeat_test_a4w`, `_ == "aaaa"w`));
329 
330 	enum dstring ctfe_repeat_test_a4d = ctfe_repeat("a"d, 4);
331 	mixin(deferEnsure!(`ctfe_repeat_test_a4d`, `_ == "aaaa"d`));
332 
333 	enum ctfe_repeat_test_日本語3 = ctfe_repeat("日本語", 3);
334 	mixin(deferEnsure!(`ctfe_repeat_test_日本語3`, `_ == "日本語日本語日本語"`));
335 
336 	// ctfe_subMapJoin ---------------------------
337 	enum ctfe_subMapJoin_test_c = ctfe_subMapJoin("Hi WHO. ", "WHO", ["Joey", "Q", "Sue"]);
338 	mixin(deferEnsure!(`ctfe_subMapJoin_test_c`, `_ == "Hi Joey. Hi Q. Hi Sue. "`));
339 	
340 	enum wstring ctfe_subMapJoin_test_w = ctfe_subMapJoin("Hi WHO. "w, "WHO"w, ["Joey"w, "Q"w, "Sue"w]);
341 	mixin(deferEnsure!(`ctfe_subMapJoin_test_w`, `_ == "Hi Joey. Hi Q. Hi Sue. "w`));
342 	
343 	enum dstring ctfe_subMapJoin_test_d = ctfe_subMapJoin("Hi WHO. "d, "WHO"d, ["Joey"d, "Q"d, "Sue"d]);
344 	mixin(deferEnsure!(`ctfe_subMapJoin_test_d`, `_ == "Hi Joey. Hi Q. Hi Sue. "d`));
345 
346 	enum ctfe_subMapJoin_test_cj = ctfe_subMapJoin("こんにちわ、 だれさん。 ", "だれ", ["わたなべ", "ニク", "あおい"]);
347 	mixin(deferEnsure!(`ctfe_subMapJoin_test_cj`, `_ == "こんにちわ、 わたなべさん。 こんにちわ、 ニクさん。 こんにちわ、 あおいさん。 "`));
348 
349 	// ctfe_to!uint(string) ---------------------------
350 	enum ctfe_to_uint_string_1 = ctfe_to!uint("0");
351 	mixin(deferEnsure!(`ctfe_to_uint_string_1`, `_ == 0`));
352 	
353 	enum ctfe_to_uint_string_2 = ctfe_to!uint("9");
354 	mixin(deferEnsure!(`ctfe_to_uint_string_2`, `_ == 9`));
355 	
356 	enum ctfe_to_uint_string_3 = ctfe_to!uint("42");
357 	mixin(deferEnsure!(`ctfe_to_uint_string_3`, `_ == 42`));
358 	
359 	enum ctfe_to_uint_string_4 = ctfe_to!uint("65536");
360 	mixin(deferEnsure!(`ctfe_to_uint_string_4`, `_ == 65536`));
361 
362 	// ctfe_strip ---------------------------
363 	enum ctfe_strip_test_1 = ctfe_strip(" \tHi \r\n");
364 	mixin(deferEnsure!(`ctfe_strip_test_1`, `_ == "Hi"`));
365 	
366 	enum ctfe_strip_test_2 = ctfe_strip("Hi");
367 	mixin(deferEnsure!(`ctfe_strip_test_2`, `_ == "Hi"`));
368 	
369 	enum ctfe_strip_test_3 = ctfe_strip(" \t \r\n");
370 	mixin(deferEnsure!(`ctfe_strip_test_3`, `_ == ""`));
371 	
372 	enum ctfe_strip_test_4 = ctfe_strip("");
373 	mixin(deferEnsure!(`ctfe_strip_test_4`, `_ == ""`));
374 	
375 	enum ctfe_strip_test_5 = ctfe_strip(" \tHi \r\n"w);
376 	mixin(deferEnsure!(`ctfe_strip_test_5`, `_ == "Hi"w`));
377 	
378 	enum ctfe_strip_test_6 = ctfe_strip(" \tHi \r\n"d);
379 	mixin(deferEnsure!(`ctfe_strip_test_6`, `_ == "Hi"d`));
380 
381 }));
382