From 80f4eb0850cf53db4b0608cfc9c3898bc1403489 Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Fri, 30 Jun 2017 12:19:49 -0700 Subject: [PATCH] Remove RevString and deprecate reverse(::AbstractString) The `RevString` type for lazily reversed strings has been moved to the LegacyStrings package. Fixes #22611. Calling `reverse` on an `AbstractString` with no more specific method has been deprecated in favor of explicitly converting to `String` before reversing. --- NEWS.md | 8 ++++ base/deprecated.jl | 6 +++ base/exports.jl | 1 - base/precompile.jl | 3 -- base/repl/REPLCompletions.jl | 17 +++---- base/shell.jl | 15 +++--- base/strings/search.jl | 58 ++++++++++++------------ base/strings/string.jl | 23 ++++++++++ base/strings/types.jl | 44 +----------------- base/strings/util.jl | 13 ++---- contrib/Julia_Notepad++.xml | 2 +- contrib/julia.xml | 1 - doc/src/manual/interacting-with-julia.md | 2 +- doc/src/stdlib/strings.md | 2 +- test/strings/types.jl | 40 ++++++---------- 15 files changed, 106 insertions(+), 129 deletions(-) diff --git a/NEWS.md b/NEWS.md index 68bf8c098e302..124038add3ff5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -100,6 +100,12 @@ Language changes * Prefix `&` for by-reference arguments to `ccall` has been deprecated in favor of `Ref` argument types ([#6080]). + * The `RevString` type for lazily reversed strings has been moved to the LegacyStrings + package ([#22611]). + + * `reverse` on `AbstractString`s has been deprecated in favor of converting to `String` + before reversing ([#23612]). + Breaking changes ---------------- @@ -1315,6 +1321,7 @@ Command-line option changes [#22532]: https://github.com/JuliaLang/julia/issues/22532 [#22588]: https://github.com/JuliaLang/julia/issues/22588 [#22605]: https://github.com/JuliaLang/julia/issues/22605 +[#22611]: https://github.com/JuliaLang/julia/issues/22611 [#22666]: https://github.com/JuliaLang/julia/issues/22666 [#22696]: https://github.com/JuliaLang/julia/issues/22696 [#22703]: https://github.com/JuliaLang/julia/issues/22703 @@ -1344,3 +1351,4 @@ Command-line option changes [#23233]: https://github.com/JuliaLang/julia/issues/23233 [#23342]: https://github.com/JuliaLang/julia/issues/23342 [#23404]: https://github.com/JuliaLang/julia/issues/23404 +[#23612]: https://github.com/JuliaLang/julia/issues/23612 diff --git a/base/deprecated.jl b/base/deprecated.jl index 34176d7259858..19f9e5469e62d 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1855,6 +1855,12 @@ end nothing end +# Issue #22611 +@deprecate_moved RevString "LegacyStrings" + +# PR 23612 +@deprecate reverse(s::AbstractString) reverse(String(s)) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/exports.jl b/base/exports.jl index 5d80d9feddfd6..c1c1bd023d9ef 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -92,7 +92,6 @@ export Rational, Regex, RegexMatch, - RevString, RoundFromZero, RoundDown, RoundingMode, diff --git a/base/precompile.jl b/base/precompile.jl index 4981c89231dd3..18d929e7d596e 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -581,9 +581,6 @@ precompile(Tuple{typeof(Base.LineEdit.complete_line), Base.LineEdit.PromptState, precompile(Tuple{typeof(Base.LineEdit.input_string_newlines_aftercursor), Base.LineEdit.PromptState}) precompile(Tuple{typeof(Base.LineEdit.complete_line), Base.REPL.REPLCompletionProvider, Base.LineEdit.PromptState}) precompile(Tuple{getfield(Base, Symbol("#kw##parse")), Array{Any, 1}, typeof(Base.parse), String}) -precompile(Tuple{typeof(Base.isvalid), Base.RevString{String}, Int64}) -precompile(Tuple{typeof(Base.nextind), Base.RevString{String}, Int64}) -precompile(Tuple{typeof(Base.search), Base.RevString{String}, Array{Char, 1}, Int64}) precompile(Tuple{typeof(Base.rsearch), String, Array{Char, 1}, Int64}) precompile(Tuple{getfield(Base.REPLCompletions, Symbol("#kw##find_start_brace")), Array{Any, 1}, typeof(Base.REPLCompletions.find_start_brace), String}) precompile(Tuple{typeof(Core.Inference.isbits), Tuple{Void, Void, Void}}) diff --git a/base/repl/REPLCompletions.jl b/base/repl/REPLCompletions.jl index 6ba64e7a2152e..a8901881bfc6b 100644 --- a/base/repl/REPLCompletions.jl +++ b/base/repl/REPLCompletions.jl @@ -225,13 +225,13 @@ end # closed start brace from the end of the string. function find_start_brace(s::AbstractString; c_start='(', c_end=')') braces = 0 - r = RevString(s) - i = start(r) + i = endof(s) in_single_quotes = false in_double_quotes = false in_back_ticks = false - while !done(r, i) - c, i = next(r, i) + while i > 0 + c = s[i] + nexti = prevind(s, i) if !in_single_quotes && !in_double_quotes && !in_back_ticks if c == c_start braces += 1 @@ -245,18 +245,19 @@ function find_start_brace(s::AbstractString; c_start='(', c_end=')') in_back_ticks = true end else - if !in_back_ticks && !in_double_quotes && c == '\'' && !done(r, i) && next(r, i)[1]!='\\' + if !in_back_ticks && !in_double_quotes && c == '\'' && i > 0 && s[nexti] != '\\' in_single_quotes = !in_single_quotes - elseif !in_back_ticks && !in_single_quotes && c == '"' && !done(r, i) && next(r, i)[1]!='\\' + elseif !in_back_ticks && !in_single_quotes && c == '"' && i > 0 && s[nexti] != '\\' in_double_quotes = !in_double_quotes - elseif !in_single_quotes && !in_double_quotes && c == '`' && !done(r, i) && next(r, i)[1]!='\\' + elseif !in_single_quotes && !in_double_quotes && c == '`' && i > 0 && s[nexti] != '\\' in_back_ticks = !in_back_ticks end end braces == 1 && break + i = nexti end braces != 1 && return 0:-1, -1 - method_name_end = reverseind(r, i) + method_name_end = i - 1 startind = nextind(s, rsearch(s, non_identifier_chars, method_name_end)) return (startind:endof(s), method_name_end) end diff --git a/base/shell.jl b/base/shell.jl index 841865790b9c9..680cd45c722ad 100644 --- a/base/shell.jl +++ b/base/shell.jl @@ -12,21 +12,20 @@ function shell_parse(str::AbstractString, interpolate::Bool=true; special::AbstractString="") s = lstrip(str) # strips the end but respects the space when the string ends with "\\ " - r = RevString(s) - i = start(r) - c_old = nothing - while !done(r,i) - c, j = next(r,i) + i = endof(s) + c_old = '\0' # initialized to a null byte for type stability + while i > 0 + c = s[i] if c == '\\' && c_old == ' ' - i -= 1 + i += 1 break elseif !(c in _default_delims) break end - i = j + i = prevind(s, i) c_old = c end - s = s[1:end-i+1] + s = s[1:i] last_parse = 0:-1 isempty(s) && return interpolate ? (Expr(:tuple,:()),last_parse) : ([],last_parse) diff --git a/base/strings/search.jl b/base/strings/search.jl index 23f813ea28b26..d09decd7ec9ab 100644 --- a/base/strings/search.jl +++ b/base/strings/search.jl @@ -194,12 +194,6 @@ end search(s::AbstractString, t::AbstractString, i::Integer=start(s)) = _search(s, t, i) search(s::ByteArray, t::ByteArray, i::Integer=start(s)) = _search(s, t, i) -function rsearch(s::AbstractString, c::Chars) - j = search(RevString(s), c) - j == 0 && return 0 - endof(s)-j+1 -end - """ rsearch(s::AbstractString, chars::Chars, [start::Integer]) @@ -212,44 +206,52 @@ julia> rsearch("aaabbb","b") 6:6 ``` """ -function rsearch(s::AbstractString, c::Chars, i::Integer) - e = endof(s) - j = search(RevString(s), c, e-i+1) - j == 0 && return 0 - e-j+1 +function rsearch(s::AbstractString, c::Chars, i::Integer=start(s)) + @boundscheck checkbounds(s, i) + isempty(c) && return i + j = Int(i) + @inbounds while j > 0 + s[j] in c && return j + j = prevind(s, j) + end + return 0 end function _rsearchindex(s, t, i) if isempty(t) - return 1 <= i <= nextind(s,endof(s)) ? i : - throw(BoundsError(s, i)) + @boundscheck checkbounds(s, i) + return i end - t = RevString(t) - rs = RevString(s) l = endof(s) - t1, j2 = next(t,start(t)) + j2 = endof(t) + t1 = t[j2] while true - i = rsearch(s,t1,i) - if i == 0 return 0 end - c, ii = next(rs,l-i+1) - j = j2; k = ii + i = rsearch(s, t1, i) + i == 0 && return 0 + c = s[i] + ii = prevind(s, i) + j, k = j2, ii matched = true - while !done(t,j) - if done(rs,k) + while j > 0 + if k < 1 matched = false break end - c, k = next(rs,k) - d, j = next(t,j) + # Using `reverseind` with `prevind` mimics `next` but for iteration over + # `reverse(s)` (without actually having to call `reverse`) since `reverseind` + # is the current index in `reverse(s)` and `prevind` is the index for the + # next iteration. + rs_k = reverseind(s, k) + c, k = s[rs_k], prevind(s, rs_k) + rt_j = reverseind(t, j) + d, j = t[rt_j], prevind(t, rt_j) if c != d matched = false break end end - if matched - return nextind(s,l-k+1) - end - i = l-ii+1 + matched && return nextind(s, k) + i = ii end end diff --git a/base/strings/string.jl b/base/strings/string.jl index ce2298653a0ff..c0a1dc3336167 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -402,6 +402,29 @@ function string(a::Union{String,Char}...) return out end +""" + reverse(s::String) -> String + +Reverse a string. + +Technically, this function reverses the codepoints in a string, and its +main utility is for reversed-order string processing, especially for reversed +regular-expression searches. See also [`reverseind`](@ref) to convert indices +in `s` to indices in `reverse(s)` and vice-versa, and [`graphemes`](@ref) +to operate on user-visible "characters" (graphemes) rather than codepoints. + +# Examples +```jldoctest +julia> reverse("JuliaLang") +"gnaLailuJ" + +julia> reverse("ax̂e") # combining characters can lead to surprising results +"êxa" + +julia> join(reverse(collect(graphemes("ax̂e")))) # reverses graphemes +"ex̂a" +``` +""" function reverse(s::String) dat = Vector{UInt8}(s) n = length(dat) diff --git a/base/strings/types.jl b/base/strings/types.jl index 38f3a2b521112..71758642e5ad7 100644 --- a/base/strings/types.jl +++ b/base/strings/types.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# SubString and RevString types +# SubString type ## substrings reference original strings ## @@ -97,47 +97,6 @@ function unsafe_convert(::Type{Ptr{R}}, s::SubString{String}) where R<:Union{Int convert(Ptr{R}, pointer(s.string)) + s.offset end -## reversed strings without data movement ## - -struct RevString{T<:AbstractString} <: AbstractString - string::T -end - -endof(s::RevString) = endof(s.string) -length(s::RevString) = length(s.string) -sizeof(s::RevString) = sizeof(s.string) - -function next(s::RevString, i::Int) - n = endof(s); j = n-i+1 - (s.string[j], n-prevind(s.string,j)+1) -end - -""" - reverse(s::AbstractString) -> AbstractString - -Reverses a string. - -Technically, this function reverses the codepoints in a string, and its -main utility is for reversed-order string processing, especially for reversed -regular-expression searches. See also [`reverseind`](@ref) to convert indices -in `s` to indices in `reverse(s)` and vice-versa, and [`graphemes`](@ref) -to operate on user-visible "characters" (graphemes) rather than codepoints. - -# Examples -```jldoctest -julia> reverse("JuliaLang") -"gnaLailuJ" - -julia> reverse("ax̂e") # combining characters can lead to surprising results -"êxa" - -julia> join(reverse(collect(graphemes("ax̂e")))) # reverses graphemes -"ex̂a" -``` -""" -reverse(s::AbstractString) = RevString(s) -reverse(s::RevString) = s.string - ## reverse an index i so that reverse(s)[i] == s[reverseind(s,i)] """ @@ -160,7 +119,6 @@ Julia """ reverseind(s::AbstractString, i) = chr2ind(s, length(s) + 1 - ind2chr(reverse(s), i)) reverseind(s::Union{DirectIndexString,SubString{DirectIndexString}}, i::Integer) = length(s) + 1 - i -reverseind(s::RevString, i::Integer) = endof(s) - i + 1 reverseind(s::SubString{String}, i::Integer) = reverseind(s.string, nextind(s.string, endof(s.string))-s.offset-s.endof+i-1) - s.offset diff --git a/base/strings/util.jl b/base/strings/util.jl index 665fae4c6398d..07b288dedf322 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -173,14 +173,11 @@ julia> rstrip(a) ``` """ function rstrip(s::AbstractString, chars::Chars=_default_delims) - r = RevString(s) - i = start(r) - while !done(r,i) - c, j = next(r,i) - if !(c in chars) - return SubString(s, 1, endof(s)-i+1) - end - i = j + i = endof(s) + while i > 0 + c = s[i] + c in chars || return SubString(s, 1, i) + i = prevind(s, i) end SubString(s, 1, 0) end diff --git a/contrib/Julia_Notepad++.xml b/contrib/Julia_Notepad++.xml index e43a1ab4865ee..306706ffc3c8e 100644 --- a/contrib/Julia_Notepad++.xml +++ b/contrib/Julia_Notepad++.xml @@ -25,7 +25,7 @@ true false C_NULL Inf NaN Inf32 NaN32 nothing - AbstractArray AbstractMatrix AbstractRange AbstractRemoteRef AbstractSparseMatrix AbstractString AbstractVector Any ArgumentError Array Associative BigFloat BigInt BitArray BitMatrix BitVector Bool BunchKaufman Cchar Cdouble Cfloat Char CharString CholeskyDense CholeskyPivotedDense Cint Cintmax_t Clong Clonglong Colon Complex Complex128 Complex64 ComplexPair Cptrdiff_t Cshort Csize_t Cuchar Cuint Cuintmax_t Culong Culonglong Cushort DArray Dict Dims DisconnectException EOFError EachLine EnvHash ErrorException Exception Expr Factorization Filter Float Float32 Float64 Function GSVDDense IO IOBuffer IOStream ImaginaryUnit InsertionSort Int Int128 Int16 Int32 Int64 Int8 IntSet Integer KeyError LDLTTridiagonal LUDense LUTridiagonal LoadError LocalProcess Matrix MergeSort MethodError NTuple Number ObjectIdDict ObjectIdDict OrdinalRange ParseError PipeBuffer ProcessGroup Ptr QRDense QRPivotedDense QuickSort RangeIndex Rational Real Regex RegexMatch RegexMatchIterator RepString RevString Reverse SVDDense Set Signed SparseMatrixCSC SpawnNullStream Stat StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubDArray SubOrDArray SubString SymTridiagonal Symbol SystemError Task TCPSocket TimSort Tridiagonal Tuple Type TypeError UInt UInt128 UInt16 UInt32 UInt64 UInt8 UVError Union UnitRange Unsigned VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef Zip + AbstractArray AbstractMatrix AbstractRange AbstractRemoteRef AbstractSparseMatrix AbstractString AbstractVector Any ArgumentError Array Associative BigFloat BigInt BitArray BitMatrix BitVector Bool BunchKaufman Cchar Cdouble Cfloat Char CharString CholeskyDense CholeskyPivotedDense Cint Cintmax_t Clong Clonglong Colon Complex Complex128 Complex64 ComplexPair Cptrdiff_t Cshort Csize_t Cuchar Cuint Cuintmax_t Culong Culonglong Cushort DArray Dict Dims DisconnectException EOFError EachLine EnvHash ErrorException Exception Expr Factorization Filter Float Float32 Float64 Function GSVDDense IO IOBuffer IOStream ImaginaryUnit InsertionSort Int Int128 Int16 Int32 Int64 Int8 IntSet Integer KeyError LDLTTridiagonal LUDense LUTridiagonal LoadError LocalProcess Matrix MergeSort MethodError NTuple Number ObjectIdDict ObjectIdDict OrdinalRange ParseError PipeBuffer ProcessGroup Ptr QRDense QRPivotedDense QuickSort RangeIndex Rational Real Regex RegexMatch RegexMatchIterator RepString Reverse SVDDense Set Signed SparseMatrixCSC SpawnNullStream Stat StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubDArray SubOrDArray SubString SymTridiagonal Symbol SystemError Task TCPSocket TimSort Tridiagonal Tuple Type TypeError UInt UInt128 UInt16 UInt32 UInt64 UInt8 UVError Union UnitRange Unsigned VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef Zip abstract begin baremodule primitive break catch ccall const continue do else elseif end export finally for function global if struct import importall let local macro module quote return try mutable typealias using while close enumerate error info open print println read write warn print println diff --git a/contrib/julia.xml b/contrib/julia.xml index a0bb9c33b7984..46b35c0f307ff 100644 --- a/contrib/julia.xml +++ b/contrib/julia.xml @@ -156,7 +156,6 @@ RegexMatch RegexMatchIterator RepString - RevString Reverse Schur Set diff --git a/doc/src/manual/interacting-with-julia.md b/doc/src/manual/interacting-with-julia.md index 40b68bf8221b3..365c24fa02650 100644 --- a/doc/src/manual/interacting-with-julia.md +++ b/doc/src/manual/interacting-with-julia.md @@ -67,7 +67,7 @@ When the cursor is at the beginning of the line, the prompt can be changed to a julia> ? # upon typing ?, the prompt changes (in place) to: help?> help?> string -search: string String stringmime Cstring Cwstring RevString randstring bytestring SubString +search: string String stringmime Cstring Cwstring randstring bytestring SubString string(xs...) diff --git a/doc/src/stdlib/strings.md b/doc/src/stdlib/strings.md index 9be348f2ec9f8..60d5c53c79887 100644 --- a/doc/src/stdlib/strings.md +++ b/doc/src/stdlib/strings.md @@ -37,7 +37,7 @@ Base.rsearch Base.searchindex Base.rsearchindex Base.contains(::AbstractString, ::AbstractString) -Base.reverse(::AbstractString) +Base.reverse(::String) Base.replace Base.split Base.rsplit diff --git a/test/strings/types.jl b/test/strings/types.jl index 6efab9d92a18d..4714d2b4e9693 100644 --- a/test/strings/types.jl +++ b/test/strings/types.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -## SubString, RevString and Cstring tests ## +## SubString and Cstring tests ## ## SubString tests ## u8str = "∀ ε > 0, ∃ δ > 0: |x-y| < δ ⇒ |f(x)-f(y)| < ε" @@ -163,36 +163,24 @@ end ## Reverse strings ## -let rs = RevString("foobar") - @test length(rs) == 6 - @test sizeof(rs) == 6 - @test isascii(rs) -end - -# issue #4586 -@test rsplit(RevString("ailuj"),'l') == ["ju","ia"] -@test parse(Float64,RevString("64")) === 46.0 - # reverseind for T in (String, GenericString) for prefix in ("", "abcd", "\U0001d6a4\U0001d4c1", "\U0001d6a4\U0001d4c1c", " \U0001d6a4\U0001d4c1") for suffix in ("", "abcde", "\U0001d4c1β\U0001d6a4", "\U0001d4c1β\U0001d6a4c", " \U0001d4c1β\U0001d6a4") for c in ('X', 'δ', '\U0001d6a5') - s = convert(T, string(prefix, c, suffix)) - r = reverse(s) - ri = search(r, c) - @test r == RevString(s) - @test c == s[reverseind(s, ri)] == r[ri] - s = RevString(s) - r = reverse(s) - ri = search(r, c) - @test c == s[reverseind(s, ri)] == r[ri] - s = convert(T, string(prefix, prefix, c, suffix, suffix)) - pre = convert(T, prefix) - sb = SubString(s, nextind(pre, endof(pre)), endof(convert(T, string(prefix, prefix, c, suffix)))) - r = reverse(sb) - ri = search(r, c) - @test c == sb[reverseind(sb, ri)] == r[ri] + let s = convert(T, string(prefix, c, suffix)) + r = reverse(String(s)) + ri = search(r, c) + @test c == s[reverseind(s, ri)] == r[ri] + end + let s = convert(T, string(prefix, prefix, c, suffix, suffix)) + pre = convert(T, prefix) + sb = SubString(s, nextind(pre, endof(pre)), endof(convert(T, string(prefix, prefix, c, suffix)))) + r = reverse(String(sb)) + ri = search(r, c) + sbs = String(sb) + @test c == sbs[reverseind(sbs, ri)] == r[ri] + end end end end