root/trunk/docsrc/templates-revisited.dd

Revision 2040, 26.1 kB (checked in by walter, 2 years ago)

typography

  • Property svn:eol-style set to native
Line 
1 Ddoc
2
3 $(D_S Templates Revisited,
4
5 <center>
6 $(I by Walter Bright, $(LINK http://www.digitalmars.com/d))
7
8 <br>
9 <br>
10
11 $(BLOCKQUOTE
12 "What I am going to tell you about is what we teach our programming students in
13 the third or fourth year of graduate school... It is my task to convince you not
14 to turn away because you don't understand it. You see my programming students
15 don't understand it... That is because I don't understand it. Nobody does."<br>
16 -- Richard Deeman
17 )
18 </center>
19
20 <h2>Abstract</h2>
21
22 $(P
23 Templates in C++ have evolved from little more than token substitution into a
24 programming language in itself. Many useful aspects of C++ templates have been
25 discovered rather than designed. A side effect of this is that C++ templates are
26 often criticized for having an awkward syntax, many arcane rules, and being very
27 difficult to implement properly. What might templates look like if one takes a
28 step back, looks at what templates can do and what uses they are put to, and
29 redesign them? Can templates be powerful, aesthetically pleasing, easy to
30 explain and straightforward to implement?
31 This article takes a look at an alternative design of templates in the D
32 Programming Language [1].
33 )
34
35 <h2>Similarities</h2>
36
37 $(UL
38 $(LI compile time semantics)
39 $(LI function templates)
40 $(LI class templates)
41 $(LI type parameters)
42 $(LI value parameters)
43 $(LI template parameters)
44 $(LI partial and explicit specialization)
45 $(LI type deduction)
46 $(LI implicit function template instantiation)
47 $(LI $(ACRONYM SFINAE, Substitution Failure Is Not An Error))
48 )
49
50
51 <h2>Argument Syntax</h2>
52
53 $(P
54 The first thing that comes to mind is the use of &lt; &gt; to enclose parameter
55 lists and argument lists. &lt; &gt; has a couple serious problem, however.
56 They are ambiguous with operators &lt;, &gt;, and &gt;&gt;. This means that expressions
57 like:
58 )
59
60 $(CCODE
61 a&lt;b,c&gt;d;
62 )
63
64 and:
65
66 $(CCODE
67 a&lt;b&lt;c&gt;&gt;d;
68 )
69
70 $(P
71 are syntactically ambiguous, both to the compiler and the programmer.
72 If you run across $(TT a&lt;b,c&gt;d;) in unfamiliar code, you've got to slog through
73 an arbitrarily large amount of declarations and $(TT .h) files to figure out
74 if it is a template or not.
75 How much effort has been expended by programmers, compiler writers, and
76 language standard writers to deal with this?
77 )
78  
79 $(P
80 There's got to be a better way. D solves it by noticing that ! is not used
81 as a binary operator, so replacing:
82 )
83
84 $(CCODE
85 a&lt;b,c&gt;
86 )
87
88 $(P
89 with:
90 )
91
92 ---
93 a!(b,c)
94 ---
95
96 $(P
97 is syntactically unambiguous. This makes it easy to parse, easy to generate
98 reasonable error messages for, and makes it easy for someone inspecting the
99 code to determine that yes, $(TT a) must be a template.
100 )
101
102
103 <h2>Template Definition Syntax</h2>
104
105 $(P
106 C++ can define two broad types of templates: class templates, and function
107 templates. Each template is written independently, even if they are
108 closely related:
109 )
110
111 $(CCODE
112 template&lt;class T, class U&gt; class Bar { ... };
113
114 template&lt;class T, class U&gt; T foo(T t, U u) { ... }
115
116 template&lt;class T, class U&gt; static T abc;
117 )
118
119 $(P
120 POD (Plain Old Data, as in C style) structs
121 bring together related data declarations, classes bring together
122 related data and function declarations, but there's nothing to logically group
123 together templates that are to be instantiated together.
124 In D, we can write:
125 )
126
127 ----
128 template Foo(T, U)
129 {
130   class Bar { ... }
131
132   T foo(T t, U u) { ... }
133
134   T abc;
135
136   typedef T* Footype;   // any declarations can be templated
137 }
138 ----
139
140 $(P
141 The $(TT Foo) forms a name space for the templates, which are accessed by,
142 for example:
143 )
144
145 ---
146 Foo!(int,char).Bar b;
147 Foo!(int,char).foo(1,2);
148 Foo!(int,char).abc = 3;
149 ---
150
151 $(P
152 Of course, this can get a little tedious, so one can use an alias
153 for a particular instantiation:
154 )
155
156 ---
157 alias Foo!(int,char) f;
158 f.Bar b;
159 f.foo(1,2);
160 f.abc = 3;
161 ---
162
163 $(P
164 For class templates, there's an even simpler syntax. A class is defined
165 like:
166 )
167
168 ---
169 class Abc
170 {
171     int t;
172     ...
173 }
174 ---
175
176 $(P
177 This can be turned into a template by just adding a parameter list:
178 )
179
180 ---
181 class Abc(T)
182 {
183     T t;
184     ...
185 }
186 ---
187
188
189 <h2>Template Declaration, Definition, and Export</h2>
190
191 $(P
192 C++ templates can be in the form of a template declaration, a template
193 definition, and an exported template.
194 Because D has a true module system, rather than textual #include files,
195 there are only template definitions in D. The need for template declarations
196 and export is irrelevant. For example, given a template definition in
197 module $(TT A):
198 )
199
200 ---
201 module A;
202
203 template Foo(T)
204 {
205     T bar;
206 }
207 ---
208
209 $(P
210 it can be accessed from module $(TT B) like:
211 )
212
213 ---
214 module B;
215
216 import A;
217
218 void test()
219 {
220     A.Foo!(int).bar = 3;
221 }
222 ---
223
224 $(P
225 As usual, an alias can be used to simplify access:
226 )
227
228 ---
229 module B;
230
231 import A;
232 alias A.Foo!(int).bar bar;
233
234 void test()
235 {
236     bar = 3;
237 }
238 ---
239
240
241 <h2>Template Parameters</h2>
242
243 $(P
244 C++ template parameters can be:)
245
246 $(UL
247 $(LI types)
248 $(LI integral values)
249 $(LI static/global addresses)
250 $(LI template names)
251 )
252
253 $(P D template parameters can be:)
254
255 $(UL
256 $(LI types)
257 $(LI integral values)
258 $(LI floating point values)
259 $(LI string literals)
260 $(LI templates)
261 $(LI or any symbol)
262 )
263
264 $(P Each can have default values,
265 and type parameters can have (a limited form of) constraints:
266 )
267
268 ---
269 class B { ... }
270 interface I { ... }
271
272 class Foo(
273   R,            // R can be any type
274   P:P*,         // P must be a pointer type
275   T:int,        // T must be int type
276   S:T*,         // S must be pointer to T
277   C:B,          // C must be of class B or derived
278                 // from B
279   U:I,          // U must be a class that
280                 // implements interface I
281   string str = "hello",
282                 // string literal,
283                 // default is "hello"
284   alias A = B   // A is any symbol
285                 // (including template symbols),
286                 // defaulting to B
287   )
288 {
289     ...
290 }
291 ---
292
293 <h2>Specialization</h2>
294
295 $(P
296 Partial and explicit specialization work as they do in C++, except that
297 there is no notion of a $(SINGLEQUOTE primary) template. All the templates with the
298 same name are examined upon template instantiation, and the one with the
299 best fit of arguments to parameters is instantiated.
300 )
301
302 ---
303 template Foo(T) ...
304 template Foo(T:T*) ...
305 template Foo(T, U:T) ...
306 template Foo(T, U) ...
307 template Foo(T, U:int) ...
308
309 Foo!(long)       // picks Foo(T)
310 Foo!(long[])     // picks Foo(T), T is long[]
311 Foo!(int*)       // picks Foo(T*), T is int
312 Foo!(long,long)  // picks Foo(T, T)
313 Foo!(long,short) // picks Foo(T, U)
314 Foo!(long,int)   // picks Foo(T, U:int)
315 Foo!(int,int)    // ambiguous - Foo(T, U:T)
316                  // or Foo(T, U:int)
317 ---
318
319 <h2>Two Level Name Lookup</h2>
320
321 $(P
322 C++ has some unusual rules for name lookup inside templates, such
323 as not looking inside base classes, not allowing scoped redeclaration
324 of template parameter names, and not considering overloads that
325 happen after the point of definition (this example is
326 derived from one in the C++98 Standard):
327 )
328
329 $(CCODE
330 int g(double d) { return 1; }
331
332 typedef double A;
333
334 template&lt;class T&gt; B
335 {
336   typedef int A;
337 };
338
339 template&lt;class T&gt; struct X : B&lt;T&gt;
340 {
341   A a;              // a has type double
342   int T;            // error, T redeclared
343   int foo()
344   {  char T;        // error, T redeclared
345      return g(1);   // always returns 1
346   }
347 };
348
349 int g(int i) { return 2; }  // this definition not seen by X
350 )
351
352 $(P
353 Scoped lookup rules in D match the rules for the rest of the language:
354 )
355
356 ---
357 int g(double d) { return 1; }
358
359 typedef double A;
360
361 class B(T)
362 {
363   typedef int A;
364 }
365
366 class X(T) : B!(T)
367 {
368   A a;             // a has type int
369   int T;           // ok, T redeclared as int
370   int foo()
371   {   char T;      // ok, T redeclared as char
372      return g(1);  // always returns 2
373   }
374 };
375
376 int g(int i) { return 2; }  // functions can be forward referenced
377 ---
378
379
380
381 <h2>Template Recursion</h2>
382
383 $(P
384 Template recursion combined with specialization means that C++ templates
385 actually form a programming language, although certainly
386 an odd one. Consider a set of templates that computes a factorial at
387 run time. Like "hello world" programs, factorial is the canonical example
388 of template metaprogramming:
389 )
390
391 $(CCODE
392 template&lt;int n&gt; class factorial
393 {
394   public:
395     enum
396     {
397       result = n * factorial&lt;n - 1&gt;::result
398     };
399 };
400
401 template&lt;&gt; class factorial&lt;1&gt;
402 {
403   public:
404     enum { result = 1 };
405 };
406
407 void test()
408 {
409   // prints 24
410   printf("%d\n", factorial&lt;4&gt;::result);
411 }
412 )
413
414 $(P
415 Recursion works as well in D, though with significantly less typing:
416 )
417
418 ---
419 template factorial(int n)
420 {
421   const factorial = n * factorial!(n-1);
422 }
423
424 template factorial(int n : 1)
425 {
426   const factorial = 1;
427 }
428
429 void test()
430 {
431   writefln(factorial!(4));  // prints 24
432 }
433 ---
434
435 $(P
436 Through using the $(CODE static if) construct it can be done in just one
437 template:
438 )
439
440 ---
441 template factorial(int n)
442 {
443   static if (n == 1)
444     const factorial = 1;
445   else
446     const factorial = n * factorial!(n-1);
447 }
448 ---
449
450 $(P
451 reducing 13 lines of code to an arguably much cleaner 7 lines.
452 $(TT static if)'s are the equivalent of C++'s $(TT #if).
453 But $(TT #if) cannot access template
454 arguments, so all template conditional compilation must be handled with
455 partial and explicitly specialized templates.
456 $(TT static if) dramatically simplifies
457 such constructions.
458 )
459
460 $(P D can make this even simpler. Value generating templates such
461 as the factorial one are possible, but it's easier to just write
462 a function that can be computed at compile time:)
463
464 ---
465 int factorial(int n)
466 {
467   if (n == 1)
468     return 1;
469   else
470     return n * factorial(n - 1);
471 }
472
473 static int x = factorial(5);  // x is statically initialized to 120
474 ---
475
476 <h2>$(ACRONYM SFINAE, Substitution Failure Is Not An Error)</h2>
477
478 $(P
479 This determines if the template's argument type is a function,
480 from "$(I C++ Templates: The Complete Guide)",
481 Vandevoorde &amp; Josuttis pg. 353:
482 )
483
484 $(CCODE
485 template&lt;U&gt; class IsFunctionT
486 {
487   private:
488     typedef char One;
489     typedef struct { char a[2]; } Two;
490     template static One test(...);
491     template static Two test(U (*)[1]);
492   public:
493     enum {
494       Yes = sizeof(IsFunctionT::test(0)) == 1
495     };
496 };
497
498 void test()
499 {
500   typedef int (fp)(int);
501
502   assert(IsFunctionT&lt;fp&gt;::Yes == 1);
503 }
504 )
505
506 $(P
507 Template $(TT IsFunctionT) relies on two side effects to achieve its result.
508 First, it relies on arrays of functions being an invalid C++ type.
509 Thus, if $(TT U) is a function type, the second $(TT test) will not be selected
510 since to do so would cause an error ($(SFINAE)).
511 The first $(TT test) will be selected.
512 If $(TT U) is not a function type, the second $(TT test) is a better fit than ... .
513 Next, it is determined which $(TT test) was selected by examining the size
514 of the return value, i.e. $(TT sizeof(One)) or $(TT sizeof(Two)).
515 Unfortunately, template metaprogramming in C++ often seems to be relying
516 on side effects rather than being able to expressly code what is desired.
517 )
518
519 $(P
520 In D this can be written:
521 )
522
523 ---
524 template IsFunctionT(T)
525 {
526   static if ( is(T[]) )
527     const int IsFunctionT = 0;
528   else
529     const int IsFunctionT = 1;
530 }
531
532 void test()
533 {
534   alias int fp(int);
535
536   assert(IsFunctionT!(fp) == 1);
537 }
538 ---
539
540 $(P
541 The $(TT is(T[])) is the equivalent of $(SFINAE).
542 It tries to build an array of $(TT T),
543 and if $(TT T) is a function type, it is an array of functions. Since this is
544 an invalid type, the $(TT T[]) fails and $(TT is(T[])) returns false.
545 )
546
547 $(P
548 Although $(SFINAE) can be used, the is expressions can test a type directly,
549 so it isn't even necessary to use a template to ask questions about a type:
550 )
551
552 ---
553 void test()
554 {
555   alias int fp(int);
556
557   assert( is(fp == function) );
558 }
559 ---
560
561 <h2>Template Metaprogramming With Floats</h2>
562
563 $(P
564 Let's move on to things that are impractical with templates in C++.
565 For example, this template returns the square root of
566 real number $(TT x) using the Babylonian method:
567 )
568
569 ---
570 import std.stdio;
571
572 template sqrt(real x, real root = x/2, int ntries = 0)
573 {
574   static if (ntries == 5)
575     // precision doubles with each iteration,
576     // 5 should be enough
577     const sqrt = root;
578   else static if (root * root - x == 0)
579     const sqrt = root;  // exact match
580   else
581     // iterate again
582     const sqrt = sqrt!(x, (root+x/root)/2, ntries+1);
583 }
584
585 void main()
586 {
587     real x = sqrt!(2);
588     writefln("%.20g", x); // 1.4142135623730950487
589 }
590 ---
591
592 $(P
593 Literal square roots are often needed for speed reasons in other runtime
594 floating point computations, such as computing the gamma function.
595 These template floating point algorithms need not be efficient as they are
596 computed at compile time, they only need to be accurate.
597 )
598
599 $(P
600 Much more complex templates can be built, for example, Don Clugston
601 has written a template to compute &pi; at compile time. [2]
602 )
603
604 $(P Again, we can just do this with a function that can be executed
605 at compile time:)
606
607 ---
608 real sqrt(real x)
609 {
610     real root = x / 2;
611     for (int ntries = 0; ntries < 5; ntries++)
612     {
613     if (root * root - x == 0)
614         break;
615     root = (root + x / root) / 2;
616     }
617     return root;
618 }
619 static y = sqrt(10);   // y is statically initialized to 3.16228
620 ---
621
622 <h2>Template Metaprogramming With Strings</h2>
623
624 $(P
625 Even more interesting things can be done with strings. This example
626 converts an integer to a string at compile time:
627 )
628
629 ---
630 template decimalDigit(int n)    // [3]
631 {
632   const string decimalDigit = "0123456789"[n..n+1];
633 }
634
635 template itoa(long n)
636 {   
637   static if (n < 0)
638      const string itoa = "-" ~ itoa!(-n); 
639   else static if (n < 10)
640      const string itoa = decimalDigit!(n);
641   else
642      const string itoa = itoa!(n/10L) ~ decimalDigit!(n%10L);
643 }
644
645 string foo()
646 {
647   return itoa!(264);   // returns "264"
648 }
649 ---
650
651 $(P
652 This template will compute the hash of a string literal:
653 )
654
655 ---
656 template hash(char [] s, uint sofar=0)
657 {
658    static if (s.length == 0)
659       const hash = sofar;
660    else
661       const hash = hash!(s[1 .. length], sofar * 11 + s[0]);
662 }
663
664 uint foo()
665 {
666     return hash!("hello world");
667 }
668 ---
669
670 <h2>Regular Expression Compiler</h2>
671
672 $(P
673 How do D templates fare with something much more significant, like
674 a regular expression compiler? Eric Niebler has written one for C++
675 that relies on expression templates. [4]
676 The problem with using expression templates is that one is restricted
677 to using only C++ operator syntax and precedence.
678 Hence, regular expressions using expression templates don't look like
679 regular expressions, they look like C++ expressions.
680 Eric Anderton has written one for D that relies on the ability of
681 templates to parse strings. [5]
682 This means that, within the strings, one can use the expected regular
683 expression grammar and operators.
684 )
685
686 $(P
687 The regex compiler templates parse the regex string argument,
688 pulling off tokens
689 one by one from the front, and instantiating custom template functions
690 for each token predicate,
691 eventually combining them all into one function that directly implements
692 the regular expression.
693 It even gives reasonable error messages for syntax errors in
694 the regular expression.
695 )
696
697 $(P
698 Calling that function with an argument of a string to match returns
699 an array of matching strings:
700 )
701
702 ---
703 import std.stdio;
704 import regex;
705
706 void main()
707 {
708     auto exp = &regexMatch!(r"[a-z]*\s*\w*");
709     writefln("matches: %s", exp("hello    world"));
710 }
711 ---
712
713 $(P
714 What follows is a cut-down version of Eric Anderton's regex compiler.
715 It is just enough to compile the regular expression above,
716 serving to illustrate how it is done.
717 )
718
719 -------------
720 module regex;
721
722 const int testFail = -1;
723
724 /**
725  * Compile pattern[] and expand to a custom generated
726  * function that will take a string str[] and apply the
727  * regular expression to it, returning an array of matches.
728  */
729
730 template regexMatch(string pattern)
731 {
732   string[] regexMatch(string str)
733   {
734     string[] results;
735     int n = regexCompile!(pattern).fn(str);
736     if (n != testFail && n > 0)
737       results ~= str[0..n];
738     return results;
739   }
740 }
741
742 /******************************
743  * The testXxxx() functions are custom generated by templates
744  * to match each predicate of the regular expression.
745  *
746  * Params:
747  *  string str  the input string to match against
748  *
749  * Returns:
750  *  testFail    failed to have a match
751  *  n >= 0      matched n characters
752  */
753
754 /// Always match
755 template testEmpty()
756 {
757   int testEmpty(string str) { return 0; }
758 }
759
760 /// Match if testFirst(str) and testSecond(str) match
761 template testUnion(alias testFirst, alias testSecond)
762 {
763   int testUnion(string str)
764   {
765     int n1 = testFirst(str);
766     if (n1 != testFail)
767     {
768       int n2 = testSecond(str[n1 .. $]);
769       if (n2 != testFail)
770         return n1 + n2;
771     }
772     return testFail;
773   }
774 }
775
776 /// Match if first part of str[] matches text[]
777 template testText(string text)
778 {
779   int testText(string str)
780   {
781     if (str.length &&
782         text.length <= str.length &&
783         str[0..text.length] == text
784        )
785       return text.length;
786     return testFail;
787   }
788 }
789
790 /// Match if testPredicate(str) matches 0 or more times
791 template testZeroOrMore(alias testPredicate)
792 {
793   int testZeroOrMore(string str)
794   {
795     if (str.length == 0)
796       return 0;
797     int n = testPredicate(str);
798     if (n != testFail)
799     {
800       int n2 = testZeroOrMore!(testPredicate)(str[n .. $]);
801       if (n2 != testFail)
802         return n + n2;
803       return n;
804     }
805     return 0;
806   }
807 }
808
809 /// Match if term1[0] <= str[0] <= term2[0]
810 template testRange(string term1, string term2)
811 {
812   int testRange(string str)
813   {
814     if (str.length && str[0] >= term1[0]
815                    && str[0] <= term2[0])
816       return 1;
817     return testFail;
818   }
819 }
820
821 /// Match if ch[0]==str[0]
822 template testChar(string ch)
823 {
824   int testChar(string str)
825   {
826     if (str.length && str[0] == ch[0])
827       return 1;
828     return testFail;
829   }
830 }
831
832 /// Match if str[0] is a word character
833 template testWordChar()
834 {
835   int testWordChar(string str)
836   {
837     if (str.length &&
838         (
839          (str[0] >= 'a' && str[0] <= 'z') ||
840          (str[0] >= 'A' && str[0] <= 'Z') ||
841          (str[0] >= '0' && str[0] <= '9') ||
842          str[0] == '_'
843         )
844        )
845     {
846       return 1;
847     }
848     return testFail;
849   }
850 }
851
852 /*****************************************************/
853
854 /**
855  * Returns the front of pattern[] up until
856  * the end or a special character.
857  */
858
859 template parseTextToken(string pattern)
860 {
861   static if (pattern.length > 0)
862   {
863     static if (isSpecial!(pattern))
864       const string parseTextToken = "";
865     else
866       const string parseTextToken =
867            pattern[0..1] ~ parseTextToken!(pattern[1..$]);
868   }
869   else
870     const string parseTextToken="";
871 }
872
873 /**
874  * Parses pattern[] up to and including terminator.
875  * Returns:
876  *  token[]     everything up to terminator.
877  *  consumed    number of characters in pattern[] parsed
878  */
879 template parseUntil(string pattern,char terminator,bool fuzzy=false)
880 {
881   static if (pattern.length > 0)
882   {
883     static if (pattern[0] == '\\')
884     {
885       static if (pattern.length > 1)
886       {
887         const string nextSlice = pattern[2 .. $];
888         alias parseUntil!(nextSlice,terminator,fuzzy) next;
889         const string token = pattern[0 .. 2] ~ next.token;
890         const uint consumed = next.consumed+2;
891       }
892       else
893       {
894         pragma(msg,"Error: expected character to follow \\");
895         static assert(false);
896       }
897     }
898     else static if (pattern[0] == terminator)
899     {
900       const string token="";
901       const uint consumed = 1;
902     }
903     else
904     {
905       const string nextSlice = pattern[1 .. $];
906       alias parseUntil!(nextSlice,terminator,fuzzy) next;
907       const string token = pattern[0..1] ~ next.token;
908       const uint consumed = next.consumed+1;
909     }
910   }
911   else static if (fuzzy)
912   {
913     const string token = "";
914     const uint consumed = 0;
915   }
916   else
917   {
918     pragma(msg,"Error: expected " ~
919                terminator ~
920                " to terminate group expression");
921     static assert(false);
922   }         
923 }
924
925 /**
926  * Parse contents of character class.
927  * Params:
928  *   pattern[] = rest of pattern to compile
929  * Output:
930  *   fn       = generated function
931  *   consumed = number of characters in pattern[] parsed
932  */
933
934 template regexCompileCharClass2(string pattern)
935 {
936   static if (pattern.length > 0)
937   {
938     static if (pattern.length > 1)
939     {
940       static if (pattern[1] == '-')
941       {
942         static if (pattern.length > 2)
943         {
944           alias testRange!(pattern[0..1], pattern[2..3]) termFn;
945           const uint thisConsumed = 3;
946           const string remaining = pattern[3 .. $];
947         }
948         else // length is 2
949         {
950           pragma(msg,
951             "Error: expected char following '-' in char class");
952           static assert(false);
953         }
954       }
955       else // not '-'
956       {
957         alias testChar!(pattern[0..1]) termFn;
958         const uint thisConsumed = 1;
959         const string remaining = pattern[1 .. $];
960       }
961     }
962     else
963     {
964       alias testChar!(pattern[0..1]) termFn;
965       const uint thisConsumed = 1;
966       const string remaining = pattern[1 .. $];
967     }
968     alias regexCompileCharClassRecurse!(termFn,remaining) recurse;
969     alias recurse.fn fn;
970     const uint consumed = recurse.consumed + thisConsumed;
971   }
972   else
973   {
974     alias testEmpty!() fn;
975     const uint consumed = 0;
976   }
977 }
978
979 /**
980  * Used to recursively parse character class.
981  * Params:
982  *  termFn = generated function up to this point
983  *  pattern[] = rest of pattern to compile
984  * Output:
985  *  fn = generated function including termFn and
986  *       parsed character class
987  *  consumed = number of characters in pattern[] parsed
988  */
989
990 template regexCompileCharClassRecurse(alias termFn,string pattern)
991 {
992   static if (pattern.length > 0 && pattern[0] != ']')
993   {
994     alias regexCompileCharClass2!(pattern) next;
995     alias testOr!(termFn,next.fn,pattern) fn;
996     const uint consumed = next.consumed;
997   }
998   else
999   {
1000     alias termFn fn;
1001     const uint consumed = 0;
1002   }
1003 }
1004
1005 /**
1006  * At start of character class. Compile it.
1007  * Params:
1008  *  pattern[] = rest of pattern to compile
1009  * Output:
1010  *  fn = generated function
1011  *  consumed = number of characters in pattern[] parsed
1012  */
1013
1014 template regexCompileCharClass(string pattern)
1015 {   
1016   static if (pattern.length > 0)
1017   {
1018     static if (pattern[0] == ']')
1019     {
1020       alias testEmpty!() fn;
1021       const uint consumed = 0;
1022     }
1023     else
1024     {
1025       alias regexCompileCharClass2!(pattern) charClass;
1026       alias charClass.fn fn;
1027       const uint consumed = charClass.consumed;
1028     }
1029   }
1030   else
1031   {
1032     pragma(msg,"Error: expected closing ']' for character class");
1033     static assert(false);   
1034   }
1035 }
1036
1037 /**
1038  * Look for and parse '*' postfix.
1039  * Params:
1040  *  test = function compiling regex up to this point
1041  *  pattern[] = rest of pattern to compile
1042  * Output:
1043  *  fn = generated function
1044  *  consumed = number of characters in pattern[] parsed
1045  */
1046
1047 template regexCompilePredicate(alias test, string pattern)
1048 {
1049   static if (pattern.length > 0 && pattern[0] == '*')
1050   {
1051     alias testZeroOrMore!(test) fn;
1052     const uint consumed = 1;
1053   }
1054   else
1055   {
1056     alias test fn;
1057     const uint consumed = 0;
1058   }
1059 }
1060
1061 /**
1062  * Parse escape sequence.
1063  * Params:
1064  *  pattern[] = rest of pattern to compile
1065  * Output:
1066  *  fn = generated function
1067  *  consumed = number of characters in pattern[] parsed
1068  */
1069
1070 template regexCompileEscape(string pattern)
1071 {
1072   static if (pattern.length > 0)
1073   {
1074     static if (pattern[0] == 's')
1075     {
1076       // whitespace char
1077       alias testRange!("\x00","\x20") fn;
1078     }
1079     else static if (pattern[0] == 'w')
1080     {
1081       //word char
1082       alias testWordChar!() fn;
1083     }
1084     else
1085     {
1086       alias testChar!(pattern[0 .. 1]) fn;
1087     }
1088     const uint consumed = 1;
1089   }
1090   else
1091   {
1092     pragma(msg,"Error: expected char following '\\'");
1093     static assert(false);
1094   }
1095 }
1096
1097 /**
1098  * Parse and compile regex represented by pattern[].
1099  * Params:
1100  *  pattern[] = rest of pattern to compile
1101  * Output:
1102  *  fn = generated function
1103  */
1104
1105 template regexCompile(string pattern)
1106 {
1107   static if (pattern.length > 0)
1108   {
1109     static if (pattern[0] == '[')
1110     {
1111       const string charClassToken =
1112           parseUntil!(pattern[1 .. $],']').token;
1113       alias regexCompileCharClass!(charClassToken) charClass;
1114       const string token = pattern[0 .. charClass.consumed+2];
1115       const string next = pattern[charClass.consumed+2 .. $];
1116       alias charClass.fn test;
1117     }
1118     else static if (pattern[0] == '\\')
1119     {
1120       alias regexCompileEscape!(pattern[1..pattern.length]) escapeSequence;
1121       const string token = pattern[0 .. escapeSequence.consumed+1];
1122       const string next =
1123           pattern[escapeSequence.consumed+1 .. $];
1124       alias escapeSequence.fn test;
1125     }
1126     else
1127     {
1128       const string token = parseTextToken!(pattern);
1129       static assert(token.length > 0);
1130       const string next = pattern[token.length .. $];
1131       alias testText!(token) test;
1132     }
1133    
1134     alias regexCompilePredicate!(test, next) term;
1135     const string remaining = next[term.consumed .. next.length];
1136    
1137     alias regexCompileRecurse!(term,remaining).fn fn;
1138   }
1139   else
1140     alias testEmpty!() fn;
1141 }
1142
1143 template regexCompileRecurse(alias term,string pattern)
1144 {
1145   static if (pattern.length > 0)
1146   {
1147     alias regexCompile!(pattern) next;
1148     alias testUnion!(term.fn, next.fn) fn;
1149   }
1150   else
1151     alias term.fn fn;
1152 }
1153
1154 /// Utility function for parsing
1155 template isSpecial(string pattern)
1156 {
1157   static if (
1158     pattern[0] == '*' ||
1159     pattern[0] == '+' ||
1160     pattern[0] == '?' ||
1161     pattern[0] == '.' ||
1162     pattern[0] == '[' ||
1163     pattern[0] == '{' ||
1164     pattern[0] == '(' ||
1165     pattern[0] == ')' ||
1166     pattern[0] == '$' ||
1167     pattern[0] == '^' ||
1168     pattern[0] == '\\'
1169   )
1170     const isSpecial = true;
1171   else
1172     const isSpecial = false;
1173 }
1174 ---
1175
1176 <h2>More Template Metaprogramming</h2>
1177
1178     $(OL
1179     $(LI Tomasz Stachowiak's compile time $(LINK2 http://www-users.mat.uni.torun.pl/~h3r3tic/ctrace/, raytracer).)
1180
1181     $(LI Don Clugston's compile time $(LINK2 http://www.99-bottles-of-beer.net/language-d-1212.html?PHPSESSID=1c686874f412f908530c69924496192d, 99 Bottles of Beer).)
1182     )
1183
1184 <h2>References</h2>
1185
1186     $(P [1] D programming language,
1187     see $(LINK http://www.digitalmars.com/d/)
1188     )
1189
1190     $(P [2] Don Clugston's &pi; calculator, see
1191     $(LINK http://trac.dsource.org/projects/ddl/browser/trunk/meta/demo/calcpi.d)
1192     )
1193
1194     $(P [3] Don Clugston's decimaldigit and itoa,
1195     see $(LINK http://trac.dsource.org/projects/ddl/browser/trunk/meta/conv.d)
1196     )
1197
1198     $(P [4] Eric Niebler's Boost.Xpressive regular expression template
1199     library is at $(LINK http://boost-sandbox.sourceforge.net/libs/xpressive/doc/html/index.html)
1200     )
1201
1202     $(P [5] Eric Anderton's Regular Expression template library
1203     for D is at $(LINK http://trac.dsource.org/projects/ddl/browser/trunk/meta/regex.d)
1204     )
1205
1206 <h2>Acknowledgements</h2>
1207
1208 $(P
1209 I gratefully acknowledge the inspiration and assistance
1210 of Don Clugston, Eric Anderton and Matthew Wilson.
1211 )
1212
1213 )
1214
1215 Macros:
1216     TITLE=Templates Revisited
1217     WIKI=TemplatesRevisited
Note: See TracBrowser for help on using the browser.