|
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