comparison tango/tango/text/convert/Layout.d @ 132:1700239cab2e trunk

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