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