Mercurial > projects > ldc
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 ±, followed by at least a two digit exponent: $(I d.dddddd)e$(I ±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 ±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 ±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 |