diff --git a/README.md b/README.md index 5e498609c..0450f2b85 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,11 @@ Currently, the `@compat` macro supports the following syntaxes: * `codeunits(s)` returns an array-like view of the `UInt8` code units of a string and `ncodeunits(s)` returns the number of code units ([#25241]). + `codeunit(s)` returns the type of the code units of `s` ([#24999]). + +* `thisind(s, i)` returns the character index for codeunit `i` ([#24414]). + +* Three-argument methods `prevind(s,i,n)`, `nextind(s,i,n)` ([#23805]), and `length(s,i,j)` ([#24999]); the latter two replace `chr2ind` and `ind2chr` in Julia 0.7, respectively. * `printstyled` prints to a given stream optionally in color and/or bolded ([#25522]). @@ -562,12 +567,14 @@ includes this fix. Find the minimum version from there. [#23642]: https://github.com/JuliaLang/julia/issues/23642 [#23666]: https://github.com/JuliaLang/julia/issues/23666 [#23757]: https://github.com/JuliaLang/julia/issues/23757 +[#23805]: https://github.com/JuliaLang/julia/issues/23805 [#23931]: https://github.com/JuliaLang/julia/issues/23931 [#24047]: https://github.com/JuliaLang/julia/issues/24047 [#24182]: https://github.com/JuliaLang/julia/issues/24182 [#24282]: https://github.com/JuliaLang/julia/issues/24282 [#24361]: https://github.com/JuliaLang/julia/issues/24361 [#24372]: https://github.com/JuliaLang/julia/issues/24372 +[#24414]: https://github.com/JuliaLang/julia/issues/24414 [#24443]: https://github.com/JuliaLang/julia/issues/24443 [#24459]: https://github.com/JuliaLang/julia/issues/24459 [#24490]: https://github.com/JuliaLang/julia/issues/24490 @@ -582,6 +589,7 @@ includes this fix. Find the minimum version from there. [#24808]: https://github.com/JuliaLang/julia/issues/24808 [#24831]: https://github.com/JuliaLang/julia/issues/24831 [#24874]: https://github.com/JuliaLang/julia/issues/24874 +[#24999]: https://github.com/JuliaLang/julia/issues/24999 [#25012]: https://github.com/JuliaLang/julia/issues/25012 [#25021]: https://github.com/JuliaLang/julia/issues/25021 [#25056]: https://github.com/JuliaLang/julia/issues/25056 diff --git a/src/Compat.jl b/src/Compat.jl index 915ff6ef4..8d018b591 100644 --- a/src/Compat.jl +++ b/src/Compat.jl @@ -1925,6 +1925,60 @@ end export @cfunction end +if VERSION < v"0.7.0-DEV.2920" # julia#24999 + Base.length(s::AbstractString, i::Integer, j::Integer) = length(s, Int(i), Int(j)) + function Base.length(s::AbstractString, i::Int, j::Int) + @boundscheck begin + 0 < i ≤ ncodeunits(s)+1 || throw(BoundsError(s, i)) + 0 ≤ j < ncodeunits(s)+1 || throw(BoundsError(s, j)) + end + n = 0 + for k = i:j + @inbounds n += isvalid(s, k) + end + return n + end + Base.codeunit(s::String) = UInt8 + Base.codeunit(s::SubString) = codeunit(s.string) +end +if !isdefined(Base, :thisind) # #24414 + thisind(s::AbstractString, i::Integer) = thisind(s, Int(i)) + function thisind(s::AbstractString, i::Int) + z = ncodeunits(s) + 1 + i == z && return i + @boundscheck 0 ≤ i ≤ z || throw(BoundsError(s, i)) + @inbounds while 1 < i && !isvalid(s, i) + i -= 1 + end + return i + end + export thisind +end +if VERSION < v"0.7.0-DEV.2019" # julia#23805 + Base.prevind(s::AbstractString, i::Integer, n::Integer) = prevind(s, Int(i), Int(n)) + Base.nextind(s::AbstractString, i::Integer, n::Integer) = nextind(s, Int(i), Int(n)) + function Base.nextind(s::AbstractString, i::Int, n::Int) + n < 0 && throw(ArgumentError("n cannot be negative: $n")) + z = ncodeunits(s) + @boundscheck 0 ≤ i ≤ z || throw(BoundsError(s, i)) + n == 0 && return thisind(s, i) == i ? i : throw(BoundsError(s, i)) + while n > 0 && i < z + @inbounds n -= isvalid(s, i += 1) + end + return i + n + end + function Base.prevind(s::AbstractString, i::Int, n::Int) + n < 0 && throw(ArgumentError("n cannot be negative: $n")) + z = ncodeunits(s) + 1 + @boundscheck 0 < i ≤ z || throw(BoundsError(s, i)) + n == 0 && return thisind(s, i) == i ? i : throw(BoundsError(s, i)) + while n > 0 && 1 < i + @inbounds n -= isvalid(s, i -= 1) + end + return i - n + end +end + if VERSION < v"0.7.0-DEV.5278" something() = throw(ArgumentError("No value arguments present")) something(x::Nothing, y...) = something(y...) diff --git a/test/runtests.jl b/test/runtests.jl index 73963fa47..7c8aee338 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1238,6 +1238,7 @@ end @test codeunits("foo") == [0x66,0x6f,0x6f] == codeunits(SubString("fooαβγ",1,3)) @test ncodeunits("αβγ") == 6 == ncodeunits(SubString("fooαβγ",4,8)) +@test codeunit("foo") == codeunit(SubString("fooαβγ",1,3)) == UInt8 # 0.7.0-DEV.3539 @test nameof(Compat.Sys) == :Sys @@ -1768,6 +1769,103 @@ end @test something(Some(2), 1) === 2 @test something(nothing, Some(1)) === 1 +# julia#24999 +let s = "∀α>β:α+" + @test [length(s,i,j) for i=1:ncodeunits(s)+1, j=0:ncodeunits(s)] == + [0 1 1 1 2 2 3 4 4 5 6 6 7; 0 0 0 0 1 1 2 3 3 4 5 5 6; 0 0 0 0 1 1 2 3 3 4 5 5 6; 0 0 0 0 1 1 2 3 3 4 5 5 6; 0 0 0 0 0 0 1 2 2 3 4 4 5; 0 0 0 0 0 0 1 2 2 3 4 4 5; 0 0 0 0 0 0 0 1 1 2 3 3 4; 0 0 0 0 0 0 0 0 0 1 2 2 3; 0 0 0 0 0 0 0 0 0 1 2 2 3; 0 0 0 0 0 0 0 0 0 0 1 1 2; 0 0 0 0 0 0 0 0 0 0 0 0 1; 0 0 0 0 0 0 0 0 0 0 0 0 1; 0 0 0 0 0 0 0 0 0 0 0 0 0] +end +@test_throws BoundsError length("hello", 1, -1) +@test_throws BoundsError length("hellø", 1, -1) +@test_throws BoundsError length("hello", 1, 10) +@test_throws BoundsError length("hellø", 1, 10) == 9 +@test_throws BoundsError prevind("hello", 0, 1) +@test_throws BoundsError prevind("hellø", 0, 1) +@test nextind("hello", 0, 10) == 10 +# julia#24414 +let strs = Any["∀α>β:α+1>β", SubString("123∀α>β:α+1>β123", 4, 18)] + for s in strs + @test_throws BoundsError thisind(s, -2) + @test_throws BoundsError thisind(s, -1) + @test thisind(s, 0) == 0 + @test thisind(s, 1) == 1 + @test thisind(s, 2) == 1 + @test thisind(s, 3) == 1 + @test thisind(s, 4) == 4 + @test thisind(s, 5) == 4 + @test thisind(s, 6) == 6 + @test thisind(s, 15) == 15 + @test thisind(s, 16) == 15 + @test thisind(s, 17) == 17 + @test_throws BoundsError thisind(s, 18) + @test_throws BoundsError thisind(s, 19) + end +end +let strs = Any["", SubString("123", 2, 1)] + for s in strs + @test_throws BoundsError thisind(s, -1) + @test thisind(s, 0) == 0 + @test thisind(s, 1) == 1 + @test_throws BoundsError thisind(s, 2) + end +end +# prevind and nextind, julia#23805 +let s = "∀α>β:α+1>β" + @test_throws BoundsError prevind(s, 0, 0) + @test_throws BoundsError prevind(s, 0, 1) + @test prevind(s, 1, 1) == 0 + @test prevind(s, 1, 0) == 1 + @test prevind(s, 2, 1) == 1 + @test prevind(s, 4, 1) == 1 + @test prevind(s, 5, 1) == 4 + @test prevind(s, 5, 2) == 1 + @test prevind(s, 5, 3) == 0 + @test prevind(s, 15, 1) == 14 + @test prevind(s, 15, 2) == 13 + @test prevind(s, 15, 3) == 12 + @test prevind(s, 15, 4) == 10 + @test prevind(s, 15, 10) == 0 + @test prevind(s, 15, 9) == 1 + @test prevind(s, 16, 1) == 15 + @test prevind(s, 16, 2) == 14 + @test prevind(s, 17, 1) == 15 + @test prevind(s, 17, 2) == 14 + @test_throws BoundsError prevind(s, 18, 0) + @test_throws BoundsError prevind(s, 18, 1) + @test_throws BoundsError nextind(s, -1, 0) + @test_throws BoundsError nextind(s, -1, 1) + @test nextind(s, 0, 2) == 4 + @test nextind(s, 0, 20) == 26 + @test nextind(s, 0, 10) == 15 + @test nextind(s, 1, 1) == 4 + @test nextind(s, 1, 2) == 6 + @test nextind(s, 1, 9) == 15 + @test nextind(s, 1, 10) == 17 + @test nextind(s, 2, 1) == 4 + @test nextind(s, 3, 1) == 4 + @test nextind(s, 4, 1) == 6 + @test nextind(s, 14, 1) == 15 + @test nextind(s, 15, 1) == 17 + @test nextind(s, 15, 2) == 18 + @test nextind(s, 16, 1) == 17 + @test nextind(s, 16, 2) == 18 + @test nextind(s, 16, 3) == 19 + @test_throws BoundsError nextind(s, 17, 0) + @test_throws BoundsError nextind(s, 17, 1) + for k in 0:ncodeunits(s)+1 + n = p = k + for j in 1:40 + if 1 ≤ p + p = prevind(s, p) + @test prevind(s, k, j) == p + end + if n ≤ ncodeunits(s) + n = nextind(s, n) + @test nextind(s, k, j) == n + end + end + end +end + # 0.7.0-DEV.5171 let sep = Compat.Sys.iswindows() ? ';' : ':' withenv("PATH" => string(Compat.Sys.BINDIR, sep, get(ENV, "PATH", ""))) do