From 93aabf5ff48a79dd45bec7caf9321260e68e7c12 Mon Sep 17 00:00:00 2001 From: ScottPJones Date: Mon, 21 Sep 2020 10:33:51 -0400 Subject: [PATCH] Use new Printf.format call directly, don't use macro --- src/cformat.jl | 69 +++++++++++++++++++++++++++++++++++++++++++------- src/fmtcore.jl | 8 ++++++ 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/cformat.jl b/src/cformat.jl index e9737a9..65f6123 100644 --- a/src/cformat.jl +++ b/src/cformat.jl @@ -1,17 +1,67 @@ +@static if VERSION > v"1.6.0-DEV.854" + +const NoCommas = 0 +const CheckCommas = 1 +const CheckRat = 2 +const AddCommas = 3 + +const formatters = Dict{ ASCIIStr, Tuple{Printf.Format, Int} }() + +function _checkfmt(fmt) + test = Printf.Format(fmt) + len = length(test.formats) + len === 0 && error("Invalid format string $fmt") + len === 1 || error("Only one undecorated format string is allowed") + test +end + +function _get_formatter(fmt) + global formatters + + chkfmt = get(formatters, fmt, nothing) + chkfmt === nothing || return chkfmt + # Check for thousands separator + if occursin("'", fmt) + conversion = fmt[end] + conversion in "sduifFgG" || + error( string("thousand separator not defined for ", conversion, " conversion") ) + typ = conversion in "dui" ? CheckCommas : conversion === 's' ? CheckRat : AddCommas + formatters[fmt] = (_checkfmt( replace( fmt, "'" => ""; count=1 ) ), typ) + else + formatters[fmt] = (_checkfmt(fmt), NoCommas) + end +end + +function cfmt(fmt::ASCIIStr, x::T) where {T} + formatter, typ = _get_formatter(fmt) + s = Printf.format(formatter, x) + typ === NoCommas ? s : + typ === CheckCommas ? checkcommas(s) : + (typ === CheckRat && T <: Rational) ? addcommasrat(s) : addcommasreal(s) +end + +function _checkrat(formatter, x::T) where {T} + s = Printf.format(formatter, x) + T <: Rational ? addcommasrat(s) : addcommasreal(s) +end + +function generate_formatter( fmt::ASCIIStr ) + formatter, typ = _get_formatter(fmt) + typ === NoCommas ? x -> Printf.format(formatter, x) : + typ === CheckCommas ? x -> checkcomma(Printf.format(formatter, x)) : + typ === CheckRat ? x -> _checkrat(formatter, x) : + x -> addcommasreal(Printf.format(formatter, x)) +end + +else formatters = Dict{ ASCIIStr, Function }() cfmt( fmt::ASCIIStr, x ) = m_eval(Expr(:call, generate_formatter( fmt ), x)) function checkfmt(fmt) - @static if VERSION > v"1.6.0-DEV.854" - test = Printf.Format(fmt) - length(test.formats) == 1 || - error( "Only one AND undecorated format string is allowed") - else - test = @static VERSION >= v"1.4.0-DEV.180" ? Printf.parse(fmt) : Base.Printf.parse( fmt ) - (length( test ) == 1 && typeof( test[1] ) <: Tuple) || - error( "Only one AND undecorated format string is allowed") - end + test = @static VERSION >= v"1.4.0-DEV.180" ? Printf.parse(fmt) : 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::ASCIIStr ) @@ -43,6 +93,7 @@ function generate_formatter( fmt::ASCIIStr ) end return (formatters[ fmt ] = x->Base.invokelatest(formatter, x)) end +end function addcommasreal(s) len = length(s) diff --git a/src/fmtcore.jl b/src/fmtcore.jl index 712b675..0c24363 100644 --- a/src/fmtcore.jl +++ b/src/fmtcore.jl @@ -217,6 +217,14 @@ function _pfmt_e(out::IO, fs::FormatSpec, x::AbstractFloat) rax = round(ax; sigdigits = fs.prec + 1) e = floor(Integer, log10(rax)) # exponent u = rax * exp10(-e) # significand + i = 0 + v10 = 1 + while isinf(u) + i += 1 + i > 18 && (u = 0.0; e = 0; break) + v10 *= 10 + u = v10 * rax * exp(-e - i) + end end # calculate length