Changeset 1719
- Timestamp:
- 07/04/10 21:44:42 (14 years ago)
- Files:
-
- trunk/phobos/std/format.d (modified) (14 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/phobos/std/format.d
r1649 r1719 1 1 // Written in the D programming language. 2 2 3 3 /** 4 4 * This module implements the workhorse functionality for string and 5 5 * I/O formatting. It's comparable to C99's vsprintf(). 6 6 * 7 7 * Macros: 8 8 * WIKI = Phobos/StdFormat 9 9 * 10 * Copyright: Copyright Digital Mars 2000 - 2009. 11 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 12 * Authors: $(WEB digitalmars.com, Walter Bright) 10 * Copyright: Copyright Digital Mars 2000-. 11 * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 13 12 * 14 * Copyright Digital Mars 2000 - 2009. 15 * Distributed under the Boost Software License, Version 1.0. 16 * (See accompanying file LICENSE_1_0.txt or copy at 17 * http://www.boost.org/LICENSE_1_0.txt) 13 * Authors: $(WEB digitalmars.com, Walter Bright), $(WEB 14 * erdani.com, Andrei Alexandrescu) 18 15 */ 19 16 module std.format; 20 17 21 //debug=format; // uncomment to turn on debugging printf's 22 23 import std.algorithm, std.array, std.bitmanip; 24 import std.stdarg; // caller will need va_list 25 import std.utf; 18 //debug=format; // uncomment to turn on debugging printf's 19 26 20 import core.stdc.stdio, core.stdc.stdlib, core.stdc.string; 27 import std.string; 28 import std.ctype; 29 import std.conv; 30 import std.functional; 31 import std.traits; 32 import std.typetuple; 33 import std.contracts; 34 import std.system; 35 import std.range; 21 import std.algorithm, std.array, std.bitmanip, std.contracts, std.conv, 22 std.ctype, std.functional, std.range, std.stdarg, std.string, std.system, 23 std.traits, std.typetuple, std.utf; 36 24 version(unittest) { 37 25 import std.stdio, std.typecons; 38 26 } 39 27 40 28 version (Windows) version (DigitalMars) 41 29 { 42 30 version = DigitalMarsC; 43 31 } 44 32 45 33 version (DigitalMarsC) … … 408 396 std.format.doFormat(&putc, _arguments, _argptr); 409 397 } 410 398 411 399 ... 412 400 413 401 int x = 27; 414 402 // prints 'The answer is 27:6' 415 403 myPrint("The answer is %s:", x, 6); 416 404 ------------------------ 417 405 */ 418 419 406 void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr) 420 { int j;407 { 421 408 TypeInfo ti; 422 409 Mangle m; 423 410 uint flags; 424 411 int field_width; 425 412 int precision; 426 413 427 414 enum : uint 428 415 { 429 416 FLdash = 1, 430 417 FLplus = 2, … … 446 433 valti.classinfo.name[9..14] == "Const") 447 434 valti = (cast(TypeInfo_Const)valti).next; 448 435 else 449 436 break; 450 437 } 451 438 return valti; 452 439 } 453 440 454 441 void formatArg(char fc) 455 442 { 456 bool vbit; 457 ulong vnumber; 458 char vchar; 459 dchar vdchar; 460 Object vobject; 461 real vreal; 462 creal vcreal; 463 Mangle m2; 464 int signed = 0; 465 uint base = 10; 466 int uc; 467 char[ulong.sizeof * 8] tmpbuf; // long enough to print long in binary 468 const(char)* prefix = ""; 469 string s; 470 471 void putstr(const char[] s) 472 { 473 //printf("flags = x%x\n", flags); 474 int prepad = 0; 475 int postpad = 0; 476 int padding = field_width - (strlen(prefix) + toUCSindex(s, s.length)); 477 if (padding > 0) 478 { 479 if (flags & FLdash) 480 postpad = padding; 481 else 482 prepad = padding; 483 } 484 485 if (flags & FL0pad) 486 { 487 while (*prefix) 488 putc(*prefix++); 489 while (prepad--) 490 putc('0'); 491 } 492 else 493 { 494 while (prepad--) 495 putc(' '); 496 while (*prefix) 497 putc(*prefix++); 498 } 499 500 foreach (dchar c; s) 501 putc(c); 502 503 while (postpad--) 504 putc(' '); 505 } 506 507 void putreal(real v) 508 { 509 //printf("putreal %Lg\n", vreal); 510 511 switch (fc) 512 { 513 case 's': 514 fc = 'g'; 515 break; 516 517 case 'f', 'F', 'e', 'E', 'g', 'G', 'a', 'A': 518 break; 519 520 default: 521 //printf("fc = '%c'\n", fc); 522 Lerror: 523 throw new FormatError("floating"); 524 } 525 version (DigitalMarsC) 526 { 527 uint sl; 528 char[] fbuf = tmpbuf; 529 if (!(flags & FLprecision)) 530 precision = 6; 531 while (1) 443 bool vbit; 444 ulong vnumber; 445 char vchar; 446 dchar vdchar; 447 Object vobject; 448 real vreal; 449 creal vcreal; 450 Mangle m2; 451 int signed = 0; 452 uint base = 10; 453 int uc; 454 char[ulong.sizeof * 8] tmpbuf; // long enough to print long in binary 455 const(char)* prefix = ""; 456 string s; 457 458 void putstr(const char[] s) 459 { 460 //printf("flags = x%x\n", flags); 461 int prepad = 0; 462 int postpad = 0; 463 int padding = field_width - 464 (strlen(prefix) + toUCSindex(s, s.length)); 465 if (padding > 0) 532 466 { 533 sl = fbuf.length; 534 prefix = (*__pfloatfmt)(fc, flags | FLlngdbl, 535 precision, &v, cast(char*)fbuf, &sl, field_width); 536 if (sl != -1) 467 if (flags & FLdash) 468 postpad = padding; 469 else 470 prepad = padding; 471 } 472 473 if (flags & FL0pad) 474 { 475 while (*prefix) 476 putc(*prefix++); 477 while (prepad--) 478 putc('0'); 479 } 480 else 481 { 482 while (prepad--) 483 putc(' '); 484 while (*prefix) 485 putc(*prefix++); 486 } 487 488 foreach (dchar c; s) 489 putc(c); 490 491 while (postpad--) 492 putc(' '); 493 } 494 495 void putreal(real v) 496 { 497 //printf("putreal %Lg\n", vreal); 498 499 switch (fc) 500 { 501 case 's': 502 fc = 'g'; 503 break; 504 505 case 'f', 'F', 'e', 'E', 'g', 'G', 'a', 'A': 506 break; 507 508 default: 509 //printf("fc = '%c'\n", fc); 510 Lerror: 511 throw new FormatError("floating"); 512 } 513 version (DigitalMarsC) 514 { 515 uint sl; 516 char[] fbuf = tmpbuf; 517 if (!(flags & FLprecision)) 518 precision = 6; 519 while (1) 520 { 521 sl = fbuf.length; 522 prefix = (*__pfloatfmt)(fc, flags | FLlngdbl, 523 precision, &v, cast(char*)fbuf, &sl, field_width); 524 if (sl != -1) 525 break; 526 sl = fbuf.length * 2; 527 fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; 528 } 529 putstr(fbuf[0 .. sl]); 530 } 531 else 532 { 533 int sl; 534 char[] fbuf = tmpbuf; 535 char[12] format; 536 format[0] = '%'; 537 int i = 1; 538 if (flags & FLdash) 539 format[i++] = '-'; 540 if (flags & FLplus) 541 format[i++] = '+'; 542 if (flags & FLspace) 543 format[i++] = ' '; 544 if (flags & FLhash) 545 format[i++] = '#'; 546 if (flags & FL0pad) 547 format[i++] = '0'; 548 format[i + 0] = '*'; 549 format[i + 1] = '.'; 550 format[i + 2] = '*'; 551 format[i + 3] = 'L'; 552 format[i + 4] = fc; 553 format[i + 5] = 0; 554 if (!(flags & FLprecision)) 555 precision = -1; 556 while (1) 557 { 558 sl = fbuf.length; 559 auto n = snprintf(fbuf.ptr, sl, format.ptr, field_width, 560 precision, v); 561 //printf("format = '%s', n = %d\n", cast(char*)format, n); 562 if (n >= 0 && n < sl) 563 { sl = n; 564 break; 565 } 566 if (n < 0) 567 sl = sl * 2; 568 else 569 sl = n + 1; 570 fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; 571 } 572 putstr(fbuf[0 .. sl]); 573 } 574 return; 575 } 576 577 static Mangle getMan(TypeInfo ti) 578 { 579 auto m = cast(Mangle)ti.classinfo.name[9]; 580 if (ti.classinfo.name.length == 20 && 581 ti.classinfo.name[9..20] == "StaticArray") 582 m = cast(Mangle)'G'; 583 return m; 584 } 585 586 void putArray(void* p, size_t len, TypeInfo valti) 587 { 588 //printf("\nputArray(len = %u), tsize = %u\n", len, valti.tsize()); 589 putc('['); 590 valti = skipCI(valti); 591 size_t tsize = valti.tsize(); 592 auto argptrSave = argptr; 593 auto tiSave = ti; 594 auto mSave = m; 595 ti = valti; 596 //printf("\n%.*s\n", valti.classinfo.name); 597 m = getMan(valti); 598 while (len--) 599 { 600 //doFormat(putc, (&valti)[0 .. 1], p); 601 argptr = p; 602 formatArg('s'); 603 604 p += tsize; 605 if (len > 0) putc(','); 606 } 607 m = mSave; 608 ti = tiSave; 609 argptr = argptrSave; 610 putc(']'); 611 } 612 613 void putAArray(ubyte[long] vaa, TypeInfo valti, TypeInfo keyti) 614 { 615 putc('['); 616 bool comma=false; 617 auto argptrSave = argptr; 618 auto tiSave = ti; 619 auto mSave = m; 620 valti = skipCI(valti); 621 keyti = skipCI(keyti); 622 foreach(ref fakevalue; vaa) 623 { 624 if (comma) putc(','); 625 comma = true; 626 // the key comes before the value 627 ubyte* key = &fakevalue - long.sizeof; 628 629 //doFormat(putc, (&keyti)[0..1], key); 630 argptr = key; 631 ti = keyti; 632 m = getMan(keyti); 633 formatArg('s'); 634 635 putc(':'); 636 auto keysize = keyti.tsize; 637 keysize = (keysize + 3) & ~3; 638 ubyte* value = key + keysize; 639 //doFormat(putc, (&valti)[0..1], value); 640 argptr = value; 641 ti = valti; 642 m = getMan(valti); 643 formatArg('s'); 644 } 645 m = mSave; 646 ti = tiSave; 647 argptr = argptrSave; 648 putc(']'); 649 } 650 651 //printf("formatArg(fc = '%c', m = '%c')\n", fc, m); 652 switch (m) 653 { 654 case Mangle.Tbool: 655 vbit = va_arg!(bool)(argptr); 656 if (fc != 's') 657 { vnumber = vbit; 658 goto Lnumber; 659 } 660 putstr(vbit ? "true" : "false"); 661 return; 662 663 664 case Mangle.Tchar: 665 vchar = va_arg!(char)(argptr); 666 if (fc != 's') 667 { vnumber = vchar; 668 goto Lnumber; 669 } 670 L2: 671 putstr((&vchar)[0 .. 1]); 672 return; 673 674 case Mangle.Twchar: 675 vdchar = va_arg!(wchar)(argptr); 676 goto L1; 677 678 case Mangle.Tdchar: 679 vdchar = va_arg!(dchar)(argptr); 680 L1: 681 if (fc != 's') 682 { vnumber = vdchar; 683 goto Lnumber; 684 } 685 if (vdchar <= 0x7F) 686 { vchar = cast(char)vdchar; 687 goto L2; 688 } 689 else 690 { if (!isValidDchar(vdchar)) 691 throw new UtfException("invalid dchar in format", 0); 692 char[4] vbuf; 693 putstr(toUTF8(vbuf, vdchar)); 694 } 695 return; 696 697 698 case Mangle.Tbyte: 699 signed = 1; 700 vnumber = va_arg!(byte)(argptr); 701 goto Lnumber; 702 703 case Mangle.Tubyte: 704 vnumber = va_arg!(ubyte)(argptr); 705 goto Lnumber; 706 707 case Mangle.Tshort: 708 signed = 1; 709 vnumber = va_arg!(short)(argptr); 710 goto Lnumber; 711 712 case Mangle.Tushort: 713 vnumber = va_arg!(ushort)(argptr); 714 goto Lnumber; 715 716 case Mangle.Tint: 717 signed = 1; 718 vnumber = va_arg!(int)(argptr); 719 goto Lnumber; 720 721 case Mangle.Tuint: 722 Luint: 723 vnumber = va_arg!(uint)(argptr); 724 goto Lnumber; 725 726 case Mangle.Tlong: 727 signed = 1; 728 vnumber = cast(ulong)va_arg!(long)(argptr); 729 goto Lnumber; 730 731 case Mangle.Tulong: 732 Lulong: 733 vnumber = va_arg!(ulong)(argptr); 734 goto Lnumber; 735 736 case Mangle.Tclass: 737 vobject = va_arg!(Object)(argptr); 738 if (vobject is null) 739 s = "null"; 740 else 741 s = vobject.toString(); 742 goto Lputstr; 743 744 case Mangle.Tpointer: 745 vnumber = cast(ulong)va_arg!(void*)(argptr); 746 if (fc != 'x') uc = 1; 747 flags |= FL0pad; 748 if (!(flags & FLprecision)) 749 { flags |= FLprecision; 750 precision = (void*).sizeof; 751 } 752 base = 16; 753 goto Lnumber; 754 755 756 case Mangle.Tfloat: 757 case Mangle.Tifloat: 758 if (fc == 'x' || fc == 'X') 759 goto Luint; 760 vreal = va_arg!(float)(argptr); 761 goto Lreal; 762 763 case Mangle.Tdouble: 764 case Mangle.Tidouble: 765 if (fc == 'x' || fc == 'X') 766 goto Lulong; 767 vreal = va_arg!(double)(argptr); 768 goto Lreal; 769 770 case Mangle.Treal: 771 case Mangle.Tireal: 772 vreal = va_arg!(real)(argptr); 773 goto Lreal; 774 775 776 case Mangle.Tcfloat: 777 vcreal = va_arg!(cfloat)(argptr); 778 goto Lcomplex; 779 780 case Mangle.Tcdouble: 781 vcreal = va_arg!(cdouble)(argptr); 782 goto Lcomplex; 783 784 case Mangle.Tcreal: 785 vcreal = va_arg!(creal)(argptr); 786 goto Lcomplex; 787 788 case Mangle.Tsarray: 789 putArray(argptr, (cast(TypeInfo_StaticArray)ti).len, 790 (cast(TypeInfo_StaticArray)ti).next); 791 return; 792 793 case Mangle.Tarray: 794 int mi = 10; 795 if (ti.classinfo.name.length == 14 && 796 ti.classinfo.name[9..14] == "Array") 797 { // array of non-primitive types 798 TypeInfo tn = (cast(TypeInfo_Array)ti).next; 799 tn = skipCI(tn); 800 switch (cast(Mangle)tn.classinfo.name[9]) 801 { 802 case Mangle.Tchar: goto LarrayChar; 803 case Mangle.Twchar: goto LarrayWchar; 804 case Mangle.Tdchar: goto LarrayDchar; 805 default: 806 break; 807 } 808 void[] va = va_arg!(void[])(argptr); 809 putArray(va.ptr, va.length, tn); 810 return; 811 } 812 if (ti.classinfo.name.length == 25 && 813 ti.classinfo.name[9..25] == "AssociativeArray") 814 { // associative array 815 ubyte[long] vaa = va_arg!(ubyte[long])(argptr); 816 putAArray(vaa, 817 (cast(TypeInfo_AssociativeArray)ti).next, 818 (cast(TypeInfo_AssociativeArray)ti).key); 819 return; 820 } 821 822 while (1) 823 { 824 m2 = cast(Mangle)ti.classinfo.name[mi]; 825 switch (m2) 826 { 827 case Mangle.Tchar: 828 LarrayChar: 829 s = va_arg!(string)(argptr); 830 goto Lputstr; 831 832 case Mangle.Twchar: 833 LarrayWchar: 834 wchar[] sw = va_arg!(wchar[])(argptr); 835 s = toUTF8(sw); 836 goto Lputstr; 837 838 case Mangle.Tdchar: 839 LarrayDchar: 840 auto sd = va_arg!(dstring)(argptr); 841 s = toUTF8(sd); 842 Lputstr: 843 if (fc != 's') 844 throw new FormatError("string"); 845 if (flags & FLprecision && precision < s.length) 846 s = s[0 .. precision]; 847 putstr(s); 848 break; 849 850 case Mangle.Tconst: 851 case Mangle.Timmutable: 852 mi++; 853 continue; 854 855 default: 856 TypeInfo ti2 = primitiveTypeInfo(m2); 857 if (!ti2) 858 goto Lerror; 859 void[] va = va_arg!(void[])(argptr); 860 putArray(va.ptr, va.length, ti2); 861 } 862 return; 863 } 864 865 case Mangle.Ttypedef: 866 ti = (cast(TypeInfo_Typedef)ti).base; 867 m = cast(Mangle)ti.classinfo.name[9]; 868 formatArg(fc); 869 return; 870 871 case Mangle.Tenum: 872 ti = (cast(TypeInfo_Enum)ti).base; 873 m = cast(Mangle)ti.classinfo.name[9]; 874 formatArg(fc); 875 return; 876 877 case Mangle.Tstruct: 878 { TypeInfo_Struct tis = cast(TypeInfo_Struct)ti; 879 if (tis.xtoString is null) 880 throw new FormatError("Can't convert " ~ tis.toString() 881 ~ " to string: \"string toString()\" not defined"); 882 s = tis.xtoString(argptr); 883 argptr += (tis.tsize() + 3) & ~3; 884 goto Lputstr; 885 } 886 887 default: 888 goto Lerror; 889 } 890 891 Lnumber: 892 switch (fc) 893 { 894 case 's': 895 case 'd': 896 if (signed) 897 { if (cast(long)vnumber < 0) 898 { prefix = "-"; 899 vnumber = -vnumber; 900 } 901 else if (flags & FLplus) 902 prefix = "+"; 903 else if (flags & FLspace) 904 prefix = " "; 905 } 537 906 break; 538 sl = fbuf.length * 2; 539 fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; 907 908 case 'b': 909 signed = 0; 910 base = 2; 911 break; 912 913 case 'o': 914 signed = 0; 915 base = 8; 916 break; 917 918 case 'X': 919 uc = 1; 920 if (flags & FLhash && vnumber) 921 prefix = "0X"; 922 signed = 0; 923 base = 16; 924 break; 925 926 case 'x': 927 if (flags & FLhash && vnumber) 928 prefix = "0x"; 929 signed = 0; 930 base = 16; 931 break; 932 933 default: 934 goto Lerror; 935 } 936 937 if (!signed) 938 { 939 switch (m) 940 { 941 case Mangle.Tbyte: 942 vnumber &= 0xFF; 943 break; 944 945 case Mangle.Tshort: 946 vnumber &= 0xFFFF; 947 break; 948 949 case Mangle.Tint: 950 vnumber &= 0xFFFFFFFF; 951 break; 952 953 default: 954 break; 540 955 } 541 putstr(fbuf[0 .. sl]); 542 } 543 else 544 { 545 int sl; 546 char[] fbuf = tmpbuf; 547 char[12] format; 548 format[0] = '%'; 549 int i = 1; 550 if (flags & FLdash) 551 format[i++] = '-'; 552 if (flags & FLplus) 553 format[i++] = '+'; 554 if (flags & FLspace) 555 format[i++] = ' '; 556 if (flags & FLhash) 557 format[i++] = '#'; 558 if (flags & FL0pad) 559 format[i++] = '0'; 560 format[i + 0] = '*'; 561 format[i + 1] = '.'; 562 format[i + 2] = '*'; 563 format[i + 3] = 'L'; 564 format[i + 4] = fc; 565 format[i + 5] = 0; 566 if (!(flags & FLprecision)) 567 precision = -1; 568 while (1) 569 { int n; 570 571 sl = fbuf.length; 572 n = snprintf(fbuf.ptr, sl, format.ptr, field_width, precision, v); 573 //printf("format = '%s', n = %d\n", cast(char*)format, n); 574 if (n >= 0 && n < sl) 575 { sl = n; 576 break; 577 } 578 if (n < 0) 579 sl = sl * 2; 956 } 957 958 if (flags & FLprecision && fc != 'p') 959 flags &= ~FL0pad; 960 961 if (vnumber < base) 962 { 963 if (vnumber == 0 && precision == 0 && flags & FLprecision && 964 !(fc == 'o' && flags & FLhash)) 965 { 966 putstr(null); 967 return; 968 } 969 if (precision == 0 || !(flags & FLprecision)) 970 { vchar = cast(char)('0' + vnumber); 971 if (vnumber < 10) 972 vchar = cast(char)('0' + vnumber); 580 973 else 581 sl = n + 1;582 fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl];974 vchar = cast(char)((uc ? 'A' - 10 : 'a' - 10) + vnumber); 975 goto L2; 583 976 } 584 putstr(fbuf[0 .. sl]); 585 } 586 return; 587 } 588 589 static Mangle getMan(TypeInfo ti) 590 { 591 auto m = cast(Mangle)ti.classinfo.name[9]; 592 if (ti.classinfo.name.length == 20 && 593 ti.classinfo.name[9..20] == "StaticArray") 594 m = cast(Mangle)'G'; 595 return m; 596 } 597 598 void putArray(void* p, size_t len, TypeInfo valti) 599 { 600 //printf("\nputArray(len = %u), tsize = %u\n", len, valti.tsize()); 601 putc('['); 602 valti = skipCI(valti); 603 size_t tsize = valti.tsize(); 604 auto argptrSave = argptr; 605 auto tiSave = ti; 606 auto mSave = m; 607 ti = valti; 608 //printf("\n%.*s\n", valti.classinfo.name); 609 m = getMan(valti); 610 while (len--) 611 { 612 //doFormat(putc, (&valti)[0 .. 1], p); 613 argptr = p; 614 formatArg('s'); 615 616 p += tsize; 617 if (len > 0) putc(','); 618 } 619 m = mSave; 620 ti = tiSave; 621 argptr = argptrSave; 622 putc(']'); 623 } 624 625 void putAArray(ubyte[long] vaa, TypeInfo valti, TypeInfo keyti) 626 { 627 putc('['); 628 bool comma=false; 629 auto argptrSave = argptr; 630 auto tiSave = ti; 631 auto mSave = m; 632 valti = skipCI(valti); 633 keyti = skipCI(keyti); 634 foreach(ref fakevalue; vaa) 635 { 636 if (comma) putc(','); 637 comma = true; 638 // the key comes before the value 639 ubyte* key = &fakevalue - long.sizeof; 640 641 //doFormat(putc, (&keyti)[0..1], key); 642 argptr = key; 643 ti = keyti; 644 m = getMan(keyti); 645 formatArg('s'); 646 647 putc(':'); 648 auto keysize = keyti.tsize; 649 keysize = (keysize + 3) & ~3; 650 ubyte* value = key + keysize; 651 //doFormat(putc, (&valti)[0..1], value); 652 argptr = value; 653 ti = valti; 654 m = getMan(valti); 655 formatArg('s'); 656 } 657 m = mSave; 658 ti = tiSave; 659 argptr = argptrSave; 660 putc(']'); 661 } 662 663 //printf("formatArg(fc = '%c', m = '%c')\n", fc, m); 664 switch (m) 665 { 666 case Mangle.Tbool: 667 vbit = va_arg!(bool)(argptr); 668 if (fc != 's') 669 { vnumber = vbit; 670 goto Lnumber; 671 } 672 putstr(vbit ? "true" : "false"); 673 return; 674 675 676 case Mangle.Tchar: 677 vchar = va_arg!(char)(argptr); 678 if (fc != 's') 679 { vnumber = vchar; 680 goto Lnumber; 681 } 682 L2: 683 putstr((&vchar)[0 .. 1]); 684 return; 685 686 case Mangle.Twchar: 687 vdchar = va_arg!(wchar)(argptr); 688 goto L1; 689 690 case Mangle.Tdchar: 691 vdchar = va_arg!(dchar)(argptr); 692 L1: 693 if (fc != 's') 694 { vnumber = vdchar; 695 goto Lnumber; 696 } 697 if (vdchar <= 0x7F) 698 { vchar = cast(char)vdchar; 699 goto L2; 700 } 701 else 702 { if (!isValidDchar(vdchar)) 703 throw new UtfException("invalid dchar in format", 0); 704 char[4] vbuf; 705 putstr(toUTF8(vbuf, vdchar)); 706 } 707 return; 708 709 710 case Mangle.Tbyte: 711 signed = 1; 712 vnumber = va_arg!(byte)(argptr); 713 goto Lnumber; 714 715 case Mangle.Tubyte: 716 vnumber = va_arg!(ubyte)(argptr); 717 goto Lnumber; 718 719 case Mangle.Tshort: 720 signed = 1; 721 vnumber = va_arg!(short)(argptr); 722 goto Lnumber; 723 724 case Mangle.Tushort: 725 vnumber = va_arg!(ushort)(argptr); 726 goto Lnumber; 727 728 case Mangle.Tint: 729 signed = 1; 730 vnumber = va_arg!(int)(argptr); 731 goto Lnumber; 732 733 case Mangle.Tuint: 734 Luint: 735 vnumber = va_arg!(uint)(argptr); 736 goto Lnumber; 737 738 case Mangle.Tlong: 739 signed = 1; 740 vnumber = cast(ulong)va_arg!(long)(argptr); 741 goto Lnumber; 742 743 case Mangle.Tulong: 744 Lulong: 745 vnumber = va_arg!(ulong)(argptr); 746 goto Lnumber; 747 748 case Mangle.Tclass: 749 vobject = va_arg!(Object)(argptr); 750 if (vobject is null) 751 s = "null"; 752 else 753 s = vobject.toString(); 754 goto Lputstr; 755 756 case Mangle.Tpointer: 757 vnumber = cast(ulong)va_arg!(void*)(argptr); 758 if (fc != 'x') uc = 1; 759 flags |= FL0pad; 760 if (!(flags & FLprecision)) 761 { flags |= FLprecision; 762 precision = (void*).sizeof; 763 } 764 base = 16; 765 goto Lnumber; 766 767 768 case Mangle.Tfloat: 769 case Mangle.Tifloat: 770 if (fc == 'x' || fc == 'X') 771 goto Luint; 772 vreal = va_arg!(float)(argptr); 773 goto Lreal; 774 775 case Mangle.Tdouble: 776 case Mangle.Tidouble: 777 if (fc == 'x' || fc == 'X') 778 goto Lulong; 779 vreal = va_arg!(double)(argptr); 780 goto Lreal; 781 782 case Mangle.Treal: 783 case Mangle.Tireal: 784 vreal = va_arg!(real)(argptr); 785 goto Lreal; 786 787 788 case Mangle.Tcfloat: 789 vcreal = va_arg!(cfloat)(argptr); 790 goto Lcomplex; 791 792 case Mangle.Tcdouble: 793 vcreal = va_arg!(cdouble)(argptr); 794 goto Lcomplex; 795 796 case Mangle.Tcreal: 797 vcreal = va_arg!(creal)(argptr); 798 goto Lcomplex; 799 800 case Mangle.Tsarray: 801 putArray(argptr, (cast(TypeInfo_StaticArray)ti).len, (cast(TypeInfo_StaticArray)ti).next); 802 return; 803 804 case Mangle.Tarray: 805 int mi = 10; 806 if (ti.classinfo.name.length == 14 && 807 ti.classinfo.name[9..14] == "Array") 808 { // array of non-primitive types 809 TypeInfo tn = (cast(TypeInfo_Array)ti).next; 810 tn = skipCI(tn); 811 switch (cast(Mangle)tn.classinfo.name[9]) 812 { 813 case Mangle.Tchar: goto LarrayChar; 814 case Mangle.Twchar: goto LarrayWchar; 815 case Mangle.Tdchar: goto LarrayDchar; 816 default: 817 break; 818 } 819 void[] va = va_arg!(void[])(argptr); 820 putArray(va.ptr, va.length, tn); 821 return; 822 } 823 if (ti.classinfo.name.length == 25 && 824 ti.classinfo.name[9..25] == "AssociativeArray") 825 { // associative array 826 ubyte[long] vaa = va_arg!(ubyte[long])(argptr); 827 putAArray(vaa, 828 (cast(TypeInfo_AssociativeArray)ti).next, 829 (cast(TypeInfo_AssociativeArray)ti).key); 830 return; 831 } 832 833 while (1) 834 { 835 m2 = cast(Mangle)ti.classinfo.name[mi]; 836 switch (m2) 837 { 838 case Mangle.Tchar: 839 LarrayChar: 840 s = va_arg!(string)(argptr); 841 goto Lputstr; 842 843 case Mangle.Twchar: 844 LarrayWchar: 845 wchar[] sw = va_arg!(wchar[])(argptr); 846 s = toUTF8(sw); 847 goto Lputstr; 848 849 case Mangle.Tdchar: 850 LarrayDchar: 851 auto sd = va_arg!(dstring)(argptr); 852 s = toUTF8(sd); 853 Lputstr: 854 if (fc != 's') 855 throw new FormatError("string"); 856 if (flags & FLprecision && precision < s.length) 857 s = s[0 .. precision]; 858 putstr(s); 859 break; 860 861 case Mangle.Tconst: 862 case Mangle.Timmutable: 863 mi++; 864 continue; 865 866 default: 867 TypeInfo ti2 = primitiveTypeInfo(m2); 868 if (!ti2) 869 goto Lerror; 870 void[] va = va_arg!(void[])(argptr); 871 putArray(va.ptr, va.length, ti2); 872 } 873 return; 874 } 875 876 case Mangle.Ttypedef: 877 ti = (cast(TypeInfo_Typedef)ti).base; 878 m = cast(Mangle)ti.classinfo.name[9]; 879 formatArg(fc); 880 return; 881 882 case Mangle.Tenum: 883 ti = (cast(TypeInfo_Enum)ti).base; 884 m = cast(Mangle)ti.classinfo.name[9]; 885 formatArg(fc); 886 return; 887 888 case Mangle.Tstruct: 889 { TypeInfo_Struct tis = cast(TypeInfo_Struct)ti; 890 if (tis.xtoString is null) 891 throw new FormatError("Can't convert " ~ tis.toString() ~ " to string: \"string toString()\" not defined"); 892 s = tis.xtoString(argptr); 893 argptr += (tis.tsize() + 3) & ~3; 894 goto Lputstr; 895 } 896 897 default: 898 goto Lerror; 899 } 900 901 Lnumber: 902 switch (fc) 903 { 904 case 's': 905 case 'd': 906 if (signed) 907 { if (cast(long)vnumber < 0) 908 { prefix = "-"; 909 vnumber = -vnumber; 910 } 911 else if (flags & FLplus) 912 prefix = "+"; 913 else if (flags & FLspace) 914 prefix = " "; 915 } 916 break; 917 918 case 'b': 919 signed = 0; 920 base = 2; 921 break; 922 923 case 'o': 924 signed = 0; 925 base = 8; 926 break; 927 928 case 'X': 929 uc = 1; 930 if (flags & FLhash && vnumber) 931 prefix = "0X"; 932 signed = 0; 933 base = 16; 934 break; 935 936 case 'x': 937 if (flags & FLhash && vnumber) 938 prefix = "0x"; 939 signed = 0; 940 base = 16; 941 break; 942 943 default: 944 goto Lerror; 945 } 946 947 if (!signed) 948 { 949 switch (m) 950 { 951 case Mangle.Tbyte: 952 vnumber &= 0xFF; 953 break; 954 955 case Mangle.Tshort: 956 vnumber &= 0xFFFF; 957 break; 958 959 case Mangle.Tint: 960 vnumber &= 0xFFFFFFFF; 961 break; 962 963 default: 964 break; 965 } 966 } 967 968 if (flags & FLprecision && fc != 'p') 969 flags &= ~FL0pad; 970 971 if (vnumber < base) 972 { 973 if (vnumber == 0 && precision == 0 && flags & FLprecision && 974 !(fc == 'o' && flags & FLhash)) 975 { 976 putstr(null); 977 return; 978 } 979 if (precision == 0 || !(flags & FLprecision)) 980 { vchar = cast(char)('0' + vnumber); 981 if (vnumber < 10) 982 vchar = cast(char)('0' + vnumber); 983 else 984 vchar = cast(char)((uc ? 'A' - 10 : 'a' - 10) + vnumber); 985 goto L2; 986 } 987 } 988 989 int n = tmpbuf.length; 990 char c; 991 int hexoffset = uc ? ('A' - ('9' + 1)) : ('a' - ('9' + 1)); 992 993 while (vnumber) 994 { 995 c = cast(char)((vnumber % base) + '0'); 996 if (c > '9') 997 c += hexoffset; 998 vnumber /= base; 999 tmpbuf[--n] = c; 1000 } 1001 if (tmpbuf.length - n < precision && precision < tmpbuf.length) 1002 { 1003 int m = tmpbuf.length - precision; 1004 tmpbuf[m .. n] = '0'; 1005 n = m; 1006 } 1007 else if (flags & FLhash && fc == 'o') 1008 prefix = "0"; 1009 putstr(tmpbuf[n .. tmpbuf.length]); 1010 return; 977 } 978 979 int n = tmpbuf.length; 980 char c; 981 int hexoffset = uc ? ('A' - ('9' + 1)) : ('a' - ('9' + 1)); 982 983 while (vnumber) 984 { 985 c = cast(char)((vnumber % base) + '0'); 986 if (c > '9') 987 c += hexoffset; 988 vnumber /= base; 989 tmpbuf[--n] = c; 990 } 991 if (tmpbuf.length - n < precision && precision < tmpbuf.length) 992 { 993 int m = tmpbuf.length - precision; 994 tmpbuf[m .. n] = '0'; 995 n = m; 996 } 997 else if (flags & FLhash && fc == 'o') 998 prefix = "0"; 999 putstr(tmpbuf[n .. tmpbuf.length]); 1000 return; 1011 1001 1012 1002 Lreal: 1013 1003 putreal(vreal); 1014 1004 return; 1015 1005 1016 1006 Lcomplex: 1017 1007 putreal(vcreal.re); 1018 1008 putc('+'); 1019 1009 putreal(vcreal.im); 1020 1010 putc('i'); 1021 1011 return; 1022 1012 1023 1013 Lerror: 1024 throw new FormatError("formatArg"); 1025 } 1026 1027 1028 for (j = 0; j < arguments.length; ) 1014 throw new FormatError("formatArg"); 1015 } 1016 1017 for (int j = 0; j < arguments.length; ) 1029 1018 { 1030 1019 ti = arguments[j++]; 1031 1020 //printf("test1: '%.*s' %d\n", ti.classinfo.name, 1032 1021 //ti.classinfo.name.length); ti.print(); 1033 1022 1034 1023 flags = 0; 1035 1024 precision = 0; 1036 1025 field_width = 0; 1037 1026 1038 1027 ti = skipCI(ti); … … 1531 1520 assert(std.string.format("%8s", "bar") == " bar"); 1532 1521 assert(std.string.format("%8s", "b\u00e9ll\u00f4") == " b\u00e9ll\u00f4"); 1533 1522 } 1534 1523 1535 1524 // Andrei 1536 1525 //------------------------------------------------------------------------------ 1537 1526 1538 1527 /* 1539 1528 * A compiled version of an individual writef format 1540 1529 * specifier. FormatInfo only focuses on representation, without 1541 * assigning any semantics to the fields. */ 1530 * assigning any semantics to the fields. 1531 */ 1542 1532 struct FormatInfo 1543 1533 { 1544 /** Special values for width and precision, DYNAMIC width or 1534 /** 1535 * Special values for width and precision, DYNAMIC width or 1545 1536 * precision means that they were specified with '*' in the format 1546 * string. */ 1547 enum short DYNAMIC = short.max; 1537 * string. 1538 */ 1539 enum int DYNAMIC = int.max; 1548 1540 /** Special value for precision */ 1549 enum short UNSPECIFIED = DYNAMIC - 1;1541 enum int UNSPECIFIED = DYNAMIC - 1; 1550 1542 /** minimum width, default 0. */ 1551 short width = 0;1543 int width = 0; 1552 1544 /** precision. */ 1553 short precision = UNSPECIFIED;1545 int precision = UNSPECIFIED; 1554 1546 /** The actual format specifier, 's' by default. */ 1555 1547 char spec = 's'; 1556 1548 /** Index of the argument for positional parameters, from 1 to 1557 1549 * ubyte.max. (0 means not used) */ 1558 1550 ubyte index; 1559 1551 /* Flags: flDash for '-', flZero for '0', flSpace for ' ', flPlus 1560 1552 * for '+', flHash for '#'. */ 1561 1553 mixin(bitfields!( 1562 1554 bool, "flDash", 1, 1563 1555 bool, "flZero", 1, 1564 1556 bool, "flSpace", 1, 1565 1557 bool, "flPlus", 1, 1566 1558 bool, "flHash", 1, 1567 1559 ubyte, "", 3)); 1568 1560 /* For arrays only: the trailing */ 1569 1561 const(char)[] innerTrailing, trailing; 1570 1562 1571 /*1572 * Given a string format specification fmt, parses a format1573 * specifier. The string is assumed to start with the character1574 * immediately following the '%'. The string is advanced to right1575 * after the end of the format specifier. */1576 static FormatInfo parse(S)(ref S fmt)1577 {1578 FormatInfo result;1579 if (fmt.empty) return result;1580 scope(success) result.trailing = to!(const(char)[])(fmt);1581 size_t i = 0;1582 for (;;)1563 /** 1564 * Given a string format specification fmt, parses a format 1565 * specifier. The string is assumed to start with the character 1566 * immediately following the '%'. The string is advanced to right 1567 * after the end of the format specifier. 1568 */ 1569 this(S)(ref S fmt) 1570 { 1571 if (fmt.empty) return; 1572 scope(success) trailing = to!(typeof(trailing))(fmt); 1573 for (size_t i = 0; i < fmt.length; ) 1574 { 1583 1575 switch (fmt[i]) 1584 1576 { 1585 case '(': 1586 { 1587 // embedded format specifier 1588 auto j = i + 1; 1589 void check(bool condition) 1577 case '(': 1590 1578 { 1591 enforce( 1592 condition, 1593 text("Incorrect format specifier: %", fmt[i .. $])); 1579 // embedded format specifier 1580 auto j = i + 1; 1581 void check(bool condition) 1582 { 1583 enforce( 1584 condition, 1585 text("Incorrect format specifier: %", fmt[i .. $])); 1586 } 1587 for (uint innerParens;; ++j) 1588 { 1589 check(j < fmt.length); 1590 if (fmt[j] != '%') 1591 { 1592 // skip, we're waiting for %( and %) 1593 continue; 1594 } 1595 ++j; 1596 if (fmt[j] == ')') 1597 { 1598 if (innerParens-- == 0) break; 1599 } 1600 else if (fmt[j] == '(') 1601 { 1602 ++innerParens; 1603 } 1604 } 1605 auto innerFmtSpec = fmt[i + 1 .. j - 1]; 1606 this = FormatInfo(innerFmtSpec); 1607 innerTrailing = to!(typeof(innerTrailing))(innerFmtSpec); 1608 // We practically found the format specifier 1609 i = j + 1; 1610 //raw = to!(const(char)[])(fmt[0 .. i]); 1611 fmt = fmt[i .. $]; 1612 return; 1594 1613 } 1595 for (int innerParens;; ++j) 1596 { 1597 check(j < fmt.length); 1598 if (fmt[j] == ')') 1599 { 1600 if (innerParens-- == 0) break; 1601 } 1602 else if (fmt[j] == '(') 1603 { 1604 ++innerParens; 1605 } 1606 else if (fmt[j] == '%') 1607 { 1608 // skip the '%' and the character following 1609 // it, whatever it is 1610 ++j; 1611 } 1612 } 1613 auto innerFmtSpec = fmt[i + 1 .. j]; 1614 result = parse(innerFmtSpec); 1615 result.innerTrailing = to!(typeof(innerTrailing))(innerFmtSpec); 1616 // We practically found the format specifier 1617 i = j + 1; 1618 //result.raw = to!(const(char)[])(fmt[0 .. i]); 1619 fmt = fmt[i .. $]; 1620 return result; 1621 } 1622 case '-': result.flDash = true; ++i; break; 1623 case '+': result.flPlus = true; ++i; break; 1624 case '#': result.flHash = true; ++i; break; 1625 case '0': result.flZero = true; ++i; break; 1626 case ' ': result.flSpace = true; ++i; break; 1627 case '*': 1628 if (isdigit(fmt[++i])) 1629 { 1630 // a '*' followed by digits and '$' is a positional format 1631 fmt = fmt[1 .. $]; 1632 result.width = -.parse!(typeof(result.width))(fmt); 1633 i = 0; 1634 if (fmt[i++] != '$') throw new FormatError("$ expected"); 1635 } 1636 else 1637 { 1638 // read result 1639 result.width = DYNAMIC; 1640 } 1641 break; 1642 case '1': case '2': case '3': case '4': 1643 case '5': case '6': case '7': case '8': case '9': 1644 auto tmp = fmt[i .. $]; 1645 const widthOrArgIndex = .parse!(ushort)(tmp); 1646 assert(tmp.length, 1647 text("Incorrect format specifier %", fmt[i .. $])); 1648 i = tmp.ptr - fmt.ptr; 1649 if (tmp.length && tmp[0] == '$') 1650 { 1651 // index! 1652 result.index = to!(ubyte)(widthOrArgIndex); 1653 ++i; 1654 } 1655 else 1656 { 1657 // width 1658 result.width = cast(short) widthOrArgIndex; 1659 } 1660 break; 1661 case '.': 1662 if (fmt[++i] == '*') 1663 { 1614 case '-': flDash = true; ++i; break; 1615 case '+': flPlus = true; ++i; break; 1616 case '#': flHash = true; ++i; break; 1617 case '0': flZero = true; ++i; break; 1618 case ' ': flSpace = true; ++i; break; 1619 case '*': 1664 1620 if (isdigit(fmt[++i])) 1665 1621 { 1666 // a '.*' followed by digits and '$' is a 1667 // positional precision 1668 fmt = fmt[i .. $]; 1622 // a '*' followed by digits and '$' is a 1623 // positional format 1624 fmt = fmt[1 .. $]; 1625 width = -.parse!(typeof(width))(fmt); 1669 1626 i = 0; 1670 result.precision = cast(short) -.parse!(int)(fmt); 1671 if (fmt[i++] != '$') 1672 { 1673 throw new FormatError("$ expected"); 1674 } 1627 enforce(fmt[i++] == '$', new FormatError("$ expected")); 1675 1628 } 1676 1629 else 1677 1630 { 1678 1631 // read result 1679 result.precision= DYNAMIC;1632 width = DYNAMIC; 1680 1633 } 1681 } 1682 else if (fmt[i] == '-') 1683 { 1684 // negative precision, as good as 0 1685 result.precision = 0; 1634 break; 1635 case '1': .. case '9': 1686 1636 auto tmp = fmt[i .. $]; 1687 .parse!(int)(tmp); // skip digits 1637 const widthOrArgIndex = .parse!(uint)(tmp); 1638 assert(tmp.length, 1639 text("Incorrect format specifier %", fmt[i .. $])); 1688 1640 i = tmp.ptr - fmt.ptr; 1689 } 1690 else if (isdigit(fmt[i])) 1691 { 1692 auto tmp = fmt[i .. $]; 1693 result.precision = .parse!(short)(tmp); 1694 i = tmp.ptr - fmt.ptr; 1695 } 1696 else 1697 { 1698 // "." was specified, but nothing after it 1699 result.precision = 0; 1700 } 1701 break; 1702 default: 1703 // this is the format char 1704 result.spec = cast(char)fmt[i++]; 1705 //result.raw = to!(const(char)[])(fmt[0 .. i]); 1706 fmt = fmt[i .. $]; 1707 return result; 1708 } // end switch and for 1641 if (tmp.length && tmp[0] == '$') 1642 { 1643 // index! 1644 index = to!(ubyte)(widthOrArgIndex); 1645 ++i; 1646 } 1647 else 1648 { 1649 // width 1650 width = to!int(widthOrArgIndex); 1651 } 1652 break; 1653 case '.': 1654 if (fmt[++i] == '*') 1655 { 1656 if (isdigit(fmt[++i])) 1657 { 1658 // a '.*' followed by digits and '$' is a 1659 // positional precision 1660 fmt = fmt[i .. $]; 1661 i = 0; 1662 precision = -.parse!(int)(fmt); 1663 if (fmt[i++] != '$') 1664 { 1665 throw new FormatError("$ expected"); 1666 } 1667 } 1668 else 1669 { 1670 // read result 1671 precision = DYNAMIC; 1672 } 1673 } 1674 else if (fmt[i] == '-') 1675 { 1676 // negative precision, as good as 0 1677 precision = 0; 1678 auto tmp = fmt[i .. $]; 1679 .parse!(int)(tmp); // skip digits 1680 i = tmp.ptr - fmt.ptr; 1681 } 1682 else if (isdigit(fmt[i])) 1683 { 1684 auto tmp = fmt[i .. $]; 1685 precision = .parse!int(tmp); 1686 i = tmp.ptr - fmt.ptr; 1687 } 1688 else 1689 { 1690 // "." was specified, but nothing after it 1691 precision = 0; 1692 } 1693 break; 1694 default: 1695 // this is the format char 1696 spec = cast(char)fmt[i++]; 1697 fmt = fmt[i .. $]; 1698 return; 1699 } // end switch 1700 } // end for 1701 enforce(false, text("Incorrect format specifier: ", fmt)); 1709 1702 } 1710 1703 } 1711 1704 1712 1705 //------------------------------------------------------------------------------ 1713 1706 // Writes characters in the format strings up to the first format 1714 1707 // specifier and updates the format specifier to remove the written 1715 1708 // portion The updated format fmt does not include the '%' 1716 private void writeUpToFormatSpec(OutRange, S)( refOutRange w, ref S fmt)1709 private void writeUpToFormatSpec(OutRange, S)(OutRange w, ref S fmt) 1717 1710 { 1718 1711 for (size_t i = 0; i < fmt.length; ++i) 1719 1712 { 1720 1713 if (fmt[i] != '%') continue; 1721 1714 if (fmt[++i] != '%') 1722 1715 { 1723 1716 // spec found, print and bailout 1724 1717 w.put(fmt[0 .. i - 1]); 1725 1718 fmt = fmt[i .. $]; 1726 1719 return; … … 1731 1724 fmt = fmt[i + 1 .. $]; 1732 1725 i = 0; 1733 1726 } 1734 1727 // no format spec found 1735 1728 w.put(fmt); 1736 1729 fmt = null; 1737 1730 } 1738 1731 1739 1732 unittest 1740 1733 { 1741 Appender!(string) w; 1734 string s; 1735 auto w = appender(&s); 1742 1736 string fmt = "abc%sdef%sghi"; 1743 1737 writeUpToFormatSpec(w, fmt); 1744 assert(w.data == "abc" && fmt == "sdef%sghi"); 1738 assert(w.data == "abc"); 1739 assert(fmt == "sdef%sghi"); 1745 1740 writeUpToFormatSpec(w, fmt); 1746 1741 assert(w.data == "abcsdef" && fmt == "sghi"); 1747 1742 // test with embedded %%s 1748 1743 fmt = "ab%%cd%%ef%sg%%h%sij"; 1749 1744 w.clear; 1750 1745 writeUpToFormatSpec(w, fmt); 1751 1746 assert(w.data == "ab%cd%ef" && fmt == "sg%%h%sij"); 1752 1747 writeUpToFormatSpec(w, fmt); 1753 1748 assert(w.data == "ab%cd%efsg%h" && fmt == "sij"); 1754 1749 } 1755 1750 1756 1751 /* 1757 1752 * Formats an integral number 'arg' according to 'f' and writes it to 1758 1753 * 'w'. 1759 */ 1760 private void formatImpl(Writer, D)( refWriter w, D argx, FormatInfo f)1754 */ 1755 private void formatImpl(Writer, D)(Writer w, D argx, FormatInfo f) 1761 1756 if (isIntegral!(D)) 1762 1757 { 1763 1758 Unqual!(D) arg = argx; 1764 1759 if (f.spec == 'r') 1765 1760 { 1766 1761 // raw write, skip all else and write the thing 1767 1762 auto begin = cast(const char*) &arg; 1768 1763 if (std.system.endian == Endian.LittleEndian && f.flPlus 1769 1764 || std.system.endian == Endian.BigEndian && f.flDash) 1770 1765 { … … 1844 1839 forcedPrefix = '0'; 1845 1840 } 1846 1841 // write left pad; write sign; write 0x or 0X; write digits; 1847 1842 // write right pad 1848 1843 // Writing left pad 1849 1844 int spacesToPrint = 1850 1845 f.width // start with the minimum width 1851 1846 - digits.length // take away digits to print 1852 1847 - (forcedPrefix != 0) // take away the sign if any 1853 1848 - (base == 16 && f.flHash() && arg ? 2 : 0); // 0x or 0X 1854 int delta = f.precision - digits.length;1849 const int delta = f.precision - digits.length; 1855 1850 if (delta > 0) spacesToPrint -= delta; 1856 1851 //writeln(spacesToPrint); 1857 1852 if (spacesToPrint > 0) // need to do some padding 1858 1853 { 1859 1854 if (leftPad == '0') 1860 1855 { 1861 1856 // pad with zeros 1862 1857 f.precision = 1863 1858 cast(typeof(f.precision)) (spacesToPrint + digits.length); 1864 1859 //to!(typeof(f.precision))(spacesToPrint + digits.length); … … 2090 2085 auto s = obj.toString; 2091 2086 w.put(s); 2092 2087 } else { 2093 2088 // last resort: just print type name 2094 2089 w.put(D.stringof); 2095 2090 } 2096 2091 } 2097 2092 2098 2093 unittest 2099 2094 { 2100 Appender!(immutable(char)[]) w; 2095 string s; 2096 auto w = appender(&s); 2101 2097 int[] a = [ 1, 3, 2 ]; 2102 formattedWrite(w, "testing %(s,) embedded", a);2103 assert(w.data == "testing 1, 3, 2 embedded", w.data);2098 formattedWrite(w, "testing %(s, %) embedded", a); 2099 assert(w.data == "testing 1, 3, 2 embedded", w.data); 2104 2100 w.clear; 2105 formattedWrite(w, "testing (%(s %) %()) embedded", a);2101 formattedWrite(w, "testing (%(s) (%)) embedded", a); 2106 2102 assert(w.data == "testing (1) (3) (2) embedded", w.data); 2107 2103 2108 2104 int[0] empt = []; 2109 2105 w.clear; 2110 2106 formattedWrite(w, "(%s)", empt); 2111 2107 assert(w.data == "()", w.data); 2112 2108 } 2113 2109 2114 2110 //------------------------------------------------------------------------------ 2115 2111 // Fix for issue 1591 … … 2171 2167 The positional and non-positional styles can be mixed in the same 2172 2168 format string. (POSIX leaves this behavior undefined.) The internal 2173 2169 counter for non-positional parameters tracks the next parameter after 2174 2170 the largest positional parameter already used. 2175 2171 2176 2172 Warning: 2177 2173 This is the function internally used by writef* but it's still 2178 2174 undergoing active development. Do not rely on it. 2179 2175 */ 2180 2176 2181 void formattedWrite(Writer, F, A...)( refWriter w, const(F)[] fmt, A args)2177 void formattedWrite(Writer, F, A...)(Writer w, const(F)[] fmt, A args) 2182 2178 { 2183 2179 enum len = args.length; 2184 2180 void function(ref Writer, const(void)*, FormatInfo) funs[len] = void; 2185 2181 const(void)* argsAddresses[len] = void; 2186 2182 foreach (i, arg; args) 2187 2183 { 2188 2184 funs[i] = &formatGeneric!(Writer, typeof(arg)); 2189 2185 argsAddresses[i] = &arg; 2190 2186 } 2191 2187 // Are we already done with formats? Then just dump each parameter in turn 2192 2188 uint currentArg = 0; 2193 2189 for (;;) 2194 2190 { 2195 2191 writeUpToFormatSpec(w, fmt); 2196 2192 if (fmt.length == 0) 2197 2193 { 2198 2194 // New behavior: break if there are too few specifiers AND 2199 2195 // at least one specifier did exist 2200 2196 break; 2201 2197 } 2202 auto spec = FormatInfo .parse(fmt);2198 auto spec = FormatInfo(fmt); 2203 2199 if (currentArg == funs.length && !spec.index) 2204 2200 { 2205 2201 // leftover spec? 2206 if (fmt.length) 2207 { 2208 throw new FormatError( 2209 cast(string) ("Orphan format specifier: %" ~ fmt)); 2210 } 2202 enforce(fmt.length == 0, new FormatError( 2203 cast(string) ("Orphan format specifier: %" ~ fmt))); 2211 2204 break; 2212 2205 } 2213 2206 if (spec.width == FormatInfo.DYNAMIC) 2214 2207 { 2215 2208 auto width = to!(typeof(spec.width))(getNthInt(currentArg, args)); 2216 2209 if (width < 0) 2217 2210 { 2218 2211 spec.flDash = true; 2219 width = cast(short)(0 - width);2212 width = -width; 2220 2213 } 2221 2214 spec.width = width; 2222 2215 ++currentArg; 2223 2216 } 2224 2217 else if (spec.width < 0) 2225 2218 { 2226 2219 // means: get width as a positional parameter 2227 2220 auto index = cast(uint) -spec.width; 2228 2221 auto width = to!(typeof(spec.width))(getNthInt(index, args)); 2229 2222 if (currentArg < index) currentArg = index; 2230 2223 if (width < 0) 2231 2224 { 2232 2225 spec.flDash = true; 2233 width = cast(short)(0 - width);2226 width = -width; 2234 2227 } 2235 2228 spec.width = width; 2236 2229 } 2237 2230 if (spec.precision == FormatInfo.DYNAMIC) 2238 2231 { 2239 2232 auto precision = to!(typeof(spec.precision))( 2240 2233 getNthInt(currentArg, args)); 2241 2234 if (precision >= 0) spec.precision = precision; 2242 2235 // else negative precision is same as no precision 2243 2236 else spec.precision = FormatInfo.UNSPECIFIED; … … 2266 2259 funs[currentArg](w, argsAddresses[currentArg], spec); 2267 2260 ++currentArg; 2268 2261 } 2269 2262 } 2270 2263 } 2271 2264 2272 2265 /* ======================== Unit Tests ====================================== */ 2273 2266 2274 2267 unittest 2275 2268 { 2276 Appender!(string) stream;2269 auto stream = appender((string*).init); 2277 2270 formattedWrite(stream, "%s", 1.1); 2278 2271 assert(stream.data == "1.1", stream.data); 2279 2272 } 2280 2273 2281 2274 unittest 2282 2275 { 2283 Appender!(string) stream;2276 auto stream = appender((string*).init); 2284 2277 formattedWrite(stream, "%u", 42); 2285 2278 assert(stream.data == "42", stream.data); 2286 2279 } 2287 2280 2288 2281 unittest 2289 2282 { 2290 2283 // testing raw writes 2291 Appender!(string) w; 2284 string s; 2285 auto w = appender(&s); 2292 2286 uint a = 0x02030405; 2293 2287 formattedWrite(w, "%+r", a); 2294 2288 assert(w.data.length == 4 && w.data[0] == 2 && w.data[1] == 3 2295 2289 && w.data[2] == 4 && w.data[3] == 5); 2296 2290 w.clear; 2297 2291 formattedWrite(w, "%-r", a); 2298 2292 assert(w.data.length == 4 && w.data[0] == 5 && w.data[1] == 4 2299 2293 && w.data[2] == 3 && w.data[3] == 2); 2300 2294 } 2301 2295 2302 2296 unittest 2303 2297 { 2304 2298 // testing positional parameters 2305 Appender!(string) w;2299 auto w = appender!string(); 2306 2300 formattedWrite(w, 2307 2301 "Numbers %2$s and %1$s are reversed and %1$s%2$s repeated", 2308 2302 42, 0); 2309 2303 assert(w.data == "Numbers 0 and 42 are reversed and 420 repeated", 2310 2304 w.data); 2311 2305 w.clear; 2312 2306 formattedWrite(w, "asd%s", 23); 2313 2307 assert(w.data == "asd23", w.data); 2314 2308 w.clear; 2315 2309 formattedWrite(w, "%s%s", 23, 45); 2316 2310 assert(w.data == "2345", w.data); 2317 2311 } 2318 2312 2319 2313 unittest 2320 2314 { 2321 2315 debug(format) printf("std.format.format.unittest\n"); 2322 2323 Appender!(string) stream;2316 2317 auto stream = appender!string(); 2324 2318 //goto here; 2325 2319 2326 2320 formattedWrite(stream, 2327 2321 "hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo"); 2328 2322 assert(stream.data == "hello world! true 57 ", 2329 2323 stream.data); 2330 2324 2331 2325 stream.clear; 2332 2326 formattedWrite(stream, "%g %A %s", 1.67, -1.28, float.nan); 2333 2327 //std.c.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr); … … 2744 2738 writeln("Expected: >", exp, "<"); 2745 2739 writeln("Actual: >", stream.data, 2746 2740 "<"); 2747 2741 assert(false); 2748 2742 } 2749 2743 }+/ 2750 2744 } 2751 2745 2752 2746 unittest 2753 2747 { 2754 immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);2755 if (false) writeln(aa.keys);2756 assert(aa[3] == "hello");2757 assert(aa[4] == "betty");2758 if (false)2759 {2760 writeln(aa.values[0]);2761 writeln(aa.values[1]);2762 writefln("%s", typeid(typeof(aa.values)));2763 writefln("%s", aa[3]);2764 writefln("%s", aa[4]);2765 writefln("%s", aa.values);2766 //writefln("%s", aa);2767 wstring a = "abcd";2768 writefln(a);2769 dstring b = "abcd";2770 writefln(b);2771 }2772 2773 Appender!(string) stream;2774 alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong,2775 float, double, real,2776 ifloat, idouble, ireal, cfloat, cdouble, creal) AllNumerics;2777 foreach (T; AllNumerics)2778 {2779 static if (is(T : ireal))2780 T value = 1i;2781 else static if (is(T : creal))2782 T value = 1 + 1i;2783 else2784 T value = 1;2785 stream.clear; formattedWrite(stream, "%s", value);2786 static if (is(T : creal))2787 assert(stream.data == "1+1i");2788 else2789 assert(stream.data == "1");2790 // test typedefs too2791 typedef T Wyda;2792 Wyda another = 1;2793 stream.clear; formattedWrite(stream, "%s", another);2794 assert(stream.data == "1");2795 }2796 2797 //auto r = std.string.format("%s", aa.values);2798 stream.clear; formattedWrite(stream, "%s", aa);2799 assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]", stream.data);2748 immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); 2749 if (false) writeln(aa.keys); 2750 assert(aa[3] == "hello"); 2751 assert(aa[4] == "betty"); 2752 // if (false) 2753 // { 2754 // writeln(aa.values[0]); 2755 // writeln(aa.values[1]); 2756 // writefln("%s", typeid(typeof(aa.values))); 2757 // writefln("%s", aa[3]); 2758 // writefln("%s", aa[4]); 2759 // writefln("%s", aa.values); 2760 // //writefln("%s", aa); 2761 // wstring a = "abcd"; 2762 // writefln(a); 2763 // dstring b = "abcd"; 2764 // writefln(b); 2765 // } 2766 2767 auto stream = appender!string(); 2768 alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, 2769 float, double, real, 2770 ifloat, idouble, ireal, cfloat, cdouble, creal) AllNumerics; 2771 foreach (T; AllNumerics) 2772 { 2773 static if (is(T : ireal)) 2774 T value = 1i; 2775 else static if (is(T : creal)) 2776 T value = 1 + 1i; 2777 else 2778 T value = 1; 2779 stream.clear; formattedWrite(stream, "%s", value); 2780 static if (is(T : creal)) 2781 assert(stream.data == "1+1i"); 2782 else 2783 assert(stream.data == "1"); 2784 // test typedefs too 2785 typedef T Wyda; 2786 Wyda another = 1; 2787 stream.clear; formattedWrite(stream, "%s", another); 2788 assert(stream.data == "1"); 2789 } 2790 2791 //auto r = std.string.format("%s", aa.values); 2792 stream.clear; formattedWrite(stream, "%s", aa); 2793 assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]", stream.data); 2800 2794 // r = std.string.format("%s", aa); 2801 2795 // assert(r == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]"); 2802 2796 } 2803 2797 2804 2798 //------------------------------------------------------------------------------ 2805 2799 void formattedRead(R, S...)(ref R r, const(char)[] fmt, S args) 2806 2800 { 2807 2801 static if (!S.length) 2808 2802 { 2809 r =parseToFormatSpec(r, fmt);2803 parseToFormatSpec(r, fmt); 2810 2804 enforce(fmt.length == 0); 2811 2805 } 2812 2806 else 2813 2807 { 2814 2808 FormatInfo spec; 2815 2809 // The loop below accounts for '*' == fields meant to be read 2816 2810 // and skipped 2817 2811 for (;;) 2818 2812 { 2819 r =parseToFormatSpec(r, fmt);2820 spec = FormatInfo .parse(fmt);2813 parseToFormatSpec(r, fmt); 2814 spec = FormatInfo(fmt); 2821 2815 if (spec.width != spec.DYNAMIC) break; 2822 2816 // must skip this field 2823 2817 skipData(r, spec); 2824 2818 } 2825 2819 alias typeof(*args[0]) A; 2826 2820 //@@@BUG 2725 2827 2821 //static if (is(A X == Tuple!(T), T)) 2828 2822 static if (is(A.Types[0])) 2829 2823 { 2830 2824 //@@@BUG … … 2835 2829 // args[1 .. $]); 2836 2830 // else 2837 2831 // return formattedRead(r, fmt, args[1 .. $]); 2838 2832 // assume it's a tuple 2839 2833 foreach (i, T; A.Types) 2840 2834 { 2841 2835 //writeln("Parsing ", r, " with format ", fmt); 2842 2836 args[0].field[i] = unformat!(T)(r, spec); 2843 2837 for (;;) 2844 2838 { 2845 r =parseToFormatSpec(r, fmt);2846 spec = FormatInfo .parse(fmt);2839 parseToFormatSpec(r, fmt); 2840 spec = FormatInfo(fmt); 2847 2841 if (spec.width != spec.DYNAMIC) break; 2848 2842 // must skip this guy 2849 2843 skipData(r, spec); 2850 2844 } 2851 2845 } 2852 2846 return formattedRead(r, fmt, args[1 .. $]); 2853 2847 } 2854 2848 else 2855 2849 { 2856 2850 *args[0] = unformat!(A)(r, spec); … … 2870 2864 break; 2871 2865 default: 2872 2866 assert(false, text("Not understood: ", spec.spec)); 2873 2867 } 2874 2868 } 2875 2869 2876 2870 //------------------------------------------------------------------------------ 2877 2871 T unformat(T, Range)(ref Range input, FormatInfo spec) 2878 2872 if (isArray!T && !isSomeString!T) 2879 2873 { 2880 Appender!(T) app;2874 auto app = appender!T(); 2881 2875 for (;;) 2882 2876 { 2883 2877 auto e = parse!(ElementType!(T))(input); 2884 2878 app.put(e); 2885 2879 if (!std.string.startsWith(input, spec.innerTrailing)) break; // done 2886 2880 input = input[spec.innerTrailing.length .. $]; 2887 2881 if (input.empty) break; // the trailing is terminator, not 2888 2882 // separator 2889 2883 } 2890 2884 return app.data; 2891 2885 } 2892 2886 2893 2887 //------------------------------------------------------------------------------ 2894 T unformat(T, Range)(ref Range input, FormatInfo spec) if (isSomeString!T) 2895 { 2896 Appender!(T) app; 2888 T unformat(T, Range)(ref Range input, FormatInfo spec) 2889 if (isInputRange!Range && isSomeString!T) 2890 { 2891 auto app = appender!T(); 2897 2892 if (spec.trailing.empty) 2898 2893 { 2899 2894 for (; !input.empty; input.popFront()) 2900 2895 { 2901 2896 app.put(input.front); 2902 2897 } 2903 2898 } 2904 2899 else 2905 2900 { 2906 for (; !input.empty && !input.startsWith(spec.trailing.front);2901 for (; !input.empty && input.front != spec.trailing.front; 2907 2902 input.popFront()) 2908 2903 { 2909 2904 app.put(input.front); 2910 2905 } 2911 2906 } 2912 //input = input[spec.innerTrailing.length .. $];2913 return app.data;2907 auto result = app.data; 2908 return result; 2914 2909 } 2915 2910 2916 2911 unittest 2917 2912 { 2918 2913 string s1, s2; 2919 2914 char[] line = "hello, world".dup; 2920 2915 formattedRead(line, "%s", &s1); 2921 2916 assert(s1 == "hello, world", s1); 2922 2917 2923 2918 line = "hello, world;yah".dup; … … 2927 2922 } 2928 2923 2929 2924 private template acceptedSpecs(T) 2930 2925 { 2931 2926 static if (isIntegral!T) enum acceptedSpecs = "sdu";// + "coxX" (todo) 2932 2927 else static if (isFloatingPoint!T) enum acceptedSpecs = "seEfgG"; 2933 2928 else enum acceptedSpecs = ""; 2934 2929 } 2935 2930 2936 2931 //------------------------------------------------------------------------------ 2937 T unformat(T, Range)(ref Range input, FormatInfo spec) if (isIntegral!T) 2932 T unformat(T, Range)(ref Range input, FormatInfo spec) 2933 if (isIntegral!T && isInputRange!Range) 2938 2934 { 2939 2935 enforce(std.algorithm.find("cdosuxX", spec.spec).length, 2940 2936 text("Wrong integral type specifier: `", spec.spec, "'")); 2941 2937 if (std.algorithm.find("dsu", spec.spec).length) 2942 2938 { 2943 2939 return parse!T(input); 2944 2940 } 2945 2941 assert(0, "Parsing spec '"~spec.spec~"' not implemented."); 2946 2942 } 2947 2943 2948 2944 //------------------------------------------------------------------------------ 2949 T unformat(T, Range)(ref Range input, FormatInfo spec) if (isFloatingPoint!T) 2945 T unformat(T, Range)(ref Range input, FormatInfo spec) 2946 if (isFloatingPoint!T) 2950 2947 { 2951 2948 if (spec.spec == 'r') 2952 2949 { 2953 2950 // raw read 2954 enforce(input.length >= T.sizeof);2951 //enforce(input.length >= T.sizeof); 2955 2952 enforce(isSomeString!Range || ElementType!(Range).sizeof == 1); 2956 2953 union X 2957 2954 { 2958 2955 ubyte[T.sizeof] raw; 2959 2956 T typed; 2960 2957 } 2961 2958 X x; 2962 2959 foreach (i; 0 .. T.sizeof) 2963 2960 { 2964 2961 static if (isSomeString!Range) 2965 2962 { 2966 2963 x.raw[i] = input[0]; 2967 2964 input = input[1 .. $]; 2968 2965 } 2969 2966 else 2970 2967 { 2971 x.raw[i] = input.front; 2968 // TODO: recheck this 2969 x.raw[i] = cast(ubyte) input.front; 2972 2970 input.popFront(); 2973 2971 } 2974 2972 } 2975 2973 return x.typed; 2976 2974 } 2977 2975 enforce(std.algorithm.find(acceptedSpecs!T, spec.spec).length, 2978 2976 text("Format specifier `", spec.spec, 2979 2977 "' not accepted for floating point types")); 2980 2978 return parse!T(input); 2981 }2982 2983 //------------------------------------------------------------------------------2984 T unformat(T, Range)(ref Range input, FormatInfo spec) if (is(T == typedef))2985 {2986 static if (is(T Original == typedef))2987 {2988 return cast(T) unformat!Original(input, spec);2989 }2990 else2991 {2992 static assert(false);2993 }2994 }2995 2996 unittest2997 {2998 typedef int Int;2999 string s = "123";3000 Int x;3001 formattedRead(s, "%s", &x);3002 assert(x == 123);3003 2979 } 3004 2980 3005 2981 unittest 3006 2982 { 3007 2983 union A 3008 2984 { 3009 2985 char[float.sizeof] untyped; 3010 2986 float typed; 3011 2987 }; 3012 2988 A a; 3013 2989 a.typed = 5.5; 3014 2990 char[] input = a.untyped[]; 3015 2991 float witness; 3016 2992 formattedRead(input, "%r", &witness); 3017 2993 assert(witness == a.typed); 3018 2994 } 3019 2995 3020 2996 //------------------------------------------------------------------------------ 3021 private R parseToFormatSpec(R)(R r, ref const(char)[] fmt)2997 private void parseToFormatSpec(R)(ref R r, ref const(char)[] fmt) 3022 2998 { 3023 2999 while (fmt.length) 3024 3000 { 3025 3001 if (fmt[0] == '%') 3026 3002 { 3027 3003 if (fmt.length > 1 && fmt[1] == '%') 3028 3004 { 3029 3005 assert(!r.empty); 3030 3006 // Require a '%' 3031 3007 if (r.front != '%') break; … … 3035 3011 else 3036 3012 { 3037 3013 fmt.popFront; 3038 3014 break; 3039 3015 } 3040 3016 } 3041 3017 else 3042 3018 { 3043 3019 if (fmt.front == ' ') 3044 3020 { 3045 r = std.algorithm.find!(not!isspace)(r); 3021 while (!r.empty && isspace(r.front)) r.popFront(); 3022 //r = std.algorithm.find!(not!isspace)(r); 3046 3023 } 3047 3024 else 3048 3025 { 3049 enforce( 3050 !r.empty, 3051 text("parseToFormatSpec: Cannot find character `", 3052 fmt.front, "' in the input string.")); 3026 enforce(!r.empty, 3027 text("parseToFormatSpec: Cannot find character `", 3028 fmt.front, "' in the input string.")); 3053 3029 //static assert(0); 3054 3030 if (r.front != fmt.front) break; 3055 3031 r.popFront; 3056 3032 } 3057 3033 fmt.popFront; 3058 3034 } 3059 3035 } 3060 return r;3061 3036 } 3062 3037 3063 3038 unittest 3064 3039 { 3065 3040 char[] line = "1 2".dup; 3066 3041 string format = "%s %s"; 3067 3042 int a, b; 3068 3043 formattedRead(line, format, &a, &b); 3069 3044 assert(a == 1 && b == 2); 3070 3045 3046 line = "1 2 3".dup; 3047 format = "%d "; 3048 formattedRead(line, format, &a); 3049 assert(a == 1); 3050 3071 3051 Tuple!(int, float) t; 3072 3052 line = "1 2.125".dup; 3073 3053 formattedRead(line, format, &t); 3074 3054 assert(t.field[0] == 1 && t.field[1] == 2.125); 3075 3055 3076 3056 line = "1 7643 2.125".dup; 3077 3057 formattedRead(line, "%s %*u %s", &t); 3078 3058 assert(t.field[0] == 1 && t.field[1] == 2.125); 3079 3059 }
