Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

root/trunk/tango/text/convert/Layout.d

Revision 5652, 50.9 kB (checked in by Marenz, 14 years ago)

Merged branch dmd-1.067 into trunk

  • Property svn:mime-type set to text/x-dsrc
  • Property svn:eol-style set to native
Line 
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, Keinfarbton
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-data. 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  Utf = tango.text.convert.Utf;
32
33 private import  Float = tango.text.convert.Float,
34                 Integer = tango.text.convert.Integer;
35
36 private import  tango.io.model.IConduit : OutputStream;
37
38 version(WithVariant)
39         private import tango.core.Variant;
40
41 version(WithExtensions)
42         private import tango.text.convert.Extensions;
43 else
44 version (WithDateTime)
45         {
46         private import tango.time.Time;
47         private import tango.text.convert.DateTime;
48         }
49
50
51 /*******************************************************************************
52
53         Platform issues ...
54
55 *******************************************************************************/
56
57 version (GNU)
58         {
59         private import tango.core.Vararg;
60         alias void* Arg;
61         alias va_list ArgList;
62         }
63 else version(LDC)
64         {
65         private import tango.core.Vararg;
66         alias void* Arg;
67         alias va_list ArgList;
68         }
69 else version(DigitalMars)
70         {
71         private import tango.core.Vararg;
72         alias void* Arg;
73         alias va_list ArgList;
74
75         version(X86_64) version = DigitalMarsX64;
76         }
77      else
78         {
79         alias void* Arg;
80         alias void* ArgList;
81         }
82
83 /*******************************************************************************
84
85         Contains methods for replacing format items in a string with string
86         equivalents of each argument.
87
88 *******************************************************************************/
89
90 class Layout(T)
91 {
92         public alias convert opCall;
93         public alias uint delegate (T[]) Sink;
94
95         static if (is (DateTimeLocale))
96                    private DateTimeLocale* dateTime = &DateTimeDefault;
97
98         /**********************************************************************
99
100                 Return shared instance
101
102                 Note that this is not threadsafe, and that static-ctor
103                 usage doesn't get invoked appropriately (compiler bug)
104
105         **********************************************************************/
106
107         static Layout instance ()
108         {
109                 static Layout common;
110
111                 if (common is null)
112                     common = new Layout!(T);
113                 return common;
114         }
115
116         /**********************************************************************
117
118         **********************************************************************/
119
120         public final T[] sprint (T[] result, T[] formatStr, ...)
121         {
122                 version (DigitalMarsX64)
123                 {
124                     va_list ap;
125
126                     va_start(ap, __va_argsave);
127
128                     scope(exit) va_end(ap);
129
130                     return vprint (result, formatStr, _arguments, ap);
131                 }
132                 else
133                     return vprint (result, formatStr, _arguments, _argptr);
134         }
135
136         /**********************************************************************
137
138         **********************************************************************/
139
140         public final T[] vprint (T[] result, T[] formatStr, TypeInfo[] arguments, ArgList args)
141         {
142                 T*  p = result.ptr;
143                 auto available = result.length;
144
145                 uint sink (T[] s)
146                 {
147                         auto len = s.length;
148                         if (len > available)
149                             len = available;
150
151                         available -= len;
152                         p [0..len] = s[0..len];
153                         p += len;
154                         return len;
155                 }
156
157                 convert (&sink, arguments, args, formatStr);
158                 return result [0 .. cast(size_t) (p-result.ptr)];
159         }
160
161         /**********************************************************************
162
163                 Replaces the _format item in a string with the string
164                 equivalent of each argument.
165
166                 Params:
167                   formatStr  = A string containing _format items.
168                   args       = A list of arguments.
169
170                 Returns: A copy of formatStr in which the items have been
171                 replaced by the string equivalent of the arguments.
172
173                 Remarks: The formatStr parameter is embedded with _format
174                 items of the form: $(BR)$(BR)
175                   {index[,alignment][:_format-string]}$(BR)$(BR)
176                   $(UL $(LI index $(BR)
177                     An integer indicating the element in a list to _format.)
178                   $(LI alignment $(BR)
179                     An optional integer indicating the minimum width. The
180                     result is padded with spaces if the length of the value
181                     is less than alignment.)
182                   $(LI _format-string $(BR)
183                     An optional string of formatting codes.)
184                 )$(BR)
185
186                 The leading and trailing braces are required. To include a
187                 literal brace character, use two leading or trailing brace
188                 characters.$(BR)$(BR)
189                 If formatStr is "{0} bottles of beer on the wall" and the
190                 argument is an int with the value of 99, the return value
191                 will be:$(BR) "99 bottles of beer on the wall".
192
193         **********************************************************************/
194
195         public final T[] convert (T[] formatStr, ...)
196         {
197                 version (DigitalMarsX64)
198                 {
199                     va_list ap;
200
201                     va_start(ap, __va_argsave);
202
203                     scope(exit) va_end(ap);
204
205                     return convert (_arguments, ap, formatStr);
206                 }
207                 else
208                     return convert (_arguments, _argptr, formatStr);
209         }
210
211         /**********************************************************************
212
213         **********************************************************************/
214
215         public final uint convert (Sink sink, T[] formatStr, ...)
216         {
217                 version (DigitalMarsX64)
218                 {
219                     va_list ap;
220
221                     va_start(ap, __va_argsave);
222
223                     scope(exit) va_end(ap);
224
225                     return convert (sink, _arguments, ap, formatStr);
226                 }
227                 else
228                     return convert (sink, _arguments, _argptr, formatStr);
229         }
230
231         /**********************************************************************
232
233             Tentative convert using an OutputStream as sink - may still be
234             removed.
235
236             Since: 0.99.7
237
238         **********************************************************************/
239
240         public final uint convert (OutputStream output, T[] formatStr, ...)
241         {
242                 uint sink (T[] s)
243                 {
244                         return output.write(s);
245                 }
246
247
248                 version (DigitalMarsX64)
249                 {
250                     va_list ap;
251
252                     va_start(ap, __va_argsave);
253
254                     scope(exit) va_end(ap);
255
256                     return convert (&sink, _arguments, ap, formatStr);
257                 }
258                 else
259                     return convert (&sink, _arguments, _argptr, formatStr);
260         }
261
262         /**********************************************************************
263
264         **********************************************************************/
265
266         public final T[] convert (TypeInfo[] arguments, ArgList args, T[] formatStr)
267         {
268                 T[] output;
269
270                 uint sink (T[] s)
271                 {
272                         output ~= s;
273                         return s.length;
274                 }
275
276                 convert (&sink, arguments, args, formatStr);
277                 return output;
278         }
279
280         /**********************************************************************
281
282         **********************************************************************/
283
284         version (old) public final T[] convertOne (T[] result, TypeInfo ti, Arg arg)
285         {
286                 return dispatch (result, null, ti, arg);
287         }
288
289         /**********************************************************************
290
291         **********************************************************************/
292
293         public final uint convert (Sink sink, TypeInfo[] arguments, ArgList args, T[] formatStr)
294         {
295                 assert (formatStr, "null format specifier");
296                 assert (arguments.length < 64, "too many args in Layout.convert");
297
298                 version (GNU)
299                         {
300                         union ArgU {int i; byte b; long l; short s; void[] a;
301                                     real r; float f; double d;
302                                     cfloat cf; cdouble cd; creal cr;}
303
304                         Arg[64] arglist = void;
305                         ArgU[64] storedArgs = void;
306
307                         foreach (i, arg; arguments)
308                                 {
309                                 static if (is(typeof(args.ptr)))
310                                     arglist[i] = args.ptr;
311                                 else
312                                    arglist[i] = args;
313
314                                 /* Since floating point types don't live on
315                                  * the stack, they must be accessed by the
316                                  * correct type. */
317                                 bool converted = false;
318                                 switch (arg.classinfo.name[9])
319                                        {
320                                        case TypeCode.FLOAT, TypeCode.IFLOAT:
321                                             storedArgs[i].f = va_arg!(float)(args);
322                                             arglist[i] = &(storedArgs[i].f);
323                                             converted = true;
324                                             break;
325
326                                        case TypeCode.CFLOAT:
327                                             storedArgs[i].cf = va_arg!(cfloat)(args);
328                                             arglist[i] = &(storedArgs[i].cf);
329                                             converted = true;
330                                             break;
331
332                                        case TypeCode.DOUBLE, TypeCode.IDOUBLE:
333                                             storedArgs[i].d = va_arg!(double)(args);
334                                             arglist[i] = &(storedArgs[i].d);
335                                             converted = true;
336                                             break;
337
338                                        case TypeCode.CDOUBLE:
339                                             storedArgs[i].cd = va_arg!(cdouble)(args);
340                                             arglist[i] = &(storedArgs[i].cd);
341                                             converted = true;
342                                             break;
343
344                                        case TypeCode.REAL, TypeCode.IREAL:
345                                             storedArgs[i].r = va_arg!(real)(args);
346                                             arglist[i] = &(storedArgs[i].r);
347                                             converted = true;
348                                             break;
349
350                                        case TypeCode.CREAL:
351                                             storedArgs[i].cr = va_arg!(creal)(args);
352                                             arglist[i] = &(storedArgs[i].cr);
353                                             converted = true;
354                                             break;
355
356                                        default:
357                                             break;
358                                         }
359                                 if (! converted)
360                                    {
361                                    switch (arg.tsize)
362                                           {
363                                           case 1:
364                                                storedArgs[i].b = va_arg!(byte)(args);
365                                                arglist[i] = &(storedArgs[i].b);
366                                                break;
367                                           case 2:
368                                                storedArgs[i].s = va_arg!(short)(args);
369                                                arglist[i] = &(storedArgs[i].s);
370                                                break;
371                                           case 4:
372                                                storedArgs[i].i = va_arg!(int)(args);
373                                                arglist[i] = &(storedArgs[i].i);
374                                                break;
375                                           case 8:
376                                                storedArgs[i].l = va_arg!(long)(args);
377                                                arglist[i] = &(storedArgs[i].l);
378                                                break;
379                                           case 16:
380                                                assert((void[]).sizeof==16,"Structure size not supported");
381                                                storedArgs[i].a = va_arg!(void[])(args);
382                                                arglist[i] = &(storedArgs[i].a);
383                                                break;
384                                           default:
385                                                assert (false, "Unknown size: " ~ Integer.toString (arg.tsize));
386                                           }
387                                    }
388                                 }
389                         }
390                     else version(DigitalMarsX64)
391                     {
392                         Arg[64] arglist = void;
393                         void[] buffer;
394                         uint len = 0;
395
396                         foreach(i, argType; arguments)
397                             len +=  (argType.tsize + size_t.sizeof - 1) & ~ (size_t.sizeof - 1);
398
399                         buffer.length = len;
400                         len = 0;
401                         foreach(i, argType; arguments)
402                         {
403                             //printf("type: %s\n", argType.classinfo.name.ptr);
404
405                             va_arg(args, argType, buffer.ptr+len);
406
407                             if(argType.classinfo.name.length != 25 && argType.classinfo.name[9] == TypeCode.ARRAY &&
408                                 (argType.classinfo.name[10] == TypeCode.USHORT ||
409                                 argType.classinfo.name[10] == TypeCode.SHORT))
410                                 {
411                                     printf("Warning: (u)short[] is broken for varargs in x86_64");
412                                     // simply disable the array for now
413                                     (cast(short[]*) (buffer.ptr+len)).length = 0;
414                                 }
415
416                             arglist[i] = &buffer[len];
417
418                             len+= (argType.tsize + size_t.sizeof - 1) & ~ (size_t.sizeof - 1);
419                         }
420
421                         scope (exit) delete buffer;
422                     }
423                     else
424                     {
425                         Arg[64] arglist = void;
426                         foreach (i, arg; arguments)
427                                 {
428                                 arglist[i] = args;
429                                 args += (arg.tsize + size_t.sizeof - 1) & ~ (size_t.sizeof - 1);
430                                 }
431                     }
432                 return parse (formatStr, arguments, arglist, sink);
433         }
434
435         /**********************************************************************
436
437                 Parse the format-string, emitting formatted args and text
438                 fragments as we go
439
440         **********************************************************************/
441
442         private uint parse (T[] layout, TypeInfo[] ti, Arg[] args, Sink sink)
443         {
444                 T[512] result = void;
445                 int length, nextIndex;
446
447
448                 T* s = layout.ptr;
449                 T* fragment = s;
450                 T* end = s + layout.length;
451
452                 while (true)
453                       {
454                       while (s < end && *s != '{')
455                              ++s;
456
457                       // emit fragment
458                       length += sink (fragment [0 .. cast(size_t) (s - fragment)]);
459
460                       // all done?
461                       if (s is end)
462                           break;
463
464                       // check for "{{" and skip if so
465                       if (*++s is '{')
466                          {
467                          fragment = s++;
468                          continue;
469                          }
470
471                       int index = 0;
472                       bool indexed = false;
473
474                       // extract index
475                       while (*s >= '0' && *s <= '9')
476                             {
477                             index = index * 10 + *s++ -'0';
478                             indexed = true;
479                             }
480
481                       // skip spaces
482                       while (s < end && *s is ' ')
483                              ++s;
484
485                       bool crop;
486                       bool left;
487                       bool right;
488                       int  width;
489
490                       // has minimum or maximum width?
491                       if (*s is ',' || *s is '.')
492                          {
493                          if (*s is '.')
494                              crop = true;
495
496                          while (++s < end && *s is ' ') {}
497                          if (*s is '-')
498                             {
499                             left = true;
500                             ++s;
501                             }
502                          else
503                             right = true;
504
505                          // get width
506                          while (*s >= '0' && *s <= '9')
507                                 width = width * 10 + *s++ -'0';
508
509                          // skip spaces
510                          while (s < end && *s is ' ')
511                                 ++s;
512                          }
513
514                       T[] format;
515
516                       // has a format string?
517                       if (*s is ':' && s < end)
518                          {
519                          T* fs = ++s;
520
521                          // eat everything up to closing brace
522                          while (s < end && *s != '}')
523                                 ++s;
524                          format = fs [0 .. cast(size_t) (s - fs)];
525                          }
526
527                       // insist on a closing brace
528                       if (*s != '}')
529                          {
530                          length += sink ("{malformed format}");
531                          continue;
532                          }
533
534                       // check for default index & set next default counter
535                       if (! indexed)
536                             index = nextIndex;
537                       nextIndex = index + 1;
538
539                       // next char is start of following fragment
540                       fragment = ++s;
541
542                       // handle alignment
543                       void emit (T[] str)
544                       {
545                                 int padding = width - str.length;
546
547                                 if (crop)
548                                    {
549                                    if (padding < 0)
550                                       {
551                                       if (left)
552                                          {
553                                          length += sink ("...");
554                                          length += sink (Utf.cropLeft (str[-padding..$]));
555                                          }
556                                       else
557                                          {
558                                          length += sink (Utf.cropRight (str[0..width]));
559                                          length += sink ("...");
560                                          }
561                                       }
562                                    else
563                                        length += sink (str);
564                                    }
565                                 else
566                                    {
567                                    // if right aligned, pad out with spaces
568                                    if (right && padding > 0)
569                                        length += spaces (sink, padding);
570
571                                    // emit formatted argument
572                                    length += sink (str);
573
574                                    // finally, pad out on right
575                                    if (left && padding > 0)
576                                        length += spaces (sink, padding);
577                                    }
578                       }
579
580                       // an astonishing number of typehacks needed to handle arrays :(
581                       void process (TypeInfo _ti, Arg _arg)
582                       {
583                                 // Because Variants can contain AAs (and maybe
584                                 // even static arrays someday), we need to
585                                 // process them here.
586 version (WithVariant)
587 {
588                                 if (_ti is typeid(Variant))
589                                    {
590                                    // Unpack the variant and forward
591                                    auto vptr = cast(Variant*)_arg;
592                                    auto innerTi = vptr.type;
593                                    auto innerArg = vptr.ptr;
594                                    process (innerTi, innerArg);
595                                    }
596 }
597                                 if (_ti.classinfo.name.length is 20 && _ti.classinfo.name[9..$] == "StaticArray" )
598                                    {
599                                    auto tiStat = cast(TypeInfo_StaticArray)_ti;
600                                    auto p = _arg;
601                                    length += sink ("[");
602                                    for (int i = 0; i < tiStat.len; i++)
603                                        {
604                                        if (p !is _arg )
605                                            length += sink (", ");
606                                        process (tiStat.value, p);
607                                        p += tiStat.tsize/tiStat.len;
608                                        }
609                                    length += sink ("]");
610                                    }
611                                 else
612                                 if (_ti.classinfo.name.length is 25 && _ti.classinfo.name[9..$] == "AssociativeArray")
613                                    {
614                                    auto tiAsso = cast(TypeInfo_AssociativeArray)_ti;
615                                    auto tiKey = tiAsso.key;
616                                    auto tiVal = tiAsso.next();
617
618                                    // the knowledge of the internal k/v storage is used
619                                    // so this might break if, that internal storage changes
620                                    alias ubyte AV; // any type for key, value might be ok, the sizes are corrected later
621                                    alias ubyte AK;
622                                    auto aa = *cast(AV[AK]*) _arg;
623
624                                    length += sink ("{");
625                                    bool first = true;
626
627                                    size_t roundUp (size_t tsize)
628                                    {
629                                         //return (sz + (void*).sizeof -1) & ~((void*).sizeof - 1);
630
631                                         version (X86_64)
632                                             // Size of key needed to align value on 16 bytes
633                                             return (tsize + 15) & ~(15);
634                                         else
635                                             return (tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
636                                    }
637
638                                    foreach (ref v; aa)
639                                            {
640                                            // the key is befor the value, so substrace with fixed key size from above
641                                            auto pk = cast(Arg)( &v - roundUp(AK.sizeof));
642                                            // now the real value pos is plus the real key size
643                                            auto pv = cast(Arg)(pk + roundUp(tiKey.tsize()));
644
645                                            if (!first)
646                                                 length += sink (", ");
647                                            process (tiKey, pk);
648                                            length += sink (" => ");
649                                            process (tiVal, pv);
650                                            first = false;
651                                            }
652                                    length += sink ("}");
653                                    }
654                                 else
655                                 if (_ti.classinfo.name[9] is TypeCode.ARRAY)
656                                    {
657                                    if (_ti is typeid(char[]))
658                                        emit (Utf.fromString8 (*cast(char[]*) _arg, result));
659                                    else
660                                    if (_ti is typeid(wchar[]))
661                                        emit (Utf.fromString16 (*cast(wchar[]*) _arg, result));
662                                    else
663                                    if (_ti is typeid(dchar[]))
664                                        emit (Utf.fromString32 (*cast(dchar[]*) _arg, result));
665                                    else
666                                       {
667                                       // for all non string array types (including char[][])
668                                       auto arr = *cast(void[]*)_arg;
669                                       auto len = arr.length;
670                                       auto ptr = cast(Arg) arr.ptr;
671                                       auto elTi = _ti.next();
672                                       auto size = elTi.tsize();
673                                       length += sink ("[");
674                                       while (len > 0)
675                                             {
676                                             if (ptr !is arr.ptr)
677                                                 length += sink (", ");
678                                             process (elTi, ptr);
679                                             len -= 1;
680                                             ptr += size;
681                                             }
682                                       length += sink ("]");
683                                       }
684                                    }
685                                 else
686                                    // the standard processing
687                                    emit (dispatch (result, format, _ti, _arg));
688                       }
689
690
691                       // process this argument
692                       if (index >= ti.length)
693                           emit ("{invalid index}");
694                       else
695                          process (ti[index], args[index]);
696                       }
697                 return length;
698         }
699
700         /***********************************************************************
701
702         ***********************************************************************/
703
704         private T[] dispatch (T[] result, T[] format, TypeInfo type, Arg p)
705         {
706                 switch (type.classinfo.name[9])
707                        {
708                        case TypeCode.BOOL:
709                             static T[] t = "true";
710                             static T[] f = "false";
711                             return (*cast(bool*) p) ? t : f;
712
713                        case TypeCode.BYTE:
714                             return integer (result, *cast(byte*) p, format, ubyte.max);
715
716                        case TypeCode.VOID:
717                        case TypeCode.UBYTE:
718                             return integer (result, *cast(ubyte*) p, format, ubyte.max, "u");
719
720                        case TypeCode.SHORT:
721                             return integer (result, *cast(short*) p, format, ushort.max);
722
723                        case TypeCode.USHORT:
724                             return integer (result, *cast(ushort*) p, format, ushort.max, "u");
725
726                        case TypeCode.INT:
727                             return integer (result, *cast(int*) p, format, uint.max);
728
729                        case TypeCode.UINT:
730                             return integer (result, *cast(uint*) p, format, uint.max, "u");
731
732                        case TypeCode.ULONG:
733                             return integer (result, *cast(long*) p, format, ulong.max, "u");
734
735                        case TypeCode.LONG:
736                             return integer (result, *cast(long*) p, format, ulong.max);
737
738                        case TypeCode.FLOAT:
739                             return floater (result, *cast(float*) p, format);
740
741                        case TypeCode.IFLOAT:
742                             return imaginary (result, *cast(ifloat*) p, format);
743
744                        case TypeCode.IDOUBLE:
745                             return imaginary (result, *cast(idouble*) p, format);
746
747                        case TypeCode.IREAL:
748                            return imaginary (result, *cast(ireal*) p, format);
749
750                        case TypeCode.CFLOAT:
751                             return complex (result, *cast(cfloat*) p, format);
752
753                        case TypeCode.CDOUBLE:
754                             return complex (result, *cast(cdouble*) p, format);
755
756                        case TypeCode.CREAL:
757                             return complex (result, *cast(creal*) p, format);
758
759                        case TypeCode.DOUBLE:
760                             return floater (result, *cast(double*) p, format);
761
762                        case TypeCode.REAL:
763                             return floater (result, *cast(real*) p, format);
764
765                        case TypeCode.CHAR:
766                             return Utf.fromString8 ((cast(char*) p)[0..1], result);
767
768                        case TypeCode.WCHAR:
769                             return Utf.fromString16 ((cast(wchar*) p)[0..1], result);
770
771                        case TypeCode.DCHAR:
772                             return Utf.fromString32 ((cast(dchar*) p)[0..1], result);
773
774                        case TypeCode.POINTER:
775                             return integer (result, *cast(size_t*) p, format, size_t.max, "x");
776
777                        case TypeCode.CLASS:
778                             auto c = *cast(Object*) p;
779                             if (c)
780                                 return Utf.fromString8 (c.toString, result);
781                             break;
782
783                        case TypeCode.STRUCT:
784                             auto s = cast(TypeInfo_Struct) type;
785                             if (s.xtoString)
786                                {
787                                char[] delegate() toString;
788                                toString.ptr = p;
789                                toString.funcptr = cast(char[] function())s.xtoString;
790                                return Utf.fromString8 (toString(), result);
791                                }
792                             goto default;
793
794                        case TypeCode.INTERFACE:
795                             auto x = *cast(void**) p;
796                             if (x)
797                                {
798                                auto pi = **cast(Interface ***) x;
799                                auto o = cast(Object)(*cast(void**)p - pi.offset);
800                                return Utf.fromString8 (o.toString, result);
801                                }
802                             break;
803
804                        case TypeCode.ENUM:
805                             return dispatch (result, format, (cast(TypeInfo_Enum) type).base, p);
806
807                        case TypeCode.TYPEDEF:
808                             return dispatch (result, format, (cast(TypeInfo_Typedef) type).base, p);
809
810                        default:
811                             return unknown (result, format, type, p);
812                        }
813
814                 return cast(T[]) "{null}";
815         }
816
817         /**********************************************************************
818
819                 handle "unknown-type" errors
820
821         **********************************************************************/
822
823         protected T[] unknown (T[] result, T[] format, TypeInfo type, Arg p)
824         {
825         version (WithExtensions)
826                 {
827                 result = Extensions!(T).run (type, result, p, format);
828                 return (result) ? result :
829                        "{unhandled argument type: " ~ Utf.fromString8 (type.toString, result) ~ "}";
830                 }
831              else
832                 version (WithDateTime)
833                 {
834                 if (type is typeid(Time))
835                    {
836                    static if (is (T == char))
837                               return dateTime.format(result, *cast(Time*) p, format);
838                           else
839                              {
840                              // TODO: this needs to be cleaned up
841                              char[128] tmp0 = void;
842                              char[128] tmp1 = void;
843                              return Utf.fromString8(dateTime.format(tmp0, *cast(Time*) p, Utf.toString(format, tmp1)), result);
844                              }
845                    }
846                 }
847                 return "{unhandled argument type: " ~ Utf.fromString8 (type.toString, result) ~ "}";
848         }
849
850         /**********************************************************************
851
852                 Format an integer value
853
854         **********************************************************************/
855
856         protected T[] integer (T[] output, long v, T[] format, ulong mask = ulong.max, T[] def="d")
857         {
858                 if (format.length is 0)
859                     format = def;
860                 if (format[0] != 'd')
861                     v &= mask;
862
863                 return Integer.format (output, v, format);
864         }
865
866         /**********************************************************************
867
868                 format a floating-point value. Defaults to 2 decimal places
869
870         **********************************************************************/
871
872         protected T[] floater (T[] output, real v, T[] format)
873         {
874                 uint dec = 2,
875                      exp = 10;
876                 bool pad = true;
877
878                 for (auto p=format.ptr, e=p+format.length; p < e; ++p)
879                      switch (*p)
880                             {
881                             case '.':
882                                  pad = false;
883                                  break;
884                             case 'e':
885                             case 'E':
886                                  exp = 0;
887                                  break;
888                             case 'x':
889                             case 'X':
890                                  double d = v;
891                                  return integer (output, *cast(long*) &d, "x#");
892                             default:
893                                  auto c = *p;
894                                  if (c >= '0' && c <= '9')
895                                     {
896                                     dec = c - '0', c = p[1];
897                                     if (c >= '0' && c <= '9' && ++p < e)
898                                         dec = dec * 10 + c - '0';
899                                     }
900                                  break;
901                             }
902
903                 return Float.format (output, v, dec, exp, pad);
904         }
905
906         /**********************************************************************
907
908         **********************************************************************/
909
910         private void error (char[] msg)
911         {
912                 throw new IllegalArgumentException (msg);
913         }
914
915         /**********************************************************************
916
917         **********************************************************************/
918
919         private uint spaces (Sink sink, int count)
920         {
921                 size_t ret;
922
923                 static const T[32] Spaces = ' ';
924                 while (count > Spaces.length)
925                       {
926                       ret += sink (Spaces);
927                       count -= Spaces.length;
928                       }
929                 return ret + sink (Spaces[0..count]);
930         }
931
932         /**********************************************************************
933
934                 format an imaginary value
935
936         **********************************************************************/
937
938         private T[] imaginary (T[] result, ireal val, T[] format)
939         {
940                 return floatingTail (result, val.im, format, "*1i");
941         }
942
943         /**********************************************************************
944
945                 format a complex value
946
947         **********************************************************************/
948
949         private T[] complex (T[] result, creal val, T[] format)
950         {
951                 static bool signed (real x)
952                 {
953                         static if (real.sizeof is 4)
954                                    return ((*cast(uint *)&x) & 0x8000_0000) != 0;
955                         else
956                         static if (real.sizeof is 8)
957                                    return ((*cast(ulong *)&x) & 0x8000_0000_0000_0000) != 0;
958                                else
959                                   {
960                                   auto pe = cast(ubyte *)&x;
961                                   return (pe[9] & 0x80) != 0;
962                                   }
963                 }
964                 static T[] plus = "+";
965
966                 auto len = floatingTail (result, val.re, format, signed(val.im) ? null : plus).length;
967                 return result [0 .. len + floatingTail (result[len..$], val.im, format, "*1i").length];
968         }
969
970         /**********************************************************************
971
972                 formats a floating-point value, and appends a tail to it
973
974         **********************************************************************/
975
976         private T[] floatingTail (T[] result, real val, T[] format, T[] tail)
977         {
978                 assert (result.length > tail.length);
979
980                 auto res = floater (result[0..$-tail.length], val, format);
981                 auto len=res.length;
982                 if (res.ptr!is result.ptr)
983                     result[0..len]=res;
984                 result [len .. len + tail.length] = tail;
985                 return result [0 .. len + tail.length];
986         }
987 }
988
989
990 /*******************************************************************************
991
992 *******************************************************************************/
993
994 private enum TypeCode
995 {
996         EMPTY = 0,
997         VOID = 'v',
998         BOOL = 'b',
999         UBYTE = 'h',
1000         BYTE = 'g',
1001         USHORT = 't',
1002         SHORT = 's',
1003         UINT = 'k',
1004         INT = 'i',
1005         ULONG = 'm',
1006         LONG = 'l',
1007         REAL = 'e',
1008         FLOAT = 'f',
1009         DOUBLE = 'd',
1010         CHAR = 'a',
1011         WCHAR = 'u',
1012         DCHAR = 'w',
1013         ARRAY = 'A',
1014         CLASS = 'C',
1015         STRUCT = 'S',
1016         ENUM = 'E',
1017         CONST = 'x',
1018         INVARIANT = 'y',
1019         DELEGATE = 'D',
1020         FUNCTION = 'F',
1021         POINTER = 'P',
1022         TYPEDEF = 'T',
1023         INTERFACE = 'I',
1024         CFLOAT = 'q',
1025         CDOUBLE = 'r',
1026         CREAL = 'c',
1027         IFLOAT = 'o',
1028         IDOUBLE = 'p',
1029         IREAL = 'j'
1030 }
1031
1032
1033
1034 /*******************************************************************************
1035
1036 *******************************************************************************/
1037 import tango.stdc.stdio : printf;
1038 debug (UnitTest)
1039 {
1040         unittest
1041         {
1042         auto Formatter = Layout!(char).instance;
1043
1044         // basic layout tests
1045         assert( Formatter( "abc" ) == "abc" );
1046         assert( Formatter( "{0}", 1 ) == "1" );
1047         assert( Formatter( "{0}", -1 ) == "-1" );
1048
1049         assert( Formatter( "{}", 1 ) == "1" );
1050         assert( Formatter( "{} {}", 1, 2) == "1 2" );
1051         assert( Formatter( "{} {0} {}", 1, 3) == "1 1 3" );
1052         assert( Formatter( "{} {0} {} {}", 1, 3) == "1 1 3 {invalid index}" );
1053         assert( Formatter( "{} {0} {} {:x}", 1, 3) == "1 1 3 {invalid index}" );
1054
1055         assert( Formatter( "{0}", true ) == "true" , Formatter( "{0}", true ));
1056         assert( Formatter( "{0}", false ) == "false" );
1057
1058         assert( Formatter( "{0}", cast(byte)-128 ) == "-128" );
1059         assert( Formatter( "{0}", cast(byte)127 ) == "127" );
1060         assert( Formatter( "{0}", cast(ubyte)255 ) == "255" );
1061
1062         assert( Formatter( "{0}", cast(short)-32768  ) == "-32768" );
1063         assert( Formatter( "{0}", cast(short)32767 ) == "32767" );
1064         assert( Formatter( "{0}", cast(ushort)65535 ) == "65535" );
1065         assert( Formatter( "{0:x4}", cast(ushort)0xafe ) == "0afe" );
1066         assert( Formatter( "{0:X4}", cast(ushort)0xafe ) == "0AFE" );
1067
1068         assert( Formatter( "{0}", -2147483648 ) == "-2147483648" );
1069         assert( Formatter( "{0}", 2147483647 ) == "2147483647" );
1070         assert( Formatter( "{0}", 4294967295 ) == "4294967295" );
1071
1072         // large integers
1073         assert( Formatter( "{0}", -9223372036854775807L) == "-9223372036854775807" );
1074         assert( Formatter( "{0}", 0x8000_0000_0000_0000L) == "9223372036854775808" );
1075         assert( Formatter( "{0}", 9223372036854775807L ) == "9223372036854775807" );
1076         assert( Formatter( "{0:X}", 0xFFFF_FFFF_FFFF_FFFF) == "FFFFFFFFFFFFFFFF" );
1077         assert( Formatter( "{0:x}", 0xFFFF_FFFF_FFFF_FFFF) == "ffffffffffffffff" );
1078         assert( Formatter( "{0:x}", 0xFFFF_1234_FFFF_FFFF) == "ffff1234ffffffff" );
1079         assert( Formatter( "{0:x19}", 0x1234_FFFF_FFFF) == "00000001234ffffffff" );
1080         assert( Formatter( "{0}", 18446744073709551615UL ) == "18446744073709551615" );
1081         assert( Formatter( "{0}", 18446744073709551615UL ) == "18446744073709551615" );
1082
1083         // fragments before and after
1084         assert( Formatter( "d{0}d", "s" ) == "dsd" );
1085         assert( Formatter( "d{0}d", "1234567890" ) == "d1234567890d" );
1086
1087         // brace escaping
1088         assert( Formatter( "d{0}d", "<string>" ) == "d<string>d");
1089         assert( Formatter( "d{{0}d", "<string>" ) == "d{0}d");
1090         assert( Formatter( "d{{{0}d", "<string>" ) == "d{<string>d");
1091         assert( Formatter( "d{0}}d", "<string>" ) == "d<string>}d");
1092
1093         // hex conversions, where width indicates leading zeroes
1094         assert( Formatter( "{0:x}", 0xafe0000 ) == "afe0000" );
1095         assert( Formatter( "{0:x7}", 0xafe0000 ) == "afe0000" );
1096         assert( Formatter( "{0:x8}", 0xafe0000 ) == "0afe0000" );
1097         assert( Formatter( "{0:X8}", 0xafe0000 ) == "0AFE0000" );
1098         assert( Formatter( "{0:X9}", 0xafe0000 ) == "00AFE0000" );
1099         assert( Formatter( "{0:X13}", 0xafe0000 ) == "000000AFE0000" );
1100         assert( Formatter( "{0:x13}", 0xafe0000 ) == "000000afe0000" );
1101
1102         // decimal width
1103         assert( Formatter( "{0:d6}", 123 ) == "000123" );
1104         assert( Formatter( "{0,7:d6}", 123 ) == " 000123" );
1105         assert( Formatter( "{0,-7:d6}", 123 ) == "000123 " );
1106
1107         // width & sign combinations
1108         assert( Formatter( "{0:d7}", -123 ) == "-0000123" );
1109         assert( Formatter( "{0,7:d6}", 123 ) == " 000123" );
1110         assert( Formatter( "{0,7:d7}", -123 ) == "-0000123" );
1111         assert( Formatter( "{0,8:d7}", -123 ) == "-0000123" );
1112         assert( Formatter( "{0,5:d7}", -123 ) == "-0000123" );
1113
1114         // Negative numbers in various bases
1115         assert( Formatter( "{:b}", cast(byte) -1 ) == "11111111" );
1116         assert( Formatter( "{:b}", cast(short) -1 ) == "1111111111111111" );
1117         assert( Formatter( "{:b}", cast(int) -1 )
1118                 == "11111111111111111111111111111111" );
1119         assert( Formatter( "{:b}", cast(long) -1 )
1120                 == "1111111111111111111111111111111111111111111111111111111111111111" );
1121
1122         assert( Formatter( "{:o}", cast(byte) -1 ) == "377" );
1123         assert( Formatter( "{:o}", cast(short) -1 ) == "177777" );
1124         assert( Formatter( "{:o}", cast(int) -1 ) == "37777777777" );
1125         assert( Formatter( "{:o}", cast(long) -1 ) == "1777777777777777777777" );
1126
1127         assert( Formatter( "{:d}", cast(byte) -1 ) == "-1" );
1128         assert( Formatter( "{:d}", cast(short) -1 ) == "-1" );
1129         assert( Formatter( "{:d}", cast(int) -1 ) == "-1" );
1130         assert( Formatter( "{:d}", cast(long) -1 ) == "-1" );
1131
1132         assert( Formatter( "{:x}", cast(byte) -1 ) == "ff" );
1133         assert( Formatter( "{:x}", cast(short) -1 ) == "ffff" );
1134         assert( Formatter( "{:x}", cast(int) -1 ) == "ffffffff" );
1135         assert( Formatter( "{:x}", cast(long) -1 ) == "ffffffffffffffff" );
1136
1137         // argument index
1138         assert( Formatter( "a{0}b{1}c{2}", "x", "y", "z" ) == "axbycz" );
1139         assert( Formatter( "a{2}b{1}c{0}", "x", "y", "z" ) == "azbycx" );
1140         assert( Formatter( "a{1}b{1}c{1}", "x", "y", "z" ) == "aybycy" );
1141
1142         // alignment does not restrict the length
1143         assert( Formatter( "{0,5}", "hellohello" ) == "hellohello" );
1144
1145         // alignment fills with spaces
1146         assert( Formatter( "->{0,-10}<-", "hello" ) == "->hello     <-" );
1147         assert( Formatter( "->{0,10}<-", "hello" ) == "->     hello<-" );
1148         assert( Formatter( "->{0,-10}<-", 12345 ) == "->12345     <-" );
1149         assert( Formatter( "->{0,10}<-", 12345 ) == "->     12345<-" );
1150
1151         // chop at maximum specified length; insert ellipses when chopped
1152         assert( Formatter( "->{.5}<-", "hello" ) == "->hello<-" );
1153         assert( Formatter( "->{.4}<-", "hello" ) == "->hell...<-" );
1154         assert( Formatter( "->{.-3}<-", "hello" ) == "->...llo<-" );
1155
1156         // width specifier indicates number of decimal places
1157         assert( Formatter( "{0:f}", 1.23f ) == "1.23" );
1158         assert( Formatter( "{0:f4}", 1.23456789L ) == "1.2346" );
1159         assert( Formatter( "{0:e4}", 0.0001) == "1.0000e-04");
1160
1161         assert( Formatter( "{0:f}", 1.23f*1i ) == "1.23*1i");
1162         assert( Formatter( "{0:f4}", 1.23456789L*1i ) == "1.2346*1i" );
1163         assert( Formatter( "{0:e4}", 0.0001*1i) == "1.0000e-04*1i");
1164
1165         assert( Formatter( "{0:f}", 1.23f+1i ) == "1.23+1.00*1i" );
1166         assert( Formatter( "{0:f4}", 1.23456789L+1i ) == "1.2346+1.0000*1i" );
1167         assert( Formatter( "{0:e4}", 0.0001+1i) == "1.0000e-04+1.0000e+00*1i");
1168         assert( Formatter( "{0:f}", 1.23f-1i ) == "1.23-1.00*1i" );
1169         assert( Formatter( "{0:f4}", 1.23456789L-1i ) == "1.2346-1.0000*1i" );
1170         assert( Formatter( "{0:e4}", 0.0001-1i) == "1.0000e-04-1.0000e+00*1i");
1171
1172         // 'f.' & 'e.' format truncates zeroes from floating decimals
1173         assert( Formatter( "{:f4.}", 1.230 ) == "1.23" );
1174         assert( Formatter( "{:f6.}", 1.230 ) == "1.23" );
1175         assert( Formatter( "{:f1.}", 1.230 ) == "1.2" );
1176         assert( Formatter( "{:f.}", 1.233 ) == "1.23" );
1177         assert( Formatter( "{:f.}", 1.237 ) == "1.24" );
1178         assert( Formatter( "{:f.}", 1.000 ) == "1" );
1179         assert( Formatter( "{:f2.}", 200.001 ) == "200");
1180
1181         // array output
1182         int[] a = [ 51, 52, 53, 54, 55 ];
1183         assert( Formatter( "{}", a ) == "[51, 52, 53, 54, 55]" );
1184         assert( Formatter( "{:x}", a ) == "[33, 34, 35, 36, 37]" );
1185         assert( Formatter( "{,-4}", a ) == "[51  , 52  , 53  , 54  , 55  ]" );
1186         assert( Formatter( "{,4}", a ) == "[  51,   52,   53,   54,   55]" );
1187         int[][] b = [ [ 51, 52 ], [ 53, 54, 55 ] ];
1188         assert( Formatter( "{}", b ) == "[[51, 52], [53, 54, 55]]" );
1189
1190         char[1024] static_buffer;
1191         static_buffer[0..10] = "1234567890";
1192
1193         assert (Formatter( "{}", static_buffer[0..10]) == "1234567890");
1194
1195         version(X86)
1196         {
1197             ushort[3] c = [ cast(ushort)51, 52, 53 ];
1198             assert( Formatter( "{}", c ) == "[51, 52, 53]" );
1199         }
1200
1201         // integer AA
1202         ushort[long] d;
1203         d[234] = 2;
1204         d[345] = 3;
1205
1206         assert( Formatter( "{}", d ) == "{234 => 2, 345 => 3}" ||
1207                 Formatter( "{}", d ) == "{345 => 3, 234 => 2}");
1208
1209         // bool/string AA
1210         bool[char[]] e;
1211         e[ "key".dup ] = true;
1212         e[ "value".dup ] = false;
1213         assert( Formatter( "{}", e ) == "{key => true, value => false}" ||
1214                 Formatter( "{}", e ) == "{value => false, key => true}");
1215
1216         // string/double AA
1217         char[][ double ] f;
1218         f[ 1.0 ] = "one".dup;
1219         f[ 3.14 ] = "PI".dup;
1220         assert( Formatter( "{}", f ) == "{1.00 => one, 3.14 => PI}" ||
1221                 Formatter( "{}", f ) == "{3.14 => PI, 1.00 => one}");
1222         }
1223 }
1224
1225
1226
1227 debug (Layout)
1228 {
1229         import tango.io.Console;
1230
1231         static if (is (typeof(Time)))
1232                    import tango.time.WallClock;
1233
1234         void main ()
1235         {
1236                 auto layout = Layout!(char).instance;
1237
1238                 layout.convert (Cout.stream, "hi {}", "there\n");
1239
1240                 Cout (layout.sprint (new char[1], "hi")).newline;
1241                 Cout (layout.sprint (new char[10], "{.4}", "hello")).newline;
1242                 Cout (layout.sprint (new char[10], "{.-4}", "hello")).newline;
1243
1244                 Cout (layout ("{:f1}", 3.0)).newline;
1245                 Cout (layout ("{:g}", 3.00)).newline;
1246                 Cout (layout ("{:f1}", -0.0)).newline;
1247                 Cout (layout ("{:g1}", -0.0)).newline;
1248                 Cout (layout ("{:d2}", 56)).newline;
1249                 Cout (layout ("{:d4}", cast(byte) -56)).newline;
1250                 Cout (layout ("{:f4}", 1.0e+12)).newline;
1251                 Cout (layout ("{:f4}", 1.23e-2)).newline;
1252                 Cout (layout ("{:f8}", 3.14159)).newline;
1253                 Cout (layout ("{:e20}", 1.23e-3)).newline;
1254                 Cout (layout ("{:e4.}", 1.23e-07)).newline;
1255                 Cout (layout ("{:.}", 1.2)).newline;
1256                 Cout (layout ("ptr:{}", &layout)).newline;
1257                 Cout (layout ("ulong.max {}", ulong.max)).newline;
1258
1259                 struct S
1260                 {
1261                    char[] toString () {return "foo";}
1262                 }
1263
1264                 S s;
1265                 Cout (layout ("struct: {}", s)).newline;
1266
1267                 static if (is (typeof(Time)))
1268                            Cout (layout ("time: {}", WallClock.now)).newline;
1269         }
1270 }
Note: See TracBrowser for help on using the browser.