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