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 }