Skip to content

Commit ce122ee

Browse files
authored
Merge pull request #54 from JuliaString/spj/newprintf
Rewrite printf handling, fix bugs
2 parents ecf5bd6 + 63fc025 commit ce122ee

File tree

2 files changed

+563
-84
lines changed

2 files changed

+563
-84
lines changed

src/cformat.jl

Lines changed: 18 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,32 @@
1-
@static if VERSION > v"1.6.0-DEV.854"
1+
include("printf.jl")
22

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}()
174

185
function _get_formatter(fmt)
19-
global formatters
6+
global _formatters
207

21-
chkfmt = get(formatters, fmt, nothing)
8+
chkfmt = get(_formatters, fmt, nothing)
229
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)
3311
end
3412

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))
4716

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)
5422
end
5523

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)
5826

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)
9630
end
9731

9832
function addcommasreal(s)

0 commit comments

Comments
 (0)