1 // Written in the D programming language.
2 
3 /**
4 Templates with which to do compile-time manipulation of strings.
5 
6 Macros:
7  WIKI = Phobos/StdMetastrings
8 
9 Copyright: Copyright Digital Mars 2007 - 2009.
10 License:   <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
11 Authors:   $(WEB digitalmars.com, Walter Bright),
12            Don Clugston
13 Source:    $(PHOBOSSRC std/_metastrings.d)
14 */
15 /*
16          Copyright Digital Mars 2007 - 2009.
17 Distributed under the Boost Software License, Version 1.0.
18    (See accompanying file LICENSE_1_0.txt or copy at
19          http://www.boost.org/LICENSE_1_0.txt)
20  */
21 module undead.metastrings;
22 
23 /**
24 Formats constants into a string at compile time.  Analogous to $(XREF
25 string,format).
26 
27 Parameters:
28 
29 A = tuple of constants, which can be strings, characters, or integral
30     values.
31 
32 Formats:
33  *    The formats supported are %s for strings, and %%
34  *    for the % character.
35 Example:
36 ---
37 import std.metastrings;
38 import std.stdio;
39 
40 void main()
41 {
42   string s = Format!("Arg %s = %s", "foo", 27);
43   writefln(s); // "Arg foo = 27"
44 }
45  * ---
46  */
47 
48 template Format(A...)
49 {
50     static if (A.length == 0)
51         enum Format = "";
52     else static if (is(typeof(A[0]) : const(char)[]))
53         enum Format = FormatString!(A[0], A[1..$]);
54     else
55         enum Format = toStringNow!(A[0]) ~ Format!(A[1..$]);
56 }
57 
58 template FormatString(const(char)[] F, A...)
59 {
60     static if (F.length == 0)
61         enum FormatString = Format!(A);
62     else static if (F.length == 1)
63         enum FormatString = F[0] ~ Format!(A);
64     else static if (F[0..2] == "%s")
65         enum FormatString
66             = toStringNow!(A[0]) ~ FormatString!(F[2..$],A[1..$]);
67     else static if (F[0..2] == "%%")
68         enum FormatString = "%" ~ FormatString!(F[2..$],A);
69     else
70     {
71         static assert(F[0] != '%', "unrecognized format %" ~ F[1]);
72         enum FormatString = F[0] ~ FormatString!(F[1..$],A);
73     }
74 }
75 
76 unittest
77 {
78     auto s = Format!("hel%slo", "world", -138, 'c', true);
79     assert(s == "helworldlo-138ctrue", "[" ~ s ~ "]");
80 }
81 
82 /**
83  * Convert constant argument to a string.
84  */
85 
86 template toStringNow(ulong v)
87 {
88     static if (v < 10)
89         enum toStringNow = "" ~ cast(char)(v + '0');
90     else
91         enum toStringNow = toStringNow!(v / 10) ~ toStringNow!(v % 10);
92 }
93 
94 unittest
95 {
96     static assert(toStringNow!(1uL << 62) == "4611686018427387904");
97 }
98 
99 /// ditto
100 template toStringNow(long v)
101 {
102     static if (v < 0)
103         enum toStringNow = "-" ~ toStringNow!(cast(ulong) -v);
104     else
105         enum toStringNow = toStringNow!(cast(ulong) v);
106 }
107 
108 unittest
109 {
110     static assert(toStringNow!(0x100000000) == "4294967296");
111     static assert(toStringNow!(-138L) == "-138");
112 }
113 
114 /// ditto
115 template toStringNow(uint U)
116 {
117     enum toStringNow = toStringNow!(cast(ulong)U);
118 }
119 
120 /// ditto
121 template toStringNow(int I)
122 {
123     enum toStringNow = toStringNow!(cast(long)I);
124 }
125 
126 /// ditto
127 template toStringNow(bool B)
128 {
129     enum toStringNow = B ? "true" : "false";
130 }
131 
132 /// ditto
133 template toStringNow(string S)
134 {
135     enum toStringNow = S;
136 }
137 
138 /// ditto
139 template toStringNow(char C)
140 {
141     enum toStringNow = "" ~ C;
142 }
143 
144 
145 /********
146  * Parse unsigned integer literal from the start of string s.
147  * returns:
148  *    .value = the integer literal as a string,
149  *    .rest = the string following the integer literal
150  * Otherwise:
151  *    .value = null,
152  *    .rest = s
153  */
154 
155 template parseUinteger(const(char)[] s)
156 {
157     static if (s.length == 0)
158     {
159         enum value = "";
160         enum rest = "";
161     }
162     else static if (s[0] >= '0' && s[0] <= '9')
163     {
164         enum value = s[0] ~ parseUinteger!(s[1..$]).value;
165         enum rest = parseUinteger!(s[1..$]).rest;
166     }
167     else
168     {
169         enum value = "";
170         enum rest = s;
171     }
172 }
173 
174 /********
175 Parse integer literal optionally preceded by $(D '-') from the start
176 of string $(D s).
177 
178 Returns:
179    .value = the integer literal as a string,
180    .rest = the string following the integer literal
181 
182 Otherwise:
183    .value = null,
184    .rest = s
185 */
186 
187 template parseInteger(const(char)[] s)
188 {
189     static if (s.length == 0)
190     {
191         enum value = "";
192         enum rest = "";
193     }
194     else static if (s[0] >= '0' && s[0] <= '9')
195     {
196         enum value = s[0] ~ parseUinteger!(s[1..$]).value;
197         enum rest = parseUinteger!(s[1..$]).rest;
198     }
199     else static if (s.length >= 2 &&
200             s[0] == '-' && s[1] >= '0' && s[1] <= '9')
201     {
202         enum value = s[0..2] ~ parseUinteger!(s[2..$]).value;
203         enum rest = parseUinteger!(s[2..$]).rest;
204     }
205     else
206     {
207         enum value = "";
208         enum rest = s;
209     }
210 }
211 
212 unittest
213 {
214     assert(parseUinteger!("1234abc").value == "1234");
215     assert(parseUinteger!("1234abc").rest == "abc");
216     assert(parseInteger!("-1234abc").value == "-1234");
217     assert(parseInteger!("-1234abc").rest == "abc");
218 }