Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*******************************************************************************
- Authors: Shahid
- Date: 20, December 2011
- Version: 0.3
- Bugs: Segfaults on invalid utf8
- Notes: I've now realised that each Type will need it's own truncation code
- History: 0.3 [24.03.11] - refactored in big Itoa changes
- 0.21[31.12.11] - Support the Varient type
- 0.21[20.12.11] - better itoa (non-asm)
- 0.2 [25.07.11] - better itoa
- 0.1 [14.07.11] - initial working code
- 0.0 [25.04.11] - initial printf idea
- TODO:
- Support 0 padding for Decimal printing ( sign after padding )
- Printing arrays
- Compile time state
- repeat( string, times )
- Examples:
- Text I/O ( but only Output at the moment )
- *******************************************************************************/
- module shd.io.text;
- import shd.exception,
- shd.types,
- shd.unicode;
- import shd.aSm.x86.array;
- import GlibC = shd.c.glibc: snprintf;
- import shd.text.convert;
- public enum Colour
- {
- Default=0,
- // The Standard Colour set values
- Red=1, Yellow, Green, Blue, Orange=5, Purple, Black, White, Grey, Brown=10,
- /* ... more to come! */
- Max // The last colour +1
- }
- /// Backend of TextFormater
- interface TextPrinter
- {
- void colour( int x );
- void flush();
- void print( char_c[] s );
- int width( dchar d ); // what is this?
- }
- /**
- * String Formatter inspired by C# and python
- *
- * Start Date: 14, March 2011
- */
- class TextFormatter
- {
- private {
- TextPrinter printer;
- }
- this( TextPrinter printer )
- {
- this.printer = printer;
- }
- /**
- * Print text
- */
- final void print(A...)( A args )
- {
- // REFACTOR
- foreach( arg; args )
- format( "{}", arg );
- }
- final void println(A...)( A args ) /// ditto
- {
- print( args );
- printer.print(['\n']);
- printer.flush();
- }
- /**
- * Print formatted text
- */
- final void format(A...)( string formatString, A args )
- {
- Variant[A.length] a;
- foreach( i, arg; args )
- {
- a[i].set( arg );
- }
- parse( formatString, a[] );
- }
- final void formatln(A...)( string formatString, A args ) //ditto
- {
- format( formatString, args );
- printer.print(['\n']);
- printer.flush();
- }
- /**
- * Select the colour by index code
- *
- * Params: x - an "enum Colour". Though implementions may define more ( hence type int ).
- */
- void colour( int c ) { printer.colour( c ); }
- /// Shortcuts for colour()
- void normal() { printer.colour( Colour.Default ); }
- void red() { printer.colour( Colour.Red ); }
- void yellow() { printer.colour( Colour.Yellow ); }
- void green() { printer.colour( Colour.Green ); }
- void blue() { printer.colour( Colour.Blue ); }
- void orange() { printer.colour( Colour.Orange ); }
- void purple() { printer.colour( Colour.Purple ); }
- void black() { printer.colour( Colour.Black ); }
- void white() { printer.colour( Colour.White ); }
- void grey() { printer.colour( Colour.Grey ); }
- void brown() { printer.colour( Colour.Brown ); }
- void flush() { printer.flush(); }
- /* *************************************
- Internals
- ***************************************/
- /*
- * Implementation of format()
- *
- * TODO explain { Error msgs }
- *
- */
- private void parse( char_c[] format, ref Variant[] args )
- {
- char_c* ptr = format.ptr;
- char_c* end = format.ptr + format.length;
- int indexAuto; // Remember the last index used + 1
- char[64] buffer; // must be 16 byte aligned ( not anymore )
- //
- // Begin
- //
- while( true )
- {
- AlignState s; // Formatting state
- uint index = indexAuto;
- char_c[] formatMod; // eg {:derp}
- char_c* cp = find!char( '{', ptr, end );
- // write out
- printer.print( ptr[0 .. cp-ptr] );
- if( cp is end )
- break;
- cp++;
- //
- // Second '{'. ie "{{"
- //
- if( *cp == '{' )
- {
- ptr = cp+1;
- printer.print( "{" ); // REFACTOR
- continue;
- }
- //
- // Colour formatter
- //
- if( *cp == 'c' )
- {
- try
- {
- uint colourF;
- uint colourB = uint.max;
- // Foreground Colour
- parseUInt( colourF, ++cp, end, Parse.Empty );
- // Background Colour ( Currently not supported )
- if( *cp == ',' )
- {
- parseUInt( colourB, ++cp, end, Parse.Empty );
- }
- //debug D.writefln("DEBUG colour](%d,%d)",colourF,colourB);
- // Check everything parsed ok
- if( *cp != '}' )
- throw new ParseException("Open");
- printer.colour( colourF );
- // Optional background colour
- //if( colourB != uint.max ) printer.colour2( colourB );
- ptr = cp+1;
- continue;
- }
- catch( ParseException e )
- {
- printer.print("{Bad Colour: " ~ e.msg ~ " }");
- ptr = find!char( '}', cp, end ) + 1;
- if( ptr > end )
- break;
- // Continue the loop
- continue;
- }
- } // end if 'c'
- //
- // { index , alignment ! truncation : format }
- //
- try
- {
- // Shortcut
- if( *cp == '}' )
- goto ParsingDone;
- // index
- if( true )
- {
- eatSpace( cp, end ); // Whitespace
- parseUInt( index, cp, end, Parse.Empty );
- eatSpace( cp, end ); // Whitespace
- }
- // alignment
- if( *cp == ',' )
- {
- eatSpace( ++cp, end ); // Whitespace
- parseAlign( s.alignment, cp, end, &s );
- eatSpace( cp, end ); // Whitespace
- }
- // truncation
- if( *cp == '!' )
- {
- eatSpace( ++cp, end ); // Whitespace
- parseAlign( s.truncation, cp, end );
- eatSpace( cp, end ); // Whitespace
- }
- // format
- if( *cp == ':' )
- {
- char_c* oldcp = cp+1;
- do {
- if( ++cp is end )
- throw new ParseException("Open");
- }
- while( *cp != '}' );
- formatMod = oldcp[0.. cp-oldcp];
- }
- if( *cp != '}' )
- throw new ParseException("Open");
- /++debug {
- import std.stdio;
- auto atc = ["","<","^",">", "" ];
- // { index, alignment !truncation :formatMod }
- writef("DEBUG] State: \"{%d", index );
- if( s.alignment.type != AlignState.Type.Unset )
- writef(",%s%d", atc[s.alignment.type ], s.alignment.disp );
- if( s.truncation.type != AlignState.Type.Unset )
- writef("!%s%d", atc[s.truncation.type], s.truncation.disp );
- if( formatMod )
- writef(":%s",formatMod);
- writefln("}\"");
- } // +/
- /*
- * format Parsing Done, now work on the argument(s)
- */
- ParsingDone: // setup for next Iteration
- ptr = cp+1;
- indexAuto = index + 1;
- if( index >= args.length ) {
- printer.print("{Not enough args}");
- continue;
- }
- Variant a = args[index];
- itoaControl ifmt;
- with( Variant.Type )
- with( itoaControl )
- final switch( a.type )
- {
- case Pointer:
- ifmt.base = Base.Hex;
- goto case uLong;
- case Byte:
- case Short:
- case Int:
- case Long: ifmt.isSigned = true;
- // fall through
- case uByte:
- case uShort:
- case uInt:
- case uLong:
- parseFormatMod( s, ifmt, formatMod );
- s.str = itoa( a.uLong, buffer, ifmt );
- // Derp
- if( ! s.hasAlignment )
- {
- printer.print( s.str );
- } else {
- // Alignment + Truncation
- // cast away const because s.str is a slice of buffer \\
- itoaAlign( cast(char[]) s.str, buffer, ifmt, s );
- printAlign( s );
- }
- continue; // while(true)
- case Float:
- a.Double = a.Float;
- // fall through
- case Double:
- /* Very basic floating point support */
- auto len = GlibC.snprintf( buffer.ptr, buffer.length, "%f", a.Double );
- s.str = buffer[0..len];
- break;
- //case Real:
- // a.Double = cast(double) a.Real; // FIXME
- // goto case Double;
- case Bool:
- s.str = a.Bool ? "true" : "false";
- break;
- // Already a String
- case Char:
- s.str = (&a.Char)[0..1];
- break;
- case String:
- s.str = a.String;
- break;
- case Data:
- ifmt.padL = 4;
- ifmt.padR = 10;
- auto str = itoa( a.Data.length, buffer, ifmt );
- str[0 .. 4] = "{…";
- str[$-10..$] = " bytes…}";
- s.str = str;
- break;
- // ---
- case ByteA:
- case uByteA:
- case ShortA:
- case uShortA:
- case IntA:
- case uIntA:
- case LongA:
- case uLongA: formatArray( a, s, formatMod );
- continue; // while(true)
- // --:
- case FloatA:
- case DoubleA:
- // --:
- case BoolA:
- case PointerA:
- goto case Data;
- }
- // If no Additional processing is needed
- if( ! s.hasAlignment )
- {
- printer.print( s.str );
- continue;
- }
- // First Truncate
- with( AlignState.Type ) final switch( s.truncation.type )
- {
- case Unset:
- // Calc width upto alignMax because
- // This can break early if width >= alignment
- with ( s.truncation )
- {
- disp = s.alignment.disp;
- truncTail( s ); // calcs s.width
- disp = 0;
- // ---
- s.strLeft = s.str;
- s.truncCharCount = 0;
- }
- break;
- case Left:
- truncHead( s );
- break;
- case Centre:
- truncBody( s );
- break;
- case Default:
- case Right:
- truncTail( s );
- break;
- }
- // Alignment
- s.calcA( s.width );
- // Print Aligned
- printAlign( s );
- }
- catch( ParseException e )
- {
- printer.print("{Bad Format: " ~ e.msg ~ " }");
- ptr = find!char( '}', cp, end ) + 1;
- if( ptr > end )
- break;
- }
- //catch( InvalidArgException e ) {}
- catch( UTFException e )
- {
- printer.print("{Malformed UTF8}");
- }
- } // End while {}
- return;
- }
- void printAlign( ref AlignState s )
- {
- //REFACTOR ----
- writeSpace( s.padL );
- printer.print( s.strLeft );
- foreach( x; 0..s.truncCharCount)
- printer.print( s.truncChar );
- printer.print( s.strRight );
- writeSpace( s.padR );
- //REFACTOR ----
- }
- /* *************************************
- Parse Helpers
- ***************************************/
- // Padding Assistance function
- //
- // Question: what if count is negative
- // Should i trim the string?
- // and if so from front/mid/end???
- private void writeSpace( int count )
- {
- // 16 space chars
- immutable char[16] pad = " ";
- //mutable char[16] pad = "123456789abcdefX";
- if( count <= 0 )
- return;
- while( count > 16 )
- {
- printer.print( pad );
- count -= 16;
- }
- printer.print( pad[0..count] );
- }
- private void eatSpace( ref char_c* start, char_c* end )
- {
- while( start !is end )
- {
- if( *start != ' ' )
- return;
- start++;
- }
- throw new ParseException("Open");
- }
- // Read Align/Trunc spec
- private void parseAlign( out AlignState.Input a, ref char_c* cp, char_c* end, AlignState* s=null )
- {
- with( AlignState.Type ) switch( *cp )
- {
- case '<': a.type = Left; cp++; break;
- case '^': a.type = Centre; cp++; break;
- case '>': a.type = Right; cp++; break;
- default : a.type = Default;
- }
- if( s !is null && cp !is end && *cp == '0' )
- s.padCharL = '0';
- uint saget;
- if( parseUInt( saget, cp, end, Parse.Empty ) )
- a.type = AlignState.Type.Unset;
- a.disp = cast(int) saget;
- }
- /*
- * Format Array and print
- *
- * TODO: if itoaAlign() Overflows
- * TODO: cleanup terrible mess
- */
- private void formatArray( ref Variant v, ref AlignState s, char_c[] formatMod )
- {
- // I dunno why I called it onion
- union Onion {
- long i;
- ubyte[8] a;
- } static assert( Onion.sizeof == 8 );
- int size; // size of the Element Type
- byte signExtend; // 0x80 for Signed, else 0
- char[64] buffer; // itoa
- itoaControl ctrl; // 〃
- Onion onion;
- immutable bool compact = formatMod.length &&
- formatMod[0] == 'c';
- with( Variant.Type )
- switch( v.type )
- {
- case ByteA: case uByteA: size = 1; break;
- case ShortA: case uShortA: size = 2; break;
- case IntA: case uIntA: size = 4; break;
- case LongA: case uLongA: size = 8; break;
- default: assert( false, "Not Numerical Array Type" );
- }
- // Setup
- if( compact )
- {
- if( v.ByteA.length == 0 )
- return;
- parseFormatMod( s, ctrl, formatMod[1..$] );
- if( s.alignment.type == AlignState.Type.Unset )
- {
- s.alignment.type = AlignState.Type.Default;
- s.alignment.disp = size*2;
- s.padCharL = '0';
- }
- ctrl.prefixLeft= true;
- ctrl.base = ctrl.Base.Hex;
- ctrl.isSigned = false;
- signExtend = 0;
- } else {
- if( v.ByteA.length == 0 )
- {
- printer.print("[]");
- return;
- }
- parseFormatMod( s, ctrl, formatMod );
- buffer[62..64]= ", ";
- ctrl.padR = 2;
- s.alignment.disp += 2;
- ctrl.isSigned = (v.type & 1);
- signExtend = (v.type & 1) << 7; // MSB
- debug { red(); printer.print("["); normal(); }
- else {
- printer.print("[");
- }
- }
- // Horrible Hack
- ubyte_c* cp = v.uByteA.ptr;
- ubyte_c* end = cp + v.uByteA.length * size - size;
- // Derp
- if( s.hasAlignment )
- {
- for( ; cp < end; cp += size )
- {
- auto s2 = s;
- // Sign Extend
- onion.i = signExtend & cp[size-1];
- onion.a[0..size] = cp[0..size];
- s2.str = itoa( onion.i, buffer, ctrl );
- // cast away const because s.str is a slice of buffer \\
- itoaAlign( cast(char[]) s2.str, buffer, ctrl, s2 );
- printAlign( s2 );
- }
- if( !compact )
- {
- buffer[63] = ']';
- ctrl.padR = 1;
- s.alignment.disp -= 1;
- }
- onion.i = signExtend & cp[size-1];
- onion.a[0..size] = cp[0..size];
- s.str = itoa( onion.i, buffer, ctrl );
- // cast away const because s.str is a slice of buffer \\
- itoaAlign( cast(char[]) s.str, buffer, ctrl, s );
- printAlign( s );
- }
- else
- {
- for( ; cp < end; cp += size )
- {
- // Sign Extend
- onion.i = cast(byte) (signExtend & cp[size-1]);
- onion.a[0..size] = cp[0..size];
- printer.print( itoa( onion.i, buffer, ctrl ) );
- }
- buffer[63] = ']';
- ctrl.padR = 1;
- onion.i = cast(byte) (signExtend & cp[size-1]);
- onion.a[0..size] = cp[0..size];
- printer.print( itoa( onion.i, buffer, ctrl ) );
- }
- }
- /+ // Templated version
- void formatArrayT( A : E[], E )( A a, State s, char_c[] formatMod )
- {
- char[64] buffer;
- itoaControl ctrl;
- buffer[62..64] = ", ";
- ctrl.padR = 2;
- ctrl.isSigned = isSigned!E;
- parseFormatMod( s, ctrl, formatMod );
- printer.print("[");
- foreach( e; a[0..$-1] )
- {
- printer.print( itoa( e, buffer, ctrl ) );
- }
- buffer[63] = ']';
- ctrl.padR = 1;
- printer.print( itoa( a[$-1], buffer, ctrl ) );
- }
- +/
- /* *************************************
- Trunc code
- ***************************************/
- // TODO list
- // 1) optimise for ascii
- // 2) deal with control/non-printable Unicode chars
- // This is a pain because there is no easy way to do this
- private void truncHead( ref AlignState s )
- {
- char_c* beg = s.str.ptr;
- char_c* end = s.str.ptr + s.str.length;
- char_c* cp = s.str.ptr + s.str.length-1;
- auto columns = s.truncation.disp;
- dchar d;
- for( ; beg <= cp; cp-- )
- {
- // If we are in a multibyte
- if( (*cp & 0xC0) == 0x80 )
- continue;
- // Fast lane
- if( columns > 2 )
- {
- UTF8.toUTF32( d, cp, end );
- columns -= printer.width( d );
- continue;
- }
- auto temp1 = UTF8.toUTF32( d, cp, end );
- auto temp2 = printer.width( d );
- columns -= temp2;
- if( columns < 1 )
- {
- if( cp is beg && columns == 0 )
- break;
- // Undo the last char
- cp += temp1;
- columns += temp2;
- s.strRight = s.str[ cp - beg .. $ ];
- s.width = s.truncation.disp;
- s.truncCharCount= columns;
- return;
- }
- }
- s.strRight = s.str;
- s.width = s.truncation.disp - columns;
- s.truncCharCount=0;
- return;
- }
- private void truncBody( ref AlignState s )
- {
- //REFACTOR
- switch( s.truncation.disp )
- {
- case 1:
- s.strLeft = "…";
- s.strRight = null;
- s.width = 1;
- return;
- case 2:
- // s.strRight = null;
- return truncTail( s );
- default:
- auto save = s.truncation.disp;
- s.truncation.disp = save - save/2;
- truncHead( s );
- s.truncation.disp = save/2; // real
- }
- char_c* beg = s.str.ptr;
- char_c* end = s.strRight.ptr; // s.str.ptr + s.str.length;
- char_c* cp = s.str.ptr;
- auto columns = s.truncation.disp;
- dchar d;
- // Similar version of TruncTail
- while( cp < end )
- {
- // Fast lane
- if( columns > 2 )
- {
- cp += UTF8.toUTF32( d, cp, end );
- columns -= printer.width( d );
- continue;
- }
- // attempt one more char
- auto temp1 = UTF8.toUTF32( d, cp, end );
- auto temp2 = printer.width( d );
- if( columns-temp2 >= 0 )
- {
- cp += temp1;
- columns -= temp2;
- continue;
- }
- // t.disp >= s.width ( don't use truncChar )
- if( cp+temp1 is end && temp2 <= s.truncCharCount + columns )
- {
- cp += temp1;
- columns = 0;
- s.truncCharCount = 0;
- }
- else if ( columns-temp2 == -1 && s.truncCharCount == 2 )
- {
- // rare case when we end up with 3 '…' in the middle
- cp += temp1;
- s.truncCharCount -= temp2;
- }
- s.strLeft = s.str[ 0 .. cp - beg ];
- s.width += s.truncation.disp;
- s.truncCharCount+= columns;
- return;
- }
- // We c̲a̲n̲ actually get here
- s.strLeft = s.str;
- s.strRight = null;
- s.width += s.truncation.disp - columns - s.truncCharCount;
- s.truncCharCount =0;
- return;
- }
- private void truncTail( ref AlignState s )
- {
- char_c* beg = s.str.ptr;
- char_c* end = s.str.ptr + s.str.length;
- char_c* cp = s.str.ptr;
- auto columns = s.truncation.disp;
- dchar d;
- while( cp < end )
- {
- // Fast lane
- if( columns > 2 )
- {
- cp += UTF8.toUTF32( d, cp, end );
- columns -= printer.width( d );
- continue;
- }
- // attempt one more char
- auto temp1 = UTF8.toUTF32( d, cp, end );
- auto temp2 = printer.width( d );
- columns -= temp2;
- if( columns > 0 )
- {
- cp += temp1;
- continue;
- }
- if( cp+1 is end && columns == 0 )
- break;
- s.strLeft = s.str[ 0 .. cp - beg ];
- s.width = s.truncation.disp;
- s.truncCharCount= columns + temp2;
- return;
- }
- s.strLeft = s.str;
- s.width = s.truncation.disp - columns;
- s.truncCharCount=0;
- return;
- }
- }
- /*
- * Alignment:
- *
- * + : prefix with '+' or '-'
- * - : prefix with '␠' or '-'
- * : none = only prefix '-' ( default )
- *
- * Format:
- *
- * b : Binary [ No ]
- * c : UTF8 character [ TODO ]
- * d : Decimal ( default )
- * o : Octal
- * x : Hexadecimal Lowercase
- * X : Hexadecimal Uppercase
- * n : Separate Thousands comma
- * _ : Separate Ten Thousands underscore
- */
- void parseFormatMod( ref AlignState s, ref itoaControl ctrl, char_c[] formatMod ) pure
- {
- // TODO: Make this more general
- // instead of '0' being special.
- if ( s.padCharL == '0' )
- ctrl.prefixLeft = true;
- with( itoaControl )
- foreach( c; formatMod )
- switch( c )
- {
- case '+': ctrl.sign = Sign.Plus; break;
- case '-': ctrl.sign = Sign.Minus; break;
- //
- case 'x': ctrl.lower = true; // fall through
- case 'X': ctrl.base = Base.Hex; break;
- case 'd': ctrl.base = Base.Dec; break;
- case 'o': ctrl.base = Base.Oct; break;
- //se 'b': ctrl.base = 2 ; break;
- //
- case 'n': ctrl.group = Group.Thou;
- ctrl.grpChar= ','; break;
- case '_': ctrl.group = Group.TThou;
- ctrl.grpChar= '_'; break;
- default: {
- throw new ParseException("Malformed formatMod");
- }
- }
- }
- /* ******************************************************************************
- Unit Tests!
- *******************************************************************************/
- version(unittest)
- {
- unittest
- {
- //import shd.c.glibc: LC, setlocale;
- import shd.c.glibc;
- import shd.output;
- setlocale( LC.ALL, "en_GB.UTF-8" );
- alias Shdout T;
- string[] trunc = [
- "abcdefghijklmnopqrstuvwxyz",
- "A一一一一一一一一一一一一一一一一一一一A",
- "一一一一一一一一一一一一一一一一一一一一",
- ];
- // extended
- string[] trunc2 = [
- "一一一一一一一一一一一一一一一一一一一A",
- "A一一一一一一一一一一一一一一一一一一一",
- "abcdefghijklmnopqrs一一一一一一一一一一一一一一一一",
- "abcdefghijklmnopqrs一一一一一一一一一一一一一一一A",
- "A一一一一一一一一一一一一一一一一一jklmnopqrstuvwxyz",
- "一一一一一一一一一一一一一一一一一jklmnopqrstuvwxyz",
- ];
- char_c[] truncHeadGlue( char_c[] str, int columns )
- {
- AlignState s;
- s.str = str;
- s.truncation.disp = columns;
- T.truncHead( s );
- auto n = s.truncCharCount;
- return "………"[0..(n*3)] ~ s.strRight;
- }
- char_c[] truncBodyGlue( char_c[] str, int columns )
- {
- AlignState s;
- s.str = str;
- s.truncation.disp = columns;
- T.truncBody( s );
- auto n = s.truncCharCount;
- return s.strLeft ~ "………"[0..(n*3)] ~ s.strRight;
- }
- char_c[] truncTailGlue( char_c[] str, int columns )
- {
- AlignState s;
- s.str = str;
- s.truncation.disp = columns;
- T.truncTail( s );
- auto n = s.truncCharCount;
- return s.strLeft ~ "………"[0..(n*3)];
- // TODO check i
- }
- // Trunc Head
- {
- alias truncHeadGlue func;
- string[] ans = [
- "…","…","…",
- //2
- "…z","…A","……",
- //3
- "…yz","……A","…一",
- //4
- "…xyz","…一A","……一",
- //5
- "…wxyz",
- "……一A",
- "…一一",
- //6
- "…vwxyz",
- "…一一A",
- "……一一",
- //20,21,22
- "…hijklmnopqrstuvwxyz",
- "…一一一一一一一一一A",
- "……一一一一一一一一一",
- "…ghijklmnopqrstuvwxyz",
- "……一一一一一一一一一A",
- "…一一一一一一一一一一",
- "…fghijklmnopqrstuvwxyz",
- "…一一一一一一一一一一A",
- "……一一一一一一一一一一",
- ];
- int i;
- foreach( t; trunc ) { assert( func( t, 1) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 2) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 3) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 4) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 5) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 6) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
- }
- // Trunc Tail
- {
- alias truncTailGlue func;
- string[] ans = [
- "…","…","…",
- //2
- "a…","A…","……",
- //3
- "ab…","A……","一…",
- //4
- "abc…","A一…","一……",
- //5
- "abcd…",
- "A一……",
- "一一…",
- //6
- "abcde…",
- "A一一…",
- "一一……",
- //20,21,22
- "abcdefghijklmnopqrs…",
- "A一一一一一一一一一…",
- "一一一一一一一一一……",
- "abcdefghijklmnopqrst…",
- "A一一一一一一一一一……",
- "一一一一一一一一一一…",
- "abcdefghijklmnopqrstu…",
- "A一一一一一一一一一一…",
- "一一一一一一一一一一……",
- ];
- int i;
- foreach( t; trunc ) { assert( func( t, 1) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 2) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 3) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 4) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 5) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 6) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
- }
- // Trunc Body
- {
- alias truncBodyGlue func;
- string[] ans = [
- "…","…","…",
- //2
- "a…","A…","……",
- //3
- "a…z","A…A","一…",
- //4
- "ab…z","A……A","一……",
- //5↓
- "ab…yz",
- "A一…A",
- "一…一",
- //6 ↓
- "abc…yz",
- "A一……A",
- "一……一",
- //20 ↓
- "abcdefghij…rstuvwxyz",
- "A一一一一……一一一一A",
- "一一一一一……一一一一",
- //21 ↓
- "abcdefghij…qrstuvwxyz",
- "A一一一一一…一一一一A",
- "一一一一一…一一一一一",
- //22 ↓
- "abcdefghijk…qrstuvwxyz",
- "A一一一一一……一一一一A",
- "一一一一一……一一一一一",
- ];
- int i;
- foreach( t; trunc ) { assert( func( t, 1) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 2) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 3) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 4) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 5) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 6) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
- // extended
- string[] ans2 = [
- //1
- "…","…","…","…","…","…",
- //2
- "……","A…","a…","a…","A…","……",
- //3
- "……A","A……","a……","a…A","A…z","……z",
- //4
- "A一…一",
- "abc…一",
- "abc……A",
- "A一…yz",
- "一……yz",
- //5↓
- "一……A",
- "A……一",
- "ab…一",
- "ab……A",
- "A……yz",
- "一…yz",
- //6 ↓
- "一一…A", // Good no triple Ellipsis
- "A一…一",
- "abc…一",
- "abc……A",
- "A一…yz",
- "一……yz",
- //20 ↓
- "一一一一一…一一一一A",
- "A一一一一一…一一一一",
- "abcdefghij……一一一一",
- "abcdefghij…一一一一A",
- "A一一一一……rstuvwxyz",
- "一一一一一…rstuvwxyz",
- //21 ↓
- "一一一一一……一一一一A",
- "A一一一一……一一一一一",
- "abcdefghij…一一一一一",
- "abcdefghij……一一一一A",
- "A一一一一……qrstuvwxyz",
- "一一一一一…qrstuvwxyz",
- //22 ↓
- "一一一一一一…一一一一A",
- "A一一一一一…一一一一一",
- "abcdefghijk…一一一一一",
- "abcdefghijk……一一一一A",
- "A一一一一一…qrstuvwxyz",
- "一一一一一……qrstuvwxyz",
- ];
- i=0;
- foreach( t; trunc ) { assert( func( t, 1) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 2) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 3) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 4) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 5) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 6) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
- foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
- /+ // Generate Answers
- {
- alias truncBodyGlue func;
- foreach( i; [1,2,3,4] )
- {
- Shdout.format("\n//{}\n",i);
- foreach( t; trunc2 ) { Shdout.format( "\"{}\",", func( t, i)); }
- }
- Shdout.print("\n");
- foreach( i; [5,6,20,21,22] )
- {
- auto o = ( i > 9 ? 3 : 2 );
- Shdout.format("//{}",i);
- foreach( x; 0..(i/2)-o) Shdout.print(" "); Shdout.print("↓\n");
- foreach( t; trunc2 ) { Shdout.format( "\"{}\",\n", func( t, i)); }
- }
- }// +/
- }
- }
- unittest
- {
- import shd.output;
- Shdout.println(">---Start format / itoa --<");
- immutable int n = 123;
- string[] Question = [
- "|{ ,0:+}|", "|{ ,0:-}|", "|{ ,0}|",
- "|{ ,1:+}|", "|{ ,1:-}|", "|{ ,1}|",
- "|{ ,2:+}|", "|{ ,2:-}|", "|{ ,2}|",
- "|{ ,3:+}|", "|{ ,3:-}|", "|{ ,3}|",
- "|{ ,4:+}|", "|{ ,4:-}|", "|{ ,4}|",
- "|{ ,5:+}|", "|{ ,5:-}|", "|{ ,5}|",
- "|{ ,6:+}|", "|{ ,6:-}|", "|{ ,6}|",
- "|{ ,7:+}|", "|{ ,7:-}|", "|{ ,7}|",
- "|{ ,8:+}|", "|{ ,8:-}|", "|{ ,8}|",
- "|{ ,9:+}|", "|{ ,9:-}|", "|{ ,9}|",
- "|{,10:+}|", "|{,10:-}|", "|{,10}|",
- "|{,11:+}|", "|{,11:-}|", "|{,11}|",
- "|{,12:+}|", "|{,12:-}|", "|{,12}|",
- "|{,13:+}|", "|{,13:-}|", "|{,13}|",
- "|{,14:+}|", "|{,14:-}|", "|{,14}|",
- "|{,15:+}|", "|{,15:-}|", "|{,15}|",
- "|{,16:+}|", "|{,16:-}|", "|{,16}|",
- "|{ ,<0:+}|", "|{ ,<0:-}|", "|{ ,<0}|",
- "|{ ,<1:+}|", "|{ ,<1:-}|", "|{ ,<1}|",
- "|{ ,<2:+}|", "|{ ,<2:-}|", "|{ ,<2}|",
- "|{ ,<3:+}|", "|{ ,<3:-}|", "|{ ,<3}|",
- "|{ ,<4:+}|", "|{ ,<4:-}|", "|{ ,<4}|",
- "|{ ,<5:+}|", "|{ ,<5:-}|", "|{ ,<5}|",
- "|{ ,<6:+}|", "|{ ,<6:-}|", "|{ ,<6}|",
- "|{ ,<7:+}|", "|{ ,<7:-}|", "|{ ,<7}|",
- "|{ ,<8:+}|", "|{ ,<8:-}|", "|{ ,<8}|",
- "|{ ,<9:+}|", "|{ ,<9:-}|", "|{ ,<9}|",
- "|{,<10:+}|", "|{,<10:-}|", "|{,<10}|",
- "|{,<11:+}|", "|{,<11:-}|", "|{,<11}|",
- "|{,<12:+}|", "|{,<12:-}|", "|{,<12}|",
- "|{,<13:+}|", "|{,<13:-}|", "|{,<13}|",
- "|{,<14:+}|", "|{,<14:-}|", "|{,<14}|",
- "|{,<15:+}|", "|{,<15:-}|", "|{,<15}|",
- "|{,<16:+}|", "|{,<16:-}|", "|{,<16}|",
- "|{ ,^0:+}|", "|{ ,^0:-}|", "|{ ,^0}|",
- "|{ ,^1:+}|", "|{ ,^1:-}|", "|{ ,^1}|",
- "|{ ,^2:+}|", "|{ ,^2:-}|", "|{ ,^2}|",
- "|{ ,^3:+}|", "|{ ,^3:-}|", "|{ ,^3}|",
- "|{ ,^4:+}|", "|{ ,^4:-}|", "|{ ,^4}|",
- "|{ ,^5:+}|", "|{ ,^5:-}|", "|{ ,^5}|",
- "|{ ,^6:+}|", "|{ ,^6:-}|", "|{ ,^6}|",
- "|{ ,^7:+}|", "|{ ,^7:-}|", "|{ ,^7}|",
- "|{ ,^8:+}|", "|{ ,^8:-}|", "|{ ,^8}|",
- "|{ ,^9:+}|", "|{ ,^9:-}|", "|{ ,^9}|",
- "|{,^10:+}|", "|{,^10:-}|", "|{,^10}|",
- "|{,^11:+}|", "|{,^11:-}|", "|{,^11}|",
- "|{,^12:+}|", "|{,^12:-}|", "|{,^12}|",
- "|{,^13:+}|", "|{,^13:-}|", "|{,^13}|",
- "|{,^14:+}|", "|{,^14:-}|", "|{,^14}|",
- "|{,^15:+}|", "|{,^15:-}|", "|{,^15}|",
- "|{,^16:+}|", "|{,^16:-}|", "|{,^16}|",
- ];
- string[] Question0 = [
- "|{ ,01:+}|", "|{ ,01:-}|", "|{ ,01}|",
- "|{ ,02:+}|", "|{ ,02:-}|", "|{ ,02}|",
- "|{ ,03:+}|", "|{ ,03:-}|", "|{ ,03}|",
- "|{ ,04:+}|", "|{ ,04:-}|", "|{ ,04}|",
- "|{ ,05:+}|", "|{ ,05:-}|", "|{ ,05}|",
- "|{ ,06:+}|", "|{ ,06:-}|", "|{ ,06}|",
- "|{ ,07:+}|", "|{ ,07:-}|", "|{ ,07}|",
- "|{ ,08:+}|", "|{ ,08:-}|", "|{ ,08}|",
- "|{ ,09:+}|", "|{ ,09:-}|", "|{ ,09}|",
- "|{,010:+}|", "|{,010:-}|", "|{,010}|",
- "|{,011:+}|", "|{,011:-}|", "|{,011}|",
- "|{,012:+}|", "|{,012:-}|", "|{,012}|",
- "|{,013:+}|", "|{,013:-}|", "|{,013}|",
- "|{,014:+}|", "|{,014:-}|", "|{,014}|",
- "|{,015:+}|", "|{,015:-}|", "|{,015}|",
- "|{,016:+}|", "|{,016:-}|", "|{,016}|",
- "|{ ,<01:+}|", "|{ ,<01:-}|", "|{ ,<01}|",
- "|{ ,<02:+}|", "|{ ,<02:-}|", "|{ ,<02}|",
- "|{ ,<03:+}|", "|{ ,<03:-}|", "|{ ,<03}|",
- "|{ ,<04:+}|", "|{ ,<04:-}|", "|{ ,<04}|",
- "|{ ,<05:+}|", "|{ ,<05:-}|", "|{ ,<05}|",
- "|{ ,<06:+}|", "|{ ,<06:-}|", "|{ ,<06}|",
- "|{ ,<07:+}|", "|{ ,<07:-}|", "|{ ,<07}|",
- "|{ ,<08:+}|", "|{ ,<08:-}|", "|{ ,<08}|",
- "|{ ,<09:+}|", "|{ ,<09:-}|", "|{ ,<09}|",
- "|{,<010:+}|", "|{,<010:-}|", "|{,<010}|",
- "|{,<011:+}|", "|{,<011:-}|", "|{,<011}|",
- "|{,<012:+}|", "|{,<012:-}|", "|{,<012}|",
- "|{,<013:+}|", "|{,<013:-}|", "|{,<013}|",
- "|{,<014:+}|", "|{,<014:-}|", "|{,<014}|",
- "|{,<015:+}|", "|{,<015:-}|", "|{,<015}|",
- "|{,<016:+}|", "|{,<016:-}|", "|{,<016}|",
- "|{ ,^01:+}|", "|{ ,^01:-}|", "|{ ,^01}|",
- "|{ ,^02:+}|", "|{ ,^02:-}|", "|{ ,^02}|",
- "|{ ,^03:+}|", "|{ ,^03:-}|", "|{ ,^03}|",
- "|{ ,^04:+}|", "|{ ,^04:-}|", "|{ ,^04}|",
- "|{ ,^05:+}|", "|{ ,^05:-}|", "|{ ,^05}|",
- "|{ ,^06:+}|", "|{ ,^06:-}|", "|{ ,^06}|",
- "|{ ,^07:+}|", "|{ ,^07:-}|", "|{ ,^07}|",
- "|{ ,^08:+}|", "|{ ,^08:-}|", "|{ ,^08}|",
- "|{ ,^09:+}|", "|{ ,^09:-}|", "|{ ,^09}|",
- "|{,^010:+}|", "|{,^010:-}|", "|{,^010}|",
- "|{,^011:+}|", "|{,^011:-}|", "|{,^011}|",
- "|{,^012:+}|", "|{,^012:-}|", "|{,^012}|",
- "|{,^013:+}|", "|{,^013:-}|", "|{,^013}|",
- "|{,^014:+}|", "|{,^014:-}|", "|{,^014}|",
- "|{,^015:+}|", "|{,^015:-}|", "|{,^015}|",
- "|{,^016:+}|", "|{,^016:-}|", "|{,^016}|",
- ];
- string[] Answer = [
- // Default
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- "| +123|", "| 123|", "| 123|",
- // Left
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- // Centre
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123 |",
- "|+123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- "| +123 |", "| 123 |", "| 123 |",
- ];
- string[] Answer0 = [
- // Default
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|0123|",
- "|+0123|", "| 0123|", "|00123|",
- "|+00123|", "| 00123|", "|000123|",
- "|+000123|", "| 000123|", "|0000123|",
- "|+0000123|", "| 0000123|", "|00000123|",
- "|+00000123|", "| 00000123|", "|000000123|",
- "|+000000123|", "| 000000123|", "|0000000123|",
- "|+0000000123|", "| 0000000123|", "|00000000123|",
- "|+00000000123|", "| 00000000123|", "|000000000123|",
- "|+000000000123|", "| 000000000123|", "|0000000000123|",
- "|+0000000000123|", "| 0000000000123|", "|00000000000123|",
- "|+00000000000123|", "| 00000000000123|", "|000000000000123|",
- "|+000000000000123|", "| 000000000000123|", "|0000000000000123|",
- // Left
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- "|+123 |", "| 123 |", "|123 |",
- // Centre
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123|",
- "|+123|", "| 123|", "|123 |",
- "|+123 |", "| 123 |", "|0123 |",
- "|+0123 |", "| 0123 |", "|0123 |",
- "|+0123 |", "| 0123 |", "|00123 |",
- "|+00123 |", "| 00123 |", "|00123 |",
- "|+00123 |", "| 00123 |", "|000123 |",
- "|+000123 |", "| 000123 |", "|000123 |",
- "|+000123 |", "| 000123 |", "|0000123 |",
- "|+0000123 |", "| 0000123 |", "|0000123 |",
- "|+0000123 |", "| 0000123 |", "|00000123 |",
- "|+00000123 |", "| 00000123 |", "|00000123 |",
- "|+00000123 |", "| 00000123 |", "|000000123 |",
- "|+000000123 |", "| 000000123 |", "|000000123 |",
- ];
- import std.array;
- auto array = appender!(char[])();
- class ArrayPrinter : TextPrinter
- {
- void colour( int x ) {};
- void flush() {};
- int width( dchar d ) { return 1; }
- void print( char_c[] s ) { array.put( s ); }
- }
- auto A = new ArrayPrinter();
- auto F = new TextFormatter( A );
- foreach( i, q; Question )
- {
- array.clear;
- F.format( q, n );
- assert( Answer[i] == array.data );
- }
- foreach( i, q; Question0 )
- {
- array.clear;
- F.format( q, n );
- assert( Answer0[i] == array.data );
- }
- // TODO octal/dec tests
- /+ // Generate Answers
- {
- import shd.output;
- Shdout.print("\n\"");
- foreach( i, q; Question )
- {
- Shdout.format( q, n );
- if( (i+1)%3 )
- Shdout.print("\",\t\"");
- else
- Shdout.print("\",\n\"");
- }
- }// +/
- Shdout.println(">--- End format / itoa --<");
- }
- }// end version(unittest)
Advertisement
Add Comment
Please, Sign In to add comment