comparison lphobos/std/format.d @ 108:288fe1029e1f trunk

[svn r112] Fixed 'case 1,2,3:' style case statements. Fixed a bunch of bugs with return/break/continue in loops. Fixed support for the DMDFE hidden implicit return value variable. This can be needed for some foreach statements where the loop body is converted to a nested delegate, but also possibly returns from the function. Added std.math to phobos. Added AA runtime support code, done ground work for implementing AAs. Several other bugfixes.
author lindquist
date Tue, 20 Nov 2007 05:29:20 +0100
parents
children 5ce8ab11e75a
comparison
equal deleted inserted replaced
107:3efbcc81ba45 108:288fe1029e1f
1
2 // Written in the D programming language.
3
4 /**
5 * This module implements the workhorse functionality for string and I/O formatting.
6 * It's comparable to C99's vsprintf().
7 *
8 * Macros:
9 * WIKI = Phobos/StdFormat
10 */
11
12 /*
13 * Copyright (C) 2004-2006 by Digital Mars, www.digitalmars.com
14 * Written by Walter Bright
15 * Modified for LLVMDC by Tomas Lindquist Olsen
16 *
17 * This software is provided 'as-is', without any express or implied
18 * warranty. In no event will the authors be held liable for any damages
19 * arising from the use of this software.
20 *
21 * Permission is granted to anyone to use this software for any purpose,
22 * including commercial applications, and to alter it and redistribute it
23 * freely, subject to the following restrictions:
24 *
25 * o The origin of this software must not be misrepresented; you must not
26 * claim that you wrote the original software. If you use this software
27 * in a product, an acknowledgment in the product documentation would be
28 * appreciated but is not required.
29 * o Altered source versions must be plainly marked as such, and must not
30 * be misrepresented as being the original software.
31 * o This notice may not be removed or altered from any source
32 * distribution.
33 */
34
35 module std.format;
36
37 //debug=format; // uncomment to turn on debugging printf's
38
39 import std.stdarg; // caller will need va_list
40
41 private import std.utf;
42 private import std.c.stdlib;
43 private import std.c.string;
44 private import std.string;
45
46 version (Windows)
47 {
48 version (DigitalMars)
49 {
50 version = DigitalMarsC;
51 }
52 }
53
54 version (DigitalMarsC)
55 {
56 // This is DMC's internal floating point formatting function
57 extern (C)
58 {
59 extern char* function(int c, int flags, int precision, real* pdval,
60 char* buf, int* psl, int width) __pfloatfmt;
61 }
62 }
63 else
64 {
65 // Use C99 snprintf
66 extern (C) int snprintf(char* s, size_t n, char* format, ...);
67 }
68
69 /**********************************************************************
70 * Signals a mismatch between a format and its corresponding argument.
71 */
72 class FormatError : Error
73 {
74 private:
75
76 this()
77 {
78 super("std.format");
79 }
80
81 this(char[] msg)
82 {
83 super("std.format " ~ msg);
84 }
85 }
86
87
88 enum Mangle : char
89 {
90 Tvoid = 'v',
91 Tbool = 'b',
92 Tbyte = 'g',
93 Tubyte = 'h',
94 Tshort = 's',
95 Tushort = 't',
96 Tint = 'i',
97 Tuint = 'k',
98 Tlong = 'l',
99 Tulong = 'm',
100 Tfloat = 'f',
101 Tdouble = 'd',
102 Treal = 'e',
103
104 Tifloat = 'o',
105 Tidouble = 'p',
106 Tireal = 'j',
107 Tcfloat = 'q',
108 Tcdouble = 'r',
109 Tcreal = 'c',
110
111 Tchar = 'a',
112 Twchar = 'u',
113 Tdchar = 'w',
114
115 Tarray = 'A',
116 Tsarray = 'G',
117 Taarray = 'H',
118 Tpointer = 'P',
119 Tfunction = 'F',
120 Tident = 'I',
121 Tclass = 'C',
122 Tstruct = 'S',
123 Tenum = 'E',
124 Ttypedef = 'T',
125 Tdelegate = 'D',
126
127 Tconst = 'x',
128 Tinvariant = 'y',
129 }
130
131 // return the TypeInfo for a primitive type and null otherwise.
132 // This is required since for arrays of ints we only have the mangled
133 // char to work from. If arrays always subclassed TypeInfo_Array this
134 // routine could go away.
135 private TypeInfo primitiveTypeInfo(Mangle m)
136 {
137 TypeInfo ti;
138
139 switch (m)
140 {
141 case Mangle.Tvoid:
142 ti = typeid(void);break;
143 case Mangle.Tbool:
144 ti = typeid(bool);break;
145 case Mangle.Tbyte:
146 ti = typeid(byte);break;
147 case Mangle.Tubyte:
148 ti = typeid(ubyte);break;
149 case Mangle.Tshort:
150 ti = typeid(short);break;
151 case Mangle.Tushort:
152 ti = typeid(ushort);break;
153 case Mangle.Tint:
154 ti = typeid(int);break;
155 case Mangle.Tuint:
156 ti = typeid(uint);break;
157 case Mangle.Tlong:
158 ti = typeid(long);break;
159 case Mangle.Tulong:
160 ti = typeid(ulong);break;
161 case Mangle.Tfloat:
162 ti = typeid(float);break;
163 case Mangle.Tdouble:
164 ti = typeid(double);break;
165 case Mangle.Treal:
166 ti = typeid(real);break;
167 case Mangle.Tifloat:
168 ti = typeid(ifloat);break;
169 case Mangle.Tidouble:
170 ti = typeid(idouble);break;
171 case Mangle.Tireal:
172 ti = typeid(ireal);break;
173 /+
174 // No complex in LLVMDC yes
175 case Mangle.Tcfloat:
176 ti = typeid(cfloat);break;
177 case Mangle.Tcdouble:
178 ti = typeid(cdouble);break;
179 case Mangle.Tcreal:
180 ti = typeid(creal);break;
181 +/
182 case Mangle.Tchar:
183 ti = typeid(char);break;
184 case Mangle.Twchar:
185 ti = typeid(wchar);break;
186 case Mangle.Tdchar:
187 ti = typeid(dchar);
188 default:
189 ti = null;
190 }
191 return ti;
192 }
193
194 /************************************
195 * Interprets variadic argument list pointed to by argptr whose types are given
196 * by arguments[], formats them according to embedded format strings in the
197 * variadic argument list, and sends the resulting characters to putc.
198 *
199 * The variadic arguments are consumed in order.
200 * Each is formatted into a sequence of chars, using the default format
201 * specification for its type, and the
202 * characters are sequentially passed to putc.
203 * If a char[], wchar[], or dchar[]
204 * argument is encountered, it is interpreted as a format string. As many
205 * arguments as specified in the format string are consumed and formatted
206 * according to the format specifications in that string and passed to putc. If
207 * there are too few remaining arguments, a FormatError is thrown. If there are
208 * more remaining arguments than needed by the format specification, the default
209 * processing of arguments resumes until they are all consumed.
210 *
211 * Params:
212 * putc = Output is sent do this delegate, character by character.
213 * arguments = Array of TypeInfo's, one for each argument to be formatted.
214 * argptr = Points to variadic argument list.
215 *
216 * Throws:
217 * Mismatched arguments and formats result in a FormatError being thrown.
218 *
219 * Format_String:
220 * <a name="format-string">$(I Format strings)</a>
221 * consist of characters interspersed with
222 * $(I format specifications). Characters are simply copied
223 * to the output (such as putc) after any necessary conversion
224 * to the corresponding UTF-8 sequence.
225 *
226 * A $(I format specification) starts with a '%' character,
227 * and has the following grammar:
228
229 <pre>
230 $(I FormatSpecification):
231 $(B '%%')
232 $(B '%') $(I Flags) $(I Width) $(I Precision) $(I FormatChar)
233
234 $(I Flags):
235 $(I empty)
236 $(B '-') $(I Flags)
237 $(B '+') $(I Flags)
238 $(B '#') $(I Flags)
239 $(B '0') $(I Flags)
240 $(B ' ') $(I Flags)
241
242 $(I Width):
243 $(I empty)
244 $(I Integer)
245 $(B '*')
246
247 $(I Precision):
248 $(I empty)
249 $(B '.')
250 $(B '.') $(I Integer)
251 $(B '.*')
252
253 $(I Integer):
254 $(I Digit)
255 $(I Digit) $(I Integer)
256
257 $(I Digit):
258 $(B '0')
259 $(B '1')
260 $(B '2')
261 $(B '3')
262 $(B '4')
263 $(B '5')
264 $(B '6')
265 $(B '7')
266 $(B '8')
267 $(B '9')
268
269 $(I FormatChar):
270 $(B 's')
271 $(B 'b')
272 $(B 'd')
273 $(B 'o')
274 $(B 'x')
275 $(B 'X')
276 $(B 'e')
277 $(B 'E')
278 $(B 'f')
279 $(B 'F')
280 $(B 'g')
281 $(B 'G')
282 $(B 'a')
283 $(B 'A')
284 </pre>
285 <dl>
286 <dt>$(I Flags)
287 <dl>
288 <dt>$(B '-')
289 <dd>
290 Left justify the result in the field.
291 It overrides any $(B 0) flag.
292
293 <dt>$(B '+')
294 <dd>Prefix positive numbers in a signed conversion with a $(B +).
295 It overrides any $(I space) flag.
296
297 <dt>$(B '#')
298 <dd>Use alternative formatting:
299 <dl>
300 <dt>For $(B 'o'):
301 <dd> Add to precision as necessary so that the first digit
302 of the octal formatting is a '0', even if both the argument
303 and the $(I Precision) are zero.
304 <dt> For $(B 'x') ($(B 'X')):
305 <dd> If non-zero, prefix result with $(B 0x) ($(B 0X)).
306 <dt> For floating point formatting:
307 <dd> Always insert the decimal point.
308 <dt> For $(B 'g') ($(B 'G')):
309 <dd> Do not elide trailing zeros.
310 </dl>
311
312 <dt>$(B '0')
313 <dd> For integer and floating point formatting when not nan or
314 infinity, use leading zeros
315 to pad rather than spaces.
316 Ignore if there's a $(I Precision).
317
318 <dt>$(B ' ')
319 <dd>Prefix positive numbers in a signed conversion with a space.
320 </dl>
321
322 <dt>$(I Width)
323 <dd>
324 Specifies the minimum field width.
325 If the width is a $(B *), the next argument, which must be
326 of type $(B int), is taken as the width.
327 If the width is negative, it is as if the $(B -) was given
328 as a $(I Flags) character.
329
330 <dt>$(I Precision)
331 <dd> Gives the precision for numeric conversions.
332 If the precision is a $(B *), the next argument, which must be
333 of type $(B int), is taken as the precision. If it is negative,
334 it is as if there was no $(I Precision).
335
336 <dt>$(I FormatChar)
337 <dd>
338 <dl>
339 <dt>$(B 's')
340 <dd>The corresponding argument is formatted in a manner consistent
341 with its type:
342 <dl>
343 <dt>$(B bool)
344 <dd>The result is <tt>'true'</tt> or <tt>'false'</tt>.
345 <dt>integral types
346 <dd>The $(B %d) format is used.
347 <dt>floating point types
348 <dd>The $(B %g) format is used.
349 <dt>string types
350 <dd>The result is the string converted to UTF-8.
351 A $(I Precision) specifies the maximum number of characters
352 to use in the result.
353 <dt>classes derived from $(B Object)
354 <dd>The result is the string returned from the class instance's
355 $(B .toString()) method.
356 A $(I Precision) specifies the maximum number of characters
357 to use in the result.
358 <dt>non-string static and dynamic arrays
359 <dd>The result is [s<sub>0</sub>, s<sub>1</sub>, ...]
360 where s<sub>k</sub> is the kth element
361 formatted with the default format.
362 </dl>
363
364 <dt>$(B 'b','d','o','x','X')
365 <dd> The corresponding argument must be an integral type
366 and is formatted as an integer. If the argument is a signed type
367 and the $(I FormatChar) is $(B d) it is converted to
368 a signed string of characters, otherwise it is treated as
369 unsigned. An argument of type $(B bool) is formatted as '1'
370 or '0'. The base used is binary for $(B b), octal for $(B o),
371 decimal
372 for $(B d), and hexadecimal for $(B x) or $(B X).
373 $(B x) formats using lower case letters, $(B X) uppercase.
374 If there are fewer resulting digits than the $(I Precision),
375 leading zeros are used as necessary.
376 If the $(I Precision) is 0 and the number is 0, no digits
377 result.
378
379 <dt>$(B 'e','E')
380 <dd> A floating point number is formatted as one digit before
381 the decimal point, $(I Precision) digits after, the $(I FormatChar),
382 &plusmn;, followed by at least a two digit exponent: $(I d.dddddd)e$(I &plusmn;dd).
383 If there is no $(I Precision), six
384 digits are generated after the decimal point.
385 If the $(I Precision) is 0, no decimal point is generated.
386
387 <dt>$(B 'f','F')
388 <dd> A floating point number is formatted in decimal notation.
389 The $(I Precision) specifies the number of digits generated
390 after the decimal point. It defaults to six. At least one digit
391 is generated before the decimal point. If the $(I Precision)
392 is zero, no decimal point is generated.
393
394 <dt>$(B 'g','G')
395 <dd> A floating point number is formatted in either $(B e) or
396 $(B f) format for $(B g); $(B E) or $(B F) format for
397 $(B G).
398 The $(B f) format is used if the exponent for an $(B e) format
399 is greater than -5 and less than the $(I Precision).
400 The $(I Precision) specifies the number of significant
401 digits, and defaults to six.
402 Trailing zeros are elided after the decimal point, if the fractional
403 part is zero then no decimal point is generated.
404
405 <dt>$(B 'a','A')
406 <dd> A floating point number is formatted in hexadecimal
407 exponential notation 0x$(I h.hhhhhh)p$(I &plusmn;d).
408 There is one hexadecimal digit before the decimal point, and as
409 many after as specified by the $(I Precision).
410 If the $(I Precision) is zero, no decimal point is generated.
411 If there is no $(I Precision), as many hexadecimal digits as
412 necessary to exactly represent the mantissa are generated.
413 The exponent is written in as few digits as possible,
414 but at least one, is in decimal, and represents a power of 2 as in
415 $(I h.hhhhhh)*2<sup>$(I &plusmn;d)</sup>.
416 The exponent for zero is zero.
417 The hexadecimal digits, x and p are in upper case if the
418 $(I FormatChar) is upper case.
419 </dl>
420
421 Floating point NaN's are formatted as $(B nan) if the
422 $(I FormatChar) is lower case, or $(B NAN) if upper.
423 Floating point infinities are formatted as $(B inf) or
424 $(B infinity) if the
425 $(I FormatChar) is lower case, or $(B INF) or $(B INFINITY) if upper.
426 </dl>
427
428 Example:
429
430 -------------------------
431 import std.c.stdio;
432 import std.format;
433
434 void formattedPrint(...)
435 {
436 void putc(char c)
437 {
438 fputc(c, stdout);
439 }
440
441 std.format.doFormat(&putc, _arguments, _argptr);
442 }
443
444 ...
445
446 int x = 27;
447 // prints 'The answer is 27:6'
448 formattedPrint("The answer is %s:", x, 6);
449 ------------------------
450 */
451
452 void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr)
453 { int j;
454 TypeInfo ti;
455 Mangle m;
456 uint flags;
457 int field_width;
458 int precision;
459
460 enum : uint
461 {
462 FLdash = 1,
463 FLplus = 2,
464 FLspace = 4,
465 FLhash = 8,
466 FLlngdbl = 0x20,
467 FL0pad = 0x40,
468 FLprecision = 0x80,
469 }
470
471 static TypeInfo skipCI(TypeInfo valti)
472 {
473 while (1)
474 {
475 if (valti.classinfo.name.length == 18 &&
476 valti.classinfo.name[9..18] == "Invariant")
477 valti = (cast(TypeInfo_Invariant)valti).next;
478 else if (valti.classinfo.name.length == 14 &&
479 valti.classinfo.name[9..14] == "Const")
480 valti = (cast(TypeInfo_Const)valti).next;
481 else
482 break;
483 }
484 return valti;
485 }
486
487 void formatArg(char fc)
488 {
489 bool vbit;
490 ulong vnumber;
491 char vchar;
492 dchar vdchar;
493 Object vobject;
494 real vreal;
495 creal vcreal;
496 Mangle m2;
497 int signed = 0;
498 uint base = 10;
499 int uc;
500 char[ulong.sizeof * 8] tmpbuf; // long enough to print long in binary
501 char* prefix = "";
502 string s;
503
504 void putstr(char[] s)
505 {
506 //printf("flags = x%x\n", flags);
507 int prepad = 0;
508 int postpad = 0;
509 int padding = field_width - (strlen(prefix) + s.length);
510 if (padding > 0)
511 {
512 if (flags & FLdash)
513 postpad = padding;
514 else
515 prepad = padding;
516 }
517
518 if (flags & FL0pad)
519 {
520 while (*prefix)
521 putc(*prefix++);
522 while (prepad--)
523 putc('0');
524 }
525 else
526 {
527 while (prepad--)
528 putc(' ');
529 while (*prefix)
530 putc(*prefix++);
531 }
532
533 foreach (dchar c; s)
534 putc(c);
535
536 while (postpad--)
537 putc(' ');
538 }
539
540 void putreal(real v)
541 {
542 //printf("putreal %Lg\n", vreal);
543
544 switch (fc)
545 {
546 case 's':
547 fc = 'g';
548 break;
549
550 case 'f', 'F', 'e', 'E', 'g', 'G', 'a', 'A':
551 break;
552
553 default:
554 //printf("fc = '%c'\n", fc);
555 Lerror:
556 throw new FormatError("floating");
557 }
558 version (DigitalMarsC)
559 {
560 int sl;
561 char[] fbuf = tmpbuf;
562 if (!(flags & FLprecision))
563 precision = 6;
564 while (1)
565 {
566 sl = fbuf.length;
567 prefix = (*__pfloatfmt)(fc, flags | FLlngdbl,
568 precision, &v, cast(char*)fbuf, &sl, field_width);
569 if (sl != -1)
570 break;
571 sl = fbuf.length * 2;
572 fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl];
573 }
574 putstr(fbuf[0 .. sl]);
575 }
576 else
577 {
578 int sl;
579 char[] fbuf = tmpbuf;
580 char[12] format;
581 format[0] = '%';
582 int i = 1;
583 if (flags & FLdash)
584 format[i++] = '-';
585 if (flags & FLplus)
586 format[i++] = '+';
587 if (flags & FLspace)
588 format[i++] = ' ';
589 if (flags & FLhash)
590 format[i++] = '#';
591 if (flags & FL0pad)
592 format[i++] = '0';
593 format[i + 0] = '*';
594 format[i + 1] = '.';
595 format[i + 2] = '*';
596 format[i + 3] = 'L';
597 format[i + 4] = fc;
598 format[i + 5] = 0;
599 if (!(flags & FLprecision))
600 precision = -1;
601 while (1)
602 { int n;
603
604 sl = fbuf.length;
605 n = snprintf(fbuf.ptr, sl, format.ptr, field_width, precision, v);
606 //printf("format = '%s', n = %d\n", cast(char*)format, n);
607 if (n >= 0 && n < sl)
608 { sl = n;
609 break;
610 }
611 if (n < 0)
612 sl = sl * 2;
613 else
614 sl = n + 1;
615 fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl];
616 }
617 putstr(fbuf[0 .. sl]);
618 }
619 return;
620 }
621
622 static Mangle getMan(TypeInfo ti)
623 {
624 auto m = cast(Mangle)ti.classinfo.name[9];
625 if (ti.classinfo.name.length == 20 &&
626 ti.classinfo.name[9..20] == "StaticArray")
627 m = cast(Mangle)'G';
628 return m;
629 }
630
631 void putArray(void* p, size_t len, TypeInfo valti)
632 {
633 //printf("\nputArray(len = %u), tsize = %u\n", len, valti.tsize());
634 putc('[');
635 valti = skipCI(valti);
636 size_t tsize = valti.tsize();
637 auto argptrSave = argptr;
638 auto tiSave = ti;
639 auto mSave = m;
640 ti = valti;
641 //printf("\n%.*s\n", valti.classinfo.name);
642 m = getMan(valti);
643 while (len--)
644 {
645 //doFormat(putc, (&valti)[0 .. 1], p);
646 argptr = p;
647 formatArg('s');
648
649 p += tsize;
650 if (len > 0) putc(',');
651 }
652 m = mSave;
653 ti = tiSave;
654 argptr = argptrSave;
655 putc(']');
656 }
657
658 void putAArray(ubyte[long] vaa, TypeInfo valti, TypeInfo keyti)
659 {
660 putc('[');
661 bool comma=false;
662 auto argptrSave = argptr;
663 auto tiSave = ti;
664 auto mSave = m;
665 valti = skipCI(valti);
666 keyti = skipCI(keyti);
667 foreach(inout fakevalue; vaa)
668 {
669 if (comma) putc(',');
670 comma = true;
671 // the key comes before the value
672 ubyte* key = &fakevalue - long.sizeof;
673
674 //doFormat(putc, (&keyti)[0..1], key);
675 argptr = key;
676 ti = keyti;
677 m = getMan(keyti);
678 formatArg('s');
679
680 putc(':');
681 auto keysize = keyti.tsize;
682 keysize = (keysize + 3) & ~3;
683 ubyte* value = key + keysize;
684 //doFormat(putc, (&valti)[0..1], value);
685 argptr = value;
686 ti = valti;
687 m = getMan(valti);
688 formatArg('s');
689 }
690 m = mSave;
691 ti = tiSave;
692 argptr = argptrSave;
693 putc(']');
694 }
695
696 //printf("formatArg(fc = '%c', m = '%c')\n", fc, m);
697 switch (m)
698 {
699 case Mangle.Tbool:
700 vbit = va_arg!(bool)(argptr);
701 if (fc != 's')
702 { vnumber = vbit;
703 goto Lnumber;
704 }
705 putstr(vbit ? "true" : "false");
706 return;
707
708
709 case Mangle.Tchar:
710 vchar = va_arg!(char)(argptr);
711 if (fc != 's')
712 { vnumber = vchar;
713 goto Lnumber;
714 }
715 L2:
716 putstr((&vchar)[0 .. 1]);
717 return;
718
719 case Mangle.Twchar:
720 vdchar = va_arg!(wchar)(argptr);
721 goto L1;
722
723 case Mangle.Tdchar:
724 vdchar = va_arg!(dchar)(argptr);
725 L1:
726 if (fc != 's')
727 { vnumber = vdchar;
728 goto Lnumber;
729 }
730 if (vdchar <= 0x7F)
731 { vchar = cast(char)vdchar;
732 goto L2;
733 }
734 else
735 { if (!isValidDchar(vdchar))
736 throw new UtfException("invalid dchar in format", 0);
737 char[4] vbuf;
738 putstr(toUTF8(vbuf, vdchar));
739 }
740 return;
741
742
743 case Mangle.Tbyte:
744 signed = 1;
745 vnumber = va_arg!(byte)(argptr);
746 goto Lnumber;
747
748 case Mangle.Tubyte:
749 vnumber = va_arg!(ubyte)(argptr);
750 goto Lnumber;
751
752 case Mangle.Tshort:
753 signed = 1;
754 vnumber = va_arg!(short)(argptr);
755 goto Lnumber;
756
757 case Mangle.Tushort:
758 vnumber = va_arg!(ushort)(argptr);
759 goto Lnumber;
760
761 case Mangle.Tint:
762 signed = 1;
763 vnumber = va_arg!(int)(argptr);
764 goto Lnumber;
765
766 case Mangle.Tuint:
767 Luint:
768 vnumber = va_arg!(uint)(argptr);
769 goto Lnumber;
770
771 case Mangle.Tlong:
772 signed = 1;
773 vnumber = cast(ulong)va_arg!(long)(argptr);
774 goto Lnumber;
775
776 case Mangle.Tulong:
777 Lulong:
778 vnumber = va_arg!(ulong)(argptr);
779 goto Lnumber;
780
781 case Mangle.Tclass:
782 vobject = va_arg!(Object)(argptr);
783 if (vobject is null)
784 s = "null";
785 else
786 s = vobject.toString();
787 goto Lputstr;
788
789 case Mangle.Tpointer:
790 vnumber = cast(ulong)va_arg!(void*)(argptr);
791 uc = 1;
792 flags |= FL0pad;
793 if (!(flags & FLprecision))
794 { flags |= FLprecision;
795 precision = (void*).sizeof;
796 }
797 base = 16;
798 goto Lnumber;
799
800
801 case Mangle.Tfloat:
802 case Mangle.Tifloat:
803 if (fc == 'x' || fc == 'X')
804 goto Luint;
805 vreal = va_arg!(float)(argptr);
806 goto Lreal;
807
808 case Mangle.Tdouble:
809 case Mangle.Tidouble:
810 if (fc == 'x' || fc == 'X')
811 goto Lulong;
812 vreal = va_arg!(double)(argptr);
813 goto Lreal;
814
815 case Mangle.Treal:
816 case Mangle.Tireal:
817 vreal = va_arg!(real)(argptr);
818 goto Lreal;
819
820
821 case Mangle.Tcfloat:
822 vcreal = va_arg!(cfloat)(argptr);
823 goto Lcomplex;
824
825 case Mangle.Tcdouble:
826 vcreal = va_arg!(cdouble)(argptr);
827 goto Lcomplex;
828
829 case Mangle.Tcreal:
830 vcreal = va_arg!(creal)(argptr);
831 goto Lcomplex;
832
833 case Mangle.Tsarray:
834 putArray(argptr, (cast(TypeInfo_StaticArray)ti).len, (cast(TypeInfo_StaticArray)ti).next);
835 return;
836
837 case Mangle.Tarray:
838 int mi = 10;
839 if (ti.classinfo.name.length == 14 &&
840 ti.classinfo.name[9..14] == "Array")
841 { // array of non-primitive types
842 TypeInfo tn = (cast(TypeInfo_Array)ti).next;
843 tn = skipCI(tn);
844 switch (cast(Mangle)tn.classinfo.name[9])
845 {
846 case Mangle.Tchar: goto LarrayChar;
847 case Mangle.Twchar: goto LarrayWchar;
848 case Mangle.Tdchar: goto LarrayDchar;
849 default:
850 break;
851 }
852 void[] va = va_arg!(void[])(argptr);
853 putArray(va.ptr, va.length, tn);
854 return;
855 }
856 if (ti.classinfo.name.length == 25 &&
857 ti.classinfo.name[9..25] == "AssociativeArray")
858 { // associative array
859 ubyte[long] vaa = va_arg!(ubyte[long])(argptr);
860 putAArray(vaa,
861 (cast(TypeInfo_AssociativeArray)ti).next,
862 (cast(TypeInfo_AssociativeArray)ti).key);
863 return;
864 }
865
866 while (1)
867 {
868 m2 = cast(Mangle)ti.classinfo.name[mi];
869 switch (m2)
870 {
871 case Mangle.Tchar:
872 LarrayChar:
873 s = va_arg!(char[])(argptr);
874 goto Lputstr;
875
876 case Mangle.Twchar:
877 LarrayWchar:
878 wchar[] sw = va_arg!(wchar[])(argptr);
879 s = toUTF8(sw);
880 goto Lputstr;
881
882 case Mangle.Tdchar:
883 LarrayDchar:
884 dchar[] sd = va_arg!(dchar[])(argptr);
885 s = toUTF8(sd);
886 Lputstr:
887 if (fc != 's')
888 throw new FormatError("string");
889 if (flags & FLprecision && precision < s.length)
890 s = s[0 .. precision];
891 putstr(s);
892 break;
893
894 case Mangle.Tconst:
895 case Mangle.Tinvariant:
896 mi++;
897 continue;
898
899 default:
900 TypeInfo ti2 = primitiveTypeInfo(m2);
901 if (!ti2)
902 goto Lerror;
903 void[] va = va_arg!(void[])(argptr);
904 putArray(va.ptr, va.length, ti2);
905 }
906 return;
907 }
908
909 case Mangle.Ttypedef:
910 ti = (cast(TypeInfo_Typedef)ti).base;
911 m = cast(Mangle)ti.classinfo.name[9];
912 formatArg(fc);
913 return;
914
915 case Mangle.Tenum:
916 ti = (cast(TypeInfo_Enum)ti).base;
917 m = cast(Mangle)ti.classinfo.name[9];
918 formatArg(fc);
919 return;
920
921 case Mangle.Tstruct:
922 { TypeInfo_Struct tis = cast(TypeInfo_Struct)ti;
923 if (tis.xtoString is null)
924 throw new FormatError("Can't convert " ~ tis.toString() ~ " to string: \"string toString()\" not defined");
925 s = tis.xtoString(argptr);
926 argptr += (tis.tsize() + 3) & ~3;
927 goto Lputstr;
928 }
929
930 default:
931 goto Lerror;
932 }
933
934 Lnumber:
935 switch (fc)
936 {
937 case 's':
938 case 'd':
939 if (signed)
940 { if (cast(long)vnumber < 0)
941 { prefix = "-";
942 vnumber = -vnumber;
943 }
944 else if (flags & FLplus)
945 prefix = "+";
946 else if (flags & FLspace)
947 prefix = " ";
948 }
949 break;
950
951 case 'b':
952 signed = 0;
953 base = 2;
954 break;
955
956 case 'o':
957 signed = 0;
958 base = 8;
959 break;
960
961 case 'X':
962 uc = 1;
963 if (flags & FLhash && vnumber)
964 prefix = "0X";
965 signed = 0;
966 base = 16;
967 break;
968
969 case 'x':
970 if (flags & FLhash && vnumber)
971 prefix = "0x";
972 signed = 0;
973 base = 16;
974 break;
975
976 default:
977 goto Lerror;
978 }
979
980 if (!signed)
981 {
982 switch (m)
983 {
984 case Mangle.Tbyte:
985 vnumber &= 0xFF;
986 break;
987
988 case Mangle.Tshort:
989 vnumber &= 0xFFFF;
990 break;
991
992 case Mangle.Tint:
993 vnumber &= 0xFFFFFFFF;
994 break;
995
996 default:
997 break;
998 }
999 }
1000
1001 if (flags & FLprecision && fc != 'p')
1002 flags &= ~FL0pad;
1003
1004 if (vnumber < base)
1005 {
1006 if (vnumber == 0 && precision == 0 && flags & FLprecision &&
1007 !(fc == 'o' && flags & FLhash))
1008 {
1009 putstr(null);
1010 return;
1011 }
1012 if (precision == 0 || !(flags & FLprecision))
1013 { vchar = cast(char)('0' + vnumber);
1014 if (vnumber < 10)
1015 vchar = cast(char)('0' + vnumber);
1016 else
1017 vchar = cast(char)((uc ? 'A' - 10 : 'a' - 10) + vnumber);
1018 goto L2;
1019 }
1020 }
1021
1022 int n = tmpbuf.length;
1023 char c;
1024 int hexoffset = uc ? ('A' - ('9' + 1)) : ('a' - ('9' + 1));
1025
1026 while (vnumber)
1027 {
1028 c = cast(char)((vnumber % base) + '0');
1029 if (c > '9')
1030 c += hexoffset;
1031 vnumber /= base;
1032 tmpbuf[--n] = c;
1033 }
1034 if (tmpbuf.length - n < precision && precision < tmpbuf.length)
1035 {
1036 int m = tmpbuf.length - precision;
1037 tmpbuf[m .. n] = '0';
1038 n = m;
1039 }
1040 else if (flags & FLhash && fc == 'o')
1041 prefix = "0";
1042 putstr(tmpbuf[n .. tmpbuf.length]);
1043 return;
1044
1045 Lreal:
1046 putreal(vreal);
1047 return;
1048
1049 Lcomplex:
1050 putreal(vcreal.re);
1051 putc('+');
1052 putreal(vcreal.im);
1053 putc('i');
1054 return;
1055
1056 Lerror:
1057 throw new FormatError("formatArg");
1058 }
1059
1060
1061 for (j = 0; j < arguments.length; )
1062 { ti = arguments[j++];
1063 //printf("test1: '%.*s' %d\n", ti.classinfo.name, ti.classinfo.name.length);
1064 //ti.print();
1065
1066 flags = 0;
1067 precision = 0;
1068 field_width = 0;
1069
1070 ti = skipCI(ti);
1071 int mi = 9;
1072 do
1073 {
1074 if (ti.classinfo.name.length <= mi)
1075 goto Lerror;
1076 m = cast(Mangle)ti.classinfo.name[mi++];
1077 } while (m == Mangle.Tconst || m == Mangle.Tinvariant);
1078
1079 if (m == Mangle.Tarray)
1080 {
1081 if (ti.classinfo.name.length == 14 &&
1082 ti.classinfo.name[9..14] == "Array")
1083 {
1084 TypeInfo tn = (cast(TypeInfo_Array)ti).next;
1085 tn = skipCI(tn);
1086 switch (cast(Mangle)tn.classinfo.name[9])
1087 {
1088 case Mangle.Tchar:
1089 case Mangle.Twchar:
1090 case Mangle.Tdchar:
1091 ti = tn;
1092 mi = 9;
1093 break;
1094 default:
1095 break;
1096 }
1097 }
1098 L1:
1099 Mangle m2 = cast(Mangle)ti.classinfo.name[mi];
1100 string fmt; // format string
1101 wstring wfmt;
1102 dstring dfmt;
1103
1104 /* For performance reasons, this code takes advantage of the
1105 * fact that most format strings will be ASCII, and that the
1106 * format specifiers are always ASCII. This means we only need
1107 * to deal with UTF in a couple of isolated spots.
1108 */
1109
1110 switch (m2)
1111 {
1112 case Mangle.Tchar:
1113 fmt = va_arg!(char[])(argptr);
1114 break;
1115
1116 case Mangle.Twchar:
1117 wfmt = va_arg!(wchar[])(argptr);
1118 fmt = toUTF8(wfmt);
1119 break;
1120
1121 case Mangle.Tdchar:
1122 dfmt = va_arg!(dchar[])(argptr);
1123 fmt = toUTF8(dfmt);
1124 break;
1125
1126 case Mangle.Tconst:
1127 case Mangle.Tinvariant:
1128 mi++;
1129 goto L1;
1130
1131 default:
1132 formatArg('s');
1133 continue;
1134 }
1135
1136 for (size_t i = 0; i < fmt.length; )
1137 { dchar c = fmt[i++];
1138
1139 dchar getFmtChar()
1140 { // Valid format specifier characters will never be UTF
1141 if (i == fmt.length)
1142 throw new FormatError("invalid specifier");
1143 return fmt[i++];
1144 }
1145
1146 int getFmtInt()
1147 { int n;
1148
1149 while (1)
1150 {
1151 n = n * 10 + (c - '0');
1152 if (n < 0) // overflow
1153 throw new FormatError("int overflow");
1154 c = getFmtChar();
1155 if (c < '0' || c > '9')
1156 break;
1157 }
1158 return n;
1159 }
1160
1161 int getFmtStar()
1162 { Mangle m;
1163 TypeInfo ti;
1164
1165 if (j == arguments.length)
1166 throw new FormatError("too few arguments");
1167 ti = arguments[j++];
1168 m = cast(Mangle)ti.classinfo.name[9];
1169 if (m != Mangle.Tint)
1170 throw new FormatError("int argument expected");
1171 return va_arg!(int)(argptr);
1172 }
1173
1174 if (c != '%')
1175 {
1176 if (c > 0x7F) // if UTF sequence
1177 {
1178 i--; // back up and decode UTF sequence
1179 c = std.utf.decode(fmt, i);
1180 }
1181 Lputc:
1182 putc(c);
1183 continue;
1184 }
1185
1186 // Get flags {-+ #}
1187 flags = 0;
1188 while (1)
1189 {
1190 c = getFmtChar();
1191 switch (c)
1192 {
1193 case '-': flags |= FLdash; continue;
1194 case '+': flags |= FLplus; continue;
1195 case ' ': flags |= FLspace; continue;
1196 case '#': flags |= FLhash; continue;
1197 case '0': flags |= FL0pad; continue;
1198
1199 case '%': if (flags == 0)
1200 goto Lputc;
1201 default: break;
1202 }
1203 break;
1204 }
1205
1206 // Get field width
1207 field_width = 0;
1208 if (c == '*')
1209 {
1210 field_width = getFmtStar();
1211 if (field_width < 0)
1212 { flags |= FLdash;
1213 field_width = -field_width;
1214 }
1215
1216 c = getFmtChar();
1217 }
1218 else if (c >= '0' && c <= '9')
1219 field_width = getFmtInt();
1220
1221 if (flags & FLplus)
1222 flags &= ~FLspace;
1223 if (flags & FLdash)
1224 flags &= ~FL0pad;
1225
1226 // Get precision
1227 precision = 0;
1228 if (c == '.')
1229 { flags |= FLprecision;
1230 //flags &= ~FL0pad;
1231
1232 c = getFmtChar();
1233 if (c == '*')
1234 {
1235 precision = getFmtStar();
1236 if (precision < 0)
1237 { precision = 0;
1238 flags &= ~FLprecision;
1239 }
1240
1241 c = getFmtChar();
1242 }
1243 else if (c >= '0' && c <= '9')
1244 precision = getFmtInt();
1245 }
1246
1247 if (j == arguments.length)
1248 goto Lerror;
1249 ti = arguments[j++];
1250 ti = skipCI(ti);
1251 mi = 9;
1252 do
1253 {
1254 m = cast(Mangle)ti.classinfo.name[mi++];
1255 } while (m == Mangle.Tconst || m == Mangle.Tinvariant);
1256
1257 if (c > 0x7F) // if UTF sequence
1258 goto Lerror; // format specifiers can't be UTF
1259 formatArg(cast(char)c);
1260 }
1261 }
1262 else
1263 {
1264 formatArg('s');
1265 }
1266 }
1267 return;
1268
1269 Lerror:
1270 throw new FormatError();
1271 }
1272
1273 /* ======================== Unit Tests ====================================== */
1274
1275 unittest
1276 {
1277 int i;
1278 string s;
1279
1280 debug(format) printf("std.format.format.unittest\n");
1281
1282 s = std.string.format("hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo");
1283 assert(s == "hello world! true 57 1000000000x foo");
1284
1285 s = std.string.format(1.67, " %A ", -1.28, float.nan);
1286 /* The host C library is used to format floats.
1287 * C99 doesn't specify what the hex digit before the decimal point
1288 * is for %A.
1289 */
1290 version (linux)
1291 assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan");
1292 else
1293 assert(s == "1.67 -0X1.47AE147AE147BP+0 nan");
1294
1295 s = std.string.format("%x %X", 0x1234AF, 0xAFAFAFAF);
1296 assert(s == "1234af AFAFAFAF");
1297
1298 s = std.string.format("%b %o", 0x1234AF, 0xAFAFAFAF);
1299 assert(s == "100100011010010101111 25753727657");
1300
1301 s = std.string.format("%d %s", 0x1234AF, 0xAFAFAFAF);
1302 assert(s == "1193135 2947526575");
1303
1304 s = std.string.format("%s", 1.2 + 3.4i);
1305 assert(s == "1.2+3.4i");
1306
1307 s = std.string.format("%x %X", 1.32, 6.78f);
1308 assert(s == "3ff51eb851eb851f 40D8F5C3");
1309
1310 s = std.string.format("%#06.*f",2,12.345);
1311 assert(s == "012.35");
1312
1313 s = std.string.format("%#0*.*f",6,2,12.345);
1314 assert(s == "012.35");
1315
1316 s = std.string.format("%7.4g:", 12.678);
1317 assert(s == " 12.68:");
1318
1319 s = std.string.format("%7.4g:", 12.678L);
1320 assert(s == " 12.68:");
1321
1322 s = std.string.format("%04f|%05d|%#05x|%#5x",-4.,-10,1,1);
1323 assert(s == "-4.000000|-0010|0x001| 0x1");
1324
1325 i = -10;
1326 s = std.string.format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
1327 assert(s == "-10|-10|-10|-10|-10.0000");
1328
1329 i = -5;
1330 s = std.string.format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
1331 assert(s == "-5| -5|-05|-5|-5.0000");
1332
1333 i = 0;
1334 s = std.string.format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
1335 assert(s == "0| 0|000|0|0.0000");
1336
1337 i = 5;
1338 s = std.string.format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
1339 assert(s == "5| 5|005|5|5.0000");
1340
1341 i = 10;
1342 s = std.string.format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
1343 assert(s == "10| 10|010|10|10.0000");
1344
1345 s = std.string.format("%.0d", 0);
1346 assert(s == "");
1347
1348 s = std.string.format("%.g", .34);
1349 assert(s == "0.3");
1350
1351 s = std.string.format("%.0g", .34);
1352 assert(s == "0.3");
1353
1354 s = std.string.format("%.2g", .34);
1355 assert(s == "0.34");
1356
1357 s = std.string.format("%0.0008f", 1e-08);
1358 assert(s == "0.00000001");
1359
1360 s = std.string.format("%0.0008f", 1e-05);
1361 assert(s == "0.00001000");
1362
1363 s = "helloworld";
1364 string r;
1365 r = std.string.format("%.2s", s[0..5]);
1366 assert(r == "he");
1367 r = std.string.format("%.20s", s[0..5]);
1368 assert(r == "hello");
1369 r = std.string.format("%8s", s[0..5]);
1370 assert(r == " hello");
1371
1372 byte[] arrbyte = new byte[4];
1373 arrbyte[0] = 100;
1374 arrbyte[1] = -99;
1375 arrbyte[3] = 0;
1376 r = std.string.format(arrbyte);
1377 assert(r == "[100,-99,0,0]");
1378
1379 ubyte[] arrubyte = new ubyte[4];
1380 arrubyte[0] = 100;
1381 arrubyte[1] = 200;
1382 arrubyte[3] = 0;
1383 r = std.string.format(arrubyte);
1384 assert(r == "[100,200,0,0]");
1385
1386 short[] arrshort = new short[4];
1387 arrshort[0] = 100;
1388 arrshort[1] = -999;
1389 arrshort[3] = 0;
1390 r = std.string.format(arrshort);
1391 assert(r == "[100,-999,0,0]");
1392 r = std.string.format("%s",arrshort);
1393 assert(r == "[100,-999,0,0]");
1394
1395 ushort[] arrushort = new ushort[4];
1396 arrushort[0] = 100;
1397 arrushort[1] = 20_000;
1398 arrushort[3] = 0;
1399 r = std.string.format(arrushort);
1400 assert(r == "[100,20000,0,0]");
1401
1402 int[] arrint = new int[4];
1403 arrint[0] = 100;
1404 arrint[1] = -999;
1405 arrint[3] = 0;
1406 r = std.string.format(arrint);
1407 assert(r == "[100,-999,0,0]");
1408 r = std.string.format("%s",arrint);
1409 assert(r == "[100,-999,0,0]");
1410
1411 long[] arrlong = new long[4];
1412 arrlong[0] = 100;
1413 arrlong[1] = -999;
1414 arrlong[3] = 0;
1415 r = std.string.format(arrlong);
1416 assert(r == "[100,-999,0,0]");
1417 r = std.string.format("%s",arrlong);
1418 assert(r == "[100,-999,0,0]");
1419
1420 ulong[] arrulong = new ulong[4];
1421 arrulong[0] = 100;
1422 arrulong[1] = 999;
1423 arrulong[3] = 0;
1424 r = std.string.format(arrulong);
1425 assert(r == "[100,999,0,0]");
1426
1427 string[] arr2 = new string[4];
1428 arr2[0] = "hello";
1429 arr2[1] = "world";
1430 arr2[3] = "foo";
1431 r = std.string.format(arr2);
1432 assert(r == "[hello,world,,foo]");
1433
1434 r = std.string.format("%.8d", 7);
1435 assert(r == "00000007");
1436 r = std.string.format("%.8x", 10);
1437 assert(r == "0000000a");
1438
1439 r = std.string.format("%-3d", 7);
1440 assert(r == "7 ");
1441
1442 r = std.string.format("%*d", -3, 7);
1443 assert(r == "7 ");
1444
1445 r = std.string.format("%.*d", -3, 7);
1446 assert(r == "7");
1447
1448 typedef int myint;
1449 myint m = -7;
1450 r = std.string.format(m);
1451 assert(r == "-7");
1452
1453 r = std.string.format("abc"c);
1454 assert(r == "abc");
1455 r = std.string.format("def"w);
1456 assert(r == "def");
1457 r = std.string.format("ghi"d);
1458 assert(r == "ghi");
1459
1460 void* p = cast(void*)0xDEADBEEF;
1461 r = std.string.format(p);
1462 assert(r == "DEADBEEF");
1463
1464 r = std.string.format("%#x", 0xabcd);
1465 assert(r == "0xabcd");
1466 r = std.string.format("%#X", 0xABCD);
1467 assert(r == "0XABCD");
1468
1469 r = std.string.format("%#o", 012345);
1470 assert(r == "012345");
1471 r = std.string.format("%o", 9);
1472 assert(r == "11");
1473
1474 r = std.string.format("%+d", 123);
1475 assert(r == "+123");
1476 r = std.string.format("%+d", -123);
1477 assert(r == "-123");
1478 r = std.string.format("% d", 123);
1479 assert(r == " 123");
1480 r = std.string.format("% d", -123);
1481 assert(r == "-123");
1482
1483 r = std.string.format("%%");
1484 assert(r == "%");
1485
1486 r = std.string.format("%d", true);
1487 assert(r == "1");
1488 r = std.string.format("%d", false);
1489 assert(r == "0");
1490
1491 r = std.string.format("%d", 'a');
1492 assert(r == "97");
1493 wchar wc = 'a';
1494 r = std.string.format("%d", wc);
1495 assert(r == "97");
1496 dchar dc = 'a';
1497 r = std.string.format("%d", dc);
1498 assert(r == "97");
1499
1500 byte b = byte.max;
1501 r = std.string.format("%x", b);
1502 assert(r == "7f");
1503 r = std.string.format("%x", ++b);
1504 assert(r == "80");
1505 r = std.string.format("%x", ++b);
1506 assert(r == "81");
1507
1508 short sh = short.max;
1509 r = std.string.format("%x", sh);
1510 assert(r == "7fff");
1511 r = std.string.format("%x", ++sh);
1512 assert(r == "8000");
1513 r = std.string.format("%x", ++sh);
1514 assert(r == "8001");
1515
1516 i = int.max;
1517 r = std.string.format("%x", i);
1518 assert(r == "7fffffff");
1519 r = std.string.format("%x", ++i);
1520 assert(r == "80000000");
1521 r = std.string.format("%x", ++i);
1522 assert(r == "80000001");
1523
1524 r = std.string.format("%x", 10);
1525 assert(r == "a");
1526 r = std.string.format("%X", 10);
1527 assert(r == "A");
1528 r = std.string.format("%x", 15);
1529 assert(r == "f");
1530 r = std.string.format("%X", 15);
1531 assert(r == "F");
1532
1533 Object c = null;
1534 r = std.string.format(c);
1535 assert(r == "null");
1536
1537 enum TestEnum
1538 {
1539 Value1, Value2
1540 }
1541 r = std.string.format("%s", TestEnum.Value2);
1542 assert(r == "1");
1543
1544 char[5][int] aa = ([3:"hello", 4:"betty"]);
1545 r = std.string.format("%s", aa.values);
1546 assert(r == "[[h,e,l,l,o],[b,e,t,t,y]]");
1547 r = std.string.format("%s", aa);
1548 assert(r == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]");
1549
1550 static const dchar[] ds = ['a','b'];
1551 for (int j = 0; j < ds.length; ++j)
1552 {
1553 r = std.string.format(" %d", ds[j]);
1554 if (j == 0)
1555 assert(r == " 97");
1556 else
1557 assert(r == " 98");
1558 }
1559
1560 r = std.string.format(">%14d<, ", 15, [1,2,3]);
1561 assert(r == "> 15<, [1,2,3]");
1562 }
1563