-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
Introduces the tryparse method: - tryparse{T<:Integer}(::Type{T<:Integer},s::AbstractString) - tryparse(::Type{Float..},s::AbstractString) - a few variants of the above And: - tryparse(Float.., ...) call the corresponding C functions jl_try_strtof, jl_try_substrtof, jl_try_strtod and jl_try_substrtod. - The parseint, parsefloat, float64_isvalid and float32_isvalid methods wrap the corresponding tryparse methods. - The jl_strtod, jl_strtof, ... functions are wrappers over the jl_try_str... functions. This should fix #10498 as well. Ref: discussions at #9316, #3631, #5704
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -277,4 +277,3 @@ immutable Nullable{T} | |
Nullable() = new(true) | ||
Nullable(value::T) = new(false, value) | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -343,6 +343,7 @@ export | |
fldmod, | ||
flipsign, | ||
float, | ||
tryparse, | ||
floor, | ||
fma, | ||
frexp, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1487,27 +1487,33 @@ parseint{T<:Integer}(::Type{T}, c::Char, base::Integer) = convert(T,parseint(c,b | |
parseint{T<:Integer}(::Type{T}, c::Char) = convert(T,parseint(c)) | ||
|
||
function parseint_next(s::AbstractString, i::Int=start(s)) | ||
done(s,i) && throw(ArgumentError("premature end of integer: $(repr(s))")) | ||
done(s,i) && (return Char(0), 0, 0) | ||
j = i | ||
c, i = next(s,i) | ||
c, i, j | ||
end | ||
|
||
function parseint_preamble(signed::Bool, s::AbstractString, base::Int) | ||
c, i, j = parseint_next(s) | ||
|
||
while isspace(c) | ||
c, i, j = parseint_next(s,i) | ||
end | ||
(j == 0) && (return 0, 0, 0) | ||
|
||
sgn = 1 | ||
if signed | ||
if c == '-' || c == '+' | ||
(c == '-') && (sgn = -1) | ||
c, i, j = parseint_next(s,i) | ||
end | ||
end | ||
|
||
while isspace(c) | ||
c, i, j = parseint_next(s,i) | ||
end | ||
(j == 0) && (return 0, 0, 0) | ||
|
||
if base == 0 | ||
if c == '0' && !done(s,i) | ||
c, i = next(s,i) | ||
|
@@ -1522,94 +1528,121 @@ function parseint_preamble(signed::Bool, s::AbstractString, base::Int) | |
return sgn, base, j | ||
end | ||
|
||
function parseint_nocheck{T<:Integer}(::Type{T}, s::AbstractString, base::Int, a::Int) | ||
safe_add{T<:Integer}(n1::T, n2::T) = ((n2 > 0) ? (n1 > (typemax(T) - n2)) : (n1 < (typemin(T) - n2))) ? Nullable{T}() : Nullable{T}(n1 + n2) | ||
safe_mul{T<:Integer}(n1::T, n2::T) = ((n2 > 0) ? ((n1 > div(typemax(T),n2)) || (n1 < div(typemin(T),n2))) : | ||
(n2 < -1) ? ((n1 > div(typemin(T),n2)) || (n1 < div(typemax(T),n2))) : | ||
((n2 == -1) && n1 == typemin(T))) ? Nullable{T}() : Nullable{T}(n1 * n2) | ||
|
||
function tryparse_internal{T<:Integer}(::Type{T}, s::AbstractString, base::Int, a::Int, raise::Bool) | ||
_n = Nullable{T}() | ||
sgn, base, i = parseint_preamble(T<:Signed,s,base) | ||
if i == 0 | ||
raise && throw(ArgumentError("premature end of integer: $(repr(s))")) | ||
return _n | ||
end | ||
c, i = parseint_next(s,i) | ||
if i == 0 | ||
raise && throw(ArgumentError("premature end of integer: $(repr(s))")) | ||
return _n | ||
end | ||
|
||
base = convert(T,base) | ||
## FIXME: remove 128-bit specific code once 128-bit div doesn't rely on BigInt | ||
m::T = T===UInt128 || T===Int128 ? typemax(T) : div(typemax(T)-base+1,base) | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
JeffBezanson
Member
|
||
m::T = div(typemax(T)-base+1,base) | ||
n::T = 0 | ||
while n <= m | ||
d::T = '0' <= c <= '9' ? c-'0' : | ||
'A' <= c <= 'Z' ? c-'A'+10 : | ||
'a' <= c <= 'z' ? c-'a'+a : base | ||
d < base || throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(s))")) | ||
if d >= base | ||
raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(s))")) | ||
return _n | ||
end | ||
n *= base | ||
n += d | ||
if done(s,i) | ||
n *= sgn | ||
return n | ||
return Nullable{T}(n) | ||
end | ||
c, i = next(s,i) | ||
isspace(c) && break | ||
end | ||
(T <: Signed) && (n *= sgn) | ||
while !isspace(c) | ||
d::T = '0' <= c <= '9' ? c-'0' : | ||
'A' <= c <= 'Z' ? c-'A'+10 : | ||
'a' <= c <= 'z' ? c-'a'+a : base | ||
d < base || throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(s))")) | ||
'A' <= c <= 'Z' ? c-'A'+10 : | ||
'a' <= c <= 'z' ? c-'a'+a : base | ||
if d >= base | ||
raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(s))")) | ||
return _n | ||
end | ||
(T <: Signed) && (d *= sgn) | ||
n = checked_mul(n,base) | ||
n = checked_add(n,d) | ||
done(s,i) && return n | ||
|
||
safe_n = safe_mul(n, base) | ||
isnull(safe_n) || (safe_n = safe_add(get(safe_n), d)) | ||
if isnull(safe_n) | ||
raise && throw(OverflowError()) | ||
return _n | ||
end | ||
n = get(safe_n) | ||
done(s,i) && return Nullable{T}(n) | ||
c, i = next(s,i) | ||
end | ||
while !done(s,i) | ||
c, i = next(s,i) | ||
isspace(c) || throw(ArgumentError("extra characters after whitespace in $(repr(s))")) | ||
if !isspace(c) | ||
raise && throw(ArgumentError("extra characters after whitespace in $(repr(s))")) | ||
return _n | ||
end | ||
end | ||
return n | ||
return Nullable{T}(n) | ||
end | ||
parseint_nocheck{T<:Integer}(::Type{T}, s::AbstractString, base::Int) = | ||
parseint_nocheck(T, s, base, base <= 36 ? 10 : 36) | ||
tryparse_internal{T<:Integer}(::Type{T}, s::AbstractString, base::Int, raise::Bool) = | ||
tryparse_internal(T, s, base, base <= 36 ? 10 : 36, raise) | ||
tryparse{T<:Integer}(::Type{T}, s::AbstractString, base::Int) = | ||
2 <= base <= 62 ? tryparse_internal(T,s,Int(base),false) : throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) | ||
tryparse{T<:Integer}(::Type{T}, s::AbstractString) = tryparse_internal(T,s,0,false) | ||
|
||
parseint{T<:Integer}(::Type{T}, s::AbstractString, base::Integer) = | ||
2 <= base <= 62 ? parseint_nocheck(T,s,Int(base)) : throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) | ||
parseint{T<:Integer}(::Type{T}, s::AbstractString) = parseint_nocheck(T,s,0) | ||
function parseint{T<:Integer}(::Type{T}, s::AbstractString, base::Integer) | ||
(2 <= base <= 62) || throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) | ||
get(tryparse_internal(T, s, base, true)) | ||
end | ||
parseint{T<:Integer}(::Type{T}, s::AbstractString) = get(tryparse_internal(T, s, 0, true)) | ||
parseint(s::AbstractString, base::Integer) = parseint(Int,s,base) | ||
parseint(s::AbstractString) = parseint_nocheck(Int,s,0) | ||
parseint(s::AbstractString) = parseint(Int,s) | ||
|
||
## stringifying integers more efficiently ## | ||
|
||
string(x::Union(Int8,Int16,Int32,Int64,Int128)) = dec(x) | ||
|
||
## string to float functions ## | ||
|
||
float64_isvalid(s::AbstractString, out::Array{Float64,1}) = | ||
ccall(:jl_strtod, Int32, (Ptr{UInt8},Ptr{Float64}), s, out) == 0 | ||
float32_isvalid(s::AbstractString, out::Array{Float32,1}) = | ||
ccall(:jl_strtof, Int32, (Ptr{UInt8},Ptr{Float32}), s, out) == 0 | ||
|
||
float64_isvalid(s::SubString, out::Array{Float64,1}) = | ||
ccall(:jl_substrtod, Int32, (Ptr{UInt8},Csize_t,Cint,Ptr{Float64}), s.string, s.offset, s.endof, out) == 0 | ||
float32_isvalid(s::SubString, out::Array{Float32,1}) = | ||
ccall(:jl_substrtof, Int32, (Ptr{UInt8},Csize_t,Cint,Ptr{Float32}), s.string, s.offset, s.endof, out) == 0 | ||
|
||
begin | ||
local tmp::Array{Float64,1} = Array(Float64,1) | ||
local tmpf::Array{Float32,1} = Array(Float32,1) | ||
global parsefloat | ||
function parsefloat(::Type{Float64}, s::AbstractString) | ||
if !float64_isvalid(s, tmp) | ||
throw(ArgumentError("parsefloat(Float64,::AbstractString): invalid number format $(repr(s))")) | ||
end | ||
return tmp[1] | ||
end | ||
tryparse(::Type{Float64}, s::AbstractString) = ccall(:jl_try_strtod, Nullable{Float64}, (Ptr{UInt8},), s) | ||
tryparse(::Type{Float64}, s::SubString) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Cint), s.string, s.offset, s.endof) | ||
|
||
function parsefloat(::Type{Float32}, s::AbstractString) | ||
if !float32_isvalid(s, tmpf) | ||
throw(ArgumentError("parsefloat(Float32,::AbstractString): invalid number format $(repr(s))")) | ||
end | ||
return tmpf[1] | ||
end | ||
tryparse(::Type{Float32}, s::AbstractString) = ccall(:jl_try_strtof, Nullable{Float32}, (Ptr{UInt8},), s) | ||
tryparse(::Type{Float32}, s::SubString) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Cint), s.string, s.offset, s.endof) | ||
|
||
function parse{T<:Union(Float32,Float64)}(::Type{T}, s::AbstractString) | ||
nf = tryparse(T, s) | ||
isnull(nf) ? throw(ArgumentError("invalid number format $(repr(s)) for $T")) : get(nf) | ||
end | ||
|
||
float(x::AbstractString) = parsefloat(x) | ||
parsefloat(x::AbstractString) = parsefloat(Float64,x) | ||
parsefloat{T<:Union(Float32,Float64)}(::Type{T}, s::AbstractString) = parse(T,s) | ||
|
||
float(x::AbstractString) = parse(Float64,x) | ||
parsefloat(x::AbstractString) = parse(Float64,x) | ||
|
||
float{S<:AbstractString}(a::AbstractArray{S}) = map!(float, similar(a,typeof(float(0))), a) | ||
|
||
function float_isvalid{T<:Union(Float32,Float64)}(s::AbstractString, out::Array{T,1}) | ||
tf = tryparse(T, s) | ||
isnull(tf) || (out[1] = get(tf)) | ||
!isnull(tf) | ||
end | ||
|
||
float32_isvalid(s::AbstractString, out::Array{Float32,1}) = float_isvalid(s, out) | ||
float64_isvalid(s::AbstractString, out::Array{Float64,1}) = float_isvalid(s, out) | ||
|
||
# find the index of the first occurrence of a value in a byte array | ||
|
||
typealias ByteArray Union(Array{UInt8,1},Array{Int8,1}) | ||
|
Interestingly, this change makes parsing Int128 literals rely on compiler intrinsics that MSVC doesn't have. Those missing intrinsics are the same reason the MSVC build immediately fails on tests (
rand(1:2)
doesn't work, since it promotes to Int128 somewhere along the way), hopefully we'll be able to get those intrinsics from LLVM after we upgrade.