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