Mercurial > projects > qtd
comparison d1/qtd/ctfe/String.d @ 344:96a75b1e5b26
project structure changes
author | Max Samukha <maxter@spambox.com> |
---|---|
date | Fri, 14 May 2010 12:14:37 +0300 |
parents | qt/qtd/ctfe/String.d@f9559a957be9 |
children |
comparison
equal
deleted
inserted
replaced
343:552647ec0f82 | 344:96a75b1e5b26 |
---|---|
1 /** | |
2 * CTFE String routines. | |
3 * | |
4 * Authors: Daniel Keep <daniel.keep@gmail.com> | |
5 * Copyright: See LICENSE. | |
6 */ | |
7 module qt.qtd.ctfe.String; | |
8 | |
9 import Integer = qt.qtd.ctfe.Integer; | |
10 | |
11 private | |
12 { | |
13 const HEX_CHARS = "0123456789abcdef"; | |
14 } | |
15 | |
16 /** | |
17 * Escapes a string into an equivalent string literal. | |
18 * | |
19 * Params: | |
20 * str = string to escape. | |
21 * aggressive = if set, the function will escape all non-printing | |
22 * characters, non-space whitespace and newlines. Defaults | |
23 * to true. | |
24 * Returns: | |
25 * Escaped string literal. | |
26 */ | |
27 string escape_ctfe(string str, bool aggressive=true) | |
28 { | |
29 string head = ""; | |
30 | |
31 foreach( i,c ; str ) | |
32 { | |
33 if( c == '"' || c == '\\' || c == '\0' ) | |
34 { | |
35 head = "\""~str[0..i]; | |
36 str = str[i..$]; | |
37 break; | |
38 } | |
39 | |
40 if( aggressive ) | |
41 { | |
42 if( c < 0x20 || c == 0x7f ) | |
43 { | |
44 head = "\""~str[0..i]; | |
45 str = str[i..$]; | |
46 break; | |
47 } | |
48 } | |
49 } | |
50 | |
51 if( head.length == 0 ) | |
52 return "\"" ~ str ~ "\""; | |
53 | |
54 string tail = ""; | |
55 | |
56 foreach( c ; str ) | |
57 { | |
58 if( c == '"' ) | |
59 tail ~= `\"`; | |
60 | |
61 else if( c == '\\' ) | |
62 tail ~= "\\\\"; | |
63 | |
64 else if( c == '\0' ) | |
65 tail ~= `\0`; | |
66 | |
67 else if( aggressive ) | |
68 { | |
69 switch( c ) | |
70 { | |
71 case '\?': | |
72 tail ~= `\?`; | |
73 break; | |
74 | |
75 case '\a': | |
76 tail ~= `\a`; | |
77 break; | |
78 | |
79 case '\b': | |
80 tail ~= `\b`; | |
81 break; | |
82 | |
83 case '\f': | |
84 tail ~= `\f`; | |
85 break; | |
86 | |
87 case '\n': | |
88 tail ~= `\n`; | |
89 break; | |
90 | |
91 case '\r': | |
92 tail ~= `\r`; | |
93 break; | |
94 | |
95 case '\t': | |
96 tail ~= `\t`; | |
97 break; | |
98 | |
99 case '\v': | |
100 tail ~= `\v`; | |
101 break; | |
102 | |
103 default: | |
104 if( c < 0x20 || c == 0x75 ) | |
105 { | |
106 tail ~= `\x`; | |
107 tail ~= HEX_CHARS[c/0xf]; | |
108 tail ~= HEX_CHARS[c&0xf]; | |
109 } | |
110 else | |
111 tail ~= c; | |
112 } | |
113 } | |
114 else | |
115 tail ~= c; | |
116 } | |
117 | |
118 return head ~ tail ~ "\""; | |
119 } | |
120 | |
121 version( Unittest ) | |
122 { | |
123 static assert( escape_ctfe("abc") == "\"abc\"" ); | |
124 static assert( escape_ctfe("a\"c") == "\"a\\\"c\"" ); | |
125 } | |
126 | |
127 /** | |
128 * Turns an array of bytes into a hexadecimal string. | |
129 * | |
130 * Params: | |
131 * arr = array to hexify. | |
132 * grouping = if non-zero, specifies after how many bytes to insert a | |
133 * space. | |
134 * Returns: | |
135 * String of hex bytes. | |
136 */ | |
137 | |
138 string hexify_ctfe(ubyte[] arr, int grouping = 0) | |
139 { | |
140 string r = ""; | |
141 int bytes = grouping; | |
142 foreach( b ; arr ) | |
143 { | |
144 if( bytes == 0 && grouping > 0 ) | |
145 { | |
146 r ~= ' '; | |
147 bytes = grouping; | |
148 } | |
149 | |
150 auto bh = b/16; | |
151 auto bl = b&15; | |
152 | |
153 assert( bh < 16 ); | |
154 assert( bl < 16 ); | |
155 | |
156 r ~= HEX_CHARS[bh]; | |
157 r ~= HEX_CHARS[bl]; | |
158 | |
159 if( grouping > 0 ) | |
160 -- bytes; | |
161 } | |
162 return r; | |
163 } | |
164 | |
165 /// ditto | |
166 | |
167 string hexify_ctfe(string arr, int grouping = 0) | |
168 { | |
169 string r = ""; | |
170 int bytes = grouping; | |
171 foreach( b ; arr ) | |
172 { | |
173 if( bytes == 0 && grouping > 0 ) | |
174 { | |
175 r ~= ' '; | |
176 bytes = grouping; | |
177 } | |
178 | |
179 auto bh = b/16; | |
180 auto bl = b&15; | |
181 | |
182 assert( bh < 16 ); | |
183 assert( bl < 16 ); | |
184 | |
185 r ~= HEX_CHARS[bh]; | |
186 r ~= HEX_CHARS[bl]; | |
187 | |
188 if( grouping > 0 ) | |
189 -- bytes; | |
190 } | |
191 return r; | |
192 } | |
193 | |
194 version( Unittest ) | |
195 { | |
196 static const ubyte[] DATA_1 = [0x00,0x01,0x02,0x03]; | |
197 static const ubyte[] DATA_2 = [0x0f,0x10,0xef,0xf0]; | |
198 | |
199 static assert( hexify_ctfe(DATA_1) == "00010203" ); | |
200 static assert( hexify_ctfe(DATA_2) == "0f10eff0" ); | |
201 | |
202 static assert( hexify_ctfe(DATA_1, 1) == "00 01 02 03" ); | |
203 static assert( hexify_ctfe(DATA_2, 1) == "0f 10 ef f0" ); | |
204 | |
205 static assert( hexify_ctfe(DATA_1, 2) == "0001 0203" ); | |
206 static assert( hexify_ctfe(DATA_2, 2) == "0f10 eff0" ); | |
207 | |
208 static assert( hexify_ctfe(DATA_1, 4) == "00010203" ); | |
209 static assert( hexify_ctfe(DATA_2, 4) == "0f10eff0" ); | |
210 } | |
211 | |
212 /** | |
213 * Pads a string. padl adds padding to the left, padr adds it to the right. | |
214 * Params: | |
215 * str = string to pad. | |
216 * len = length to pad to. | |
217 * padding = character to use for padding. Defaults to space. | |
218 * Returns: | |
219 * padded string. | |
220 */ | |
221 | |
222 string padl_ctfe(string str, int len, char padding = ' ') | |
223 { | |
224 while( str.length < len ) | |
225 str = padding ~ str; | |
226 return str; | |
227 } | |
228 | |
229 /// ditto | |
230 | |
231 string padr_ctfe(string str, int len, char padding = ' ') | |
232 { | |
233 while( str.length < len ) | |
234 str ~= padding; | |
235 return str; | |
236 } | |
237 | |
238 version( Unittest ) | |
239 { | |
240 static assert( padl_ctfe("abc", 2) == "abc" ); | |
241 static assert( padl_ctfe("abc", 3) == "abc" ); | |
242 static assert( padl_ctfe("abc", 4) == " abc" ); | |
243 static assert( padl_ctfe("abc", 4, 'x') == "xabc" ); | |
244 | |
245 static assert( padr_ctfe("abc", 2) == "abc" ); | |
246 static assert( padr_ctfe("abc", 3) == "abc" ); | |
247 static assert( padr_ctfe("abc", 4) == "abc " ); | |
248 static assert( padr_ctfe("abc", 4, 'x') == "abcx" ); | |
249 } | |
250 | |
251 /** | |
252 * Returns the tail of a string after a given splitting character. The Rev | |
253 * variant returns the tail after the last instance of the splitting | |
254 * character. | |
255 */ | |
256 | |
257 string tail_ctfe(string str, char split) | |
258 { | |
259 foreach( i,c ; str ) | |
260 { | |
261 if( c == split ) | |
262 return str[i+1..$]; | |
263 } | |
264 return str; | |
265 } | |
266 | |
267 /// ditto | |
268 | |
269 string tailRev_ctfe(string str, char split) | |
270 { | |
271 foreach_reverse( i,c ; str ) | |
272 { | |
273 if( c == split ) | |
274 return str[i+1..$]; | |
275 } | |
276 return str; | |
277 } | |
278 | |
279 /** | |
280 * Determines whether a character is valid in an identifier in a | |
281 * non-initial position. | |
282 * | |
283 * Does not support the full range of valid D identifier characters. | |
284 */ | |
285 | |
286 bool isIdentChar_ctfe(char c) | |
287 { | |
288 return ('a' <= c && c <= 'z') | |
289 || ('A' <= c && c <= 'Z') | |
290 || ('0' <= c && c <= '9') | |
291 || (c == '_'); | |
292 } | |
293 | |
294 /** | |
295 * Determines whether a character is valid in an identifier in an | |
296 * initial position. | |
297 * | |
298 * Does not support the full range of valid D identifier characters. | |
299 */ | |
300 | |
301 bool isIdentStartChar_ctfe(char c) | |
302 { | |
303 return ('a' <= c && c <= 'z') | |
304 || ('A' <= c && c <= 'Z') | |
305 || (c == '_'); | |
306 } | |
307 | |
308 /** | |
309 * Returns a line spec suitable for mixing in. This can be used with string | |
310 * mixins to ensure compile errors appear on the "correct" line in the source. | |
311 */ | |
312 | |
313 string linespec_ctfe(string file, long line) | |
314 { | |
315 return "#line "~Integer.format_ctfe(line)~" \"" ~ file ~ "\"\n"; | |
316 } | |
317 |