|
1 |
| -@static if VERSION > v"1.6.0-DEV.854" |
| 1 | +include("printf.jl") |
2 | 2 |
|
3 |
| -const NoCommas = 0 |
4 |
| -const CheckCommas = 1 |
5 |
| -const CheckRat = 2 |
6 |
| -const AddCommas = 3 |
7 |
| - |
8 |
| -const formatters = Dict{ ASCIIStr, Tuple{Printf.Format, Int} }() |
9 |
| - |
10 |
| -function _checkfmt(fmt) |
11 |
| - test = Printf.Format(fmt) |
12 |
| - len = length(test.formats) |
13 |
| - len === 0 && error("Invalid format string $fmt") |
14 |
| - len === 1 || error("Only one undecorated format string is allowed") |
15 |
| - test |
16 |
| -end |
| 3 | +const _formatters = Dict{ASCIIStr,FmtSpec}() |
17 | 4 |
|
18 | 5 | function _get_formatter(fmt)
|
19 |
| - global formatters |
| 6 | + global _formatters |
20 | 7 |
|
21 |
| - chkfmt = get(formatters, fmt, nothing) |
| 8 | + chkfmt = get(_formatters, fmt, nothing) |
22 | 9 | chkfmt === nothing || return chkfmt
|
23 |
| - # Check for thousands separator |
24 |
| - if occursin("'", fmt) |
25 |
| - conversion = fmt[end] |
26 |
| - conversion in "sduifFgG" || |
27 |
| - error( string("thousand separator not defined for ", conversion, " conversion") ) |
28 |
| - typ = conversion in "dui" ? CheckCommas : conversion === 's' ? CheckRat : AddCommas |
29 |
| - formatters[fmt] = (_checkfmt( replace( fmt, "'" => ""; count=1 ) ), typ) |
30 |
| - else |
31 |
| - formatters[fmt] = (_checkfmt(fmt), NoCommas) |
32 |
| - end |
| 10 | + _formatters[fmt] = FmtSpec(fmt) |
33 | 11 | end
|
34 | 12 |
|
35 |
| -function cfmt(fmt::ASCIIStr, x::T) where {T} |
36 |
| - formatter, typ = _get_formatter(fmt) |
37 |
| - s = Printf.format(formatter, x) |
38 |
| - typ === NoCommas ? s : |
39 |
| - typ === CheckCommas ? checkcommas(s) : |
40 |
| - (typ === CheckRat && T <: Rational) ? addcommasrat(s) : addcommasreal(s) |
41 |
| -end |
42 |
| - |
43 |
| -function _checkrat(formatter, x::T) where {T} |
44 |
| - s = Printf.format(formatter, x) |
45 |
| - T <: Rational ? addcommasrat(s) : addcommasreal(s) |
46 |
| -end |
| 13 | +_cfmt_comma(fspec::FmtSpec, x) = addcommasreal(_cfmt(fspec, x)) |
| 14 | +_cfmt_comma(fspec::FmtSpec{FmtStr}, x::Rational) = addcommasrat(_cfmt(fspec, x)) |
| 15 | +_cfmt_comma(fspec::FmtSpec{<:FmtInts}, x) = checkcommas(_cfmt(fspec, x)) |
47 | 16 |
|
48 |
| -function generate_formatter( fmt::ASCIIStr ) |
49 |
| - formatter, typ = _get_formatter(fmt) |
50 |
| - typ === NoCommas ? x -> Printf.format(formatter, x) : |
51 |
| - typ === CheckCommas ? x -> checkcomma(Printf.format(formatter, x)) : |
52 |
| - typ === CheckRat ? x -> _checkrat(formatter, x) : |
53 |
| - x -> addcommasreal(Printf.format(formatter, x)) |
| 17 | +function _cfmt(fspec::FmtSpec, x) |
| 18 | + sv = Base.StringVector(23) # Trust that lower level code will expand if necessary |
| 19 | + pos = _fmt(sv, 1, fspec, x) |
| 20 | + resize!(sv, pos - 1) |
| 21 | + String(sv) |
54 | 22 | end
|
55 | 23 |
|
56 |
| -else |
57 |
| -formatters = Dict{ ASCIIStr, Function }() |
| 24 | +cfmt(fspec::FmtSpec, x) = fspec.tsep == 0 ? _cfmt(fspec, x) : _cfmt_comma(fspec, x) |
| 25 | +cfmt(fmtstr::ASCIIStr, x) = cfmt(_get_formatter(fmtstr), x) |
58 | 26 |
|
59 |
| -cfmt( fmt::ASCIIStr, x ) = m_eval(Expr(:call, generate_formatter( fmt ), x)) |
60 |
| - |
61 |
| -function checkfmt(fmt) |
62 |
| - test = @static VERSION >= v"1.4.0-DEV.180" ? Printf.parse(fmt) : Base.Printf.parse( fmt ) |
63 |
| - (length( test ) == 1 && typeof( test[1] ) <: Tuple) || |
64 |
| - error( "Only one AND undecorated format string is allowed") |
65 |
| -end |
66 |
| - |
67 |
| -function generate_formatter( fmt::ASCIIStr ) |
68 |
| - global formatters |
69 |
| - |
70 |
| - haskey( formatters, fmt ) && return formatters[fmt] |
71 |
| - |
72 |
| - if !occursin("'", fmt) |
73 |
| - checkfmt(fmt) |
74 |
| - formatter = @eval(x->@sprintf( $fmt, x )) |
75 |
| - return (formatters[ fmt ] = x->Base.invokelatest(formatter, x)) |
76 |
| - end |
77 |
| - |
78 |
| - conversion = fmt[end] |
79 |
| - conversion in "sduifF" || |
80 |
| - error( string("thousand separator not defined for ", conversion, " conversion") ) |
81 |
| - |
82 |
| - fmtactual = replace( fmt, "'" => ""; count=1 ) |
83 |
| - checkfmt( fmtactual ) |
84 |
| - formatter = |
85 |
| - if !(conversion in "sfF") |
86 |
| - @eval(x->checkcommas(@sprintf( $fmtactual, x ))) |
87 |
| - elseif endswith( fmtactual, 's') |
88 |
| - @eval((x::Real)->((eltype(x) <: Rational) |
89 |
| - ? addcommasrat(@sprintf( $fmtactual, x )) |
90 |
| - : addcommasreal(@sprintf( $fmtactual, x )))) |
91 |
| - else |
92 |
| - @eval((x::Real)->addcommasreal(@sprintf( $fmtactual, x ))) |
93 |
| - end |
94 |
| - return (formatters[ fmt ] = x->Base.invokelatest(formatter, x)) |
95 |
| -end |
| 27 | +function generate_formatter(fmt::ASCIIStr) |
| 28 | + fspec = _get_formatter(fmt) |
| 29 | + fspec.tsep ? x -> _cfmt_comma(fspec, x) : x -> _cfmt(fspec, x) |
96 | 30 | end
|
97 | 31 |
|
98 | 32 | function addcommasreal(s)
|
|
0 commit comments