Mercurial > projects > ldc
annotate tango/tango/text/convert/Layout.d @ 264:a9dae3da4e87 trunk
[svn r285] Fixed D -> bool LLVM helper for floating point values.
Changed the way D-style varargs are passed, now each param should be aligned to size_t.sizeof.
author | lindquist |
---|---|
date | Sat, 14 Jun 2008 17:28:13 +0200 |
parents | 7816aafeea3c |
children |
rev | line source |
---|---|
132 | 1 /******************************************************************************* |
2 | |
3 copyright: Copyright (c) 2005 Kris. All rights reserved | |
4 | |
5 license: BSD style: $(LICENSE) | |
6 | |
7 version: Initial release: 2005 | |
8 | |
9 author: Kris | |
10 | |
11 This module provides a general-purpose formatting system to | |
12 convert values to text suitable for display. There is support | |
13 for alignment, justification, and common format specifiers for | |
14 numbers. | |
15 | |
16 Layout can be customized via configuring various handlers and | |
17 associated meta-date. This is utilized to plug in text.locale | |
18 for handling custom formats, date/time and culture-specific | |
19 conversions. | |
20 | |
21 The format notation is influenced by that used by the .NET | |
22 and ICU frameworks, rather than C-style printf or D-style | |
23 writef notation. | |
24 | |
25 ******************************************************************************/ | |
26 | |
27 module tango.text.convert.Layout; | |
28 | |
29 private import tango.core.Exception; | |
30 | |
31 private import Unicode = tango.text.convert.Utf; | |
32 | |
33 private import Float = tango.text.convert.Float, | |
34 Integer = tango.text.convert.Integer; | |
35 | |
36 /******************************************************************************* | |
37 | |
38 Platform issues ... | |
39 | |
40 *******************************************************************************/ | |
41 | |
42 version (DigitalMars) | |
43 { | |
44 alias void* Arg; | |
45 alias void* ArgList; | |
46 } | |
144
a27941d00351
[svn r149] fixed: a bunch of D-style variadics problems.
lindquist
parents:
132
diff
changeset
|
47 else version(LLVMDC) |
a27941d00351
[svn r149] fixed: a bunch of D-style variadics problems.
lindquist
parents:
132
diff
changeset
|
48 { |
a27941d00351
[svn r149] fixed: a bunch of D-style variadics problems.
lindquist
parents:
132
diff
changeset
|
49 private import tango.core.Vararg; |
a27941d00351
[svn r149] fixed: a bunch of D-style variadics problems.
lindquist
parents:
132
diff
changeset
|
50 alias void* Arg; |
a27941d00351
[svn r149] fixed: a bunch of D-style variadics problems.
lindquist
parents:
132
diff
changeset
|
51 alias va_list ArgList; |
a27941d00351
[svn r149] fixed: a bunch of D-style variadics problems.
lindquist
parents:
132
diff
changeset
|
52 } |
132 | 53 else |
54 version (X86_64) | |
55 { | |
56 private import std.stdarg; | |
57 alias void* Arg; | |
58 alias va_list ArgList; | |
59 } | |
60 else | |
61 { | |
62 alias char* Arg; | |
63 alias char* ArgList; | |
64 } | |
65 | |
66 /******************************************************************************* | |
67 | |
68 Contains methods for replacing format items in a string with string | |
69 equivalents of each argument. | |
70 | |
71 *******************************************************************************/ | |
72 | |
73 class Layout(T) | |
74 { | |
75 public alias convert opCall; | |
76 public alias uint delegate (T[]) Sink; | |
77 | |
78 /********************************************************************** | |
79 | |
80 **********************************************************************/ | |
81 | |
82 public final T[] sprint (T[] result, T[] formatStr, ...) | |
83 { | |
84 return vprint (result, formatStr, _arguments, _argptr); | |
85 } | |
86 | |
87 /********************************************************************** | |
88 | |
89 **********************************************************************/ | |
90 | |
91 public final T[] vprint (T[] result, T[] formatStr, TypeInfo[] arguments, ArgList args) | |
92 { | |
93 T* p = result.ptr; | |
94 | |
95 uint sink (T[] s) | |
96 { | |
97 int len = s.length; | |
98 if (len < (result.ptr + result.length) - p) | |
99 { | |
100 p [0..len] = s; | |
101 p += len; | |
102 } | |
103 else | |
104 error ("Layout.vprint :: output buffer is full"); | |
105 return len; | |
106 } | |
107 | |
108 convert (&sink, arguments, args, formatStr); | |
109 return result [0 .. p-result.ptr]; | |
110 } | |
111 | |
112 /********************************************************************** | |
113 | |
114 Replaces the _format item in a string with the string | |
115 equivalent of each argument. | |
116 | |
117 Params: | |
118 formatStr = A string containing _format items. | |
119 args = A list of arguments. | |
120 | |
121 Returns: A copy of formatStr in which the items have been | |
122 replaced by the string equivalent of the arguments. | |
123 | |
124 Remarks: The formatStr parameter is embedded with _format | |
125 items of the form: $(BR)$(BR) | |
126 {index[,alignment][:_format-string]}$(BR)$(BR) | |
127 $(UL $(LI index $(BR) | |
128 An integer indicating the element in a list to _format.) | |
129 $(LI alignment $(BR) | |
130 An optional integer indicating the minimum width. The | |
131 result is padded with spaces if the length of the value | |
132 is less than alignment.) | |
133 $(LI _format-string $(BR) | |
134 An optional string of formatting codes.) | |
135 )$(BR) | |
136 | |
137 The leading and trailing braces are required. To include a | |
138 literal brace character, use two leading or trailing brace | |
139 characters.$(BR)$(BR) | |
140 If formatStr is "{0} bottles of beer on the wall" and the | |
141 argument is an int with the value of 99, the return value | |
142 will be:$(BR) "99 bottles of beer on the wall". | |
143 | |
144 **********************************************************************/ | |
145 | |
146 public final T[] convert (T[] formatStr, ...) | |
147 { | |
148 return convert (_arguments, _argptr, formatStr); | |
149 } | |
150 | |
151 /********************************************************************** | |
152 | |
153 **********************************************************************/ | |
154 | |
155 public final uint convert (Sink sink, T[] formatStr, ...) | |
156 { | |
157 return convert (sink, _arguments, _argptr, formatStr); | |
158 } | |
159 | |
160 /********************************************************************** | |
161 | |
162 **********************************************************************/ | |
163 | |
164 public final T[] convert (TypeInfo[] arguments, ArgList args, T[] formatStr) | |
165 { | |
166 T[] output; | |
167 | |
168 uint sink (T[] s) | |
169 { | |
170 output ~= s; | |
171 return s.length; | |
172 } | |
173 | |
174 convert (&sink, arguments, args, formatStr); | |
175 return output; | |
176 } | |
177 | |
178 /********************************************************************** | |
179 | |
180 **********************************************************************/ | |
181 | |
182 public final T[] convertOne (T[] result, TypeInfo ti, Arg arg) | |
183 { | |
184 return munge (result, null, ti, arg); | |
185 } | |
186 | |
187 /********************************************************************** | |
188 | |
189 **********************************************************************/ | |
190 | |
191 public final uint convert (Sink sink, TypeInfo[] arguments, ArgList args, T[] formatStr) | |
192 { | |
193 assert (formatStr, "null format specifier"); | |
194 assert (arguments.length < 64, "too many args in Layout.convert"); | |
195 | |
213
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
196 version (LLVMDC) |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
197 { |
264
a9dae3da4e87
[svn r285] Fixed D -> bool LLVM helper for floating point values.
lindquist
parents:
213
diff
changeset
|
198 // code for LLVMDC, all targets! |
a9dae3da4e87
[svn r285] Fixed D -> bool LLVM helper for floating point values.
lindquist
parents:
213
diff
changeset
|
199 Arg[64] arglist = void; |
a9dae3da4e87
[svn r285] Fixed D -> bool LLVM helper for floating point values.
lindquist
parents:
213
diff
changeset
|
200 foreach (i, arg; arguments) |
a9dae3da4e87
[svn r285] Fixed D -> bool LLVM helper for floating point values.
lindquist
parents:
213
diff
changeset
|
201 { |
a9dae3da4e87
[svn r285] Fixed D -> bool LLVM helper for floating point values.
lindquist
parents:
213
diff
changeset
|
202 arglist[i] = args; |
a9dae3da4e87
[svn r285] Fixed D -> bool LLVM helper for floating point values.
lindquist
parents:
213
diff
changeset
|
203 args += (arg.tsize + size_t.sizeof - 1) & ~ (size_t.sizeof - 1); |
a9dae3da4e87
[svn r285] Fixed D -> bool LLVM helper for floating point values.
lindquist
parents:
213
diff
changeset
|
204 } |
a9dae3da4e87
[svn r285] Fixed D -> bool LLVM helper for floating point values.
lindquist
parents:
213
diff
changeset
|
205 /* |
213
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
206 static va_list get_va_arg(TypeInfo ti, ref va_list vp) |
132 | 207 { |
213
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
208 auto tisize = ti.tsize; |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
209 size_t size = tisize > size_t.sizeof ? size_t.sizeof : tisize; |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
210 va_list vptmp = cast(va_list)((cast(size_t)vp + size - 1) & ~(size - 1)); |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
211 vp = vptmp + tisize; |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
212 return vptmp; |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
213 } |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
214 |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
215 Arg[64] arglist = void; |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
216 foreach (i, arg; arguments) |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
217 { |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
218 arglist[i] = get_va_arg(arg, args); |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
219 } |
264
a9dae3da4e87
[svn r285] Fixed D -> bool LLVM helper for floating point values.
lindquist
parents:
213
diff
changeset
|
220 */ |
213
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
221 } |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
222 else version (X86_64) |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
223 { |
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
224 // code for x86-64 GDC |
132 | 225 Arg[64] arglist = void; |
226 int[64] intargs = void; | |
227 byte[64] byteargs = void; | |
228 long[64] longargs = void; | |
229 short[64] shortargs = void; | |
230 void[][64] voidargs = void; | |
231 real[64] realargs = void; | |
232 float[64] floatargs = void; | |
233 double[64] doubleargs = void; | |
234 | |
235 foreach (i, arg; arguments) | |
236 { | |
237 arglist[i] = args.ptr; | |
238 /* Since floating point types don't live on | |
239 * the stack, they must be accessed by the | |
240 * correct type. */ | |
241 bool converted = false; | |
242 switch (arg.classinfo.name[9]) | |
243 { | |
244 case TypeCode.FLOAT: | |
245 floatargs[i] = va_arg!(float)(args); | |
246 arglist[i] = &floatargs[i]; | |
247 converted = true; | |
248 break; | |
249 | |
250 case TypeCode.DOUBLE: | |
251 doubleargs[i] = va_arg!(double)(args); | |
252 arglist[i] = &doubleargs[i]; | |
253 converted = true; | |
254 break; | |
255 | |
256 case TypeCode.REAL: | |
257 realargs[i] = va_arg!(real)(args); | |
258 arglist[i] = &realargs[i]; | |
259 converted = true; | |
260 break; | |
261 | |
262 default: | |
263 break; | |
264 } | |
265 if (! converted) | |
266 { | |
267 switch (arg.tsize) | |
268 { | |
269 case 1: | |
270 byteargs[i] = va_arg!(byte)(args); | |
271 arglist[i] = &byteargs[i]; | |
272 break; | |
273 case 2: | |
274 shortargs[i] = va_arg!(short)(args); | |
275 arglist[i] = &shortargs[i]; | |
276 break; | |
277 case 4: | |
278 intargs[i] = va_arg!(int)(args); | |
279 arglist[i] = &intargs[i]; | |
280 break; | |
281 case 8: | |
282 longargs[i] = va_arg!(long)(args); | |
283 arglist[i] = &longargs[i]; | |
284 break; | |
285 case 16: | |
286 voidargs[i] = va_arg!(void[])(args); | |
287 arglist[i] = &voidargs[i]; | |
288 break; | |
289 default: | |
290 assert (false, "Unknown size: " ~ Integer.toString (arg.tsize)); | |
291 } | |
292 } | |
293 } | |
294 } | |
295 else | |
296 { | |
213
7816aafeea3c
[svn r229] Updated the object.d implementation to the latest Tango.
lindquist
parents:
163
diff
changeset
|
297 // code for DMD & x86 GDC (may also work on other 32-bit targets) |
132 | 298 Arg[64] arglist = void; |
299 foreach (i, arg; arguments) | |
300 { | |
301 arglist[i] = args; | |
302 args += (arg.tsize + int.sizeof - 1) & ~ (int.sizeof - 1); | |
303 } | |
304 } | |
305 return parse (formatStr, arguments, arglist, sink); | |
306 } | |
307 | |
308 /********************************************************************** | |
309 | |
310 Parse the format-string, emitting formatted args and text | |
311 fragments as we go. | |
312 | |
313 **********************************************************************/ | |
314 | |
315 private uint parse (T[] layout, TypeInfo[] ti, Arg[] args, Sink sink) | |
316 { | |
317 T[384] result = void; | |
318 int length, nextIndex; | |
319 | |
320 | |
321 T* s = layout.ptr; | |
322 T* fragment = s; | |
323 T* end = s + layout.length; | |
324 | |
325 while (true) | |
326 { | |
327 while (s < end && *s != '{') | |
328 ++s; | |
329 | |
330 // emit fragment | |
331 length += sink (fragment [0 .. s - fragment]); | |
332 | |
333 // all done? | |
334 if (s is end) | |
335 break; | |
336 | |
337 // check for "{{" and skip if so | |
338 if (*++s is '{') | |
339 { | |
340 fragment = s++; | |
341 continue; | |
342 } | |
343 | |
344 int index = 0; | |
345 bool indexed = false; | |
346 | |
347 // extract index | |
348 while (*s >= '0' && *s <= '9') | |
349 { | |
350 index = index * 10 + *s++ -'0'; | |
351 indexed = true; | |
352 } | |
353 | |
354 // skip spaces | |
355 while (s < end && *s is ' ') | |
356 ++s; | |
357 | |
358 int width; | |
359 bool leftAlign; | |
360 | |
361 // has width? | |
362 if (*s is ',') | |
363 { | |
364 while (++s < end && *s is ' ') {} | |
365 | |
366 if (*s is '-') | |
367 { | |
368 leftAlign = true; | |
369 ++s; | |
370 } | |
371 | |
372 // get width | |
373 while (*s >= '0' && *s <= '9') | |
374 width = width * 10 + *s++ -'0'; | |
375 | |
376 // skip spaces | |
377 while (s < end && *s is ' ') | |
378 ++s; | |
379 } | |
380 | |
381 T[] format; | |
382 | |
383 // has a format string? | |
384 if (*s is ':' && s < end) | |
385 { | |
386 T* fs = ++s; | |
387 | |
388 // eat everything up to closing brace | |
389 while (s < end && *s != '}') | |
390 ++s; | |
391 format = fs [0 .. s - fs]; | |
392 } | |
393 | |
394 // insist on a closing brace | |
395 if (*s != '}') | |
396 { | |
397 length += sink ("{missing or misplaced '}'}"); | |
398 continue; | |
399 } | |
400 | |
401 // check for default index & set next default counter | |
402 if (! indexed) | |
403 index = nextIndex; | |
404 nextIndex = index + 1; | |
405 | |
406 // next char is start of following fragment | |
407 fragment = ++s; | |
408 | |
409 // handle alignment | |
410 void process (T[] str) | |
411 { | |
412 int padding = width - str.length; | |
413 | |
414 // if not left aligned, pad out with spaces | |
415 if (! leftAlign && padding > 0) | |
416 length += spaces (sink, padding); | |
417 | |
418 // emit formatted argument | |
419 length += sink (str); | |
420 | |
421 // finally, pad out on right | |
422 if (leftAlign && padding > 0) | |
423 length += spaces (sink, padding); | |
424 } | |
425 | |
426 // an astonishing number of typehacks needed to handle arrays :( | |
427 void processElement (TypeInfo _ti, Arg _arg) | |
428 { | |
429 if (_ti.classinfo.name.length is 20 && _ti.classinfo.name[9..$] == "StaticArray" ) | |
430 { | |
431 auto tiStat = cast(TypeInfo_StaticArray)_ti; | |
432 auto p = _arg; | |
433 length += sink ("[ "); | |
434 for (int i = 0; i < tiStat.len; i++) | |
435 { | |
436 if (p !is _arg ) | |
437 length += sink (", "); | |
438 processElement (tiStat.value, p); | |
439 p += tiStat.tsize; | |
440 } | |
441 length += sink (" ]"); | |
442 } | |
443 else | |
444 if (_ti.classinfo.name.length is 25 && _ti.classinfo.name[9..$] == "AssociativeArray") | |
445 { | |
446 auto tiAsso = cast(TypeInfo_AssociativeArray)_ti; | |
447 auto tiKey = tiAsso.key; | |
448 auto tiVal = tiAsso.next(); | |
449 // the knowledge of the internal k/v storage is used | |
450 // so this might break if, that internal storage changes | |
451 alias ubyte AV; // any type for key, value might be ok, the sizes are corrected later | |
452 alias ubyte AK; | |
453 auto aa = *cast(AV[AK]*) _arg; | |
454 | |
455 length += sink ("{ "); | |
456 bool first = true; | |
457 | |
458 int roundUp (int sz) | |
459 { | |
460 return (sz + (void*).sizeof -1) & ~((void*).sizeof - 1); | |
461 } | |
462 | |
463 foreach (inout v; aa) | |
464 { | |
465 // the key is befor the value, so substrace with fixed key size from above | |
466 auto pk = cast(Arg)( &v - roundUp(AK.sizeof)); | |
467 // now the real value pos is plus the real key size | |
468 auto pv = cast(Arg)(pk + roundUp(tiKey.tsize())); | |
469 | |
470 if (!first) | |
471 length += sink (", "); | |
472 processElement (tiKey, pk); | |
473 length += sink ("=>"); | |
474 processElement (tiVal, pv); | |
475 first = false; | |
476 } | |
477 length += sink (" }"); | |
478 } | |
479 else | |
480 if (_ti.classinfo.name[9] is TypeCode.ARRAY && | |
481 (_ti !is typeid(char[])) && | |
482 (_ti !is typeid(wchar[])) && | |
483 (_ti !is typeid(dchar[]))) | |
484 { | |
485 // for all non string array types (including char[][]) | |
486 auto arr = *cast(void[]*)_arg; | |
487 auto len = arr.length; | |
488 auto ptr = cast(Arg) arr.ptr; | |
489 auto elTi = _ti.next(); | |
490 auto size = elTi.tsize(); | |
491 length += sink ("[ "); | |
492 while (len > 0) | |
493 { | |
494 if (ptr !is arr.ptr) | |
495 length += sink (", "); | |
496 processElement (elTi, ptr); | |
497 len -= 1; | |
498 ptr += size; | |
499 } | |
500 length += sink (" ]"); | |
501 } | |
502 else | |
503 // the standard processing | |
504 process (munge(result, format, _ti, _arg)); | |
505 } | |
506 | |
507 | |
508 // process this argument | |
509 if (index >= ti.length) | |
510 process ("{invalid index}"); | |
511 else | |
512 processElement (ti[index], args[index]); | |
513 } | |
514 return length; | |
515 } | |
516 | |
517 /********************************************************************** | |
518 | |
519 **********************************************************************/ | |
520 | |
521 private void error (char[] msg) | |
522 { | |
523 throw new IllegalArgumentException (msg); | |
524 } | |
525 | |
526 /********************************************************************** | |
527 | |
528 **********************************************************************/ | |
529 | |
530 private uint spaces (Sink sink, int count) | |
531 { | |
532 uint ret; | |
533 | |
534 static const T[32] Spaces = ' '; | |
535 while (count > Spaces.length) | |
536 { | |
537 ret += sink (Spaces); | |
538 count -= Spaces.length; | |
539 } | |
540 return ret + sink (Spaces[0..count]); | |
541 } | |
542 | |
543 /*********************************************************************** | |
544 | |
545 ***********************************************************************/ | |
546 | |
547 private T[] munge (T[] result, T[] format, TypeInfo type, Arg p) | |
548 { | |
549 switch (type.classinfo.name[9]) | |
550 { | |
551 case TypeCode.ARRAY: | |
552 if (type is typeid(char[])) | |
553 return fromUtf8 (*cast(char[]*) p, result); | |
554 | |
555 if (type is typeid(wchar[])) | |
556 return fromUtf16 (*cast(wchar[]*) p, result); | |
557 | |
558 if (type is typeid(dchar[])) | |
559 return fromUtf32 (*cast(dchar[]*) p, result); | |
560 | |
561 return fromUtf8 (type.toString, result); | |
562 | |
563 case TypeCode.BOOL: | |
564 static T[] t = "true"; | |
565 static T[] f = "false"; | |
566 return (*cast(bool*) p) ? t : f; | |
567 | |
568 case TypeCode.BYTE: | |
569 return integer (result, *cast(byte*) p, format); | |
570 | |
571 case TypeCode.UBYTE: | |
572 return integer (result, *cast(ubyte*) p, format, 'u'); | |
573 | |
574 case TypeCode.SHORT: | |
575 return integer (result, *cast(short*) p, format); | |
576 | |
577 case TypeCode.USHORT: | |
578 return integer (result, *cast(ushort*) p, format, 'u'); | |
579 | |
580 case TypeCode.INT: | |
581 return integer (result, *cast(int*) p, format); | |
582 | |
583 case TypeCode.UINT: | |
584 return integer (result, *cast(uint*) p, format, 'u'); | |
585 | |
586 case TypeCode.ULONG: | |
587 return integer (result, *cast(long*) p, format, 'u'); | |
588 | |
589 case TypeCode.LONG: | |
590 return integer (result, *cast(long*) p, format); | |
591 | |
592 case TypeCode.FLOAT: | |
593 return floater (result, *cast(float*) p, format); | |
594 | |
595 case TypeCode.DOUBLE: | |
596 return floater (result, *cast(double*) p, format); | |
597 | |
598 case TypeCode.REAL: | |
599 return floater (result, *cast(real*) p, format); | |
600 | |
601 case TypeCode.CHAR: | |
602 return fromUtf8 ((cast(char*) p)[0..1], result); | |
603 | |
604 case TypeCode.WCHAR: | |
605 return fromUtf16 ((cast(wchar*) p)[0..1], result); | |
606 | |
607 case TypeCode.DCHAR: | |
608 return fromUtf32 ((cast(dchar*) p)[0..1], result); | |
609 | |
610 case TypeCode.POINTER: | |
611 return integer (result, *cast(size_t*) p, format, 'x'); | |
612 | |
613 case TypeCode.CLASS: | |
614 auto c = *cast(Object*) p; | |
615 if (c) | |
616 return fromUtf8 (c.toString, result); | |
617 break; | |
618 | |
619 case TypeCode.STRUCT: | |
620 auto s = cast(TypeInfo_Struct) type; | |
621 if (s.xtoString) | |
622 return fromUtf8 (s.xtoString(p), result); | |
623 goto default; | |
624 | |
625 case TypeCode.INTERFACE: | |
626 auto x = *cast(void**) p; | |
627 if (x) | |
628 { | |
629 auto pi = **cast(Interface ***) x; | |
630 auto o = cast(Object)(*cast(void**)p - pi.offset); | |
631 return fromUtf8 (o.toString, result); | |
632 } | |
633 break; | |
634 | |
635 case TypeCode.ENUM: | |
636 return munge (result, format, (cast(TypeInfo_Enum) type).base, p); | |
637 | |
638 case TypeCode.TYPEDEF: | |
639 return munge (result, format, (cast(TypeInfo_Typedef) type).base, p); | |
640 | |
641 default: | |
642 return unknown (result, format, type, p); | |
643 } | |
644 | |
645 return cast(T[]) "{null}"; | |
646 } | |
647 | |
648 /********************************************************************** | |
649 | |
650 **********************************************************************/ | |
651 | |
652 protected T[] unknown (T[] result, T[] format, TypeInfo type, Arg p) | |
653 { | |
144
a27941d00351
[svn r149] fixed: a bunch of D-style variadics problems.
lindquist
parents:
132
diff
changeset
|
654 return cast(T[])("{unhandled argument type: " ~ fromUtf8 (type.toString, result) ~ "}"); |
132 | 655 } |
656 | |
657 /********************************************************************** | |
658 | |
659 **********************************************************************/ | |
660 | |
661 protected T[] integer (T[] output, long v, T[] format, T style = 'd') | |
662 { | |
663 Integer.Flags flags; | |
664 uint width = output.length; | |
665 | |
666 if (parseGeneric (format, width, style)) | |
667 if (width <= output.length) | |
668 { | |
669 output = output [0 .. width]; | |
670 flags |= flags.Zero; | |
671 } | |
672 return Integer.format (output, v, cast(Integer.Style) style, flags); | |
673 } | |
674 | |
675 /********************************************************************** | |
676 | |
677 **********************************************************************/ | |
678 | |
679 protected T[] floater (T[] output, real v, T[] format) | |
680 { | |
681 T style = 'f'; | |
682 uint places = 2; | |
683 | |
684 parseGeneric (format, places, style); | |
685 return Float.format (output, v, places, (style is 'e' || style is 'E') ? 0 : 10); | |
686 } | |
687 | |
688 /********************************************************************** | |
689 | |
690 **********************************************************************/ | |
691 | |
692 private bool parseGeneric (T[] format, ref uint width, ref T style) | |
693 { | |
694 if (format.length) | |
695 { | |
696 uint number; | |
697 auto p = format.ptr; | |
698 auto e = p + format.length; | |
699 style = *p; | |
700 while (++p < e) | |
701 if (*p >= '0' && *p <= '9') | |
702 number = number * 10 + *p - '0'; | |
703 else | |
704 break; | |
705 | |
706 if (p - format.ptr > 1) | |
707 { | |
708 width = number; | |
709 return true; | |
710 } | |
711 } | |
712 return false; | |
713 } | |
714 | |
715 /*********************************************************************** | |
716 | |
717 ***********************************************************************/ | |
718 | |
719 private static T[] fromUtf8 (char[] s, T[] scratch) | |
720 { | |
721 static if (is (T == char)) | |
722 return s; | |
723 | |
724 static if (is (T == wchar)) | |
725 return Unicode.toString16 (s, scratch); | |
726 | |
727 static if (is (T == dchar)) | |
728 return Unicode.toString32 (s, scratch); | |
729 } | |
730 | |
731 /*********************************************************************** | |
732 | |
733 ***********************************************************************/ | |
734 | |
735 private static T[] fromUtf16 (wchar[] s, T[] scratch) | |
736 { | |
737 static if (is (T == wchar)) | |
738 return s; | |
739 | |
740 static if (is (T == char)) | |
741 return Unicode.toString (s, scratch); | |
742 | |
743 static if (is (T == dchar)) | |
744 return Unicode.toString32 (s, scratch); | |
745 } | |
746 | |
747 /*********************************************************************** | |
748 | |
749 ***********************************************************************/ | |
750 | |
751 private static T[] fromUtf32 (dchar[] s, T[] scratch) | |
752 { | |
753 static if (is (T == dchar)) | |
754 return s; | |
755 | |
756 static if (is (T == char)) | |
757 return Unicode.toString (s, scratch); | |
758 | |
759 static if (is (T == wchar)) | |
760 return Unicode.toString16 (s, scratch); | |
761 } | |
762 } | |
763 | |
764 | |
765 /******************************************************************************* | |
766 | |
767 *******************************************************************************/ | |
768 | |
769 private enum TypeCode | |
770 { | |
771 EMPTY = 0, | |
772 BOOL = 'b', | |
773 UBYTE = 'h', | |
774 BYTE = 'g', | |
775 USHORT = 't', | |
776 SHORT = 's', | |
777 UINT = 'k', | |
778 INT = 'i', | |
779 ULONG = 'm', | |
780 LONG = 'l', | |
781 REAL = 'e', | |
782 FLOAT = 'f', | |
783 DOUBLE = 'd', | |
784 CHAR = 'a', | |
785 WCHAR = 'u', | |
786 DCHAR = 'w', | |
787 ARRAY = 'A', | |
788 CLASS = 'C', | |
789 STRUCT = 'S', | |
790 ENUM = 'E', | |
791 POINTER = 'P', | |
792 TYPEDEF = 'T', | |
793 INTERFACE = 'I', | |
794 } | |
795 | |
796 | |
797 | |
798 /******************************************************************************* | |
799 | |
800 *******************************************************************************/ | |
801 | |
802 debug (UnitTest) | |
803 { | |
804 //void main() {} | |
805 | |
806 unittest | |
807 { | |
808 auto Formatter = new Layout!(char); | |
809 | |
810 assert( Formatter( "abc" ) == "abc" ); | |
811 assert( Formatter( "{0}", 1 ) == "1" ); | |
812 assert( Formatter( "{0}", -1 ) == "-1" ); | |
813 | |
814 assert( Formatter( "{}", 1 ) == "1" ); | |
815 assert( Formatter( "{} {}", 1, 2) == "1 2" ); | |
816 assert( Formatter( "{} {0} {}", 1, 3) == "1 1 3" ); | |
817 assert( Formatter( "{} {0} {} {}", 1, 3) == "1 1 3 {invalid index}" ); | |
818 assert( Formatter( "{} {0} {} {:x}", 1, 3) == "1 1 3 {invalid index}" ); | |
819 | |
820 assert( Formatter( "{0}", true ) == "true" , Formatter( "{0}", true )); | |
821 assert( Formatter( "{0}", false ) == "false" ); | |
822 | |
823 assert( Formatter( "{0}", cast(byte)-128 ) == "-128" ); | |
824 assert( Formatter( "{0}", cast(byte)127 ) == "127" ); | |
825 assert( Formatter( "{0}", cast(ubyte)255 ) == "255" ); | |
826 | |
827 assert( Formatter( "{0}", cast(short)-32768 ) == "-32768" ); | |
828 assert( Formatter( "{0}", cast(short)32767 ) == "32767" ); | |
829 assert( Formatter( "{0}", cast(ushort)65535 ) == "65535" ); | |
830 // assert( Formatter( "{0:x4}", cast(ushort)0xafe ) == "0afe" ); | |
831 // assert( Formatter( "{0:X4}", cast(ushort)0xafe ) == "0AFE" ); | |
832 | |
833 assert( Formatter( "{0}", -2147483648 ) == "-2147483648" ); | |
834 assert( Formatter( "{0}", 2147483647 ) == "2147483647" ); | |
835 assert( Formatter( "{0}", 4294967295 ) == "4294967295" ); | |
836 // compiler error | |
837 assert( Formatter( "{0}", -9223372036854775807L) == "-9223372036854775807" ); | |
838 assert( Formatter( "{0}", 0x8000_0000_0000_0000L) == "9223372036854775808" ); | |
839 assert( Formatter( "{0}", 9223372036854775807L ) == "9223372036854775807" ); | |
840 // Error: prints -1 | |
841 // assert( Formatter( "{0}", 18446744073709551615UL ) == "18446744073709551615" ); | |
842 | |
843 assert( Formatter( "{0}", "s" ) == "s" ); | |
844 // fragments before and after | |
845 assert( Formatter( "d{0}d", "s" ) == "dsd" ); | |
846 assert( Formatter( "d{0}d", "1234567890" ) == "d1234567890d" ); | |
847 | |
848 // brace escaping | |
849 assert( Formatter( "d{0}d", "<string>" ) == "d<string>d"); | |
850 assert( Formatter( "d{{0}d", "<string>" ) == "d{0}d"); | |
851 assert( Formatter( "d{{{0}d", "<string>" ) == "d{<string>d"); | |
852 assert( Formatter( "d{0}}d", "<string>" ) == "d<string>}d"); | |
853 | |
854 assert( Formatter( "{0:x}", 0xafe0000 ) == "afe0000" ); | |
855 // todo: is it correct to print 7 instead of 6 chars??? | |
856 assert( Formatter( "{0:x7}", 0xafe0000 ) == "afe0000" ); | |
857 assert( Formatter( "{0:x8}", 0xafe0000 ) == "0afe0000" ); | |
858 assert( Formatter( "{0:X8}", 0xafe0000 ) == "0AFE0000" ); | |
859 assert( Formatter( "{0:X9}", 0xafe0000 ) == "00AFE0000" ); | |
860 assert( Formatter( "{0:X13}", 0xafe0000 ) == "000000AFE0000" ); | |
861 assert( Formatter( "{0:x13}", 0xafe0000 ) == "000000afe0000" ); | |
862 // decimal width | |
863 assert( Formatter( "{0:d6}", 123 ) == "000123" ); | |
864 assert( Formatter( "{0,7:d6}", 123 ) == " 000123" ); | |
865 assert( Formatter( "{0,-7:d6}", 123 ) == "000123 " ); | |
866 | |
867 assert( Formatter( "{0:d7}", -123 ) == "-000123" ); | |
868 assert( Formatter( "{0,7:d6}", 123 ) == " 000123" ); | |
869 assert( Formatter( "{0,7:d7}", -123 ) == "-000123" ); | |
870 assert( Formatter( "{0,8:d7}", -123 ) == " -000123" ); | |
871 assert( Formatter( "{0,5:d7}", -123 ) == "-000123" ); | |
872 | |
873 assert( Formatter( "{0:X}", 0xFFFF_FFFF_FFFF_FFFF) == "FFFFFFFFFFFFFFFF" ); | |
874 assert( Formatter( "{0:x}", 0xFFFF_FFFF_FFFF_FFFF) == "ffffffffffffffff" ); | |
875 assert( Formatter( "{0:x}", 0xFFFF_1234_FFFF_FFFF) == "ffff1234ffffffff" ); | |
876 assert( Formatter( "{0:x19}", 0x1234_FFFF_FFFF) == "00000001234ffffffff" ); | |
877 // Error: prints -1 | |
878 // assert( Formatter( "{0}", 18446744073709551615UL ) == "18446744073709551615" ); | |
879 assert( Formatter( "{0}", "s" ) == "s" ); | |
880 // fragments before and after | |
881 assert( Formatter( "d{0}d", "s" ) == "dsd" ); | |
882 | |
883 // argument index | |
884 assert( Formatter( "a{0}b{1}c{2}", "x", "y", "z" ) == "axbycz" ); | |
885 assert( Formatter( "a{2}b{1}c{0}", "x", "y", "z" ) == "azbycx" ); | |
886 assert( Formatter( "a{1}b{1}c{1}", "x", "y", "z" ) == "aybycy" ); | |
887 | |
888 // alignment | |
889 // align does not restrict the length | |
890 assert( Formatter( "{0,5}", "hellohello" ) == "hellohello" ); | |
891 // align fills with spaces | |
892 assert( Formatter( "->{0,-10}<-", "hello" ) == "->hello <-" ); | |
893 assert( Formatter( "->{0,10}<-", "hello" ) == "-> hello<-" ); | |
894 assert( Formatter( "->{0,-10}<-", 12345 ) == "->12345 <-" ); | |
895 assert( Formatter( "->{0,10}<-", 12345 ) == "-> 12345<-" ); | |
896 | |
897 assert( Formatter( "{0:f}", 1.23f ) == "1.23" ); | |
898 assert( Formatter( "{0:f4}", 1.23456789L ) == "1.2346" ); | |
899 assert( Formatter( "{0:e4}", 0.0001) == "0.1000e-03"); | |
900 | |
901 int[] a = [ 51, 52, 53, 54, 55 ]; | |
902 assert( Formatter( "{}", a ) == "[ 51, 52, 53, 54, 55 ]" ); | |
903 assert( Formatter( "{:x}", a ) == "[ 33, 34, 35, 36, 37 ]" ); | |
904 assert( Formatter( "{,-4}", a ) == "[ 51 , 52 , 53 , 54 , 55 ]" ); | |
905 assert( Formatter( "{,4}", a ) == "[ 51, 52, 53, 54, 55 ]" ); | |
906 int[][] b = [ [ 51, 52 ], [ 53, 54, 55 ] ]; | |
907 assert( Formatter( "{}", b ) == "[ [ 51, 52 ], [ 53, 54, 55 ] ]" ); | |
908 | |
909 ushort[3] c = [ cast(ushort)51, 52, 53 ]; | |
910 assert( Formatter( "{}", c ) == "[ 51, 52, 53 ]" ); | |
911 | |
912 ushort[long] d; | |
913 d[234] = 2; | |
914 d[345] = 3; | |
915 assert( Formatter( "{}", d ) == "{ 234=>2, 345=>3 }" ); | |
916 | |
917 bool[char[]] e; | |
918 e[ "key".dup ] = true; | |
919 e[ "value".dup ] = false; | |
920 assert( Formatter( "{}", e ) == "{ key=>true, value=>false }" ); | |
921 | |
922 char[][ double ] f; | |
923 f[ 1.0 ] = "one".dup; | |
924 f[ 3.14 ] = "PI".dup; | |
925 assert( Formatter( "{}", f ) == "{ 1.00=>one, 3.14=>PI }" ); | |
926 } | |
927 } | |
928 | |
929 | |
930 | |
931 debug (Layout) | |
932 { | |
933 import tango.io.Console; | |
934 | |
935 void main () | |
936 { | |
937 auto layout = new Layout!(char); | |
938 | |
939 Cout (layout ("{:d2}", 56)).newline; | |
940 Cout (layout ("{:f4}", 0.001)).newline; | |
941 Cout (layout ("{:f8}", 3.14159)).newline; | |
942 Cout (layout ("{:e20}", 0.001)).newline; | |
943 Cout (layout ("{:e4}", 0.0000001)).newline; | |
944 Cout (layout ("ptr:{}", &layout)).newline; | |
945 | |
946 struct S | |
947 { | |
948 char[] toString () {return "foo";} | |
949 } | |
950 | |
951 S s; | |
952 Cout (layout ("struct: {}", s)).newline; | |
953 } | |
954 } |