Shahid4

Untitled

Jun 22nd, 2012
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
D 37.54 KB | None | 0 0
  1. /*******************************************************************************
  2.  
  3.     Authors: Shahid
  4.  
  5.     Date: 20, December 2011
  6.  
  7.     Version: 0.3
  8.  
  9.     Bugs:    Segfaults on invalid utf8
  10.  
  11.     Notes:   I've now realised that each Type will need it's own truncation code
  12.  
  13.     History: 0.3 [24.03.11] - refactored in big Itoa changes
  14.          0.21[31.12.11] - Support the Varient type
  15.          0.21[20.12.11] - better itoa (non-asm)
  16.          0.2 [25.07.11] - better itoa
  17.          0.1 [14.07.11] - initial working code
  18.          0.0 [25.04.11] - initial printf idea
  19.  
  20.     TODO:
  21.         Support 0 padding for Decimal printing ( sign after padding )
  22.         Printing arrays
  23.         Compile time state
  24.         repeat( string, times )
  25.  
  26.     Examples:
  27.  
  28.     Text I/O ( but only Output at the moment )
  29.  
  30.  *******************************************************************************/
  31. module  shd.io.text;
  32.  
  33. import  shd.exception,
  34.     shd.types,
  35.     shd.unicode;
  36.  
  37. import  shd.aSm.x86.array;
  38.  
  39. import  GlibC = shd.c.glibc: snprintf;
  40.  
  41. import  shd.text.convert;
  42.  
  43.  
  44. public enum Colour
  45. {
  46.     Default=0,
  47.     // The Standard Colour set values
  48.     Red=1, Yellow, Green, Blue, Orange=5, Purple, Black, White, Grey, Brown=10,
  49.     /* ... more to come! */
  50.     Max // The last colour +1
  51. }
  52.  
  53.  
  54. /// Backend of TextFormater
  55. interface TextPrinter
  56. {
  57.     void  colour( int x );
  58.     void  flush();
  59.     void  print( char_c[] s );
  60.  
  61.     int   width( dchar d ); // what is this?
  62. }
  63.  
  64. /**
  65.  * String Formatter inspired by C# and python
  66.  *
  67.  * Start Date: 14, March 2011
  68.  */
  69. class TextFormatter
  70. {
  71.     private {
  72.         TextPrinter printer;
  73.     }
  74.  
  75.     this( TextPrinter printer )
  76.     {
  77.         this.printer = printer;
  78.     }
  79.  
  80.     /**
  81.      * Print text
  82.      */
  83.     final void print(A...)( A args )
  84.     {
  85.         // REFACTOR
  86.         foreach( arg; args )
  87.             format( "{}", arg );
  88.     }
  89.     final void println(A...)( A args ) /// ditto
  90.     {
  91.         print( args );
  92.         printer.print(['\n']);
  93.         printer.flush();
  94.     }
  95.     /**
  96.      * Print formatted text
  97.      */
  98.     final void format(A...)( string formatString, A args )
  99.     {
  100.         Variant[A.length] a;
  101.  
  102.         foreach( i, arg; args )
  103.         {
  104.             a[i].set( arg );
  105.         }
  106.         parse( formatString, a[] );
  107.     }
  108.     final void formatln(A...)( string formatString, A args ) //ditto
  109.     {
  110.         format( formatString, args );
  111.         printer.print(['\n']);
  112.         printer.flush();
  113.     }
  114.  
  115.     /**
  116.      * Select the colour by index code
  117.      *
  118.      * Params: x - an "enum Colour". Though implementions may define more ( hence type int ).
  119.      */
  120.     void colour( int c ) { printer.colour( c ); }
  121.  
  122.     /// Shortcuts for colour()
  123.     void normal()   { printer.colour( Colour.Default ); }
  124.     void red()  { printer.colour( Colour.Red     ); }
  125.     void yellow()   { printer.colour( Colour.Yellow  ); }
  126.     void green()    { printer.colour( Colour.Green   ); }
  127.     void blue() { printer.colour( Colour.Blue    ); }
  128.     void orange()   { printer.colour( Colour.Orange  ); }
  129.     void purple()   { printer.colour( Colour.Purple  ); }
  130.     void black()    { printer.colour( Colour.Black   ); }
  131.     void white()    { printer.colour( Colour.White   ); }
  132.     void grey() { printer.colour( Colour.Grey    ); }
  133.     void brown()    { printer.colour( Colour.Brown   ); }
  134.  
  135.     void flush()    { printer.flush();      }
  136.  
  137.     /* *************************************
  138.             Internals
  139.      ***************************************/
  140.  
  141.     /*
  142.      * Implementation of format()
  143.      *
  144.      * TODO explain { Error msgs }
  145.      *
  146.      */
  147.     private void parse( char_c[] format, ref Variant[] args )
  148.     {
  149.         char_c* ptr = format.ptr;
  150.         char_c* end = format.ptr + format.length;
  151.  
  152.         int      indexAuto; // Remember the last index used + 1
  153.         char[64] buffer;    // must be 16 byte aligned ( not anymore )
  154.  
  155.         //
  156.         // Begin
  157.         //
  158.         while( true )
  159.         {
  160.             AlignState  s;          // Formatting state
  161.             uint        index = indexAuto;
  162.             char_c[]    formatMod;      // eg {:derp}
  163.  
  164.             char_c*  cp = find!char( '{', ptr, end );
  165.  
  166.             // write out
  167.             printer.print( ptr[0 .. cp-ptr] );
  168.             if( cp is end )
  169.                 break;
  170.  
  171.             cp++;
  172.  
  173.             //
  174.             // Second '{'. ie "{{"
  175.             //
  176.             if( *cp == '{' )
  177.             {
  178.                 ptr = cp+1;
  179.                 printer.print( "{" ); // REFACTOR
  180.                 continue;
  181.             }
  182.             //
  183.             // Colour formatter
  184.             //
  185.             if( *cp == 'c' )
  186.             {
  187.             try
  188.             {
  189.                 uint colourF;
  190.                 uint colourB = uint.max;
  191.  
  192.                 // Foreground Colour
  193.                 parseUInt( colourF, ++cp, end, Parse.Empty );
  194.  
  195.                 // Background Colour ( Currently not supported )
  196.                 if( *cp == ',' )
  197.                 {
  198.                     parseUInt( colourB, ++cp, end, Parse.Empty );
  199.                 }
  200.  
  201.                 //debug D.writefln("DEBUG colour](%d,%d)",colourF,colourB);
  202.  
  203.                 // Check everything parsed ok
  204.                 if( *cp != '}' )
  205.                     throw new ParseException("Open");
  206.  
  207.                 printer.colour( colourF );
  208.                 // Optional background colour
  209.                 //if( colourB != uint.max ) printer.colour2( colourB );
  210.  
  211.                 ptr = cp+1;
  212.                 continue;
  213.             }
  214.             catch( ParseException e )
  215.             {
  216.                 printer.print("{Bad Colour: " ~ e.msg ~ " }");
  217.  
  218.                 ptr = find!char( '}', cp, end ) + 1;
  219.                 if( ptr > end )
  220.                     break;
  221.  
  222.                 // Continue the loop
  223.                 continue;
  224.             }
  225.             } // end if 'c'
  226.  
  227.             //
  228.             // { index , alignment ! truncation : format }
  229.             //
  230.             try
  231.             {
  232.  
  233.             // Shortcut
  234.             if( *cp == '}' )
  235.                 goto ParsingDone;
  236.  
  237.             // index
  238.             if( true )
  239.             {
  240.                 eatSpace( cp, end );            // Whitespace
  241.                 parseUInt( index, cp, end, Parse.Empty );
  242.                 eatSpace( cp, end );            // Whitespace
  243.             }
  244.  
  245.             // alignment
  246.             if( *cp == ',' )
  247.             {
  248.                 eatSpace( ++cp, end );          // Whitespace
  249.                 parseAlign( s.alignment, cp, end, &s );
  250.                 eatSpace( cp, end );            // Whitespace
  251.             }
  252.  
  253.             // truncation
  254.             if( *cp == '!' )
  255.             {
  256.                 eatSpace( ++cp, end );          // Whitespace
  257.                 parseAlign( s.truncation, cp, end );
  258.                 eatSpace( cp, end );            // Whitespace
  259.             }
  260.  
  261.             // format
  262.             if( *cp == ':' )
  263.             {
  264.                 char_c* oldcp = cp+1;
  265.                 do {
  266.                     if( ++cp is end )
  267.                         throw new ParseException("Open");
  268.                 }
  269.                 while( *cp != '}' );
  270.                 formatMod = oldcp[0.. cp-oldcp];
  271.             }
  272.  
  273.             if( *cp != '}' )
  274.                 throw new ParseException("Open");
  275.  
  276.             /++debug {
  277.                 import std.stdio;
  278.                 auto atc = ["","<","^",">", "" ];
  279.  
  280.                 // { index, alignment !truncation :formatMod }
  281.                 writef("DEBUG] State: \"{%d", index );
  282.                 if( s.alignment.type  != AlignState.Type.Unset )
  283.                     writef(",%s%d", atc[s.alignment.type ], s.alignment.disp  );
  284.                 if( s.truncation.type != AlignState.Type.Unset )
  285.                     writef("!%s%d", atc[s.truncation.type], s.truncation.disp );
  286.                 if( formatMod )
  287.                     writef(":%s",formatMod);
  288.                 writefln("}\"");
  289.             } // +/
  290.  
  291.             /*
  292.              * format Parsing Done, now work on the argument(s)
  293.              */
  294.  
  295.     ParsingDone:    // setup for next Iteration
  296.             ptr       = cp+1;
  297.             indexAuto = index + 1;
  298.  
  299.             if( index >= args.length ) {
  300.                 printer.print("{Not enough args}");
  301.                 continue;
  302.             }
  303.  
  304.             Variant a = args[index];
  305.  
  306.             itoaControl  ifmt;
  307.  
  308.             with( Variant.Type )
  309.             with( itoaControl  )
  310.             final switch( a.type )
  311.             {
  312.                 case Pointer:
  313.                     ifmt.base = Base.Hex;
  314.                     goto case uLong;
  315.  
  316.                 case   Byte:
  317.                 case  Short:
  318.                 case    Int:
  319.                 case   Long:    ifmt.isSigned = true;
  320.                         // fall through
  321.                 case  uByte:
  322.                 case uShort:
  323.                 case   uInt:
  324.                 case  uLong:
  325.                     parseFormatMod( s, ifmt, formatMod );
  326.                     s.str = itoa( a.uLong, buffer, ifmt );
  327.  
  328.                     // Derp
  329.                     if( ! s.hasAlignment )
  330.                     {
  331.                         printer.print( s.str );
  332.                     } else {
  333.                         // Alignment + Truncation
  334.                         // cast away const because s.str is a slice of buffer \\
  335.                         itoaAlign( cast(char[]) s.str, buffer, ifmt, s );
  336.  
  337.                         printAlign( s );
  338.                     }
  339.                     continue; // while(true)
  340.  
  341.                 case Float:
  342.                     a.Double = a.Float;
  343.                     // fall through
  344.                 case Double:
  345.                     /* Very basic floating point support */
  346.                     auto len = GlibC.snprintf( buffer.ptr, buffer.length, "%f", a.Double );
  347.                     s.str = buffer[0..len];
  348.                     break;
  349.                 //case Real:
  350.                 //  a.Double = cast(double) a.Real; // FIXME
  351.                 //  goto case Double;
  352.  
  353.                 case Bool:
  354.                     s.str = a.Bool ? "true" : "false";
  355.                     break;
  356.                 // Already a String
  357.                 case Char:
  358.                     s.str = (&a.Char)[0..1];
  359.                     break;
  360.                 case String:
  361.                     s.str = a.String;
  362.                     break;
  363.                 case Data:
  364.                     ifmt.padL =  4;
  365.                     ifmt.padR = 10;
  366.                     auto str = itoa( a.Data.length, buffer, ifmt );
  367.                     str[0  .. 4] = "{…";
  368.                     str[$-10..$] = " bytes…}";
  369.                     s.str = str;
  370.                     break;
  371.                 // ---
  372.                 case   ByteA:
  373.                 case  uByteA:
  374.                 case  ShortA:
  375.                 case uShortA:
  376.                 case    IntA:
  377.                 case   uIntA:
  378.                 case   LongA:
  379.                 case  uLongA:   formatArray( a, s, formatMod );
  380.                         continue; // while(true)
  381.                 // --:
  382.                 case  FloatA:
  383.                 case DoubleA:
  384.                 // --:
  385.                 case   BoolA:
  386.                 case PointerA:
  387.  
  388.                     goto case Data;
  389.             }
  390.  
  391.             // If no Additional processing is needed
  392.             if( ! s.hasAlignment )
  393.             {
  394.                 printer.print( s.str );
  395.                 continue;
  396.             }
  397.  
  398.             // First Truncate
  399.             with( AlignState.Type ) final switch( s.truncation.type )
  400.             {
  401.                 case Unset:
  402.                     // Calc width upto alignMax because
  403.                     // This can break early if width >= alignment
  404.                     with ( s.truncation )
  405.                     {
  406.                         disp = s.alignment.disp;
  407.                         truncTail( s ); // calcs s.width
  408.                         disp = 0;
  409.                         // ---
  410.                         s.strLeft = s.str;
  411.                         s.truncCharCount = 0;
  412.                     }
  413.                     break;
  414.                 case Left:
  415.                     truncHead( s );
  416.                     break;
  417.                 case Centre:
  418.                     truncBody( s );
  419.                     break;
  420.                 case Default:
  421.                 case Right:
  422.                     truncTail( s );
  423.                     break;
  424.             }
  425.  
  426.             // Alignment
  427.             s.calcA( s.width );
  428.  
  429.             // Print Aligned
  430.             printAlign( s );
  431.  
  432.         }
  433.         catch( ParseException e )
  434.         {
  435.             printer.print("{Bad Format: " ~ e.msg ~ " }");
  436.  
  437.             ptr = find!char( '}', cp, end ) + 1;
  438.             if( ptr > end )
  439.                 break;
  440.  
  441.         }
  442.         //catch( InvalidArgException e ) {}
  443.         catch( UTFException e )
  444.         {
  445.             printer.print("{Malformed UTF8}");
  446.         }
  447.         } // End while {}
  448.  
  449.         return;
  450.     }
  451.  
  452.     void printAlign( ref AlignState s )
  453.     {
  454.         //REFACTOR ----
  455.         writeSpace(    s.padL );
  456.         printer.print( s.strLeft  );
  457.  
  458.         foreach( x; 0..s.truncCharCount)
  459.             printer.print( s.truncChar );
  460.  
  461.         printer.print( s.strRight  );
  462.         writeSpace(    s.padR );
  463.         //REFACTOR ----
  464.     }
  465.  
  466.     /* *************************************
  467.                   Parse Helpers
  468.      ***************************************/
  469.  
  470.     // Padding Assistance function
  471.     //
  472.     // Question:  what if count is negative
  473.     //            Should i trim the string?
  474.     //            and if so from front/mid/end???
  475.     private void writeSpace( int count )
  476.     {
  477.         // 16 space chars
  478.         immutable char[16] pad = "                ";
  479.         //mutable char[16] pad = "123456789abcdefX";
  480.  
  481.         if( count <= 0 )
  482.             return;
  483.  
  484.         while( count > 16 )
  485.         {
  486.             printer.print( pad );
  487.             count -= 16;
  488.         }
  489.         printer.print( pad[0..count] );
  490.     }
  491.     private void eatSpace( ref char_c* start, char_c* end )
  492.     {
  493.         while( start !is end )
  494.         {
  495.             if( *start != ' ' )
  496.                 return;
  497.             start++;
  498.         }
  499.         throw new ParseException("Open");
  500.     }
  501.  
  502.     // Read Align/Trunc spec
  503.     private void parseAlign( out AlignState.Input a, ref char_c* cp, char_c* end, AlignState* s=null )
  504.     {
  505.         with( AlignState.Type ) switch( *cp )
  506.         {
  507.             case '<': a.type = Left;    cp++; break;
  508.             case '^': a.type = Centre;  cp++; break;
  509.             case '>': a.type = Right;   cp++; break;
  510.             default : a.type = Default;
  511.         }
  512.  
  513.         if( s !is null && cp !is end && *cp == '0' )
  514.             s.padCharL = '0';
  515.  
  516.         uint saget;
  517.         if( parseUInt( saget, cp, end, Parse.Empty ) )
  518.             a.type = AlignState.Type.Unset;
  519.         a.disp = cast(int) saget;
  520.     }
  521.  
  522.  
  523.     /*
  524.      * Format Array and print
  525.      *
  526.      * TODO: if itoaAlign() Overflows
  527.      * TODO: cleanup terrible mess
  528.      */
  529.     private void formatArray( ref Variant v, ref AlignState s, char_c[] formatMod )
  530.     {
  531.         // I dunno why I called it onion
  532.         union Onion {
  533.              long    i;
  534.             ubyte[8] a;
  535.         } static assert( Onion.sizeof == 8 );
  536.  
  537.         int     size;       // size of the Element Type
  538.         byte        signExtend; // 0x80 for Signed, else 0
  539.         char[64]    buffer;     // itoa
  540.         itoaControl ctrl;       //  〃
  541.         Onion       onion;
  542.  
  543.         immutable bool  compact = formatMod.length &&
  544.                       formatMod[0] == 'c';
  545.  
  546.         with( Variant.Type )
  547.         switch( v.type )
  548.         {
  549.             case  ByteA: case  uByteA: size = 1; break;
  550.             case ShortA: case uShortA: size = 2; break;
  551.             case   IntA: case   uIntA: size = 4; break;
  552.             case  LongA: case  uLongA: size = 8; break;
  553.  
  554.             default: assert( false, "Not Numerical Array Type" );
  555.         }
  556.  
  557.         // Setup
  558.         if( compact )
  559.         {
  560.             if( v.ByteA.length == 0 )
  561.                 return;
  562.  
  563.             parseFormatMod( s, ctrl, formatMod[1..$] );
  564.  
  565.             if( s.alignment.type == AlignState.Type.Unset )
  566.             {
  567.                 s.alignment.type = AlignState.Type.Default;
  568.                 s.alignment.disp = size*2;
  569.                 s.padCharL       = '0';
  570.             }
  571.  
  572.             ctrl.prefixLeft= true;
  573.             ctrl.base      = ctrl.Base.Hex;
  574.             ctrl.isSigned  = false;
  575.             signExtend     = 0;
  576.         } else {
  577.             if( v.ByteA.length == 0 )
  578.             {
  579.                 printer.print("[]");
  580.                 return;
  581.             }
  582.             parseFormatMod( s, ctrl, formatMod );
  583.  
  584.             buffer[62..64]= ", ";
  585.             ctrl.padR     = 2;
  586.             s.alignment.disp += 2;
  587.             ctrl.isSigned = (v.type & 1);
  588.             signExtend    = (v.type & 1) << 7; // MSB
  589.  
  590.             debug { red(); printer.print("["); normal(); }
  591.             else  {
  592.             printer.print("[");
  593.             }
  594.         }
  595.  
  596.         // Horrible Hack
  597.         ubyte_c* cp  =      v.uByteA.ptr;
  598.         ubyte_c* end = cp + v.uByteA.length * size - size;
  599.  
  600.         // Derp
  601.         if( s.hasAlignment )
  602.         {
  603.             for( ; cp < end; cp += size )
  604.             {
  605.                 auto s2 = s;
  606.  
  607.                 // Sign Extend
  608.                 onion.i = signExtend & cp[size-1];
  609.  
  610.                 onion.a[0..size] = cp[0..size];
  611.                 s2.str = itoa( onion.i, buffer, ctrl );
  612.  
  613.                 // cast away const because s.str is a slice of buffer \\
  614.                 itoaAlign( cast(char[]) s2.str, buffer, ctrl, s2 );
  615.  
  616.                 printAlign( s2 );
  617.             }
  618.  
  619.             if( !compact )
  620.             {
  621.                 buffer[63] = ']';
  622.                 ctrl.padR  = 1;
  623.                 s.alignment.disp -= 1;
  624.             }
  625.  
  626.             onion.i = signExtend & cp[size-1];
  627.             onion.a[0..size] = cp[0..size];
  628.             s.str = itoa( onion.i, buffer, ctrl );
  629.             // cast away const because s.str is a slice of buffer \\
  630.             itoaAlign( cast(char[]) s.str, buffer, ctrl, s );
  631.  
  632.             printAlign( s );
  633.         }
  634.         else
  635.         {
  636.             for( ; cp < end; cp += size )
  637.             {
  638.                 // Sign Extend
  639.                 onion.i = cast(byte) (signExtend & cp[size-1]);
  640.  
  641.                 onion.a[0..size] = cp[0..size];
  642.                 printer.print( itoa( onion.i, buffer, ctrl ) );
  643.             }
  644.  
  645.             buffer[63] = ']';
  646.             ctrl.padR  = 1;
  647.  
  648.             onion.i = cast(byte) (signExtend & cp[size-1]);
  649.             onion.a[0..size] = cp[0..size];
  650.             printer.print( itoa( onion.i, buffer, ctrl ) );
  651.         }
  652.     }
  653. /+  // Templated version
  654.     void formatArrayT( A : E[], E )( A a, State s, char_c[] formatMod )
  655.     {
  656.         char[64]    buffer;
  657.         itoaControl ctrl;
  658.  
  659.         buffer[62..64] = ", ";
  660.         ctrl.padR      = 2;
  661.         ctrl.isSigned  = isSigned!E;
  662.  
  663.         parseFormatMod( s, ctrl, formatMod );
  664.  
  665.         printer.print("[");
  666.         foreach( e; a[0..$-1] )
  667.         {
  668.             printer.print( itoa( e, buffer, ctrl ) );
  669.         }
  670.  
  671.         buffer[63] = ']';
  672.         ctrl.padR  =  1;
  673.         printer.print( itoa( a[$-1], buffer, ctrl ) );
  674.     }
  675. +/
  676.  
  677.     /* *************************************
  678.             Trunc code
  679.      ***************************************/
  680.  
  681.     // TODO list
  682.     // 1) optimise for ascii
  683.     // 2) deal with control/non-printable Unicode chars
  684.  
  685.     // This is a pain because there is no easy way to do this
  686.     private void truncHead( ref AlignState s )
  687.     {
  688.         char_c* beg = s.str.ptr;
  689.         char_c* end = s.str.ptr + s.str.length;
  690.         char_c* cp  = s.str.ptr + s.str.length-1;
  691.         auto    columns = s.truncation.disp;
  692.         dchar   d;
  693.  
  694.         for( ; beg <= cp;  cp-- )
  695.         {
  696.             // If we are in a multibyte
  697.             if( (*cp & 0xC0) == 0x80 )
  698.                 continue;
  699.  
  700.             // Fast lane
  701.             if( columns > 2 )
  702.             {
  703.                        UTF8.toUTF32( d, cp, end );
  704.                 columns -= printer.width( d );
  705.                 continue;
  706.             }
  707.  
  708.             auto temp1 = UTF8.toUTF32( d, cp, end );
  709.             auto temp2 = printer.width( d );
  710.             columns -= temp2;
  711.  
  712.             if( columns < 1 )
  713.             {
  714.                 if( cp is beg && columns == 0 )
  715.                     break;
  716.  
  717.                 // Undo the last char
  718.                      cp += temp1;
  719.                 columns += temp2;
  720.  
  721.                 s.strRight      = s.str[ cp - beg .. $ ];
  722.                 s.width         = s.truncation.disp;
  723.                 s.truncCharCount= columns;
  724.                 return;
  725.             }
  726.         }
  727.         s.strRight      = s.str;
  728.         s.width         = s.truncation.disp - columns;
  729.         s.truncCharCount=0;
  730.         return;
  731.     }
  732.  
  733.     private void truncBody( ref AlignState s )
  734.     {
  735.         //REFACTOR
  736.         switch( s.truncation.disp )
  737.         {
  738.             case 1:
  739.                 s.strLeft  = "…";
  740.                 s.strRight = null;
  741.                 s.width    = 1;
  742.                 return;
  743.             case 2:
  744.             //  s.strRight = null;
  745.                 return truncTail( s );
  746.             default:
  747.                 auto save = s.truncation.disp;
  748.  
  749.                 s.truncation.disp = save - save/2;
  750.                 truncHead( s );
  751.                 s.truncation.disp = save/2; // real
  752.         }
  753.  
  754.         char_c* beg = s.str.ptr;
  755.         char_c* end = s.strRight.ptr; // s.str.ptr + s.str.length;
  756.         char_c* cp  = s.str.ptr;
  757.         auto    columns = s.truncation.disp;
  758.         dchar   d;
  759.  
  760.         // Similar version of TruncTail
  761.         while( cp < end )
  762.         {
  763.             // Fast lane
  764.             if( columns > 2 )
  765.             {
  766.                      cp += UTF8.toUTF32( d, cp, end );
  767.                 columns -= printer.width( d );
  768.                 continue;
  769.             }
  770.  
  771.             // attempt one more char
  772.             auto temp1 = UTF8.toUTF32( d, cp, end );
  773.             auto temp2 = printer.width( d );
  774.  
  775.             if( columns-temp2 >= 0 )
  776.             {
  777.                      cp += temp1;
  778.                 columns -= temp2;
  779.                 continue;
  780.             }
  781.  
  782.             // t.disp >= s.width ( don't use truncChar )
  783.             if( cp+temp1 is end && temp2 <= s.truncCharCount + columns )
  784.             {
  785.                     cp += temp1;
  786.                 columns = 0;
  787.                 s.truncCharCount = 0;
  788.             }
  789.             else if ( columns-temp2 == -1 && s.truncCharCount == 2 )
  790.             {
  791.             // rare case when we end up with 3 '…' in the middle
  792.                           cp += temp1;
  793.             s.truncCharCount -= temp2;
  794.             }
  795.  
  796.             s.strLeft        = s.str[ 0 .. cp - beg ];
  797.             s.width         += s.truncation.disp;
  798.             s.truncCharCount+= columns;
  799.             return;
  800.         }
  801. //      We c̲a̲n̲ actually get here
  802.         s.strLeft        = s.str;
  803.         s.strRight       = null;
  804.         s.width         += s.truncation.disp - columns - s.truncCharCount;
  805.         s.truncCharCount =0;
  806.         return;
  807.     }
  808.  
  809.     private void truncTail( ref AlignState s )
  810.     {
  811.         char_c* beg = s.str.ptr;
  812.         char_c* end = s.str.ptr + s.str.length;
  813.         char_c* cp  = s.str.ptr;
  814.         auto    columns = s.truncation.disp;
  815.         dchar   d;
  816.  
  817.         while( cp < end )
  818.         {
  819.             // Fast lane
  820.             if( columns > 2 )
  821.             {
  822.                      cp += UTF8.toUTF32( d, cp, end );
  823.                 columns -= printer.width( d );
  824.                 continue;
  825.             }
  826.  
  827.             // attempt one more char
  828.             auto temp1 = UTF8.toUTF32( d, cp, end );
  829.             auto temp2 = printer.width( d );
  830.             columns -= temp2;
  831.  
  832.             if( columns > 0 )
  833.             {
  834.                 cp += temp1;
  835.                 continue;
  836.             }
  837.             if( cp+1 is end && columns == 0 )
  838.                 break;
  839.  
  840.             s.strLeft       = s.str[ 0 .. cp - beg ];
  841.             s.width         = s.truncation.disp;
  842.             s.truncCharCount= columns + temp2;
  843.             return;
  844.         }
  845.  
  846.         s.strLeft       = s.str;
  847.         s.width         = s.truncation.disp - columns;
  848.         s.truncCharCount=0;
  849.         return;
  850.     }
  851. }
  852.  
  853. /*
  854.  * Alignment:
  855.  *
  856.  * + : prefix with '+' or '-'
  857.  * - : prefix with '␠' or '-'
  858.  *   : none = only prefix '-'   ( default )
  859.  *
  860.  * Format:
  861.  *
  862.  * b : Binary           [ No ]
  863.  * c : UTF8 character       [ TODO ]
  864.  * d : Decimal          ( default )
  865.  * o : Octal
  866.  * x : Hexadecimal Lowercase
  867.  * X : Hexadecimal Uppercase
  868.  * n : Separate     Thousands comma
  869.  * _ : Separate Ten Thousands underscore
  870.  */
  871. void parseFormatMod( ref AlignState s, ref itoaControl ctrl, char_c[] formatMod ) pure
  872. {
  873.     // TODO: Make this more general
  874.     // instead of '0' being special.
  875.     if ( s.padCharL == '0' )
  876.         ctrl.prefixLeft = true;
  877.  
  878.     with( itoaControl )
  879.     foreach( c; formatMod )
  880.     switch( c )
  881.     {
  882.         case '+': ctrl.sign = Sign.Plus;    break;
  883.         case '-': ctrl.sign = Sign.Minus;   break;
  884.         //
  885.         case 'x': ctrl.lower = true;    // fall through
  886.         case 'X': ctrl.base  = Base.Hex;    break;
  887.         case 'd': ctrl.base  = Base.Dec;    break;
  888.         case 'o': ctrl.base  = Base.Oct;    break;
  889.         //se 'b': ctrl.base = 2 ;       break;
  890.         //
  891.         case 'n': ctrl.group  = Group.Thou;
  892.               ctrl.grpChar= ',';        break;
  893.         case '_': ctrl.group  = Group.TThou;
  894.               ctrl.grpChar= '_';        break;
  895.         default: {
  896.             throw new ParseException("Malformed formatMod");
  897.         }
  898.     }
  899. }
  900.  
  901. /* ******************************************************************************
  902.                 Unit Tests!
  903.  *******************************************************************************/
  904.  
  905. version(unittest)
  906. {
  907.  
  908. unittest
  909. {
  910.     //import shd.c.glibc: LC, setlocale;
  911.     import shd.c.glibc;
  912.     import shd.output;
  913.  
  914.     setlocale( LC.ALL, "en_GB.UTF-8" );
  915.  
  916.     alias Shdout T;
  917.  
  918.     string[] trunc = [
  919.         "abcdefghijklmnopqrstuvwxyz",
  920.          "A一一一一一一一一一一一一一一一一一一一A",
  921.          "一一一一一一一一一一一一一一一一一一一一",
  922.         ];
  923.         // extended
  924.     string[] trunc2 = [
  925.          "一一一一一一一一一一一一一一一一一一一A",
  926.          "A一一一一一一一一一一一一一一一一一一一",
  927.          "abcdefghijklmnopqrs一一一一一一一一一一一一一一一一",
  928.          "abcdefghijklmnopqrs一一一一一一一一一一一一一一一A",
  929.          "A一一一一一一一一一一一一一一一一一jklmnopqrstuvwxyz",
  930.           "一一一一一一一一一一一一一一一一一jklmnopqrstuvwxyz",
  931.         ];
  932.  
  933.     char_c[] truncHeadGlue( char_c[] str, int columns )
  934.     {
  935.         AlignState s;
  936.  
  937.         s.str = str;
  938.         s.truncation.disp = columns;
  939.  
  940.         T.truncHead( s );
  941.  
  942.         auto n = s.truncCharCount;
  943.  
  944.         return "………"[0..(n*3)] ~ s.strRight;
  945.     }
  946.     char_c[] truncBodyGlue( char_c[] str, int columns )
  947.     {
  948.         AlignState s;
  949.  
  950.         s.str = str;
  951.         s.truncation.disp = columns;
  952.  
  953.         T.truncBody( s );
  954.  
  955.         auto n = s.truncCharCount;
  956.  
  957.         return s.strLeft ~ "………"[0..(n*3)] ~ s.strRight;
  958.  
  959.     }
  960.     char_c[] truncTailGlue( char_c[] str, int columns )
  961.     {
  962.         AlignState s;
  963.  
  964.         s.str = str;
  965.         s.truncation.disp = columns;
  966.  
  967.         T.truncTail( s );
  968.  
  969.         auto n = s.truncCharCount;
  970.  
  971.         return s.strLeft ~ "………"[0..(n*3)];
  972.  
  973.         // TODO check i
  974.     }
  975.  
  976.     // Trunc Head
  977.     {
  978.         alias truncHeadGlue func;
  979.         string[] ans = [
  980.             "…","…","…",
  981.             //2
  982.             "…z","…A","……",
  983.             //3
  984.             "…yz","……A","…一",
  985.             //4
  986.             "…xyz","…一A","……一",
  987.             //5
  988.             "…wxyz",
  989.             "……一A",
  990.             "…一一",
  991.             //6
  992.             "…vwxyz",
  993.             "…一一A",
  994.             "……一一",
  995.             //20,21,22
  996.             "…hijklmnopqrstuvwxyz",
  997.             "…一一一一一一一一一A",
  998.             "……一一一一一一一一一",
  999.             "…ghijklmnopqrstuvwxyz",
  1000.             "……一一一一一一一一一A",
  1001.             "…一一一一一一一一一一",
  1002.             "…fghijklmnopqrstuvwxyz",
  1003.             "…一一一一一一一一一一A",
  1004.             "……一一一一一一一一一一",
  1005.         ];
  1006.         int i;
  1007.         foreach( t; trunc ) { assert( func( t,  1) == ans[i++] ); }
  1008.         foreach( t; trunc ) { assert( func( t,  2) == ans[i++] ); }
  1009.         foreach( t; trunc ) { assert( func( t,  3) == ans[i++] ); }
  1010.         foreach( t; trunc ) { assert( func( t,  4) == ans[i++] ); }
  1011.         foreach( t; trunc ) { assert( func( t,  5) == ans[i++] ); }
  1012.         foreach( t; trunc ) { assert( func( t,  6) == ans[i++] ); }
  1013.  
  1014.         foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
  1015.         foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
  1016.         foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
  1017.     }
  1018.     // Trunc Tail
  1019.     {
  1020.         alias truncTailGlue func;
  1021.         string[] ans = [
  1022.             "…","…","…",
  1023.             //2
  1024.             "a…","A…","……",
  1025.             //3
  1026.             "ab…","A……","一…",
  1027.             //4
  1028.             "abc…","A一…","一……",
  1029.             //5
  1030.             "abcd…",
  1031.             "A一……",
  1032.             "一一…",
  1033.             //6
  1034.             "abcde…",
  1035.             "A一一…",
  1036.             "一一……",
  1037.             //20,21,22
  1038.             "abcdefghijklmnopqrs…",
  1039.             "A一一一一一一一一一…",
  1040.             "一一一一一一一一一……",
  1041.             "abcdefghijklmnopqrst…",
  1042.             "A一一一一一一一一一……",
  1043.             "一一一一一一一一一一…",
  1044.             "abcdefghijklmnopqrstu…",
  1045.             "A一一一一一一一一一一…",
  1046.             "一一一一一一一一一一……",
  1047.         ];
  1048.         int i;
  1049.         foreach( t; trunc ) { assert( func( t,  1) == ans[i++] ); }
  1050.         foreach( t; trunc ) { assert( func( t,  2) == ans[i++] ); }
  1051.         foreach( t; trunc ) { assert( func( t,  3) == ans[i++] ); }
  1052.         foreach( t; trunc ) { assert( func( t,  4) == ans[i++] ); }
  1053.         foreach( t; trunc ) { assert( func( t,  5) == ans[i++] ); }
  1054.         foreach( t; trunc ) { assert( func( t,  6) == ans[i++] ); }
  1055.  
  1056.         foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
  1057.         foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
  1058.         foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
  1059.     }
  1060.     // Trunc Body
  1061.     {
  1062.         alias truncBodyGlue func;
  1063.         string[] ans = [
  1064.             "…","…","…",
  1065.             //2
  1066.             "a…","A…","……",
  1067.             //3
  1068.             "a…z","A…A","一…",
  1069.             //4
  1070.             "ab…z","A……A","一……",
  1071.             //5↓
  1072.             "ab…yz",
  1073.             "A一…A",
  1074.             "一…一",
  1075.             //6 ↓
  1076.             "abc…yz",
  1077.             "A一……A",
  1078.             "一……一",
  1079.             //20       ↓
  1080.             "abcdefghij…rstuvwxyz",
  1081.             "A一一一一……一一一一A",
  1082.             "一一一一一……一一一一",
  1083.             //21       ↓
  1084.             "abcdefghij…qrstuvwxyz",
  1085.             "A一一一一一…一一一一A",
  1086.             "一一一一一…一一一一一",
  1087.             //22        ↓
  1088.             "abcdefghijk…qrstuvwxyz",
  1089.             "A一一一一一……一一一一A",
  1090.             "一一一一一……一一一一一",
  1091.         ];
  1092.         int i;
  1093.         foreach( t; trunc ) { assert( func( t,  1) == ans[i++] ); }
  1094.         foreach( t; trunc ) { assert( func( t,  2) == ans[i++] ); }
  1095.         foreach( t; trunc ) { assert( func( t,  3) == ans[i++] ); }
  1096.         foreach( t; trunc ) { assert( func( t,  4) == ans[i++] ); }
  1097.         foreach( t; trunc ) { assert( func( t,  5) == ans[i++] ); }
  1098.         foreach( t; trunc ) { assert( func( t,  6) == ans[i++] ); }
  1099.  
  1100.         foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
  1101.         foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
  1102.         foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
  1103.  
  1104.         // extended
  1105.         string[] ans2 = [
  1106.             //1
  1107.             "…","…","…","…","…","…",
  1108.             //2
  1109.             "……","A…","a…","a…","A…","……",
  1110.             //3
  1111.             "……A","A……","a……","a…A","A…z","……z",
  1112.             //4
  1113.             "A一…一",
  1114.             "abc…一",
  1115.             "abc……A",
  1116.             "A一…yz",
  1117.             "一……yz",
  1118.             //5↓
  1119.             "一……A",
  1120.             "A……一",
  1121.             "ab…一",
  1122.             "ab……A",
  1123.             "A……yz",
  1124.             "一…yz",
  1125.             //6 ↓
  1126.             "一一…A", // Good no triple Ellipsis
  1127.             "A一…一",
  1128.             "abc…一",
  1129.             "abc……A",
  1130.             "A一…yz",
  1131.             "一……yz",
  1132.             //20       ↓
  1133.             "一一一一一…一一一一A",
  1134.             "A一一一一一…一一一一",
  1135.             "abcdefghij……一一一一",
  1136.             "abcdefghij…一一一一A",
  1137.             "A一一一一……rstuvwxyz",
  1138.             "一一一一一…rstuvwxyz",
  1139.             //21       ↓
  1140.             "一一一一一……一一一一A",
  1141.             "A一一一一……一一一一一",
  1142.             "abcdefghij…一一一一一",
  1143.             "abcdefghij……一一一一A",
  1144.             "A一一一一……qrstuvwxyz",
  1145.             "一一一一一…qrstuvwxyz",
  1146.             //22        ↓
  1147.             "一一一一一一…一一一一A",
  1148.             "A一一一一一…一一一一一",
  1149.             "abcdefghijk…一一一一一",
  1150.             "abcdefghijk……一一一一A",
  1151.             "A一一一一一…qrstuvwxyz",
  1152.             "一一一一一……qrstuvwxyz",
  1153.         ];
  1154.         i=0;
  1155.         foreach( t; trunc ) { assert( func( t,  1) == ans[i++] ); }
  1156.         foreach( t; trunc ) { assert( func( t,  2) == ans[i++] ); }
  1157.         foreach( t; trunc ) { assert( func( t,  3) == ans[i++] ); }
  1158.         foreach( t; trunc ) { assert( func( t,  4) == ans[i++] ); }
  1159.         foreach( t; trunc ) { assert( func( t,  5) == ans[i++] ); }
  1160.         foreach( t; trunc ) { assert( func( t,  6) == ans[i++] ); }
  1161.  
  1162.         foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
  1163.         foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
  1164.         foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
  1165.  
  1166.         /+  // Generate Answers
  1167.         {
  1168.             alias truncBodyGlue func;
  1169.  
  1170.             foreach( i; [1,2,3,4] )
  1171.             {
  1172.                 Shdout.format("\n//{}\n",i);
  1173.                 foreach( t; trunc2 ) {  Shdout.format( "\"{}\",", func( t,  i)); }
  1174.             }
  1175.             Shdout.print("\n");
  1176.             foreach( i; [5,6,20,21,22] )
  1177.             {
  1178.                 auto o = ( i > 9 ? 3 : 2 );
  1179.                 Shdout.format("//{}",i);
  1180.                 foreach( x; 0..(i/2)-o) Shdout.print(" "); Shdout.print("↓\n");
  1181.                 foreach( t; trunc2 ) {  Shdout.format( "\"{}\",\n", func( t,  i)); }
  1182.             }
  1183.         }// +/
  1184.     }
  1185. }
  1186.  
  1187. unittest
  1188. {
  1189.     import shd.output;
  1190.     Shdout.println(">---Start format / itoa  --<");
  1191.  
  1192.     immutable int n = 123;
  1193.     string[] Question = [
  1194.             "|{ ,0:+}|", "|{ ,0:-}|", "|{ ,0}|",
  1195.             "|{ ,1:+}|", "|{ ,1:-}|", "|{ ,1}|",
  1196.             "|{ ,2:+}|", "|{ ,2:-}|", "|{ ,2}|",
  1197.             "|{ ,3:+}|", "|{ ,3:-}|", "|{ ,3}|",
  1198.             "|{ ,4:+}|", "|{ ,4:-}|", "|{ ,4}|",
  1199.             "|{ ,5:+}|", "|{ ,5:-}|", "|{ ,5}|",
  1200.             "|{ ,6:+}|", "|{ ,6:-}|", "|{ ,6}|",
  1201.             "|{ ,7:+}|", "|{ ,7:-}|", "|{ ,7}|",
  1202.             "|{ ,8:+}|", "|{ ,8:-}|", "|{ ,8}|",
  1203.             "|{ ,9:+}|", "|{ ,9:-}|", "|{ ,9}|",
  1204.             "|{,10:+}|", "|{,10:-}|", "|{,10}|",
  1205.             "|{,11:+}|", "|{,11:-}|", "|{,11}|",
  1206.             "|{,12:+}|", "|{,12:-}|", "|{,12}|",
  1207.             "|{,13:+}|", "|{,13:-}|", "|{,13}|",
  1208.             "|{,14:+}|", "|{,14:-}|", "|{,14}|",
  1209.             "|{,15:+}|", "|{,15:-}|", "|{,15}|",
  1210.             "|{,16:+}|", "|{,16:-}|", "|{,16}|",
  1211.  
  1212.             "|{ ,<0:+}|", "|{ ,<0:-}|", "|{ ,<0}|",
  1213.             "|{ ,<1:+}|", "|{ ,<1:-}|", "|{ ,<1}|",
  1214.             "|{ ,<2:+}|", "|{ ,<2:-}|", "|{ ,<2}|",
  1215.             "|{ ,<3:+}|", "|{ ,<3:-}|", "|{ ,<3}|",
  1216.             "|{ ,<4:+}|", "|{ ,<4:-}|", "|{ ,<4}|",
  1217.             "|{ ,<5:+}|", "|{ ,<5:-}|", "|{ ,<5}|",
  1218.             "|{ ,<6:+}|", "|{ ,<6:-}|", "|{ ,<6}|",
  1219.             "|{ ,<7:+}|", "|{ ,<7:-}|", "|{ ,<7}|",
  1220.             "|{ ,<8:+}|", "|{ ,<8:-}|", "|{ ,<8}|",
  1221.             "|{ ,<9:+}|", "|{ ,<9:-}|", "|{ ,<9}|",
  1222.             "|{,<10:+}|", "|{,<10:-}|", "|{,<10}|",
  1223.             "|{,<11:+}|", "|{,<11:-}|", "|{,<11}|",
  1224.             "|{,<12:+}|", "|{,<12:-}|", "|{,<12}|",
  1225.             "|{,<13:+}|", "|{,<13:-}|", "|{,<13}|",
  1226.             "|{,<14:+}|", "|{,<14:-}|", "|{,<14}|",
  1227.             "|{,<15:+}|", "|{,<15:-}|", "|{,<15}|",
  1228.             "|{,<16:+}|", "|{,<16:-}|", "|{,<16}|",
  1229.  
  1230.             "|{ ,^0:+}|", "|{ ,^0:-}|", "|{ ,^0}|",
  1231.             "|{ ,^1:+}|", "|{ ,^1:-}|", "|{ ,^1}|",
  1232.             "|{ ,^2:+}|", "|{ ,^2:-}|", "|{ ,^2}|",
  1233.             "|{ ,^3:+}|", "|{ ,^3:-}|", "|{ ,^3}|",
  1234.             "|{ ,^4:+}|", "|{ ,^4:-}|", "|{ ,^4}|",
  1235.             "|{ ,^5:+}|", "|{ ,^5:-}|", "|{ ,^5}|",
  1236.             "|{ ,^6:+}|", "|{ ,^6:-}|", "|{ ,^6}|",
  1237.             "|{ ,^7:+}|", "|{ ,^7:-}|", "|{ ,^7}|",
  1238.             "|{ ,^8:+}|", "|{ ,^8:-}|", "|{ ,^8}|",
  1239.             "|{ ,^9:+}|", "|{ ,^9:-}|", "|{ ,^9}|",
  1240.             "|{,^10:+}|", "|{,^10:-}|", "|{,^10}|",
  1241.             "|{,^11:+}|", "|{,^11:-}|", "|{,^11}|",
  1242.             "|{,^12:+}|", "|{,^12:-}|", "|{,^12}|",
  1243.             "|{,^13:+}|", "|{,^13:-}|", "|{,^13}|",
  1244.             "|{,^14:+}|", "|{,^14:-}|", "|{,^14}|",
  1245.             "|{,^15:+}|", "|{,^15:-}|", "|{,^15}|",
  1246.             "|{,^16:+}|", "|{,^16:-}|", "|{,^16}|",
  1247.             ];
  1248.     string[] Question0 = [
  1249.             "|{ ,01:+}|", "|{ ,01:-}|", "|{ ,01}|",
  1250.             "|{ ,02:+}|", "|{ ,02:-}|", "|{ ,02}|",
  1251.             "|{ ,03:+}|", "|{ ,03:-}|", "|{ ,03}|",
  1252.             "|{ ,04:+}|", "|{ ,04:-}|", "|{ ,04}|",
  1253.             "|{ ,05:+}|", "|{ ,05:-}|", "|{ ,05}|",
  1254.             "|{ ,06:+}|", "|{ ,06:-}|", "|{ ,06}|",
  1255.             "|{ ,07:+}|", "|{ ,07:-}|", "|{ ,07}|",
  1256.             "|{ ,08:+}|", "|{ ,08:-}|", "|{ ,08}|",
  1257.             "|{ ,09:+}|", "|{ ,09:-}|", "|{ ,09}|",
  1258.             "|{,010:+}|", "|{,010:-}|", "|{,010}|",
  1259.             "|{,011:+}|", "|{,011:-}|", "|{,011}|",
  1260.             "|{,012:+}|", "|{,012:-}|", "|{,012}|",
  1261.             "|{,013:+}|", "|{,013:-}|", "|{,013}|",
  1262.             "|{,014:+}|", "|{,014:-}|", "|{,014}|",
  1263.             "|{,015:+}|", "|{,015:-}|", "|{,015}|",
  1264.             "|{,016:+}|", "|{,016:-}|", "|{,016}|",
  1265.  
  1266.             "|{ ,<01:+}|", "|{ ,<01:-}|", "|{ ,<01}|",
  1267.             "|{ ,<02:+}|", "|{ ,<02:-}|", "|{ ,<02}|",
  1268.             "|{ ,<03:+}|", "|{ ,<03:-}|", "|{ ,<03}|",
  1269.             "|{ ,<04:+}|", "|{ ,<04:-}|", "|{ ,<04}|",
  1270.             "|{ ,<05:+}|", "|{ ,<05:-}|", "|{ ,<05}|",
  1271.             "|{ ,<06:+}|", "|{ ,<06:-}|", "|{ ,<06}|",
  1272.             "|{ ,<07:+}|", "|{ ,<07:-}|", "|{ ,<07}|",
  1273.             "|{ ,<08:+}|", "|{ ,<08:-}|", "|{ ,<08}|",
  1274.             "|{ ,<09:+}|", "|{ ,<09:-}|", "|{ ,<09}|",
  1275.             "|{,<010:+}|", "|{,<010:-}|", "|{,<010}|",
  1276.             "|{,<011:+}|", "|{,<011:-}|", "|{,<011}|",
  1277.             "|{,<012:+}|", "|{,<012:-}|", "|{,<012}|",
  1278.             "|{,<013:+}|", "|{,<013:-}|", "|{,<013}|",
  1279.             "|{,<014:+}|", "|{,<014:-}|", "|{,<014}|",
  1280.             "|{,<015:+}|", "|{,<015:-}|", "|{,<015}|",
  1281.             "|{,<016:+}|", "|{,<016:-}|", "|{,<016}|",
  1282.  
  1283.             "|{ ,^01:+}|", "|{ ,^01:-}|", "|{ ,^01}|",
  1284.             "|{ ,^02:+}|", "|{ ,^02:-}|", "|{ ,^02}|",
  1285.             "|{ ,^03:+}|", "|{ ,^03:-}|", "|{ ,^03}|",
  1286.             "|{ ,^04:+}|", "|{ ,^04:-}|", "|{ ,^04}|",
  1287.             "|{ ,^05:+}|", "|{ ,^05:-}|", "|{ ,^05}|",
  1288.             "|{ ,^06:+}|", "|{ ,^06:-}|", "|{ ,^06}|",
  1289.             "|{ ,^07:+}|", "|{ ,^07:-}|", "|{ ,^07}|",
  1290.             "|{ ,^08:+}|", "|{ ,^08:-}|", "|{ ,^08}|",
  1291.             "|{ ,^09:+}|", "|{ ,^09:-}|", "|{ ,^09}|",
  1292.             "|{,^010:+}|", "|{,^010:-}|", "|{,^010}|",
  1293.             "|{,^011:+}|", "|{,^011:-}|", "|{,^011}|",
  1294.             "|{,^012:+}|", "|{,^012:-}|", "|{,^012}|",
  1295.             "|{,^013:+}|", "|{,^013:-}|", "|{,^013}|",
  1296.             "|{,^014:+}|", "|{,^014:-}|", "|{,^014}|",
  1297.             "|{,^015:+}|", "|{,^015:-}|", "|{,^015}|",
  1298.             "|{,^016:+}|", "|{,^016:-}|", "|{,^016}|",
  1299.             ];
  1300.     string[] Answer = [
  1301.         // Default
  1302.         "|+123|",       "| 123|",       "|123|",
  1303.         "|+123|",       "| 123|",       "|123|",
  1304.         "|+123|",       "| 123|",       "|123|",
  1305.         "|+123|",       "| 123|",       "|123|",
  1306.         "|+123|",       "| 123|",       "| 123|",
  1307.         "| +123|",      "|  123|",      "|  123|",
  1308.         "|  +123|",     "|   123|",     "|   123|",
  1309.         "|   +123|",        "|    123|",        "|    123|",
  1310.         "|    +123|",       "|     123|",       "|     123|",
  1311.         "|     +123|",      "|      123|",      "|      123|",
  1312.         "|      +123|",     "|       123|",     "|       123|",
  1313.         "|       +123|",    "|        123|",    "|        123|",
  1314.         "|        +123|",   "|         123|",   "|         123|",
  1315.         "|         +123|",  "|          123|",  "|          123|",
  1316.         "|          +123|", "|           123|", "|           123|",
  1317.         "|           +123|",    "|            123|",    "|            123|",
  1318.         "|            +123|",   "|             123|",   "|             123|",
  1319.         // Left
  1320.         "|+123|",       "| 123|",       "|123|",
  1321.         "|+123|",       "| 123|",       "|123|",
  1322.         "|+123|",       "| 123|",       "|123|",
  1323.         "|+123|",       "| 123|",       "|123|",
  1324.         "|+123|",       "| 123|",       "|123 |",
  1325.         "|+123 |",      "| 123 |",      "|123  |",
  1326.         "|+123  |",     "| 123  |",     "|123   |",
  1327.         "|+123   |",        "| 123   |",        "|123    |",
  1328.         "|+123    |",       "| 123    |",       "|123     |",
  1329.         "|+123     |",      "| 123     |",      "|123      |",
  1330.         "|+123      |",     "| 123      |",     "|123       |",
  1331.         "|+123       |",    "| 123       |",    "|123        |",
  1332.         "|+123        |",   "| 123        |",   "|123         |",
  1333.         "|+123         |",  "| 123         |",  "|123          |",
  1334.         "|+123          |", "| 123          |", "|123           |",
  1335.         "|+123           |",    "| 123           |",    "|123            |",
  1336.         "|+123            |",   "| 123            |",   "|123             |",
  1337.         // Centre
  1338.         "|+123|",       "| 123|",       "|123|",
  1339.         "|+123|",       "| 123|",       "|123|",
  1340.         "|+123|",       "| 123|",       "|123|",
  1341.         "|+123|",       "| 123|",       "|123|",
  1342.         "|+123|",       "| 123|",       "|123 |",
  1343.         "|+123 |",      "| 123 |",      "| 123 |",
  1344.         "| +123 |",     "|  123 |",     "| 123  |",
  1345.         "| +123  |",        "|  123  |",        "|  123  |",
  1346.         "|  +123  |",       "|   123  |",       "|  123   |",
  1347.         "|  +123   |",      "|   123   |",      "|   123   |",
  1348.         "|   +123   |",     "|    123   |",     "|   123    |",
  1349.         "|   +123    |",    "|    123    |",    "|    123    |",
  1350.         "|    +123    |",   "|     123    |",   "|    123     |",
  1351.         "|    +123     |",  "|     123     |",  "|     123     |",
  1352.         "|     +123     |", "|      123     |", "|     123      |",
  1353.         "|     +123      |",    "|      123      |",    "|      123      |",
  1354.         "|      +123      |",   "|       123      |",   "|      123       |",
  1355.         ];
  1356.     string[] Answer0 = [
  1357.         // Default
  1358.         "|+123|",       "| 123|",       "|123|",
  1359.         "|+123|",       "| 123|",       "|123|",
  1360.         "|+123|",       "| 123|",       "|123|",
  1361.         "|+123|",       "| 123|",       "|0123|",
  1362.         "|+0123|",      "| 0123|",      "|00123|",
  1363.         "|+00123|",     "| 00123|",     "|000123|",
  1364.         "|+000123|",        "| 000123|",        "|0000123|",
  1365.         "|+0000123|",       "| 0000123|",       "|00000123|",
  1366.         "|+00000123|",      "| 00000123|",      "|000000123|",
  1367.         "|+000000123|",     "| 000000123|",     "|0000000123|",
  1368.         "|+0000000123|",    "| 0000000123|",    "|00000000123|",
  1369.         "|+00000000123|",   "| 00000000123|",   "|000000000123|",
  1370.         "|+000000000123|",  "| 000000000123|",  "|0000000000123|",
  1371.         "|+0000000000123|", "| 0000000000123|", "|00000000000123|",
  1372.         "|+00000000000123|",    "| 00000000000123|",    "|000000000000123|",
  1373.         "|+000000000000123|",   "| 000000000000123|",   "|0000000000000123|",
  1374.         // Left
  1375.         "|+123|",       "| 123|",       "|123|",
  1376.         "|+123|",       "| 123|",       "|123|",
  1377.         "|+123|",       "| 123|",       "|123|",
  1378.         "|+123|",       "| 123|",       "|123 |",
  1379.         "|+123 |",      "| 123 |",      "|123  |",
  1380.         "|+123  |",     "| 123  |",     "|123   |",
  1381.         "|+123   |",        "| 123   |",        "|123    |",
  1382.         "|+123    |",       "| 123    |",       "|123     |",
  1383.         "|+123     |",      "| 123     |",      "|123      |",
  1384.         "|+123      |",     "| 123      |",     "|123       |",
  1385.         "|+123       |",    "| 123       |",    "|123        |",
  1386.         "|+123        |",   "| 123        |",   "|123         |",
  1387.         "|+123         |",  "| 123         |",  "|123          |",
  1388.         "|+123          |", "| 123          |", "|123           |",
  1389.         "|+123           |",    "| 123           |",    "|123            |",
  1390.         "|+123            |",   "| 123            |",   "|123             |",
  1391.         // Centre
  1392.         "|+123|",       "| 123|",       "|123|",
  1393.         "|+123|",       "| 123|",       "|123|",
  1394.         "|+123|",       "| 123|",       "|123|",
  1395.         "|+123|",       "| 123|",       "|123 |",
  1396.         "|+123 |",      "| 123 |",      "|0123 |",
  1397.         "|+0123 |",     "| 0123 |",     "|0123  |",
  1398.         "|+0123  |",        "| 0123  |",        "|00123  |",
  1399.         "|+00123  |",       "| 00123  |",       "|00123   |",
  1400.         "|+00123   |",      "| 00123   |",      "|000123   |",
  1401.         "|+000123   |",     "| 000123   |",     "|000123    |",
  1402.         "|+000123    |",    "| 000123    |",    "|0000123    |",
  1403.         "|+0000123    |",   "| 0000123    |",   "|0000123     |",
  1404.         "|+0000123     |",  "| 0000123     |",  "|00000123     |",
  1405.         "|+00000123     |", "| 00000123     |", "|00000123      |",
  1406.         "|+00000123      |",    "| 00000123      |",    "|000000123      |",
  1407.         "|+000000123      |",   "| 000000123      |",   "|000000123       |",
  1408.         ];
  1409.  
  1410.         import std.array;
  1411.  
  1412.         auto array = appender!(char[])();
  1413.  
  1414.         class ArrayPrinter : TextPrinter
  1415.         {
  1416.             void  colour( int x ) {};
  1417.             void  flush() {};
  1418.             int   width( dchar d ) { return 1; }
  1419.  
  1420.             void  print( char_c[] s ) { array.put( s ); }
  1421.         }
  1422.  
  1423.         auto A = new ArrayPrinter();
  1424.         auto F = new TextFormatter( A );
  1425.  
  1426.         foreach( i, q;  Question )
  1427.         {
  1428.             array.clear;
  1429.             F.format( q, n );
  1430.             assert(  Answer[i] == array.data );
  1431.         }
  1432.         foreach( i, q; Question0 )
  1433.         {
  1434.             array.clear;
  1435.             F.format( q, n );
  1436.             assert(  Answer0[i] == array.data );
  1437.         }
  1438.  
  1439.         // TODO octal/dec tests
  1440.  
  1441.         /+  // Generate Answers
  1442.         {
  1443.             import shd.output;
  1444.             Shdout.print("\n\"");
  1445.             foreach( i, q; Question )
  1446.             {
  1447.                 Shdout.format( q, n );
  1448.                 if( (i+1)%3 )
  1449.                     Shdout.print("\",\t\"");
  1450.                 else
  1451.                     Shdout.print("\",\n\"");
  1452.  
  1453.             }
  1454.         }// +/
  1455.     Shdout.println(">--- End  format / itoa  --<");
  1456. }
  1457. }// end version(unittest)
Advertisement
Add Comment
Please, Sign In to add comment