diff --git a/src/cformat.jl b/src/cformat.jl index 31fef5b..a1d8e2e 100644 --- a/src/cformat.jl +++ b/src/cformat.jl @@ -1,16 +1,80 @@ formatters = Dict{ Compat.ASCIIString, Function }() -function sprintf1( fmt::Compat.ASCIIString, x ) +if VERSION >= v"0.6.0-dev.1671" + +sprintf1( fmt::Compat.ASCIIString, x ) = eval(Expr(:call, generate_formatter( fmt ), x)) + +function checkfmt(fmt) + test = Base.Printf.parse( fmt ) + (length( test ) == 1 && typeof( test[1] ) <: Tuple) || + error( "Only one AND undecorated format string is allowed") +end + +function generate_formatter( fmt::Compat.ASCIIString ) global formatters - f = generate_formatter( fmt ) - f( x ) + + haskey( formatters, fmt ) && return formatters[fmt] + + if !contains( fmt, "'" ) + checkfmt(fmt) + return (formatters[ fmt ] = @eval(x->@sprintf( $fmt, x ))) + end + + conversion = fmt[end] + conversion in "sduifF" || + error( string("thousand separator not defined for ", conversion, " conversion") ) + + fmtactual = replace( fmt, "'", "", 1 ) + checkfmt( fmtactual ) + conversion in "sfF" || + return (formatters[ fmt ] = @eval(x->checkcommas(@sprintf( $fmtactual, x )))) + + formatters[ fmt ] = + if endswith( fmtactual, 's') + @eval((x::Real)->((eltype(x) <: Rational) + ? addcommasrat(@sprintf( $fmtactual, x )) + : addcommasreal(@sprintf( $fmtactual, x )))) + else + @eval((x::Real)->addcommasreal(@sprintf( $fmtactual, x ))) + end end +function addcommasreal(s) + dpos = findfirst( s, '.' ) + dpos != 0 && return string(addcommas( s[1:dpos-1] ), s[ dpos:end ]) + # find the rightmost digit + for i in length( s ):-1:1 + isdigit( s[i] ) && return string(addcommas( s[1:i] ), s[i+1:end]) + end + s +end + +function addcommasrat(s) + # commas are added to only the numerator + spos = findfirst( s, '/' ) + string(addcommas( s[1:spos-1] ), s[spos:end]) +end + +function checkcommas(s) + for i in length( s ):-1:1 + if isdigit( s[i] ) + s = string(addcommas( s[1:i] ), s[i+1:end]) + break + end + end + s +end + +else + +sprintf1( fmt::Compat.ASCIIString, x ) = (generate_formatter( fmt ))(x) + function generate_formatter( fmt::Compat.ASCIIString ) global formatters if haskey( formatters, fmt ) return formatters[fmt] end + func = @compat Symbol("sprintf_", replace(base64encode(fmt), "=", "!")) if !contains( fmt, "'" ) @@ -77,6 +141,7 @@ function generate_formatter( fmt::Compat.ASCIIString ) formatters[ fmt ] = f f end +end function addcommas( s::Compat.ASCIIString ) len = length(s) diff --git a/test/cformat.jl b/test/cformat.jl index d6f7932..15b396f 100644 --- a/test/cformat.jl +++ b/test/cformat.jl @@ -8,11 +8,13 @@ _erfinv(z) = sqrt(π) * Base.Math.@horner(z, 0, 1, 0, π/12, 0, 7π^2/480, 0, 12 function test_equality() println( "test cformat equality...") srand(10) - fmts = Compat.ASCIIString[ "%10.4f", "%f", "%e", "%10f", "%.3f", "%.3e" ] - for fmt in fmts - l = :( x-> x ) - l.args[2].args[2] = @compat Expr(:macrocall, Symbol("@sprintf"), fmt, :x) - mfmtr = eval( l ) + fmts = [ (x->@sprintf("%10.4f",x), "%10.4f"), + (x->@sprintf("%f", x), "%f"), + (x->@sprintf("%e", x), "%e"), + (x->@sprintf("%10f", x), "%10f"), + (x->@sprintf("%.3f", x), "%.3f"), + (x->@sprintf("%.3e", x), "%.3e")] + for (mfmtr,fmt) in fmts for i in 1:10000 n = _erfinv( rand() * 1.99 - 1.99/2.0 ) expect = mfmtr( n ) @@ -21,11 +23,11 @@ function test_equality() end end - fmts = Compat.ASCIIString[ "%d", "%10d", "%010d", "%-10d" ] - for fmt in fmts - l = :( x-> x ) - l.args[2].args[2] = @compat Expr(:macrocall, Symbol("@sprintf"), fmt, :x) - mfmtr = eval( l ) + fmts = [ (x->@sprintf("%d",x), "%d"), + (x->@sprintf("%10d",x), "%10d"), + (x->@sprintf("%010d",x), "%010d"), + (x->@sprintf("%-10d",x), "%-10d")] + for (mfmtr,fmt) in fmts for i in 1:10000 j = round(Int, _erfinv( rand() * 1.99 - 1.99/2.0 ) * 100000 ) expect = mfmtr( j )