diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift b/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift index 696f36eca..484ff4648 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift @@ -20,15 +20,6 @@ extension Collection { } } -extension BidirectionalCollection { - func _lastRange( - of searcher: S - ) -> Range? where S.BackwardSearched == Self { - var state = searcher.backwardState(for: self, in: startIndex.. Range? { - _firstRange(of: RegexConsumer(regex)) - } - - @available(SwiftStdlib 5.7, *) - func _lastRange(of regex: R) -> Range? { - _lastRange(of: RegexConsumer(regex)) + let s = self[...] + return try? regex.regex.firstMatch(in: s)?.range } } diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift index fc6b23af2..030ce1f64 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift @@ -115,43 +115,6 @@ extension RangesCollection.Index: Comparable { } } -// MARK: `ReversedRangesCollection` - -struct ReversedRangesCollection { - typealias Base = Searcher.BackwardSearched - - let base: Base - let searcher: Searcher - - init(base: Base, searcher: Searcher) { - self.base = base - self.searcher = searcher - } -} - -extension ReversedRangesCollection: Sequence { - public struct Iterator: IteratorProtocol { - let base: Base - let searcher: Searcher - var state: Searcher.BackwardState - - init(base: Base, searcher: Searcher) { - self.base = base - self.searcher = searcher - self.state = searcher.backwardState( - for: base, in: base.startIndex.. Range? { - searcher.searchBack(base, &state) - } - } - - public func makeIterator() -> Iterator { - Iterator(base: base, searcher: searcher) - } -} - // TODO: `Collection` conformance // MARK: `CollectionSearcher` algorithms @@ -164,14 +127,6 @@ extension Collection { } } -extension BidirectionalCollection { - func _rangesFromBack( - of searcher: S - ) -> ReversedRangesCollection where S.BackwardSearched == Self { - ReversedRangesCollection(base: self, searcher: searcher) - } -} - // MARK: Fixed pattern algorithms extension Collection where Element: Equatable { @@ -195,37 +150,6 @@ extension Collection where Element: Equatable { } } -extension BidirectionalCollection where Element: Equatable { - // FIXME -// public func rangesFromBack( -// of other: S -// ) -> ReversedRangesCollection> -// where S.Element == Element -// { -// fatalError() -// } -} - -extension BidirectionalCollection where Element: Comparable { - func _ranges( - of other: C - ) -> RangesCollection>> - where C.Element == Element - { - _ranges(of: PatternOrEmpty(searcher: TwoWaySearcher(pattern: Array(other)))) - } - - // FIXME -// public func rangesFromBack( -// of other: S -// ) -> ReversedRangesCollection>> -// where S.Element == Element -// { -// rangesFromBack( -// of: PatternOrEmpty(searcher: TwoWaySearcher(pattern: Array(other)))) -// } -} - @available(SwiftStdlib 5.7, *) struct RegexRangesCollection { let base: RegexMatchesCollection @@ -299,13 +223,6 @@ extension Collection where SubSequence == Substring { } extension BidirectionalCollection where SubSequence == Substring { - @available(SwiftStdlib 5.7, *) - func _rangesFromBack( - of regex: R - ) -> ReversedRangesCollection> { - _rangesFromBack(of: RegexConsumer(regex)) - } - // FIXME: Return `some Collection>` for SE-0346 /// Finds and returns the ranges of the all occurrences of a given sequence /// within the collection. diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift index 44364df71..6634d2945 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift @@ -106,121 +106,6 @@ extension SplitCollection: Sequence { } } -//extension SplitCollection: Collection { -// public struct Index { -// var start: Base.Index -// var base: RangesCollection.Index -// var isEndIndex: Bool -// } -// -// public var startIndex: Index { -// let base = ranges.startIndex -// return Index(start: ranges.base.startIndex, base: base, isEndIndex: false) -// } -// -// public var endIndex: Index { -// Index(start: ranges.base.endIndex, base: ranges.endIndex, isEndIndex: true) -// } -// -// public func formIndex(after index: inout Index) { -// guard !index.isEndIndex else { fatalError("Cannot advance past endIndex") } -// -// if let range = index.base.range { -// let newStart = range.upperBound -// ranges.formIndex(after: &index.base) -// index.start = newStart -// } else { -// index.isEndIndex = true -// } -// } -// -// public func index(after index: Index) -> Index { -// var index = index -// formIndex(after: &index) -// return index -// } -// -// public subscript(index: Index) -> Base.SubSequence { -// guard !index.isEndIndex else { -// fatalError("Cannot subscript using endIndex") -// } -// let end = index.base.range?.lowerBound ?? ranges.base.endIndex -// return ranges.base[index.start.. Bool { -// switch (lhs.isEndIndex, rhs.isEndIndex) { -// case (false, false): -// return lhs.start == rhs.start -// case (let lhs, let rhs): -// return lhs == rhs -// } -// } -// -// static func < (lhs: Self, rhs: Self) -> Bool { -// switch (lhs.isEndIndex, rhs.isEndIndex) { -// case (true, _): -// return false -// case (_, true): -// return true -// case (false, false): -// return lhs.start < rhs.start -// } -// } -//} - -// MARK: `ReversedSplitCollection` - -struct ReversedSplitCollection { - public typealias Base = Searcher.BackwardSearched - - let ranges: ReversedRangesCollection - - init(ranges: ReversedRangesCollection) { - self.ranges = ranges - } - - init(base: Base, searcher: Searcher) { - self.ranges = base._rangesFromBack(of: searcher) - } -} - -extension ReversedSplitCollection: Sequence { - public struct Iterator: IteratorProtocol { - let base: Base - var index: Base.Index - var ranges: ReversedRangesCollection.Iterator - var isDone: Bool - - init(ranges: ReversedRangesCollection) { - self.base = ranges.base - self.index = base.endIndex - self.ranges = ranges.makeIterator() - self.isDone = false - } - - public mutating func next() -> Base.SubSequence? { - guard !isDone else { return nil } - - guard let range = ranges.next() else { - isDone = true - return base[.. Iterator { - Iterator(ranges: ranges) - } -} - -// TODO: `Collection` conformance - // MARK: `CollectionSearcher` algorithms extension Collection { @@ -237,16 +122,6 @@ extension Collection { } } -extension BidirectionalCollection { - func splitFromBack( - by separator: Searcher - ) -> ReversedSplitCollection - where Searcher.BackwardSearched == Self - { - ReversedSplitCollection(base: self, searcher: separator) - } -} - // MARK: Predicate algorithms extension Collection { @@ -260,14 +135,6 @@ extension Collection { } } -extension BidirectionalCollection where Element: Equatable { - func splitFromBack( - whereSeparator predicate: @escaping (Element) -> Bool - ) -> ReversedSplitCollection> { - splitFromBack(by: PredicateConsumer(predicate: predicate)) - } -} - // MARK: Single element algorithms extension Collection where Element: Equatable { @@ -280,14 +147,6 @@ extension Collection where Element: Equatable { } } -extension BidirectionalCollection where Element: Equatable { - func splitFromBack( - by separator: Element - ) -> ReversedSplitCollection> { - splitFromBack(whereSeparator: { $0 == separator }) - } -} - // MARK: Fixed pattern algorithms extension Collection where Element: Equatable { @@ -399,21 +258,6 @@ extension StringProtocol where SubSequence == Substring { @available(SwiftStdlib 5.7, *) extension BidirectionalCollection where SubSequence == Substring { - @_disfavoredOverload - func split( - by separator: R, - maxSplits: Int, - omittingEmptySubsequences: Bool - ) -> SplitCollection> { - split(by: RegexConsumer(separator), maxSplits: maxSplits, omittingEmptySubsequences: omittingEmptySubsequences) - } - - func splitFromBack( - by separator: R - ) -> ReversedSplitCollection> { - splitFromBack(by: RegexConsumer(separator)) - } - // TODO: Is this @_disfavoredOverload necessary? // It prevents split(separator: String) from choosing this overload instead // of the collection-based version when String has RegexComponent conformance @@ -431,9 +275,34 @@ extension BidirectionalCollection where SubSequence == Substring { maxSplits: Int = .max, omittingEmptySubsequences: Bool = true ) -> [SubSequence] { - Array(split( - by: RegexConsumer(separator), - maxSplits: maxSplits, - omittingEmptySubsequences: omittingEmptySubsequences)) + var result: [SubSequence] = [] + var subSequenceStart = startIndex + + func appendSubsequence(end: Index) -> Bool { + if subSequenceStart == end && omittingEmptySubsequences { + return false + } + result.append(self[subSequenceStart.. 0 && !isEmpty else { + _ = appendSubsequence(end: endIndex) + return result + } + + for match in _matches(of: separator) { + defer { subSequenceStart = match.range.upperBound } + let didAppend = appendSubsequence(end: match.range.lowerBound) + if didAppend && result.count == maxSplits { + break + } + } + + if subSequenceStart != endIndex || !omittingEmptySubsequences { + result.append(self[subSequenceStart..(with consumer: C) -> Bool - where C.Consumed == SubSequence - { - consumer.consuming(self[...]) != nil - } -} - -extension BidirectionalCollection { - func _ends(with consumer: C) -> Bool - where C.Consumed == SubSequence - { - consumer.consumingBack(self[...]) != nil - } -} - -// MARK: Fixed pattern algorithms - -extension Collection where Element: Equatable { - func _starts(with prefix: C) -> Bool - where C.Element == Element - { - _starts(with: FixedPatternConsumer(pattern: prefix)) - } -} - -extension BidirectionalCollection where Element: Equatable { - func _ends(with suffix: C) -> Bool - where C.Element == Element - { - _ends(with: FixedPatternConsumer(pattern: suffix)) - } -} - // MARK: Regex algorithms @available(SwiftStdlib 5.7, *) @@ -56,10 +20,7 @@ extension BidirectionalCollection where SubSequence == Substring { /// - Returns: `true` if the initial elements of the sequence matches the /// beginning of `regex`; otherwise, `false`. public func starts(with regex: some RegexComponent) -> Bool { - _starts(with: RegexConsumer(regex)) - } - - func _ends(with regex: R) -> Bool { - _ends(with: RegexConsumer(regex)) + let s = self[...] + return (try? regex.regex.prefixMatch(in: s)) != nil } } diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Trim.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Trim.swift index 37f7513db..2908c4dc5 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Trim.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Trim.swift @@ -41,64 +41,6 @@ extension RangeReplaceableCollection { } } -extension BidirectionalCollection { - func _trimmingSuffix( - _ consumer: Consumer - ) -> SubSequence - where Consumer.Consumed == Self - { - let end = consumer.consumingBack(self) ?? endIndex - return self[..( - _ consumer: Consumer - ) -> SubSequence where Consumer.Consumed == Self { - // NOTE: Might give different results than trimming the suffix before - // trimming the prefix - let start = consumer.consuming(self) ?? startIndex - let end = consumer.consumingBack(self) ?? endIndex - let actualEnd = end < start ? start : end - return self[start..( - _ consumer: Consumer - ) where Consumer.Consumed == SubSequence - { - _ = consumer.consumeBack(&self) - } - - mutating func _trim( - _ consumer: Consumer - ) where Consumer.Consumed == Self { - _trimPrefix(consumer) - _trimSuffix(consumer) - } -} - -extension RangeReplaceableCollection where Self: BidirectionalCollection { - @_disfavoredOverload - mutating func _trimSuffix( - _ consumer: Consumer - ) where Consumer.Consumed == Self - { - if let end = consumer.consumingBack(self) { - removeSubrange(end...) - } - } - - @_disfavoredOverload - mutating func _trim( - _ consumer: Consumer - ) where Consumer.Consumed == Self { - _trimSuffix(consumer) - _trimPrefix(consumer) - } -} - // MARK: Predicate algorithms extension Collection { @@ -136,53 +78,6 @@ extension RangeReplaceableCollection { } } -extension BidirectionalCollection { - func _trimmingSuffix( - while predicate: @escaping (Element) -> Bool - ) -> SubSequence { - _trimmingSuffix(ManyConsumer(base: PredicateConsumer(predicate: predicate))) - } - - func _trimming( - while predicate: @escaping (Element) -> Bool - ) -> SubSequence { - _trimming(ManyConsumer(base: PredicateConsumer(predicate: predicate))) - } -} - -extension BidirectionalCollection where SubSequence == Self { - mutating func _trimSuffix( - while predicate: @escaping (Element) -> Bool - ) { - _trimSuffix(ManyConsumer( - base: PredicateConsumer(predicate: predicate))) - } - - mutating func _trim(while predicate: @escaping (Element) -> Bool) { - let consumer = ManyConsumer( - base: PredicateConsumer(predicate: predicate)) - _trimPrefix(consumer) - _trimSuffix(consumer) - } -} - -extension RangeReplaceableCollection where Self: BidirectionalCollection { - @_disfavoredOverload - mutating func _trimSuffix( - while predicate: @escaping (Element) -> Bool - ) { - _trimSuffix(ManyConsumer(base: PredicateConsumer(predicate: predicate))) - } - - @_disfavoredOverload - mutating func _trim(while predicate: @escaping (Element) -> Bool) { - let consumer = ManyConsumer( - base: PredicateConsumer(predicate: predicate)) - _trimPrefix(consumer) - _trimSuffix(consumer) - } -} - // MARK: Fixed pattern algorithms extension Collection where Element: Equatable { @@ -229,58 +124,6 @@ extension RangeReplaceableCollection where Element: Equatable { } } -extension BidirectionalCollection where Element: Equatable { - func _trimmingSuffix( - _ suffix: Suffix - ) -> SubSequence where Suffix.Element == Element { - _trimmingSuffix(FixedPatternConsumer(pattern: suffix)) - } - - func _trimming( - _ pattern: Pattern - ) -> SubSequence where Pattern.Element == Element { - _trimming(FixedPatternConsumer(pattern: pattern)) - } -} - -extension BidirectionalCollection - where SubSequence == Self, Element: Equatable -{ - mutating func _trimSuffix( - _ suffix: Suffix - ) where Suffix.Element == Element { - _trimSuffix(FixedPatternConsumer(pattern: suffix)) - } - - mutating func _trim( - _ pattern: Pattern - ) where Pattern.Element == Element { - let consumer = FixedPatternConsumer(pattern: pattern) - _trimPrefix(consumer) - _trimSuffix(consumer) - } -} - -extension RangeReplaceableCollection - where Self: BidirectionalCollection, Element: Equatable -{ - @_disfavoredOverload - mutating func _trimSuffix( - _ prefix: Suffix - ) where Suffix.Element == Element { - _trimSuffix(FixedPatternConsumer(pattern: prefix)) - } - - @_disfavoredOverload - mutating func _trim( - _ pattern: Pattern - ) where Pattern.Element == Element { - let consumer = FixedPatternConsumer(pattern: pattern) - _trimPrefix(consumer) - _trimSuffix(consumer) - } -} - // MARK: Regex algorithms extension BidirectionalCollection where SubSequence == Substring { @@ -292,17 +135,11 @@ extension BidirectionalCollection where SubSequence == Substring { @_disfavoredOverload @available(SwiftStdlib 5.7, *) public func trimmingPrefix(_ regex: some RegexComponent) -> SubSequence { - _trimmingPrefix(RegexConsumer(regex)) - } - - @available(SwiftStdlib 5.7, *) - func _trimmingSuffix(_ regex: R) -> SubSequence { - _trimmingSuffix(RegexConsumer(regex)) - } - - @available(SwiftStdlib 5.7, *) - func _trimming(_ regex: R) -> SubSequence { - _trimming(RegexConsumer(regex)) + let s = self[...] + guard let prefix = try? regex.regex.prefixMatch(in: s) else { + return s + } + return s[prefix.range.upperBound...] } } @@ -314,37 +151,11 @@ extension RangeReplaceableCollection @_disfavoredOverload @available(SwiftStdlib 5.7, *) public mutating func trimPrefix(_ regex: some RegexComponent) { - _trimPrefix(RegexConsumer(regex)) - } - - @available(SwiftStdlib 5.7, *) - mutating func _trimSuffix(_ regex: R) { - _trimSuffix(RegexConsumer(regex)) - } - - @available(SwiftStdlib 5.7, *) - mutating func _trim(_ regex: R) { - let consumer = RegexConsumer(regex) - _trimPrefix(consumer) - _trimSuffix(consumer) + let s = self[...] + guard let prefix = try? regex.regex.prefixMatch(in: s) else { + return + } + self.removeSubrange(prefix.range) } } -extension Substring { - @available(SwiftStdlib 5.7, *) - mutating func _trimPrefix(_ regex: R) { - _trimPrefix(RegexConsumer(regex)) - } - - @available(SwiftStdlib 5.7, *) - mutating func _trimSuffix(_ regex: R) { - _trimSuffix(RegexConsumer(regex)) - } - - @available(SwiftStdlib 5.7, *) - mutating func _trim(_ regex: R) { - let consumer = RegexConsumer(regex) - _trimPrefix(consumer) - _trimSuffix(consumer) - } -} diff --git a/Sources/_StringProcessing/Algorithms/Consumers/RegexConsumer.swift b/Sources/_StringProcessing/Algorithms/Consumers/RegexConsumer.swift deleted file mode 100644 index 4956406da..000000000 --- a/Sources/_StringProcessing/Algorithms/Consumers/RegexConsumer.swift +++ /dev/null @@ -1,98 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -// FIXME: What even is this? Can we delete this whole thing? -@available(SwiftStdlib 5.7, *) -struct RegexConsumer< - R: RegexComponent, Consumed: BidirectionalCollection -> where Consumed.SubSequence == Substring { - // TODO: Should `Regex` itself implement these protocols? - let regex: R - - init(_ regex: R) { - self.regex = regex - } -} - -@available(SwiftStdlib 5.7, *) -extension RegexConsumer { - func _matchingConsuming( - _ consumed: Substring, in range: Range - ) -> (upperBound: String.Index, match: Match)? { - guard let result = try! regex.regex._match( - consumed.base, - in: range, mode: .partialFromFront - ) else { return nil } - return (result.range.upperBound, result.output) - } -} - -// TODO: Explicitly implement the non-matching consumer/searcher protocols as -// well, taking advantage of the fact that the captures can be ignored - -@available(SwiftStdlib 5.7, *) -extension RegexConsumer: MatchingCollectionConsumer { - typealias Match = R.RegexOutput - - func matchingConsuming( - _ consumed: Consumed, in range: Range - ) -> (upperBound: String.Index, match: Match)? { - _matchingConsuming(consumed[...], in: range) - } -} - -// TODO: We'll want to bake backwards into the engine -@available(SwiftStdlib 5.7, *) -extension RegexConsumer: BidirectionalMatchingCollectionConsumer { - func matchingConsumingBack( - _ consumed: Consumed, in range: Range - ) -> (lowerBound: String.Index, match: Match)? { - var i = range.lowerBound - while true { - if let (end, capture) = _matchingConsuming( - consumed[...], - in: i.. - ) -> (range: Range, match: Match)? { - ConsumerSearcher(consumer: self).matchingSearch(searched, in: range) - } -} - -// TODO: Bake in search-back to engine too -@available(SwiftStdlib 5.7, *) -extension RegexConsumer: BackwardMatchingStatelessCollectionSearcher { - typealias BackwardSearched = Consumed - - func matchingSearchBack( - _ searched: BackwardSearched, in range: Range - ) -> (range: Range, match: Match)? { - ConsumerSearcher(consumer: self).matchingSearchBack(searched, in: range) - } -} diff --git a/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift b/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift index c8c6867f4..3ee74f258 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift @@ -38,21 +38,6 @@ extension BidirectionalCollection { // MARK: Regex algorithms extension BidirectionalCollection where SubSequence == Substring { - @available(SwiftStdlib 5.7, *) - @_disfavoredOverload - func firstMatch( - of regex: R - ) -> _MatchResult>? { - _firstMatch(of: RegexConsumer(regex)) - } - - @available(SwiftStdlib 5.7, *) - func lastMatch( - of regex: R - ) -> _BackwardMatchResult>? { - lastMatch(of: RegexConsumer(regex)) - } - /// Returns the first match of the specified regex within the collection. /// - Parameter regex: The regex to search for. /// - Returns: The first match of `regex` in the collection, or `nil` if diff --git a/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift b/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift index 6bc9b4d77..0f23280ce 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift @@ -75,45 +75,6 @@ extension RangeReplaceableCollection { // MARK: Regex algorithms extension RangeReplaceableCollection where SubSequence == Substring { - @available(SwiftStdlib 5.7, *) - func _replacing( - _ regex: R, - with replacement: (_MatchResult>) throws -> Replacement, - subrange: Range, - maxReplacements: Int = .max - ) rethrows -> Self where Replacement.Element == Element { - try _replacing( - RegexConsumer(regex), - with: replacement, - subrange: subrange, - maxReplacements: maxReplacements) - } - - @available(SwiftStdlib 5.7, *) - func _replacing( - _ regex: R, - with replacement: (_MatchResult>) throws -> Replacement, - maxReplacements: Int = .max - ) rethrows -> Self where Replacement.Element == Element { - try _replacing( - regex, - with: replacement, - subrange: startIndex..( - _ regex: R, - with replacement: (_MatchResult>) throws -> Replacement, - maxReplacements: Int = .max - ) rethrows where Replacement.Element == Element { - self = try _replacing( - regex, - with: replacement, - maxReplacements: maxReplacements) - } - /// Returns a new collection in which all occurrences of a sequence matching /// the given regex are replaced by another regex match. /// - Parameters: diff --git a/Sources/_StringProcessing/Algorithms/Matching/Matches.swift b/Sources/_StringProcessing/Algorithms/Matching/Matches.swift index f5c645105..12846bf0f 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/Matches.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/Matches.swift @@ -357,13 +357,6 @@ extension BidirectionalCollection where SubSequence == Substring { regex: regex.regex) } - @available(SwiftStdlib 5.7, *) - func _matchesFromBack( - of regex: R - ) -> ReversedMatchesCollection> { - _matchesFromBack(of: RegexConsumer(regex)) - } - // FIXME: Return `some Collection.Match> for SE-0346 /// Returns a collection containing all matches of the specified regex. /// - Parameter regex: The regex to search for. diff --git a/Sources/_StringProcessing/Algorithms/Searchers/TwoWaySearcher.swift b/Sources/_StringProcessing/Algorithms/Searchers/TwoWaySearcher.swift index 837493a30..c3537d415 100644 --- a/Sources/_StringProcessing/Algorithms/Searchers/TwoWaySearcher.swift +++ b/Sources/_StringProcessing/Algorithms/Searchers/TwoWaySearcher.swift @@ -9,6 +9,14 @@ // //===----------------------------------------------------------------------===// +extension BidirectionalCollection where Element: Equatable { + fileprivate func _ends(with suffix: C) -> Bool + where C.Element == Element + { + FixedPatternConsumer(pattern: suffix).consumingBack(self[...]) != nil + } + } + struct TwoWaySearcher where Searched.Element: Comparable { diff --git a/Tests/RegexTests/AlgorithmsInternalsTests.swift b/Tests/RegexTests/AlgorithmsInternalsTests.swift index 96cd12076..af0007bbe 100644 --- a/Tests/RegexTests/AlgorithmsInternalsTests.swift +++ b/Tests/RegexTests/AlgorithmsInternalsTests.swift @@ -24,25 +24,17 @@ extension AlgorithmTests { let str = "a string with the letter b in it" let first = str.firstRange(of: r) - let last = str._lastRange(of: r) let (expectFirst, expectLast) = ( str.index(atOffset: 0).. Bool) -> SubSequence { + var i = endIndex + while i > startIndex { + formIndex(before: &i) + if !predicate(self[i]) { return self[...i] } + } + return self[..