1 // Written in the D programming language. 2 /* 3 NYSL Version 0.9982 4 5 A. This software is "Everyone'sWare". It means: 6 Anybody who has this software can use it as if he/she is 7 the author. 8 9 A-1. Freeware. No fee is required. 10 A-2. You can freely redistribute this software. 11 A-3. You can freely modify this software. And the source 12 may be used in any software with no limitation. 13 A-4. When you release a modified version to public, you 14 must publish it with your name. 15 16 B. The author is not responsible for any kind of damages or loss 17 while using or misusing this software, which is distributed 18 "AS IS". No warranty of any kind is expressed or implied. 19 You use AT YOUR OWN RISK. 20 21 C. Copyrighted to Kazuki KOMATSU 22 23 D. Above three clauses are applied both to source and binary 24 form of this software. 25 */ 26 27 /** 28 このモジュールは、様々なtemplateを提供します。 29 */ 30 31 module carbon.templates; 32 33 import std.algorithm; 34 import std.regex; 35 import std.traits; 36 import std.typetuple; 37 import std.typecons; 38 39 40 /** 41 あるテンプレートが、テンプレート版レンジかどうか判定します。 42 43 Example: 44 ------- 45 alias head = tmplt.front; // 先頭要素 46 alias tail = tmplt.tail!(); // 残り 47 ------- 48 */ 49 enum isTemplateRange(alias tmplt) = is(typeof({ 50 static if(!tmplt.empty){ 51 alias head = tmplt.front; 52 alias tail = tmplt.tail!(); 53 } 54 })); 55 56 unittest 57 { 58 template number(size_t a, size_t b) 59 if(a <= b) 60 { 61 static if(a == b) 62 enum bool empty = true; 63 else 64 { 65 enum bool empty = false; 66 67 enum front = a; 68 69 template tail() 70 { 71 alias tail = number!(a+1, b); 72 } 73 } 74 } 75 76 static assert(isTemplateRange!(number!(0, 10))); 77 static assert(isTemplateRange!(number!(10, 10))); 78 } 79 80 81 /** 82 タプルをテンプレート版レンジにします。 83 */ 84 template ToTRange(T...) 85 { 86 static if(T.length == 0) 87 enum empty = true; 88 else 89 { 90 enum empty = false; 91 92 static if(is(typeof({ alias f = T[0]; }))) 93 alias front = T[0]; 94 else 95 enum front = T[0]; 96 97 alias tail() = ToTRange!(T[1 .. $]); 98 } 99 } 100 101 102 /** 103 テンプレート版レンジからタプルを作ります。 104 */ 105 template ToTuple(alias TR) 106 { 107 static if(TR.empty) 108 alias ToTuple = TypeTuple!(); 109 else 110 alias ToTuple = TypeTuple!(TR.front, ToTuple!(TR.tail!())); 111 } 112 113 114 /** 115 2つのTemplateRangeが等しいかどうか検証します。 116 */ 117 template isEquals(alias pred, alias A, alias B) 118 if(isTemplateRange!A && isTemplateRange!B) 119 { 120 static if(A.empty) 121 enum bool isEquals = B.empty; 122 else static if(B.empty) 123 enum bool isEquals = false; 124 else 125 enum bool isEquals = pred!(A.front, B.front) && isEquals!(pred, A.tail!(), B.tail!()); 126 } 127 128 129 /// ditto 130 template isEqualTypes(alias A, alias B) 131 if(isTemplateRange!A && isTemplateRange!B) 132 { 133 enum pred(A, B) = is(A == B); 134 enum bool isEqualTypes = isEquals!(pred, A, B); 135 } 136 137 138 /// ditto 139 template isEqualValues(alias A, alias B) 140 if(isTemplateRange!A && isTemplateRange!B) 141 { 142 enum pred(alias A, alias B) = A == B; 143 enum bool isEqualValues = isEquals!(pred, A, B); 144 } 145 146 147 /// 148 unittest 149 { 150 enum predT(A, B) = is(A == B); 151 alias Ts1 = ToTRange!(int, int, long); 152 alias Ts2 = ToTRange!(int, int, long); 153 154 static assert(isEquals!(predT, Ts1, Ts2)); 155 static assert(isEqualTypes!(Ts1, Ts2)); 156 157 enum predV(alias A, alias B) = A == B; 158 alias Vs1 = ToTRange!(1, 2, 3); 159 alias Vs2 = ToTRange!(1, 2, 3); 160 161 static assert(isEquals!(predV, Vs1, Vs2)); 162 static assert(isEqualValues!(Vs1, Vs2)); 163 } 164 165 166 /** 167 テンプレート版レンジでの$(D_CODE std.range.iota)です。 168 */ 169 template TRIota(size_t a, size_t b) 170 if(a <= b) 171 { 172 static if(a == b) 173 enum empty = true; 174 else 175 { 176 enum empty = false; 177 enum front = a; 178 alias tail() = TRIota!(a+1, b); 179 } 180 } 181 182 /// 183 unittest 184 { 185 alias Is = TRIota!(0, 10); 186 alias Rs = ToTRange!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 187 188 static assert(isEqualValues!(Is, Rs)); 189 } 190 191 192 /** 193 テンプレート版レンジでの、$(D_CODE std.algorithm.map)に相当します。 194 */ 195 template TRMap(alias tmpl, alias TR) 196 if(isTemplateRange!TR) 197 { 198 static if(TR.empty) 199 enum empty = true; 200 else 201 { 202 enum empty = false; 203 alias front = tmpl!(TR.front); 204 alias tail() = TRMap!(tmpl, TR.tail!()); 205 } 206 } 207 208 /// 209 unittest 210 { 211 alias Ts = TypeTuple!(int, long, char); 212 alias ToConstArray(T) = const(T)[]; 213 alias Result = ToTuple!(TRMap!(ToConstArray, ToTRange!Ts)); 214 215 static assert(is(Result 216 == TypeTuple!(const(int)[], 217 const(long)[], 218 const(char)[]))); 219 } 220 221 /+ 222 template TRReduce(alias tmpl, alias TR) 223 if(isTemplateRange!TR && !TR.empty) 224 { 225 static if(TR.empty) 226 alias Reduce = TypeTuple!(); 227 else 228 alias Reduce = Reduce!(tmpl, TR.front, TR.tail!()); 229 } 230 231 232 template Reduce(alias tmpl, alias Ini, alias TR) 233 { 234 static if(TR.empty) 235 alias Reduce = Ini; 236 else 237 alias Reduce = Reduce!(tmpl, tmpl!(Ini, TR.front), TR.tail!()); 238 } 239 240 241 /** 242 永遠とタプルを返すようなテンプレートレンジを返します。 243 */ 244 template Repeat(T...) 245 { 246 enum empty = false; 247 alias front = T; 248 alias tail() = Repeat!T; 249 } 250 251 252 /// 253 template RepeatN(size_t N, T...) 254 { 255 static if(N == 0) 256 enum empty = true; 257 else 258 { 259 enum empty = false; 260 alias front = T; 261 alias tail() = Repeat!(N-1, T); 262 } 263 } 264 265 266 /** 267 Template RangeのZipバージョンです 268 */ 269 template Zip(alias TR1, alias TR2) 270 if(isTemplateRange!TR1 && isTemplateRange!TR2) 271 { 272 static if(TR1.empty) 273 alias Zip = TR2; 274 else static if(TR2.empty) 275 alias Zip = TR1; 276 else 277 { 278 enum empty = false; 279 alias front = TypeTuple!(TR1.front, TR2.front); 280 alias tail() = Zip!(TR1.tail!(), TR2.tail!()); 281 } 282 } 283 284 285 /** 286 287 */ 288 template Take(alias TR, size_t N) 289 if(isTemplateRange!TR) 290 { 291 static if(TR1.empty || N == 0) 292 enum empty = true; 293 else 294 { 295 enum empty = false; 296 alias front = TR1.front; 297 alias tail() = Take!(TR1.tail!(), N-1); 298 } 299 } 300 +/ 301 302 303 /** 304 ある型や値をN個並べたタプルを返します 305 */ 306 template TypeNuple(A...) 307 if(A.length == 2 && is(typeof(A[1]) : size_t)) 308 { 309 static if(A[1] == 0) 310 alias TypeNuple = TypeTuple!(); 311 else 312 alias TypeNuple = TypeTuple!(A[0], TypeNuple!(A[0], A[1] - 1)); 313 } 314 315 /// 316 unittest 317 { 318 static assert(is(TypeNuple!(int, 2) == TypeTuple!(int, int))); 319 static assert(is(TypeNuple!(long, 3) == TypeTuple!(long, long, long))); 320 } 321 322 323 /** 324 自身を返します 325 */ 326 alias Identity(alias A) = A; 327 alias Identity(A) = A; /// ditto 328 329 /// 330 unittest 331 { 332 static assert(is(int == Identity!int)); 333 } 334 335 336 /** 337 大域変数を宣言定義初期化します。 338 339 Example: 340 -------- 341 module foo; 342 343 import std.stdio; 344 import graphite.utils.logger; 345 import carbon.templates; 346 347 mixin defGlobalVariables!("logger", "logFile", 348 { 349 auto file = File("foo.txt", "w"); 350 return tuple(.logger!(LogFormat.readable)(file), file); 351 }); 352 -------- 353 */ 354 mixin template defGlobalVariables(A...) 355 if(A.length >= 2 && is(typeof(A[$-1]()))) 356 { 357 private alias idstrs = A[0 .. $-1]; 358 359 import std.typecons; 360 static if(idstrs.length == 1 && is(typeof(A[$-1]()) == Tuple!E, E...)) 361 private enum fn = (() => A[$-1]().tupleof[0]); 362 else 363 private alias fn = A[$-1]; 364 365 private string[2] makeCode() 366 { 367 import std.array, std.format, std..string; 368 auto defs = appender!string(); 369 auto inis = appender!string(); 370 371 static if(idstrs.length >= 2) 372 { 373 foreach(i, e; idstrs){ 374 auto sp = e.split(); 375 376 if(sp.length >= 2) 377 defs.formattedWrite("%s typeof(fn()[%s]) %s;\n", sp[0], i, sp[1]); 378 else 379 defs.formattedWrite("typeof(fn()[%s]) %s;\n", i, sp[0]); 380 381 inis.formattedWrite("%s = inits[%s];\n", sp[$-1], i); 382 } 383 } 384 else 385 { 386 auto sp = idstrs[0].split(); 387 if(sp.length >= 2) 388 defs.formattedWrite("%s typeof(fn()) %s;\n", sp[0], sp[1]); 389 else 390 defs.formattedWrite("typeof(fn()) %s;\n", sp[0]); 391 392 inis.formattedWrite("%s = inits;\n", sp[$-1]); 393 } 394 395 return [defs.data, inis.data]; 396 } 397 398 private enum defInitCode = makeCode(); 399 mixin(defInitCode[0]); 400 401 static this() 402 { 403 auto inits = fn(); 404 mixin(defInitCode[1]); 405 } 406 } 407 408 409 version(unittest) 410 { 411 mixin defGlobalVariables!("foobarNhogehoge", "immutable foofoobogeNbar", 412 (){ 413 return tuple(12, 13); 414 }); 415 416 mixin defGlobalVariables!("myonmyonNFoo", 417 (){ 418 return tuple(2); 419 }); 420 421 mixin defGlobalVariables!("momimomiNFoo", 422 (){ 423 return 3; 424 }); 425 426 unittest{ 427 assert(foobarNhogehoge == 12); 428 assert(foofoobogeNbar == 13); 429 static assert(is(typeof(myonmyonNFoo) == int)); 430 assert(myonmyonNFoo == 2); 431 static assert(is(typeof(momimomiNFoo) == int)); 432 assert(momimomiNFoo == 3); 433 } 434 } 435 436 437 /** 438 式を埋め込み可能な文字列リテラルを構築します 439 */ 440 template Lstr(alias str) 441 if(isSomeString!(typeof(str))) 442 { 443 import std.array, std.algorithm; 444 enum string Lstr = `(){ import std.format; import std.array; auto app = appender!string();` ~ generate(str) ~ ` return app.data ;}()`; 445 446 string generate(string s) 447 { 448 if(s.empty) return ``; 449 450 auto swF = s.findSplit("%["); 451 if(swF[1].empty) return "app ~= `" ~ s ~ "`;"; 452 453 auto swE = swF[2].findSplit("%]"); 454 if(swE[1].empty) return "app ~= `" ~ s ~ "`;"; 455 456 if(swE[0].empty) return "app ~= `" ~ swF[0] ~ "`;"; 457 458 return "app ~= `" ~ swF[0] ~ "`; app.formattedWrite(`%s`, " ~ swE[0] ~ ");" ~ generate(swE[2]); 459 } 460 } 461 462 /// 463 unittest{ 464 { 465 int a = 12, b = 13; 466 467 assert(mixin(Lstr!"aaaa") == "aaaa"); 468 469 // %[ から %] までがDの任意の式を表す。 470 assert(mixin(Lstr!`foo%[a+b%]bar%[a+10%]%[a%]`) == "foo25bar2212"); 471 } 472 473 { 474 int a = 12; 475 string b = "3"; 476 auto t = tuple(a, b); 477 string str = mixin(Lstr!`Element1 : %[t[0]%], Element2 : %[t[1]%]`); 478 assert(str == `Element1 : 12, Element2 : 3`); 479 } 480 481 { 482 int a = 12; 483 assert(mixin(Lstr!`foo%[a%]`) == "foo12"); 484 assert(mixin(Lstr!`foo%[a%`) == `foo%[a%`); 485 assert(mixin(Lstr!`foo%[a`) == `foo%[a`); 486 assert(mixin(Lstr!`foo%[%]`) == `foo`); 487 assert(mixin(Lstr!`foo%[`) == `foo%[`); 488 assert(mixin(Lstr!`foo%`) == `foo%`); 489 } 490 } 491 492 493 template Qualifier(string qual) 494 if(qual == "const" 495 || qual == "immutable" 496 || qual == "shared" 497 || qual == "inout") 498 { 499 template ApplyTo(T) 500 { 501 alias ApplyTo = typeof(mixin(`cast(` ~ qual ~ `)T.init`)); 502 } 503 } 504 505 unittest { 506 alias CI = Qualifier!"const".ApplyTo!int; 507 static assert(is(CI == const(int))); 508 } 509 510 511 template ApplySameTopQualifier(T, U) 512 { 513 static if(is(T == const)) 514 alias ApplySameTopQualifier = const(U); 515 else static if(is(T == immutable)) 516 alias ApplySameTopQualifier = immutable(U); 517 else static if(is(T == shared)) 518 alias ApplySameTopQualifier = shared(U); 519 else 520 alias ApplySameTopQualifier = U; 521 } 522 523 524 void opOpAssign(string op, T, U)(ref T t, auto ref U u) 525 { 526 mixin("t " ~ op ~ "= u;"); 527 } 528 529 void opIndexOpAssign(string op, T, U, I)(ref T t, auto ref U u, I idx) 530 { 531 mixin("t[idx] " ~ op ~ "= u;"); 532 }