diff --git a/NEWS.md b/NEWS.md index 55640e6516b93..ed041050fb357 100644 --- a/NEWS.md +++ b/NEWS.md @@ -282,11 +282,15 @@ Deprecated or removed names have been removed. To convert a scalar, use the type name, e.g. `Int32(x)`. To convert an array to a different element type, use `Array{T}(x)`, `map(T,x)`, or `round(T,x)`. To parse a string as an integer - or floating-point number, use `parseint` or `parsefloat` ([#1470], [#6211]). + or floating-point number, use `parse` ([#1470], [#6211]). * Low-level functions from the C library and dynamic linker have been moved to modules `Libc` and `Libdl`, respectively ([#10328]). + * The functions `parseint`, `parsefloat`, `float32_isvalid`, and `float64_isvalid` + have been replaced by `parse` and `tryparse` with a type argument + ([#3631], [#5704], [#9487], [#10543]). + Julia v0.3.0 Release Notes ========================== @@ -1070,6 +1074,7 @@ Too numerous to mention. [#3483]: https://github.com/JuliaLang/julia/issues/3483 [#3523]: https://github.com/JuliaLang/julia/issues/3523 [#3605]: https://github.com/JuliaLang/julia/issues/3605 +[#3631]: https://github.com/JuliaLang/julia/issues/3631 [#3649]: https://github.com/JuliaLang/julia/issues/3649 [#3665]: https://github.com/JuliaLang/julia/issues/3665 [#3688]: https://github.com/JuliaLang/julia/issues/3688 @@ -1158,6 +1163,7 @@ Too numerous to mention. [#5671]: https://github.com/JuliaLang/julia/issues/5671 [#5677]: https://github.com/JuliaLang/julia/issues/5677 [#5703]: https://github.com/JuliaLang/julia/issues/5703 +[#5704]: https://github.com/JuliaLang/julia/issues/5704 [#5726]: https://github.com/JuliaLang/julia/issues/5726 [#5737]: https://github.com/JuliaLang/julia/issues/5737 [#5748]: https://github.com/JuliaLang/julia/issues/5748 @@ -1278,6 +1284,7 @@ Too numerous to mention. [#9425]: https://github.com/JuliaLang/julia/issues/9425 [#9434]: https://github.com/JuliaLang/julia/issues/9434 [#9452]: https://github.com/JuliaLang/julia/issues/9452 +[#9487]: https://github.com/JuliaLang/julia/issues/9487 [#9569]: https://github.com/JuliaLang/julia/issues/9569 [#9575]: https://github.com/JuliaLang/julia/issues/9575 [#9578]: https://github.com/JuliaLang/julia/issues/9578 @@ -1295,6 +1302,10 @@ Too numerous to mention. [#10150]: https://github.com/JuliaLang/julia/issues/10150 [#10180]: https://github.com/JuliaLang/julia/issues/10180 [#10228]: https://github.com/JuliaLang/julia/issues/10228 +[#10328]: https://github.com/JuliaLang/julia/issues/10328 [#10332]: https://github.com/JuliaLang/julia/issues/10332 [#10333]: https://github.com/JuliaLang/julia/issues/10333 [#10400]: https://github.com/JuliaLang/julia/issues/10400 +[#10446]: https://github.com/JuliaLang/julia/issues/10446 +[#10458]: https://github.com/JuliaLang/julia/issues/10458 +[#10543]: https://github.com/JuliaLang/julia/issues/10543 diff --git a/base/Terminals.jl b/base/Terminals.jl index b1743b821f969..c487299c29339 100644 --- a/base/Terminals.jl +++ b/base/Terminals.jl @@ -161,7 +161,7 @@ let s = zeros(Int32, 2) function Base.size(t::TTYTerminal) @windows_only if ispty(t.out_stream) try - h,w = map(parseint,split(readall(open(`stty size`, "r", t.out_stream)[1]))) + h,w = map(x->parse(Int,x),split(readall(open(`stty size`, "r", t.out_stream)[1]))) w > 0 || (w = 80) h > 0 || (h = 24) return h,w diff --git a/base/base.jl b/base/base.jl index 5940c40099257..545360216541f 100644 --- a/base/base.jl +++ b/base/base.jl @@ -277,4 +277,3 @@ immutable Nullable{T} Nullable() = new(true) Nullable(value::T) = new(false, value) end - diff --git a/base/client.jl b/base/client.jl index f8a05906e6c3e..b2542dd791c5c 100644 --- a/base/client.jl +++ b/base/client.jl @@ -190,7 +190,7 @@ function init_bind_addr() bind_to = split(bytestring(opts.bindto), ":") bind_addr = string(parseip(bind_to[1])) if length(bind_to) > 1 - bind_port = parseint(bind_to[2]) + bind_port = parse(Int,bind_to[2]) else bind_port = 0 end @@ -358,7 +358,7 @@ function load_machine_file(path::AbstractString) for line in split(readall(path),'\n'; keep=false) s = map!(strip, split(line,'*'; keep=false)) if length(s) > 1 - cnt = isnumber(s[1]) ? parseint(s[1]) : symbol(s[1]) + cnt = isnumber(s[1]) ? parse(Int,s[1]) : symbol(s[1]) push!(machines,(s[2], cnt)) else push!(machines,line) diff --git a/base/combinatorics.jl b/base/combinatorics.jl index 765cd48149c5d..76c042214b75f 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -49,18 +49,6 @@ function gamma(n::Union(Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64)) @inbounds return Float64(_fact_table64[n-1]) end -function factorial(n::Integer) - n < 0 && throw(DomainError()) - local f::typeof(n*n), i::typeof(n*n) - f = 1 - for i = 2:n - f *= i - end - return f -end - -factorial(x::Number) = gamma(x + 1) # fallback for x not Integer - # computes n!/k! function factorial{T<:Integer}(n::T, k::T) if k < 0 || n < 0 || k > n @@ -75,34 +63,6 @@ function factorial{T<:Integer}(n::T, k::T) end factorial(n::Integer, k::Integer) = factorial(promote(n, k)...) -function binomial{T<:Integer}(n::T, k::T) - k < 0 && return zero(T) - sgn = one(T) - if n < 0 - n = -n + k -1 - if isodd(k) - sgn = -sgn - end - end - k > n && return zero(T) - (k == 0 || k == n) && return sgn - k == 1 && return sgn*n - if k > (n>>1) - k = (n - k) - end - x::T = nn = n - k + 1 - nn += 1 - rr = 2 - while rr <= k - xt = div(widemul(x, nn), rr) - x = xt - x == xt || throw(OverflowError()) - rr += 1 - nn += 1 - end - convert(T, copysign(x, sgn)) -end - ## other ordering related functions ## function nthperm!(a::AbstractVector, k::Integer) k -= 1 # make k 1-indexed diff --git a/base/dates/io.jl b/base/dates/io.jl index 509b2ce230ac8..deddb78f95682 100644 --- a/base/dates/io.jl +++ b/base/dates/io.jl @@ -96,7 +96,7 @@ function slotparse(slot::Slot{Month},x) return Month(MONTHTOVALUE[slot.locale][lowercase(x)]) end end -slotparse(slot::Slot{Millisecond},x) = !ismatch(r"[^0-9\s]",x) ? slot.period(parsefloat("."*x)*1000.0) : throw(SLOTERROR) +slotparse(slot::Slot{Millisecond},x) = !ismatch(r"[^0-9\s]",x) ? slot.period(Base.parse(Float64,"."*x)*1000.0) : throw(SLOTERROR) slotparse(slot::Slot{DayOfWeekSlot},x) = nothing function getslot(x,slot::DelimitedSlot,df,cursor) diff --git a/base/dates/periods.jl b/base/dates/periods.jl index 36e623a391c32..a46e23f2e2e9b 100644 --- a/base/dates/periods.jl +++ b/base/dates/periods.jl @@ -10,7 +10,7 @@ for p in (:Year,:Month,:Week,:Day,:Hour,:Minute,:Second,:Millisecond) # periodisless @eval periodisless(x::$p,y::$p) = value(x) < value(y) # AbstractString parsing (mainly for IO code) - @eval $p(x::AbstractString) = $p(parseint(Int64,x)) + @eval $p(x::AbstractString) = $p(Base.parse(Int64,x)) # Period accessors @eval $p(x::TimeType) = $p($(symbol(lowercase(string(p))))(x)) end diff --git a/base/deprecated.jl b/base/deprecated.jl index dceda3a83a720..3355b45f1a7fc 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -496,3 +496,27 @@ function to_index{T<:Real}(A::AbstractArray{T}) depwarn("indexing with non Integer AbstractArrays is deprecated", :to_index) Int[to_index_nodep(x) for x in A] end + +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 + +function float32_isvalid(s::AbstractString, out::Array{Float32,1}) + depwarn("float32_isvalid is deprecated, use tryparse(Float32,s) instead", :float32_isvalid) + float_isvalid(s, out) +end + +function float64_isvalid(s::AbstractString, out::Array{Float64,1}) + depwarn("float64_isvalid is deprecated, use tryparse(Float64,s) instead", :float64_isvalid) + float_isvalid(s, out) +end + +@deprecate parsefloat(s::AbstractString) parse(Float64,s) +@deprecate parsefloat(T, s) parse(T, s) + +@deprecate parseint(s) parse(Int, s) +@deprecate parseint(s,base) parse(Int, s, base) +@deprecate parseint(T::Type, s) parse(T, s) +@deprecate parseint(T::Type, s, base) parse(T, s, base) diff --git a/base/env.jl b/base/env.jl index 8355ba4583822..4d25f5563e0ad 100644 --- a/base/env.jl +++ b/base/env.jl @@ -189,6 +189,6 @@ function tty_size() return size(os) end end - return (parseint(get(ENV,"LINES","24")), - parseint(get(ENV,"COLUMNS","80"))) + return (parse(Int,get(ENV,"LINES","24")), + parse(Int,get(ENV,"COLUMNS","80"))) end diff --git a/base/exports.jl b/base/exports.jl index f8cd1de2fa4f9..927fee493851a 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -343,6 +343,7 @@ export fldmod, flipsign, float, + tryparse, floor, fma, frexp, @@ -806,8 +807,6 @@ export eachmatch, endswith, escape_string, - float32_isvalid, - float64_isvalid, graphemes, hex, hex2bytes, @@ -844,8 +843,6 @@ export nextind, normalize_string, oct, - parsefloat, - parseint, prevind, print, print_escaped, diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index a60b301a27748..23756b137aeb2 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -30,9 +30,9 @@ num2hex(x::Float64) = hex(box(UInt64,unbox(Float64,x)),16) function hex2num(s::AbstractString) if length(s) <= 8 - return box(Float32,unbox(Int32,parseint(Int32,s,16))) + return box(Float32,unbox(Int32,parse(Int32,s,16))) end - return box(Float64,unbox(Int64,parseint(Int64,s,16))) + return box(Float64,unbox(Int64,parse(Int64,s,16))) end @vectorize_1arg Number abs diff --git a/base/gmp.jl b/base/gmp.jl index f885cdc452cba..90a7161c64594 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -5,7 +5,7 @@ export BigInt import Base: *, +, -, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), ($), binomial, cmp, convert, div, divrem, factorial, fld, gcd, gcdx, lcm, mod, ndigits, promote_rule, rem, show, isqrt, string, isprime, powermod, - sum, trailing_zeros, trailing_ones, count_ones, base, parseint, + sum, trailing_zeros, trailing_ones, count_ones, base, tryparse_internal, serialize, deserialize, bin, oct, dec, hex, isequal, invmod, prevpow2, nextpow2, ndigits0z, widen, signed @@ -74,17 +74,25 @@ widen(::Type{BigInt}) = BigInt signed(x::BigInt) = x BigInt(x::BigInt) = x -BigInt(s::AbstractString) = parseint(BigInt,s) +BigInt(s::AbstractString) = parse(BigInt,s) -function Base.parseint_nocheck(::Type{BigInt}, s::AbstractString, base::Int) +function tryparse_internal(::Type{BigInt}, s::AbstractString, base::Int, raise::Bool) + _n = Nullable{BigInt}() s = bytestring(s) sgn, base, i = Base.parseint_preamble(true,s,base) + if i == 0 + raise && throw(ArgumentError("premature end of integer: $(repr(s))")) + return _n + end z = BigInt() err = ccall((:__gmpz_set_str, :libgmp), Int32, (Ptr{BigInt}, Ptr{UInt8}, Int32), &z, SubString(s,i), base) - err == 0 || throw(ArgumentError("invalid BigInt: $(repr(s))")) - return sgn < 0 ? -z : z + if err != 0 + raise && throw(ArgumentError("invalid BigInt: $(repr(s))")) + return _n + end + Nullable(sgn < 0 ? -z : z) end function BigInt(x::Union(Clong,Int32)) @@ -217,7 +225,7 @@ function serialize(s, n::BigInt) serialize(s, base(62,n)) end -deserialize(s, ::Type{BigInt}) = Base.parseint_nocheck(BigInt, deserialize(s), 62) +deserialize(s, ::Type{BigInt}) = get(tryparse_internal(BigInt, deserialize(s), 62, true)) # Binary ops for (fJ, fC) in ((:+, :add), (:-,:sub), (:*, :mul), diff --git a/base/int.jl b/base/int.jl index a79ee16a563c1..4babaf77dad3f 100644 --- a/base/int.jl +++ b/base/int.jl @@ -248,7 +248,7 @@ floor{T<:Integer}(::Type{T},x::Integer) = convert(T,x) macro int128_str(x) if isa(x,AbstractString) - parseint(Int128,x) + parse(Int128,x) else Int128(x) end @@ -256,7 +256,7 @@ end macro uint128_str(x) if isa(x,AbstractString) - parseint(UInt128,x) + parse(UInt128,x) else UInt128(x) end diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 3bdfb5e838d1a..3079c92dd824f 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -333,3 +333,41 @@ function isqrt(x::Union(Int64,UInt64,Int128,UInt128)) s = (s + div(x,s)) >> 1 s*s > x ? s-1 : s end + +function factorial(n::Integer) + n < 0 && throw(DomainError()) + local f::typeof(n*n), i::typeof(n*n) + f = 1 + for i = 2:n + f *= i + end + return f +end + +function binomial{T<:Integer}(n::T, k::T) + k < 0 && return zero(T) + sgn = one(T) + if n < 0 + n = -n + k -1 + if isodd(k) + sgn = -sgn + end + end + k > n && return zero(T) + (k == 0 || k == n) && return sgn + k == 1 && return sgn*n + if k > (n>>1) + k = (n - k) + end + x::T = nn = n - k + 1 + nn += 1 + rr = 2 + while rr <= k + xt = div(widemul(x, nn), rr) + x = xt + x == xt || throw(OverflowError()) + rr += 1 + nn += 1 + end + convert(T, copysign(x, sgn)) +end diff --git a/base/latex_symbols.jl b/base/latex_symbols.jl index 7870ac9c712b5..16af435b07e73 100644 --- a/base/latex_symbols.jl +++ b/base/latex_symbols.jl @@ -20,7 +20,7 @@ for c in child_nodes(root(xdoc)) if latex != nothing L = strip(content(latex)) id = attribute(ce, "id") - U = string(map(s -> Char(parseint(s, 16)), + U = string(map(s -> Char(parse(Int, s, 16)), split(id[2:end], "-"))...) if ismatch(r"^\\[A-Za-z]+$",L) && !isa(U,ASCIIString) if L in Ls @@ -51,7 +51,7 @@ open("unicode-math-table.tex") do f for L in eachline(f) x = map(s -> rstrip(s, [' ','\t','\n']), split(replace(L, r"[{}\"]+", "\t"), "\t")) - c = Char(parseint(x[2], 16)) + c = Char(parse(Int, x[2], 16)) if (Base.is_id_char(c) || Base.isoperator(symbol(c))) && string(c) ∉ latex_strings && !isascii(c) println(" \"", escape_string(x[3]), "\" => \"", diff --git a/base/multi.jl b/base/multi.jl index 8b292705cf46f..0964d19edeba5 100644 --- a/base/multi.jl +++ b/base/multi.jl @@ -1024,7 +1024,7 @@ end function parse_connection_info(str) m = match(r"^julia_worker:(\d+)#(.*)", str) if m != nothing - (m.captures[2], parseint(Int16, m.captures[1])) + (m.captures[2], parse(Int16, m.captures[1])) else ("", Int16(-1)) end diff --git a/base/nullable.jl b/base/nullable.jl index dd8e6d0591709..8b681a73da70d 100644 --- a/base/nullable.jl +++ b/base/nullable.jl @@ -17,9 +17,9 @@ convert( ::Type{Nullable }, ::Void) = Nullable{Union()}() function show{T}(io::IO, x::Nullable{T}) if x.isnull - @printf(io, "Nullable{%s}()", repr(T)) + print(io, "Nullable{"); show(io, T); print(io, "}()") else - @printf(io, "Nullable(%s)", repr(x.value)) + print(io, "Nullable("); show(io, x.value); print(io, ")") end end diff --git a/base/number.jl b/base/number.jl index 20e856a5611d6..ea55cf2d2728c 100644 --- a/base/number.jl +++ b/base/number.jl @@ -45,3 +45,5 @@ zero(x::Number) = oftype(x,0) zero{T<:Number}(::Type{T}) = convert(T,0) one(x::Number) = oftype(x,1) one{T<:Number}(::Type{T}) = convert(T,1) + +factorial(x::Number) = gamma(x + 1) # fallback for x not Integer diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index 8c633488eaad8..ade5f331d292b 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -310,7 +310,7 @@ function publish(branch::AbstractString) Git.branch(dir="METADATA") == branch || error("METADATA must be on $branch to publish changes") Git.run(`fetch -q`, dir="METADATA") - ahead_remote, ahead_local = map(parseint,split(Git.readchomp(`rev-list --count --left-right origin/$branch...$branch`, dir="METADATA"),'\t')) + ahead_remote, ahead_local = map(x->parse(Int,x),split(Git.readchomp(`rev-list --count --left-right origin/$branch...$branch`, dir="METADATA"),'\t')) ahead_remote > 0 && error("METADATA is behind origin/$branch – run `Pkg.update()` before publishing") ahead_local == 0 && error("There are no METADATA changes to publish") diff --git a/base/pkg/github.jl b/base/pkg/github.jl index 6d776d1775c96..485e52dc513ce 100644 --- a/base/pkg/github.jl +++ b/base/pkg/github.jl @@ -35,7 +35,7 @@ function curl(url::AbstractString, opts::Cmd=``) success(`curl --version`) || error("using the GitHub API requires having `curl` installed") out, proc = open(`curl -i -s -S $opts $url`,"r") head = readline(out) - status = parseint(split(head,r"\s+";limit=3)[2]) + status = parse(Int,split(head,r"\s+";limit=3)[2]) header = Dict{AbstractString,AbstractString}() for line in eachline(out) if !ismatch(r"^\s*$",line) diff --git a/base/pkg/resolve/maxsum.jl b/base/pkg/resolve/maxsum.jl index f12eb6a1bf185..a5a1a1558a59f 100644 --- a/base/pkg/resolve/maxsum.jl +++ b/base/pkg/resolve/maxsum.jl @@ -21,7 +21,7 @@ type MaxSumParams # step function MaxSumParams() - accuracy = parseint(get(ENV, "JULIA_PKGRESOLVE_ACCURACY", "1")) + accuracy = parse(Int,get(ENV, "JULIA_PKGRESOLVE_ACCURACY", "1")) if accuracy <= 0 error("JULIA_PKGRESOLVE_ACCURACY must be >= 1") end diff --git a/base/precompile.jl b/base/precompile.jl index 7609aebf21915..00d9dbd84b19b 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -309,7 +309,7 @@ precompile(Base.occurs_outside_tupleref, (Function, Symbol, Base.StaticVarInfo, precompile(Base.occurs_undef, (Symbol, Expr)) precompile(Base.open, (ASCIIString, ASCIIString)) precompile(Base.parse_input_line, (ASCIIString,)) -precompile(Base.parseint, (Type{Int}, ASCIIString, Int)) +precompile(Base.parse, (Type{Int}, ASCIIString, Int)) precompile(Base.peek, (Base.IOBuffer,)) precompile(Base.pop!, (Array{Any,1},)) precompile(Base.position, (IOBuffer,)) diff --git a/base/random.jl b/base/random.jl index 68e396d613f66..f1183f582b886 100644 --- a/base/random.jl +++ b/base/random.jl @@ -129,7 +129,7 @@ function make_seed() seed = reinterpret(UInt64, time()) seed = hash(seed, UInt64(getpid())) try - seed = hash(seed, parseint(UInt64, readall(pipe(`ifconfig`, `sha1sum`))[1:40], 16)) + seed = hash(seed, parse(UInt64, readall(pipe(`ifconfig`, `sha1sum`))[1:40], 16)) end return make_seed(seed) end diff --git a/base/sharedarray.jl b/base/sharedarray.jl index 501b510c94eac..39bdbd003de13 100644 --- a/base/sharedarray.jl +++ b/base/sharedarray.jl @@ -331,9 +331,9 @@ function print_shmem_limits(slen) @linux_only pfx = "kernel" @osx_only pfx = "kern.sysv" - shmmax_MB = div(parseint(split(readall(`sysctl $(pfx).shmmax`))[end]), 1024*1024) - page_size = parseint(split(readall(`getconf PAGE_SIZE`))[end]) - shmall_MB = div(parseint(split(readall(`sysctl $(pfx).shmall`))[end]) * page_size, 1024*1024) + shmmax_MB = div(parse(Int, split(readall(`sysctl $(pfx).shmmax`))[end]), 1024*1024) + page_size = parse(Int, split(readall(`getconf PAGE_SIZE`))[end]) + shmall_MB = div(parse(Int, split(readall(`sysctl $(pfx).shmall`))[end]) * page_size, 1024*1024) println("System max size of single shmem segment(MB) : ", shmmax_MB, "\nSystem max size of all shmem segments(MB) : ", shmall_MB, diff --git a/base/socket.jl b/base/socket.jl index 72763c0ac0fd8..44112ad6be6f7 100644 --- a/base/socket.jl +++ b/base/socket.jl @@ -148,15 +148,15 @@ function parseipv4(str) if length(f) > 8 # 2+(3*2) - prevent parseint from overflowing on 32bit throw(ArgumentError("IPv4 field too large")) end - r = parseint(f[3:end],16) + r = parse(Int,f[3:end],16) else if length(f) > 9 # 1+8 - prevent parseint from overflowing on 32bit throw(ArgumentError("IPv4 field too large")) end - r = parseint(f,8) + r = parse(Int,f,8) end else - r = parseint(f,10) + r = parse(Int,f,10) end if i != length(fields) if r < 0 || r > 255 @@ -189,7 +189,7 @@ function parseipv6fields(fields,num_fields) cf -= 1 continue end - ret |= UInt128(parseint(f,16))<<(cf*16) + ret |= UInt128(parse(Int,f,16))<<(cf*16) cf -= 1 end ret diff --git a/base/string.jl b/base/string.jl index 300b7d83b5cae..d85f6a8cd783e 100644 --- a/base/string.jl +++ b/base/string.jl @@ -1475,19 +1475,18 @@ strip(s::AbstractString, chars::Chars) = lstrip(rstrip(s, chars), chars) ## string to integer functions ## -function parseint(c::Char, base::Integer=36, a::Int=(base <= 36 ? 10 : 36)) +function parse{T<:Integer}(::Type{T}, c::Char, base::Integer=36) + a::Int = (base <= 36 ? 10 : 36) 2 <= base <= 62 || throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) d = '0' <= c <= '9' ? c-'0' : 'A' <= c <= 'Z' ? c-'A'+10 : 'a' <= c <= 'z' ? c-'a'+a : throw(ArgumentError("invalid digit: $(repr(c))")) d < base || throw(ArgumentError("invalid base $base digit $(repr(c))")) - d + convert(T, d) end -parseint{T<:Integer}(::Type{T}, c::Char, base::Integer) = convert(T,parseint(c,base)) -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 @@ -1495,9 +1494,12 @@ 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 == '+' @@ -1505,9 +1507,12 @@ function parseint_preamble(signed::Bool, s::AbstractString, base::Int) 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,23 +1527,40 @@ 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) + 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 @@ -1546,29 +1568,44 @@ function parseint_nocheck{T<:Integer}(::Type{T}, s::AbstractString, base::Int, a (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) -parseint(s::AbstractString, base::Integer) = parseint(Int,s,base) -parseint(s::AbstractString) = parseint_nocheck(Int,s,0) +function parse{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 +parse{T<:Integer}(::Type{T}, s::AbstractString) = get(tryparse_internal(T, s, 0, true)) ## stringifying integers more efficiently ## @@ -1576,37 +1613,18 @@ 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) +float(x::AbstractString) = parse(Float64,x) float{S<:AbstractString}(a::AbstractArray{S}) = map!(float, similar(a,typeof(float(0))), a) diff --git a/base/sysimg.jl b/base/sysimg.jl index 1f4e2ec77fad2..b291b971ab92a 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -107,6 +107,8 @@ include("env.jl") include("path.jl") include("intfuncs.jl") +# nullable types +include("nullable.jl") # I/O include("task.jl") @@ -162,7 +164,6 @@ include("collections.jl") # Combinatorics include("sort.jl") importall .Sort -include("combinatorics.jl") # version include("version.jl") @@ -176,6 +177,8 @@ big(n::Integer) = convert(BigInt,n) big(x::FloatingPoint) = convert(BigFloat,x) big(q::Rational) = big(num(q))//big(den(q)) +include("combinatorics.jl") + # more hashing definitions include("hashing2.jl") @@ -188,9 +191,6 @@ importall .Random include("printf.jl") importall .Printf -# nullable types -include("nullable.jl") - # concurrency and parallelism include("serialize.jl") include("multi.jl") diff --git a/base/sysinfo.jl b/base/sysinfo.jl index e9a178b40b74c..9069b510e673a 100644 --- a/base/sysinfo.jl +++ b/base/sysinfo.jl @@ -21,7 +21,7 @@ global CPU_CORES function init_sysinfo() # set CPU core count global const CPU_CORES = - haskey(ENV,"JULIA_CPU_CORES") ? parseint(ENV["JULIA_CPU_CORES"]) : + haskey(ENV,"JULIA_CPU_CORES") ? parse(Int,ENV["JULIA_CPU_CORES"]) : Int(ccall(:jl_cpu_cores, Int32, ())) global const SC_CLK_TCK = ccall(:jl_SC_CLK_TCK, Clong, ()) global const cpu_name = ccall(:jl_get_cpu_name, ByteString, ()) diff --git a/base/version.jl b/base/version.jl index 5874295e855e5..1d3cc2cc89700 100644 --- a/base/version.jl +++ b/base/version.jl @@ -75,7 +75,7 @@ function split_idents(s::AbstractString) idents = split(s, '.') ntuple(length(idents)) do i ident = idents[i] - ismatch(r"^\d+$", ident) ? parseint(ident) : bytestring(ident) + ismatch(r"^\d+$", ident) ? parse(Int, ident) : bytestring(ident) end end @@ -83,9 +83,9 @@ VersionNumber(v::AbstractString) = begin m = match(VERSION_REGEX, v) m == nothing && throw(ArgumentError("invalid version string: $v")) major, minor, patch, minus, prerl, plus, build = m.captures - major = parseint(major) - minor = minor != nothing ? parseint(minor) : 0 - patch = patch != nothing ? parseint(patch) : 0 + major = parse(Int, major) + minor = minor != nothing ? parse(Int, minor) : 0 + patch = patch != nothing ? parse(Int, patch) : 0 if prerl != nothing && !isempty(prerl) && prerl[1] == '-' prerl = prerl[2:end] # strip leading '-' end diff --git a/doc/NEWS-update.jl b/doc/NEWS-update.jl index e6b5aa3da1b04..f39cc295d909e 100644 --- a/doc/NEWS-update.jl +++ b/doc/NEWS-update.jl @@ -8,7 +8,7 @@ s = readall(NEWS) s = s[1:match(r"\[#[0-9]+\]:", s).offset-1]; footnote(n) = "[#$n]: https://github.com/JuliaLang/julia/issues/$n" -N = map(m -> parseint(m.captures[1]), eachmatch(r"\[#([0-9]+)\]", s)) +N = map(m -> parse(Int,m.captures[1]), eachmatch(r"\[#([0-9]+)\]", s)) foots = join(map(footnote, sort!(unique(N))), "\n") open(NEWS, "w") do f diff --git a/doc/manual/conversion-and-promotion.rst b/doc/manual/conversion-and-promotion.rst index 331f56ed66573..f29b7af3abf17 100644 --- a/doc/manual/conversion-and-promotion.rst +++ b/doc/manual/conversion-and-promotion.rst @@ -105,7 +105,7 @@ numbers as strings to be conversions (many dynamic languages will even perform conversion for you automatically), however Julia does not: even though some strings can be parsed as numbers, most strings are not valid representations of numbers, and only a very limited subset of them are. -Therefore in Julia the dedicated ``parseint`` function must be used +Therefore in Julia the dedicated ``parse`` function must be used to perform this operation, making it more explicit. Defining New Conversions diff --git a/doc/manual/functions.rst b/doc/manual/functions.rst index 2b6bb594ff5ab..a433e3070b4ce 100644 --- a/doc/manual/functions.rst +++ b/doc/manual/functions.rst @@ -423,11 +423,11 @@ Optional Arguments In many cases, function arguments have sensible default values and therefore might not need to be passed explicitly in every call. For example, the -library function :func:`parseint(num,base) ` interprets a string as a number +library function :func:`parse(type,num,base) ` interprets a string as a number in some base. The ``base`` argument defaults to ``10``. This behavior can be expressed concisely as:: - function parseint(num, base=10) + function parse(type, num, base=10) ### end @@ -437,13 +437,13 @@ specified: .. doctest:: - julia> parseint("12",10) + julia> parse(Int,"12",10) 12 - julia> parseint("12",3) + julia> parse(Int,"12",3) 5 - julia> parseint("12") + julia> parse(Int,"12") 12 Optional arguments are actually just a convenient syntax for writing diff --git a/doc/stdlib/numbers.rst b/doc/stdlib/numbers.rst index f1f4c81d61814..19f4703df13ae 100644 --- a/doc/stdlib/numbers.rst +++ b/doc/stdlib/numbers.rst @@ -48,13 +48,15 @@ Data Formats A string giving the literal bit representation of a number. -.. function:: parseint([type], str, [base]) +.. function:: parse(type, str, [base]) - Parse a string as an integer in the given base (default 10), yielding a number of the specified type (default ``Int``). + Parse a string as a number. If the type is an integer type, then a base can be specified (the default is 10). If the type is a floating point type, the string is parsed as a decimal floating point number. + If the string does not contain a valid number, an error is raised. -.. function:: parsefloat([type], str) +.. function:: tryparse(type, str, [base]) - Parse a string as a decimal floating point number, yielding a number of the specified type. + Like ``parse``, but returns a ``Nullable`` of the requested type. + The result will be null if the string does not contain a valid number. .. function:: big(x) @@ -67,13 +69,6 @@ Data Formats .. function:: unsigned(x) -> Unsigned Convert a number to an unsigned integer. If the argument is signed, it is reinterpreted as unsigned without checking for negative values. -.. function:: float32_isvalid(x, out::Vector{Float32}) -> Bool - - Convert a number or array to ``Float32`` data type, returning true if successful. The result of the conversion is stored in ``out[1]``. - -.. function:: float64_isvalid(x, out::Vector{Float64}) -> Bool - - Convert a number or array to ``Float64`` data type, returning true if successful. The result of the conversion is stored in ``out[1]``. .. function:: float(x) diff --git a/examples/clustermanager/0mq/ZMQCM.jl b/examples/clustermanager/0mq/ZMQCM.jl index 0cde5f05b94f3..e30c3a8fb127b 100644 --- a/examples/clustermanager/0mq/ZMQCM.jl +++ b/examples/clustermanager/0mq/ZMQCM.jl @@ -132,10 +132,10 @@ end function recv_data() try #println("On $(manager.zid_self) waiting to recv message") - zid = parseint(bytestring(ZMQ.recv(manager.sub))) + zid = parse(Int,bytestring(ZMQ.recv(manager.sub))) assert(zid == manager.zid_self) - from_zid = parseint(bytestring(ZMQ.recv(manager.sub))) + from_zid = parse(Int,bytestring(ZMQ.recv(manager.sub))) mtype = bytestring(ZMQ.recv(manager.sub)) #println("$zid received message of type $mtype from $from_zid") diff --git a/examples/clustermanager/0mq/head.jl b/examples/clustermanager/0mq/head.jl index a0465b4c36dcf..64f7e95090e05 100644 --- a/examples/clustermanager/0mq/head.jl +++ b/examples/clustermanager/0mq/head.jl @@ -2,7 +2,7 @@ include("ZMQCM.jl") # @spawn run(`julia broker.jl`) -start_master(parseint(ARGS[1])) +start_master(parse(Int,ARGS[1])) resp = pmap(x -> myid() *2, [1:nworkers()]) diff --git a/examples/clustermanager/0mq/worker.jl b/examples/clustermanager/0mq/worker.jl index 4955153769a07..0091f8485c763 100644 --- a/examples/clustermanager/0mq/worker.jl +++ b/examples/clustermanager/0mq/worker.jl @@ -1,3 +1,3 @@ include("ZMQCM.jl") -start_worker(parseint(ARGS[1])) +start_worker(parse(Int,ARGS[1])) diff --git a/examples/clustermanager/simple/head.jl b/examples/clustermanager/simple/head.jl index 6a7000b4c2d6d..af8ce9085e748 100644 --- a/examples/clustermanager/simple/head.jl +++ b/examples/clustermanager/simple/head.jl @@ -1,5 +1,5 @@ include("UnixDomainCM.jl") -addprocs(UnixDomainCM(parseint(ARGS[1]))) +addprocs(UnixDomainCM(parse(Int,ARGS[1]))) resp = pmap(x -> myid() *2, [1:nworkers()]) println(resp) diff --git a/src/builtins.c b/src/builtins.c index 31cdc992a8daa..e461ecde6a519 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -733,13 +733,36 @@ DLLEXPORT jl_value_t *jl_value_ptr(jl_value_t *a) // printing ------------------------------------------------------------------- -DLLEXPORT int jl_substrtod(char *str, size_t offset, int len, double *out) +int substr_isspace(char *p, char *pend) +{ + while (p != pend) { + if (!isspace((unsigned char)*p)) { + return 0; + } + p++; + } + return 1; +} + +int str_isspace(char *p) +{ + while (*p != '\0') { + if (!isspace((unsigned char)*p)) { + return 0; + } + p++; + } + return 1; +} + +DLLEXPORT jl_nullable_float64_t jl_try_substrtod(char *str, size_t offset, int len) { char *p; - errno = 0; char *bstr = str+offset; char *pend = bstr+len; int err = 0; + + errno = 0; if (!(*pend == '\0' || isspace((unsigned char)*pend) || *pend == ',')) { // confusing data outside substring. must copy. char *newstr = (char*)malloc(len+1); @@ -748,38 +771,65 @@ DLLEXPORT int jl_substrtod(char *str, size_t offset, int len, double *out) bstr = newstr; pend = bstr+len; } - *out = strtod_c(bstr, &p); - if (p == bstr || - (errno==ERANGE && (*out==0 || *out==HUGE_VAL || *out==-HUGE_VAL))) + double out = strtod_c(bstr, &p); + + if (errno==ERANGE && (out==0 || out==HUGE_VAL || out==-HUGE_VAL)) { err = 1; - // Deal with case where the substring might be something like "1 ", - // which is OK, and "1 X", which we don't allow. - while (p != pend) { - if (!isspace((unsigned char)*p)) { - err = 1; - break; - } - p++; } + else if (p == bstr) { + err = 1; + } + else { + // Deal with case where the substring might be something like "1 ", + // which is OK, and "1 X", which we don't allow. + err = substr_isspace(p, pend) ? 0 : 1; + } + if (bstr != str+offset) free(bstr); - return err; + + return (jl_nullable_float64_t){(uint8_t)err, out}; } -DLLEXPORT int jl_strtod(char *str, double *out) +DLLEXPORT jl_nullable_float64_t jl_try_strtod(char *str) { char *p; + int err = 0; + errno = 0; - *out = strtod_c(str, &p); - if (p == str || - (errno==ERANGE && (*out==0 || *out==HUGE_VAL || *out==-HUGE_VAL))) - return 1; - while (*p != '\0') { - if (!isspace((unsigned char)*p)) - return 1; - p++; + double out = strtod_c(str, &p); + + if (errno==ERANGE && (out==0 || out==HUGE_VAL || out==-HUGE_VAL)) { + err = 1; + } + else if (p == str) { + err = 1; + } + else { + err = str_isspace(p) ? 0 : 1; + } + + return (jl_nullable_float64_t){(uint8_t)err, out}; +} + +DLLEXPORT int jl_substrtod(char *str, size_t offset, int len, double *out) +{ + jl_nullable_float64_t nd = jl_try_substrtod(str, offset, len); + if (0 == nd.isnull) { + *out = nd.value; + return 0; } - return 0; + return 1; +} + +DLLEXPORT int jl_strtod(char *str, double *out) +{ + jl_nullable_float64_t nd = jl_try_strtod(str); + if (0 == nd.isnull) { + *out = nd.value; + return 0; + } + return 1; } // MSVC pre-2013 did not define HUGE_VALF @@ -787,13 +837,14 @@ DLLEXPORT int jl_strtod(char *str, double *out) #define HUGE_VALF (1e25f * 1e25f) #endif -DLLEXPORT int jl_substrtof(char *str, int offset, int len, float *out) +DLLEXPORT jl_nullable_float32_t jl_try_substrtof(char *str, size_t offset, int len) { char *p; - errno = 0; char *bstr = str+offset; char *pend = bstr+len; int err = 0; + + errno = 0; if (!(*pend == '\0' || isspace((unsigned char)*pend) || *pend == ',')) { // confusing data outside substring. must copy. char *newstr = (char*)malloc(len+1); @@ -803,46 +854,71 @@ DLLEXPORT int jl_substrtof(char *str, int offset, int len, float *out) pend = bstr+len; } #if defined(_OS_WINDOWS_) && !defined(_COMPILER_MINGW_) - *out = (float)strtod_c(bstr, &p); + float out = (float)strtod_c(bstr, &p); #else - *out = strtof_c(bstr, &p); + float out = strtof_c(bstr, &p); #endif - if (p == bstr || - (errno==ERANGE && (*out==0 || *out==HUGE_VALF || *out==-HUGE_VALF))) + if (errno==ERANGE && (out==0 || out==HUGE_VALF || out==-HUGE_VALF)) { err = 1; - // Deal with case where the substring might be something like "1 ", - // which is OK, and "1 X", which we don't allow. - while (p != pend) { - if (!isspace((unsigned char)*p)) { - err = 1; - break; - } - p++; } + else if (p == bstr) { + err = 1; + } + else { + // Deal with case where the substring might be something like "1 ", + // which is OK, and "1 X", which we don't allow. + err = substr_isspace(p, pend) ? 0 : 1; + } + if (bstr != str+offset) free(bstr); - return err; + + return (jl_nullable_float32_t){(uint8_t)err, out}; } -DLLEXPORT int jl_strtof(char *str, float *out) +DLLEXPORT jl_nullable_float32_t jl_try_strtof(char *str) { char *p; + int err = 0; + errno = 0; #if defined(_OS_WINDOWS_) && !defined(_COMPILER_MINGW_) - *out = (float)strtod_c(str, &p); + float out = (float)strtod_c(str, &p); #else - *out = strtof_c(str, &p); + float out = strtof_c(str, &p); #endif - if (p == str || - (errno==ERANGE && (*out==0 || *out==HUGE_VALF || *out==-HUGE_VALF))) - return 1; - while (*p != '\0') { - if (!isspace((unsigned char)*p)) - return 1; - p++; + if (errno==ERANGE && (out==0 || out==HUGE_VALF || out==-HUGE_VALF)) { + err = 1; } - return 0; + else if (p == str) { + err = 1; + } + else { + err = str_isspace(p) ? 0 : 1; + } + + return (jl_nullable_float32_t){(uint8_t)err, out}; +} + +DLLEXPORT int jl_substrtof(char *str, int offset, int len, float *out) +{ + jl_nullable_float32_t nf = jl_try_substrtof(str, offset, len); + if (0 == nf.isnull) { + *out = nf.value; + return 0; + } + return 1; +} + +DLLEXPORT int jl_strtof(char *str, float *out) +{ + jl_nullable_float32_t nf = jl_try_strtof(str); + if (0 == nf.isnull) { + *out = nf.value; + return 0; + } + return 1; } // showing -------------------------------------------------------------------- diff --git a/src/julia.h b/src/julia.h index 618ba84b121d3..c2f42fe4cd404 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1494,6 +1494,17 @@ DLLEXPORT extern int jl_ver_patch(void); DLLEXPORT extern int jl_ver_is_release(void); DLLEXPORT extern const char* jl_ver_string(void); +// nullable struct representations +typedef struct { + uint8_t isnull; + double value; +} jl_nullable_float64_t; + +typedef struct { + uint8_t isnull; + float value; +} jl_nullable_float32_t; + #ifdef __cplusplus } #endif diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 9f332ed840633..8869d69e1c741 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -99,8 +99,8 @@ let exename = joinpath(JULIA_HOME, Base.julia_exename()) # --startup-file let JL_OPTIONS_STARTUPFILE_ON = 1, JL_OPTIONS_STARTUPFILE_OFF = 2 - @test parseint(readchomp(`$exename -E "Base.JLOptions().startupfile" --startup-file=yes`)) == JL_OPTIONS_STARTUPFILE_ON - @test parseint(readchomp(`$exename -E "Base.JLOptions().startupfile" --startup-file=no`)) == JL_OPTIONS_STARTUPFILE_OFF + @test parse(Int,readchomp(`$exename -E "Base.JLOptions().startupfile" --startup-file=yes`)) == JL_OPTIONS_STARTUPFILE_ON + @test parse(Int,readchomp(`$exename -E "Base.JLOptions().startupfile" --startup-file=no`)) == JL_OPTIONS_STARTUPFILE_OFF end @test !success(`$exename --startup-file=false`) @@ -127,9 +127,9 @@ let exename = joinpath(JULIA_HOME, Base.julia_exename()) let JL_OPTIONS_CHECK_BOUNDS_DEFAULT = 0, JL_OPTIONS_CHECK_BOUNDS_ON = 1, JL_OPTIONS_CHECK_BOUNDS_OFF = 2 - @test parseint(readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)"`)) == JL_OPTIONS_CHECK_BOUNDS_DEFAULT - @test parseint(readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)" --check-bounds=yes`)) == JL_OPTIONS_CHECK_BOUNDS_ON - @test parseint(readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)" --check-bounds=no`)) == JL_OPTIONS_CHECK_BOUNDS_OFF + @test parse(Int,readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)"`)) == JL_OPTIONS_CHECK_BOUNDS_DEFAULT + @test parse(Int,readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)" --check-bounds=yes`)) == JL_OPTIONS_CHECK_BOUNDS_ON + @test parse(Int,readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)" --check-bounds=no`)) == JL_OPTIONS_CHECK_BOUNDS_OFF end # check-bounds takes yes/no as argument @test !success(`$exename -E "exit(0)" --check-bounds=false`) @@ -149,9 +149,9 @@ let exename = joinpath(JULIA_HOME, Base.julia_exename()) # --fast-math let JL_OPTIONS_FAST_MATH_DEFAULT = 0, JL_OPTIONS_FAST_MATH_OFF = 2 - @test parseint(readchomp(`$exename -E "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_DEFAULT - @test parseint(readchomp(`$exename --math-mode=user -E "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_DEFAULT - @test parseint(readchomp(`$exename --math-mode=ieee -E "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_OFF + @test parse(Int,readchomp(`$exename -E "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_DEFAULT + @test parse(Int,readchomp(`$exename --math-mode=user -E "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_DEFAULT + @test parse(Int,readchomp(`$exename --math-mode=ieee -E "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_OFF end # --math-mode takes ieee/user as argument @test !success(`$exename --math-mode=fast`) diff --git a/test/dates/periods.jl b/test/dates/periods.jl index 4bf2e65b9f069..bff98af1b6b87 100644 --- a/test/dates/periods.jl +++ b/test/dates/periods.jl @@ -238,7 +238,7 @@ test = ((((((((dt + y) - m) + w) - d) + h) - mi) + s) - ms) @test Dates.Second("1") == s @test Dates.Millisecond("1") == ms @test_throws ArgumentError Dates.Year("1.0") -@test Dates.Year(parsefloat("1.0")) == y +@test Dates.Year(parse(Float64,"1.0")) == y dt = Dates.DateTime(2014) @test typeof(Dates.Year(dt)) <: Dates.Year diff --git a/test/functional.jl b/test/functional.jl index add6e5c68f783..4f94914d1ba15 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -53,7 +53,7 @@ let zeb = IOBuffer("1\n2\n3\n4\n5\n"), letters = ['a', 'b', 'c', 'd', 'e'], res = [] for (number, letter) in zip(eachline(zeb), letters) - push!(res, (parseint(strip(number)), letter)) + push!(res, (parse(Int,strip(number)), letter)) end @test res == [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')] end diff --git a/test/intfuncs.jl b/test/intfuncs.jl index c9eca3364c6bb..4b033a0f8be1b 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -93,7 +93,7 @@ @test isqrt(5) == 2 # issue #4884 @test isqrt(9223372030926249000) == 3037000498 -@test isqrt(typemax(Int128)) == parseint(Int128,"13043817825332782212") +@test isqrt(typemax(Int128)) == parse(Int128,"13043817825332782212") @test isqrt(Int128(typemax(Int64))^2-1) == 9223372036854775806 @test isqrt(0) == 0 for i = 1:1000 diff --git a/test/perf/micro/perf.jl b/test/perf/micro/perf.jl index 26c46f9b886b0..4bfd4701ebb5e 100644 --- a/test/perf/micro/perf.jl +++ b/test/perf/micro/perf.jl @@ -16,7 +16,7 @@ function parseintperf(t) for i=1:t n = rand(UInt32) s = hex(n) - m = UInt32(parseint(Int64,s,16)) + m = UInt32(parse(Int64,s,16)) end @test m == n return n diff --git a/test/perf/perfcomp.jl b/test/perf/perfcomp.jl index 09d000f93942c..c6a9519aaebdc 100644 --- a/test/perf/perfcomp.jl +++ b/test/perf/perfcomp.jl @@ -5,7 +5,7 @@ # The file format is the output of running `make` in this directory. function readperf(f) - [ rstrip(l[1:19])=>[parsefloat(l[20:27]),parsefloat(l[29:36]),parsefloat(l[38:45]),parsefloat(l[47:54])] for l in eachline(f) ] + [ rstrip(l[1:19])=>[parse(Float64,l[20:27]),parse(Float64,l[29:36]),parse(Float64,l[38:45]),parse(Float64,l[47:54])] for l in eachline(f) ] end function main() diff --git a/test/socket.jl b/test/socket.jl index f8f345f86a99b..66ac032e7c16e 100644 --- a/test/socket.jl +++ b/test/socket.jl @@ -19,7 +19,7 @@ end @test_throws ArgumentError Base.parseipv4("192.") @test ip"::1" == IPv6(1) -@test ip"2605:2700:0:3::4713:93e3" == IPv6(parseint(UInt128,"260527000000000300000000471393e3",16)) +@test ip"2605:2700:0:3::4713:93e3" == IPv6(parse(UInt128,"260527000000000300000000471393e3",16)) @test ip"2001:db8:0:0:0:0:2:1" == ip"2001:db8::2:1" == ip"2001:db8::0:2:1" diff --git a/test/strings.jl b/test/strings.jl index 5dbaabef9ba2d..3734a2e5da025 100644 --- a/test/strings.jl +++ b/test/strings.jl @@ -133,34 +133,34 @@ end @test "\x0F" == unescape_string("\\x0F") # integer parsing -@test is(parseint(Int32,"0",36),Int32(0)) -@test is(parseint(Int32,"1",36),Int32(1)) -@test is(parseint(Int32,"9",36),Int32(9)) -@test is(parseint(Int32,"A",36),Int32(10)) -@test is(parseint(Int32,"a",36),Int32(10)) -@test is(parseint(Int32,"B",36),Int32(11)) -@test is(parseint(Int32,"b",36),Int32(11)) -@test is(parseint(Int32,"F",36),Int32(15)) -@test is(parseint(Int32,"f",36),Int32(15)) -@test is(parseint(Int32,"Z",36),Int32(35)) -@test is(parseint(Int32,"z",36),Int32(35)) - -@test parseint("0") == 0 -@test parseint("-0") == 0 -@test parseint("1") == 1 -@test parseint("-1") == -1 -@test parseint("9") == 9 -@test parseint("-9") == -9 -@test parseint("10") == 10 -@test parseint("-10") == -10 -@test parseint(Int64,"3830974272") == 3830974272 -@test parseint(Int64,"-3830974272") == -3830974272 -@test parseint('3') == 3 -@test parseint('3', 8) == 3 - -parsebin(s) = parseint(s,2) -parseoct(s) = parseint(s,8) -parsehex(s) = parseint(s,16) +@test is(parse(Int32,"0",36),Int32(0)) +@test is(parse(Int32,"1",36),Int32(1)) +@test is(parse(Int32,"9",36),Int32(9)) +@test is(parse(Int32,"A",36),Int32(10)) +@test is(parse(Int32,"a",36),Int32(10)) +@test is(parse(Int32,"B",36),Int32(11)) +@test is(parse(Int32,"b",36),Int32(11)) +@test is(parse(Int32,"F",36),Int32(15)) +@test is(parse(Int32,"f",36),Int32(15)) +@test is(parse(Int32,"Z",36),Int32(35)) +@test is(parse(Int32,"z",36),Int32(35)) + +@test parse(Int,"0") == 0 +@test parse(Int,"-0") == 0 +@test parse(Int,"1") == 1 +@test parse(Int,"-1") == -1 +@test parse(Int,"9") == 9 +@test parse(Int,"-9") == -9 +@test parse(Int,"10") == 10 +@test parse(Int,"-10") == -10 +@test parse(Int64,"3830974272") == 3830974272 +@test parse(Int64,"-3830974272") == -3830974272 +@test parse(Int,'3') == 3 +@test parse(Int,'3', 8) == 3 + +parsebin(s) = parse(Int,s,2) +parseoct(s) = parse(Int,s,8) +parsehex(s) = parse(Int,s,16) @test parsebin("0") == 0 @test parsebin("-0") == 0 @@ -202,49 +202,49 @@ parsehex(s) = parseint(s,16) @test parsehex("-10") == -16 @test parsehex("0BADF00D") == 195948557 @test parsehex("-0BADF00D") == -195948557 -@test parseint(Int64,"BADCAB1E",16) == 3135023902 -@test parseint(Int64,"-BADCAB1E",16) == -3135023902 -@test parseint(Int64,"CafeBabe",16) == 3405691582 -@test parseint(Int64,"-CafeBabe",16) == -3405691582 -@test parseint(Int64,"DeadBeef",16) == 3735928559 -@test parseint(Int64,"-DeadBeef",16) == -3735928559 - -@test parseint("2\n") == 2 -@test parseint(" 2 \n ") == 2 -@test parseint(" 2 ") == 2 -@test parseint("2 ") == 2 -@test parseint(" 2") == 2 -@test parseint("+2\n") == 2 -@test parseint("-2") == -2 -@test_throws ArgumentError parseint(" 2 \n 0") -@test_throws ArgumentError parseint("2x") -@test_throws ArgumentError parseint("-") - -@test parseint('a') == 10 -@test_throws ArgumentError parseint(typemax(Char)) - -@test parseint("1234") == 1234 -@test parseint("0x1234") == 0x1234 -@test parseint("0o1234") == 0o1234 -@test parseint("0b1011") == 0b1011 -@test parseint("-1234") == -1234 -@test parseint("-0x1234") == -Int(0x1234) -@test parseint("-0o1234") == -Int(0o1234) -@test parseint("-0b1011") == -Int(0b1011) +@test parse(Int64,"BADCAB1E",16) == 3135023902 +@test parse(Int64,"-BADCAB1E",16) == -3135023902 +@test parse(Int64,"CafeBabe",16) == 3405691582 +@test parse(Int64,"-CafeBabe",16) == -3405691582 +@test parse(Int64,"DeadBeef",16) == 3735928559 +@test parse(Int64,"-DeadBeef",16) == -3735928559 + +@test parse(Int,"2\n") == 2 +@test parse(Int," 2 \n ") == 2 +@test parse(Int," 2 ") == 2 +@test parse(Int,"2 ") == 2 +@test parse(Int," 2") == 2 +@test parse(Int,"+2\n") == 2 +@test parse(Int,"-2") == -2 +@test_throws ArgumentError parse(Int," 2 \n 0") +@test_throws ArgumentError parse(Int,"2x") +@test_throws ArgumentError parse(Int,"-") + +@test parse(Int,'a') == 10 +@test_throws ArgumentError parse(Int,typemax(Char)) + +@test parse(Int,"1234") == 1234 +@test parse(Int,"0x1234") == 0x1234 +@test parse(Int,"0o1234") == 0o1234 +@test parse(Int,"0b1011") == 0b1011 +@test parse(Int,"-1234") == -1234 +@test parse(Int,"-0x1234") == -Int(0x1234) +@test parse(Int,"-0o1234") == -Int(0o1234) +@test parse(Int,"-0b1011") == -Int(0b1011) ## FIXME: #4905, do these tests for Int128/UInt128! for T in (Int8, Int16, Int32, Int64) - @test parseint(T,string(typemin(T))) == typemin(T) - @test parseint(T,string(typemax(T))) == typemax(T) - @test_throws OverflowError parseint(T,string(big(typemin(T))-1)) - @test_throws OverflowError parseint(T,string(big(typemax(T))+1)) + @test parse(T,string(typemin(T))) == typemin(T) + @test parse(T,string(typemax(T))) == typemax(T) + @test_throws OverflowError parse(T,string(big(typemin(T))-1)) + @test_throws OverflowError parse(T,string(big(typemax(T))+1)) end for T in (UInt8,UInt16,UInt32,UInt64) - @test parseint(T,string(typemin(T))) == typemin(T) - @test parseint(T,string(typemax(T))) == typemax(T) - @test_throws ArgumentError parseint(T,string(big(typemin(T))-1)) - @test_throws OverflowError parseint(T,string(big(typemax(T))+1)) + @test parse(T,string(typemin(T))) == typemin(T) + @test parse(T,string(typemax(T))) == typemax(T) + @test_throws ArgumentError parse(T,string(big(typemin(T))-1)) + @test_throws OverflowError parse(T,string(big(typemax(T))+1)) end # string manipulation @@ -928,17 +928,17 @@ bin_val = hex2bytes("07bf") # issue #4586 @test rsplit(RevString("ailuj"),'l') == ["ju","ia"] -@test parsefloat(Float64,RevString("64")) === 46.0 +@test parse(Float64,RevString("64")) === 46.0 # issue #6772 @test float(SubString("10",1,1)) === 1.0 @test float(SubString("1 0",1,1)) === 1.0 -@test parsefloat(Float32,SubString("10",1,1)) === 1.0f0 +@test parse(Float32,SubString("10",1,1)) === 1.0f0 for T = (UInt8,Int8,UInt16,Int16,UInt32,Int32,UInt64,Int64,UInt128,Int128,BigInt), b = 2:62, _ = 1:10 n = T != BigInt ? rand(T) : BigInt(rand(Int128)) - @test parseint(T,base(b,n),b) == n + @test parse(T,base(b,n),b) == n end # normalize_string (Unicode normalization etc.): @@ -1337,12 +1337,12 @@ end # float(SubString) wasn't tolerant of trailing whitespace, which was different # to "normal" strings. This also checks we aren't being too tolerant and allowing # any arbitrary trailing characters. -@test parsefloat(Float64,"1\n") == 1.0 -@test [parsefloat(Float64,x) for x in split("0,1\n",",")][2] == 1.0 -@test_throws ArgumentError parsefloat(Float64,split("0,1 X\n",",")[2]) -@test parsefloat(Float32,"1\n") == 1.0 -@test [parsefloat(Float32,x) for x in split("0,1\n",",")][2] == 1.0 -@test_throws ArgumentError parsefloat(Float32,split("0,1 X\n",",")[2]) +@test parse(Float64,"1\n") == 1.0 +@test [parse(Float64,x) for x in split("0,1\n",",")][2] == 1.0 +@test_throws ArgumentError parse(Float64,split("0,1 X\n",",")[2]) +@test parse(Float32,"1\n") == 1.0 +@test [parse(Float32,x) for x in split("0,1\n",",")][2] == 1.0 +@test_throws ArgumentError parse(Float32,split("0,1 X\n",",")[2]) #more ascii tests @test convert(ASCIIString, UInt8[32,107,75], "*") == " kK" @@ -1402,3 +1402,25 @@ gstr = Base.GenericString("12"); # issue #10307 @test typeof(map(Int16,String[])) == Vector{Int16} + +for T in [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128] + for i in [typemax(T), typemin(T)] + s = "$i" + @test get(tryparse(T, s)) == i + end +end + +for T in [Int8, Int16, Int32, Int64, Int128] + for i in [typemax(T), typemin(T)] + f = "$(i)0" + @test isnull(tryparse(T, f)) + end +end + +@test get(tryparse(BigInt, "1234567890")) == BigInt(1234567890) +@test isnull(tryparse(BigInt, "1234567890-")) + +@test get(tryparse(Float64, "64")) == 64.0 +@test isnull(tryparse(Float64, "64o")) +@test get(tryparse(Float32, "32")) == 32.0f0 +@test isnull(tryparse(Float32, "32o"))