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 }