diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift index dbe7923ec..46568192f 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift @@ -12,7 +12,7 @@ // MARK: `CollectionSearcher` algorithms extension Collection { - public func contains( + func contains( _ searcher: Searcher ) -> Bool where Searcher.Searched == Self { firstRange(of: searcher) != nil @@ -22,6 +22,11 @@ extension Collection { // MARK: Fixed pattern algorithms extension Collection where Element: Equatable { + /// Returns a Boolean value indicating whether the collection contains the + /// given sequence. + /// - Parameter other: A sequence to search for within this collection. + /// - Returns: `true` if the collection contains the specified sequence, + /// otherwise `false`. public func contains(_ other: S) -> Bool where S.Element == Element { @@ -30,7 +35,7 @@ extension Collection where Element: Equatable { } extension BidirectionalCollection where Element: Comparable { - public func contains(_ other: S) -> Bool + func contains(_ other: S) -> Bool where S.Element == Element { firstRange(of: other) != nil @@ -40,6 +45,11 @@ extension BidirectionalCollection where Element: Comparable { // MARK: Regex algorithms extension BidirectionalCollection where SubSequence == Substring { + /// Returns a Boolean value indicating whether the collection contains the + /// given regex. + /// - Parameter regex: A regex to search for within this collection. + /// - Returns: `true` if the regex was found in the collection, otherwise + /// `false`. public func contains(_ regex: R) -> Bool { contains(RegexConsumer(regex)) } diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift b/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift index 40d5b9950..405fe47ef 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift @@ -12,7 +12,7 @@ // MARK: `CollectionSearcher` algorithms extension Collection { - public func firstRange( + func firstRange( of searcher: S ) -> Range? where S.Searched == Self { var state = searcher.state(for: self, in: startIndex..( + func lastRange( of searcher: S ) -> Range? where S.BackwardSearched == Self { var state = searcher.backwardState(for: self, in: startIndex..( of sequence: S ) -> Range? where S.Element == Element { @@ -42,6 +47,11 @@ extension Collection where Element: Equatable { } extension BidirectionalCollection where Element: Comparable { + /// Finds and returns the range of the first occurrence of a given sequence + /// within the collection. + /// - Parameter other: The sequence to search for. + /// - Returns: A range in the collection of the first occurrence of `sequence`. + /// Returns `nil` if `sequence` is not found. public func firstRange( of other: S ) -> Range? where S.Element == Element { @@ -56,11 +66,16 @@ extension BidirectionalCollection where Element: Comparable { // MARK: Regex algorithms extension BidirectionalCollection where SubSequence == Substring { + /// Finds and returns the range of the first occurrence of a given regex + /// within the collection. + /// - Parameter regex: The regex to search for. + /// - Returns: A range in the collection of the first occurrence of `regex`. + /// Returns `nil` if `regex` is not found. public func firstRange(of regex: R) -> Range? { firstRange(of: RegexConsumer(regex)) } - public func lastRange(of regex: R) -> Range? { + func lastRange(of regex: R) -> Range? { lastRange(of: RegexConsumer(regex)) } } diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift index e56e35e72..3a45b86a2 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift @@ -11,7 +11,7 @@ // MARK: `RangesCollection` -public struct RangesCollection { +struct RangesCollection { public typealias Base = Searcher.Searched let base: Base @@ -33,7 +33,7 @@ public struct RangesCollection { } } -public struct RangesIterator: IteratorProtocol { +struct RangesIterator: IteratorProtocol { public typealias Base = Searcher.Searched let base: Base @@ -92,7 +92,7 @@ extension RangesCollection: Collection { } extension RangesCollection.Index: Comparable { - public static func == (lhs: Self, rhs: Self) -> Bool { + static func == (lhs: Self, rhs: Self) -> Bool { switch (lhs.range, rhs.range) { case (nil, nil): return true @@ -103,7 +103,7 @@ extension RangesCollection.Index: Comparable { } } - public static func < (lhs: Self, rhs: Self) -> Bool { + static func < (lhs: Self, rhs: Self) -> Bool { switch (lhs.range, rhs.range) { case (nil, _): return false @@ -117,8 +117,8 @@ extension RangesCollection.Index: Comparable { // MARK: `ReversedRangesCollection` -public struct ReversedRangesCollection { - public typealias Base = Searcher.BackwardSearched +struct ReversedRangesCollection { + typealias Base = Searcher.BackwardSearched let base: Base let searcher: Searcher @@ -157,7 +157,7 @@ extension ReversedRangesCollection: Sequence { // MARK: `CollectionSearcher` algorithms extension Collection { - public func ranges( + func ranges( of searcher: S ) -> RangesCollection where S.Searched == Self { RangesCollection(base: self, searcher: searcher) @@ -165,7 +165,7 @@ extension Collection { } extension BidirectionalCollection { - public func rangesFromBack( + func rangesFromBack( of searcher: S ) -> ReversedRangesCollection where S.BackwardSearched == Self { ReversedRangesCollection(base: self, searcher: searcher) @@ -175,7 +175,8 @@ extension BidirectionalCollection { // MARK: Fixed pattern algorithms extension Collection where Element: Equatable { - public func ranges( + // FIXME: Replace `RangesCollection` when SE-0346 is enabled + func ranges( of other: S ) -> RangesCollection> where S.Element == Element { ranges(of: ZSearcher(pattern: Array(other), by: ==)) @@ -194,7 +195,7 @@ extension BidirectionalCollection where Element: Equatable { } extension BidirectionalCollection where Element: Comparable { - public func ranges( + func ranges( of other: S ) -> RangesCollection>> where S.Element == Element @@ -216,13 +217,14 @@ extension BidirectionalCollection where Element: Comparable { // MARK: Regex algorithms extension BidirectionalCollection where SubSequence == Substring { - public func ranges( + // FIXME: Replace `RangesCollection` when SE-0346 is enabled + func ranges( of regex: R ) -> RangesCollection> { ranges(of: RegexConsumer(regex)) } - public func rangesFromBack( + func rangesFromBack( of regex: R ) -> ReversedRangesCollection> { rangesFromBack(of: RegexConsumer(regex)) diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift index e2c9d78a4..bc23a284c 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift @@ -12,7 +12,7 @@ // MARK: `CollectionSearcher` algorithms extension RangeReplaceableCollection { - public func replacing( + func replacing( _ searcher: Searcher, with replacement: Replacement, subrange: Range, @@ -36,7 +36,7 @@ extension RangeReplaceableCollection { return result } - public func replacing( + func replacing( _ searcher: Searcher, with replacement: Replacement, maxReplacements: Int = .max @@ -50,7 +50,7 @@ extension RangeReplaceableCollection { maxReplacements: maxReplacements) } - public mutating func replace< + mutating func replace< Searcher: CollectionSearcher, Replacement: Collection >( _ searcher: Searcher, @@ -67,6 +67,16 @@ extension RangeReplaceableCollection { // MARK: Fixed pattern algorithms extension RangeReplaceableCollection where Element: Equatable { + /// Returns a new collection in which all occurrences of a target sequence + /// are replaced by another collection. + /// - Parameters: + /// - other: The sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - subrange: The range in the collection in which to search for `other`. + /// - maxReplacements: A number specifying how many occurrences of `other` + /// to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of `other` in + /// `subrange` of the collection are replaced by `replacement`. public func replacing( _ other: S, with replacement: Replacement, @@ -79,7 +89,16 @@ extension RangeReplaceableCollection where Element: Equatable { subrange: subrange, maxReplacements: maxReplacements) } - + + /// Returns a new collection in which all occurrences of a target sequence + /// are replaced by another collection. + /// - Parameters: + /// - other: The sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - maxReplacements: A number specifying how many occurrences of `other` + /// to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of `other` in + /// `subrange` of the collection are replaced by `replacement`. public func replacing( _ other: S, with replacement: Replacement, @@ -91,7 +110,13 @@ extension RangeReplaceableCollection where Element: Equatable { subrange: startIndex..( _ other: S, with replacement: Replacement, @@ -108,7 +133,7 @@ extension RangeReplaceableCollection where Element: Equatable { extension RangeReplaceableCollection where Self: BidirectionalCollection, Element: Comparable { - public func replacing( + func replacing( _ other: S, with replacement: Replacement, subrange: Range, @@ -121,7 +146,7 @@ extension RangeReplaceableCollection maxReplacements: maxReplacements) } - public func replacing( + func replacing( _ other: S, with replacement: Replacement, maxReplacements: Int = .max @@ -133,7 +158,7 @@ extension RangeReplaceableCollection maxReplacements: maxReplacements) } - public mutating func replace( + mutating func replace( _ other: S, with replacement: Replacement, maxReplacements: Int = .max @@ -149,6 +174,16 @@ extension RangeReplaceableCollection // MARK: Regex algorithms extension RangeReplaceableCollection where SubSequence == Substring { + /// Returns a new collection in which all occurrences of a sequence matching + /// the given regex are replaced by another collection. + /// - Parameters: + /// - regex: A regex describing the sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - subrange: The range in the collection in which to search for `regex`. + /// - maxReplacements: A number specifying how many occurrences of the + /// sequence matching `regex` to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of subsequence + /// matching `regex` in `subrange` are replaced by `replacement`. public func replacing( _ regex: R, with replacement: Replacement, @@ -161,7 +196,16 @@ extension RangeReplaceableCollection where SubSequence == Substring { subrange: subrange, maxReplacements: maxReplacements) } - + + /// Returns a new collection in which all occurrences of a sequence matching + /// the given regex are replaced by another collection. + /// - Parameters: + /// - regex: A regex describing the sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - maxReplacements: A number specifying how many occurrences of the + /// sequence matching `regex` to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of subsequence + /// matching `regex` are replaced by `replacement`. public func replacing( _ regex: R, with replacement: Replacement, @@ -173,7 +217,14 @@ extension RangeReplaceableCollection where SubSequence == Substring { subrange: startIndex..( _ regex: R, with replacement: Replacement, diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift index 15c946ca2..898f93048 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift @@ -11,7 +11,7 @@ // MARK: `SplitCollection` -public struct SplitCollection { +struct SplitCollection { public typealias Base = Searcher.Searched let ranges: RangesCollection @@ -101,7 +101,7 @@ extension SplitCollection: Collection { } extension SplitCollection.Index: Comparable { - public static func == (lhs: Self, rhs: Self) -> Bool { + static func == (lhs: Self, rhs: Self) -> Bool { switch (lhs.isEndIndex, rhs.isEndIndex) { case (false, false): return lhs.start == rhs.start @@ -110,7 +110,7 @@ extension SplitCollection.Index: Comparable { } } - public static func < (lhs: Self, rhs: Self) -> Bool { + static func < (lhs: Self, rhs: Self) -> Bool { switch (lhs.isEndIndex, rhs.isEndIndex) { case (true, _): return false @@ -124,7 +124,7 @@ extension SplitCollection.Index: Comparable { // MARK: `ReversedSplitCollection` -public struct ReversedSplitCollection { +struct ReversedSplitCollection { public typealias Base = Searcher.BackwardSearched let ranges: ReversedRangesCollection @@ -175,7 +175,7 @@ extension ReversedSplitCollection: Sequence { // MARK: `CollectionSearcher` algorithms extension Collection { - public func split( + func split( by separator: Searcher ) -> SplitCollection where Searcher.Searched == Self { // TODO: `maxSplits`, `omittingEmptySubsequences`? @@ -184,7 +184,7 @@ extension Collection { } extension BidirectionalCollection { - public func splitFromBack( + func splitFromBack( by separator: Searcher ) -> ReversedSplitCollection where Searcher.BackwardSearched == Self @@ -197,7 +197,7 @@ extension BidirectionalCollection { extension Collection { // TODO: Non-escaping and throwing - public func split( + func split( whereSeparator predicate: @escaping (Element) -> Bool ) -> SplitCollection> { split(by: PredicateConsumer(predicate: predicate)) @@ -205,7 +205,7 @@ extension Collection { } extension BidirectionalCollection where Element: Equatable { - public func splitFromBack( + func splitFromBack( whereSeparator predicate: @escaping (Element) -> Bool ) -> ReversedSplitCollection> { splitFromBack(by: PredicateConsumer(predicate: predicate)) @@ -215,7 +215,7 @@ extension BidirectionalCollection where Element: Equatable { // MARK: Single element algorithms extension Collection where Element: Equatable { - public func split( + func split( by separator: Element ) -> SplitCollection> { split(whereSeparator: { $0 == separator }) @@ -223,7 +223,7 @@ extension Collection where Element: Equatable { } extension BidirectionalCollection where Element: Equatable { - public func splitFromBack( + func splitFromBack( by separator: Element ) -> ReversedSplitCollection> { splitFromBack(whereSeparator: { $0 == separator }) @@ -233,7 +233,13 @@ extension BidirectionalCollection where Element: Equatable { // MARK: Fixed pattern algorithms extension Collection where Element: Equatable { - public func split( + // FIXME: Replace `SplitCollection` when SE-0346 is enabled + /// Returns the longest possible subsequences of the collection, in order, + /// around elements equal to the given separator. + /// - Parameter separator: The element to be split upon. + /// - Returns: A collection of subsequences, split from this collection's + /// elements. + func split( by separator: S ) -> SplitCollection> where S.Element == Element { split(by: ZSearcher(pattern: Array(separator), by: ==)) @@ -252,7 +258,7 @@ extension BidirectionalCollection where Element: Equatable { } extension BidirectionalCollection where Element: Comparable { - public func split( + func split( by separator: S ) -> SplitCollection>> where S.Element == Element @@ -275,13 +281,19 @@ extension BidirectionalCollection where Element: Comparable { // MARK: Regex algorithms extension BidirectionalCollection where SubSequence == Substring { - public func split( + // FIXME: Replace `SplitCollection` when SE-0346 is enabled + /// Returns the longest possible subsequences of the collection, in order, + /// around elements equal to the given separator. + /// - Parameter separator: A regex describing elements to be split upon. + /// - Returns: A collection of substrings, split from this collection's + /// elements. + func split( by separator: R ) -> SplitCollection> { split(by: RegexConsumer(separator)) } - public func splitFromBack( + func splitFromBack( by separator: R ) -> ReversedSplitCollection> { splitFromBack(by: RegexConsumer(separator)) diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/StartsWith.swift b/Sources/_StringProcessing/Algorithms/Algorithms/StartsWith.swift index 75c6e1133..346094f9e 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/StartsWith.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/StartsWith.swift @@ -12,7 +12,7 @@ // MARK: `CollectionConsumer` algorithms extension Collection { - public func starts(with consumer: C) -> Bool + func starts(with consumer: C) -> Bool where C.Consumed == SubSequence { consumer.consuming(self[...]) != nil @@ -20,7 +20,7 @@ extension Collection { } extension BidirectionalCollection { - public func ends(with consumer: C) -> Bool + func ends(with consumer: C) -> Bool where C.Consumed == SubSequence { consumer.consumingBack(self[...]) != nil @@ -30,7 +30,7 @@ extension BidirectionalCollection { // MARK: Fixed pattern algorithms extension Collection where Element: Equatable { - public func starts(with prefix: C) -> Bool + func starts(with prefix: C) -> Bool where C.Element == Element { starts(with: FixedPatternConsumer(pattern: prefix)) @@ -38,7 +38,7 @@ extension Collection where Element: Equatable { } extension BidirectionalCollection where Element: Equatable { - public func ends(with suffix: C) -> Bool + func ends(with suffix: C) -> Bool where C.Element == Element { ends(with: FixedPatternConsumer(pattern: suffix)) @@ -48,11 +48,16 @@ extension BidirectionalCollection where Element: Equatable { // MARK: Regex algorithms extension BidirectionalCollection where SubSequence == Substring { + /// Returns a Boolean value indicating whether the initial elements of the + /// sequence are the same as the elements in the specified regex. + /// - Parameter regex: A regex to compare to this sequence. + /// - Returns: `true` if the initial elements of the sequence matches the + /// beginning of `regex`; otherwise, `false`. public func starts(with regex: R) -> Bool { starts(with: RegexConsumer(regex)) } - public func ends(with regex: R) -> Bool { + func ends(with regex: R) -> Bool { ends(with: RegexConsumer(regex)) } } diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Trim.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Trim.swift index 65a71e1b7..ed7cd7bc4 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Trim.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Trim.swift @@ -12,7 +12,7 @@ // MARK: `CollectionConsumer` algorithms extension Collection { - public func trimmingPrefix( + func trimmingPrefix( _ consumer: Consumer ) -> SubSequence where Consumer.Consumed == Self { let start = consumer.consuming(self) ?? startIndex @@ -21,7 +21,7 @@ extension Collection { } extension Collection where SubSequence == Self { - public mutating func trimPrefix( + mutating func trimPrefix( _ consumer: Consumer ) where Consumer.Consumed == Self { _ = consumer.consume(&self) @@ -32,7 +32,7 @@ extension RangeReplaceableCollection { // NOTE: Disfavored because the `Collection with SubSequence == Self` overload // should be preferred whenever both are available @_disfavoredOverload - public mutating func trimPrefix( + mutating func trimPrefix( _ consumer: Consumer ) where Consumer.Consumed == Self { if let start = consumer.consuming(self) { @@ -42,7 +42,7 @@ extension RangeReplaceableCollection { } extension BidirectionalCollection { - public func trimmingSuffix( + func trimmingSuffix( _ consumer: Consumer ) -> SubSequence where Consumer.Consumed == Self @@ -51,7 +51,7 @@ extension BidirectionalCollection { return self[..( + func trimming( _ consumer: Consumer ) -> SubSequence where Consumer.Consumed == Self { // NOTE: Might give different results than trimming the suffix before @@ -64,7 +64,7 @@ extension BidirectionalCollection { } extension BidirectionalCollection where SubSequence == Self { - public mutating func trimSuffix( + mutating func trimSuffix( _ consumer: Consumer ) where Consumer.Consumed == SubSequence { @@ -81,7 +81,7 @@ extension BidirectionalCollection where SubSequence == Self { extension RangeReplaceableCollection where Self: BidirectionalCollection { @_disfavoredOverload - public mutating func trimSuffix( + mutating func trimSuffix( _ consumer: Consumer ) where Consumer.Consumed == Self { @@ -103,7 +103,7 @@ extension RangeReplaceableCollection where Self: BidirectionalCollection { extension Collection { // TODO: Non-escaping and throwing - public func trimmingPrefix( + func trimmingPrefix( while predicate: @escaping (Element) -> Bool ) -> SubSequence { trimmingPrefix(ManyConsumer(base: PredicateConsumer(predicate: predicate))) @@ -129,13 +129,13 @@ extension RangeReplaceableCollection { } extension BidirectionalCollection { - public func trimmingSuffix( + func trimmingSuffix( while predicate: @escaping (Element) -> Bool ) -> SubSequence { trimmingSuffix(ManyConsumer(base: PredicateConsumer(predicate: predicate))) } - public func trimming( + func trimming( while predicate: @escaping (Element) -> Bool ) -> SubSequence { trimming(ManyConsumer(base: PredicateConsumer(predicate: predicate))) @@ -143,14 +143,14 @@ extension BidirectionalCollection { } extension BidirectionalCollection where SubSequence == Self { - public mutating func trimSuffix( + mutating func trimSuffix( while predicate: @escaping (Element) -> Bool ) { trimSuffix(ManyConsumer( base: PredicateConsumer(predicate: predicate))) } - public mutating func trim(while predicate: @escaping (Element) -> Bool) { + mutating func trim(while predicate: @escaping (Element) -> Bool) { let consumer = ManyConsumer( base: PredicateConsumer(predicate: predicate)) trimPrefix(consumer) @@ -160,14 +160,14 @@ extension BidirectionalCollection where SubSequence == Self { extension RangeReplaceableCollection where Self: BidirectionalCollection { @_disfavoredOverload - public mutating func trimSuffix( + mutating func trimSuffix( while predicate: @escaping (Element) -> Bool ) { trimSuffix(ManyConsumer(base: PredicateConsumer(predicate: predicate))) } @_disfavoredOverload - public mutating func trim(while predicate: @escaping (Element) -> Bool) { + mutating func trim(while predicate: @escaping (Element) -> Bool) { let consumer = ManyConsumer( base: PredicateConsumer(predicate: predicate)) trimPrefix(consumer) @@ -178,6 +178,13 @@ extension RangeReplaceableCollection where Self: BidirectionalCollection { // MARK: Fixed pattern algorithms extension Collection where Element: Equatable { + /// Returns a new collection of the same type by removing initial elements + /// that satisfy the given predicate from the start. + /// - Parameter predicate: A closure that takes an element of the sequence + /// as its argument and returns a Boolean value indicating whether the + /// element should be removed from the collection. + /// - Returns: A collection containing the elements of the collection that are + /// not removed by `predicate`. public func trimmingPrefix( _ prefix: Prefix ) -> SubSequence where Prefix.Element == Element { @@ -186,6 +193,11 @@ extension Collection where Element: Equatable { } extension Collection where SubSequence == Self, Element: Equatable { + /// Removes the initial elements that satisfy the given predicate from the + /// start of the sequence. + /// - Parameter predicate: A closure that takes an element of the sequence + /// as its argument and returns a Boolean value indicating whether the + /// element should be removed from the collection. public mutating func trimPrefix( _ prefix: Prefix ) where Prefix.Element == Element { @@ -195,6 +207,11 @@ extension Collection where SubSequence == Self, Element: Equatable { extension RangeReplaceableCollection where Element: Equatable { @_disfavoredOverload + /// Removes the initial elements that satisfy the given predicate from the + /// start of the sequence. + /// - Parameter predicate: A closure that takes an element of the sequence + /// as its argument and returns a Boolean value indicating whether the + /// element should be removed from the collection. public mutating func trimPrefix( _ prefix: Prefix ) where Prefix.Element == Element { @@ -203,13 +220,13 @@ extension RangeReplaceableCollection where Element: Equatable { } extension BidirectionalCollection where Element: Equatable { - public func trimmingSuffix( + func trimmingSuffix( _ suffix: Suffix ) -> SubSequence where Suffix.Element == Element { trimmingSuffix(FixedPatternConsumer(pattern: suffix)) } - public func trimming( + func trimming( _ pattern: Pattern ) -> SubSequence where Pattern.Element == Element { trimming(FixedPatternConsumer(pattern: pattern)) @@ -219,13 +236,13 @@ extension BidirectionalCollection where Element: Equatable { extension BidirectionalCollection where SubSequence == Self, Element: Equatable { - public mutating func trimSuffix( + mutating func trimSuffix( _ suffix: Suffix ) where Suffix.Element == Element { trimSuffix(FixedPatternConsumer(pattern: suffix)) } - public mutating func trim( + mutating func trim( _ pattern: Pattern ) where Pattern.Element == Element { let consumer = FixedPatternConsumer(pattern: pattern) @@ -238,14 +255,14 @@ extension RangeReplaceableCollection where Self: BidirectionalCollection, Element: Equatable { @_disfavoredOverload - public mutating func trimSuffix( + mutating func trimSuffix( _ prefix: Suffix ) where Suffix.Element == Element { trimSuffix(FixedPatternConsumer(pattern: prefix)) } @_disfavoredOverload - public mutating func trim( + mutating func trim( _ pattern: Pattern ) where Pattern.Element == Element { let consumer = FixedPatternConsumer(pattern: pattern) @@ -257,15 +274,20 @@ extension RangeReplaceableCollection // MARK: Regex algorithms extension BidirectionalCollection where SubSequence == Substring { + /// Returns a new collection of the same type by removing `prefix` from the + /// start. + /// - Parameter prefix: The collection to remove from this collection. + /// - Returns: A collection containing the elements that does not match + /// `prefix` from the start. public func trimmingPrefix(_ regex: R) -> SubSequence { trimmingPrefix(RegexConsumer(regex)) } - public func trimmingSuffix(_ regex: R) -> SubSequence { + func trimmingSuffix(_ regex: R) -> SubSequence { trimmingSuffix(RegexConsumer(regex)) } - public func trimming(_ regex: R) -> SubSequence { + func trimming(_ regex: R) -> SubSequence { trimming(RegexConsumer(regex)) } } @@ -273,15 +295,17 @@ extension BidirectionalCollection where SubSequence == Substring { extension RangeReplaceableCollection where Self: BidirectionalCollection, SubSequence == Substring { + /// Removes the initial elements that matches the given regex. + /// - Parameter regex: The regex to remove from this collection. public mutating func trimPrefix(_ regex: R) { trimPrefix(RegexConsumer(regex)) } - public mutating func trimSuffix(_ regex: R) { + mutating func trimSuffix(_ regex: R) { trimSuffix(RegexConsumer(regex)) } - public mutating func trim(_ regex: R) { + mutating func trim(_ regex: R) { let consumer = RegexConsumer(regex) trimPrefix(consumer) trimSuffix(consumer) @@ -289,15 +313,15 @@ extension RangeReplaceableCollection } extension Substring { - public mutating func trimPrefix(_ regex: R) { + mutating func trimPrefix(_ regex: R) { trimPrefix(RegexConsumer(regex)) } - public mutating func trimSuffix(_ regex: R) { + mutating func trimSuffix(_ regex: R) { trimSuffix(RegexConsumer(regex)) } - public mutating func trim(_ regex: R) { + mutating func trim(_ regex: R) { let consumer = RegexConsumer(regex) trimPrefix(consumer) trimSuffix(consumer) diff --git a/Sources/_StringProcessing/Algorithms/Consumers/CollectionConsumer.swift b/Sources/_StringProcessing/Algorithms/Consumers/CollectionConsumer.swift index 95f772d7b..b71af470e 100644 --- a/Sources/_StringProcessing/Algorithms/Consumers/CollectionConsumer.swift +++ b/Sources/_StringProcessing/Algorithms/Consumers/CollectionConsumer.swift @@ -9,7 +9,7 @@ // //===----------------------------------------------------------------------===// -public protocol CollectionConsumer { +protocol CollectionConsumer { associatedtype Consumed: Collection func consuming( _ consumed: Consumed, @@ -18,13 +18,13 @@ public protocol CollectionConsumer { } extension CollectionConsumer { - public func consuming(_ consumed: Consumed) -> Consumed.Index? { + func consuming(_ consumed: Consumed) -> Consumed.Index? { consuming(consumed, in: consumed.startIndex.. Bool + func consume(_ consumed: inout Consumed) -> Bool where Consumed.SubSequence == Consumed { guard let index = consuming(consumed) else { return false } @@ -35,7 +35,7 @@ extension CollectionConsumer { // MARK: Consuming from the back -public protocol BidirectionalCollectionConsumer: CollectionConsumer +protocol BidirectionalCollectionConsumer: CollectionConsumer where Consumed: BidirectionalCollection { func consumingBack( @@ -45,11 +45,11 @@ public protocol BidirectionalCollectionConsumer: CollectionConsumer } extension BidirectionalCollectionConsumer { - public func consumingBack(_ consumed: Consumed) -> Consumed.Index? { + func consumingBack(_ consumed: Consumed) -> Consumed.Index? { consumingBack(consumed, in: consumed.startIndex.. Bool + func consumeBack(_ consumed: inout Consumed) -> Bool where Consumed.SubSequence == Consumed { guard let index = consumingBack(consumed) else { return false } diff --git a/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift b/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift index c0699b805..2aea0f342 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift @@ -12,7 +12,7 @@ // MARK: `CollectionSearcher` algorithms extension Collection { - public func firstMatch( + func firstMatch( of searcher: S ) -> _MatchResult? where S.Searched == Self { var state = searcher.state(for: self, in: startIndex..( + func lastMatch( of searcher: S ) -> _BackwardMatchResult? where S.BackwardSearched == Self @@ -38,15 +38,26 @@ extension BidirectionalCollection { // MARK: Regex algorithms extension BidirectionalCollection where SubSequence == Substring { - public func firstMatch( + func firstMatch( of regex: R ) -> _MatchResult>? { firstMatch(of: RegexConsumer(regex)) } - public func lastMatch( + 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 + /// there isn't a match. + public func firstMatch( + of regex: R + ) -> Regex.Match? { + let slice = self[...] + return try? regex.firstMatch(in: slice.base) + } } diff --git a/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift b/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift index f99e525b5..74ab48839 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift @@ -12,7 +12,7 @@ // MARK: `MatchingCollectionSearcher` algorithms extension RangeReplaceableCollection { - public func replacing< + func replacing< Searcher: MatchingCollectionSearcher, Replacement: Collection >( _ searcher: Searcher, @@ -40,7 +40,7 @@ extension RangeReplaceableCollection { return result } - public func replacing< + func replacing< Searcher: MatchingCollectionSearcher, Replacement: Collection >( _ searcher: Searcher, @@ -56,7 +56,7 @@ extension RangeReplaceableCollection { maxReplacements: maxReplacements) } - public mutating func replace< + mutating func replace< Searcher: MatchingCollectionSearcher, Replacement: Collection >( _ searcher: Searcher, @@ -75,7 +75,7 @@ extension RangeReplaceableCollection { // MARK: Regex algorithms extension RangeReplaceableCollection where SubSequence == Substring { - public func replacing( + func replacing( _ regex: R, with replacement: (_MatchResult>) throws -> Replacement, subrange: Range, @@ -88,7 +88,7 @@ extension RangeReplaceableCollection where SubSequence == Substring { maxReplacements: maxReplacements) } - public func replacing( + func replacing( _ regex: R, with replacement: (_MatchResult>) throws -> Replacement, maxReplacements: Int = .max @@ -100,7 +100,7 @@ extension RangeReplaceableCollection where SubSequence == Substring { maxReplacements: maxReplacements) } - public mutating func replace( + mutating func replace( _ regex: R, with replacement: (_MatchResult>) throws -> Replacement, maxReplacements: Int = .max @@ -110,4 +110,82 @@ extension RangeReplaceableCollection where SubSequence == Substring { 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: + /// - regex: A regex describing the sequence to replace. + /// - replacement: A closure that receives the full match information, + /// including captures, and returns a replacement collection. + /// - subrange: The range in the collection in which to search for `regex`. + /// - maxReplacements: A number specifying how many occurrences of the + /// sequence matching `regex` to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of subsequence + /// matching `regex` are replaced by `replacement`. + public func replacing( + _ regex: R, + with replacement: (Regex.Match) throws -> Replacement, + subrange: Range, + maxReplacements: Int = .max + ) rethrows -> Self where Replacement.Element == Element { + + precondition(maxReplacements >= 0) + + var index = subrange.lowerBound + var result = Self() + result.append(contentsOf: self[..( + _ regex: R, + with replacement: (Regex.Match) throws -> Replacement, + maxReplacements: Int = .max + ) rethrows -> Self where Replacement.Element == Element { + try replacing( + regex, + with: replacement, + subrange: startIndex..( + _ regex: R, + with replacement: (Regex.Match) throws -> Replacement, + maxReplacements: Int = .max + ) rethrows where Replacement.Element == Element { + self = try replacing( + regex, + with: replacement, + subrange: startIndex.. { - public let match: S.Searched.SubSequence - public let result: S.Match +struct _MatchResult { + let match: S.Searched.SubSequence + let result: S.Match - public var range: Range { + var range: Range { match.startIndex.. { - public let match: S.BackwardSearched.SubSequence - public let result: S.Match +struct _BackwardMatchResult { + let match: S.BackwardSearched.SubSequence + let result: S.Match - public var range: Range { + var range: Range { match.startIndex.. { +struct MatchesCollection { public typealias Base = Searcher.Searched let base: Base @@ -33,7 +33,7 @@ public struct MatchesCollection { } } -public struct MatchesIterator< +struct MatchesIterator< Searcher: MatchingCollectionSearcher >: IteratorProtocol { public typealias Base = Searcher.Searched @@ -64,7 +64,7 @@ extension MatchesCollection: Sequence { extension MatchesCollection: Collection { // TODO: Custom `SubSequence` for the sake of more efficient slice iteration - public struct Index { + struct Index { var match: (range: Range, match: Searcher.Match)? var state: Searcher.State } @@ -122,7 +122,7 @@ extension MatchesCollection.Index: Comparable { // MARK: `ReversedMatchesCollection` // TODO: reversed matches -public struct ReversedMatchesCollection< +struct ReversedMatchesCollection< Searcher: BackwardMatchingCollectionSearcher > { public typealias Base = Searcher.BackwardSearched @@ -137,7 +137,7 @@ public struct ReversedMatchesCollection< } extension ReversedMatchesCollection: Sequence { - public struct Iterator: IteratorProtocol { + struct Iterator: IteratorProtocol { let base: Base let searcher: Searcher var state: Searcher.BackwardState @@ -166,7 +166,7 @@ extension ReversedMatchesCollection: Sequence { // MARK: `CollectionSearcher` algorithms extension Collection { - public func matches( + func matches( of searcher: S ) -> MatchesCollection where S.Searched == Self { MatchesCollection(base: self, searcher: searcher) @@ -174,7 +174,7 @@ extension Collection { } extension BidirectionalCollection { - public func matchesFromBack( + func matchesFromBack( of searcher: S ) -> ReversedMatchesCollection where S.BackwardSearched == Self { ReversedMatchesCollection(base: self, searcher: searcher) @@ -184,15 +184,38 @@ extension BidirectionalCollection { // MARK: Regex algorithms extension BidirectionalCollection where SubSequence == Substring { - public func matches( + // FIXME: Replace `MatchesCollection` when SE-0346 is enabled + /// Returns a collection containing all matches of the specified regex. + /// - Parameter regex: The regex to search for. + /// - Returns: A collection of matches of `regex`. + func matches( of regex: R ) -> MatchesCollection> { matches(of: RegexConsumer(regex)) } - - public func matchesFromBack( + + func matchesFromBack( of regex: R ) -> ReversedMatchesCollection> { matchesFromBack(of: RegexConsumer(regex)) } + + // FIXME: Replace the returned value as `some Collection.Match> + // when SE-0346 is enabled + func _matches(of regex: R) -> [Regex.Match] { + let slice = self[...] + var start = self.startIndex + let end = self.endIndex + + var result = [Regex.Match]() + while start < end { + guard let match = try? regex._firstMatch(slice.base, in: start.. ) -> Consumed.Index? { @@ -28,7 +28,7 @@ extension MatchingCollectionConsumer { // MARK: Consuming from the back -public protocol BidirectionalMatchingCollectionConsumer: +protocol BidirectionalMatchingCollectionConsumer: MatchingCollectionConsumer, BidirectionalCollectionConsumer { func matchingConsumingBack( @@ -38,7 +38,7 @@ public protocol BidirectionalMatchingCollectionConsumer: } extension BidirectionalMatchingCollectionConsumer { - public func consumingBack( + func consumingBack( _ consumed: Consumed, in range: Range ) -> Consumed.Index? { diff --git a/Sources/_StringProcessing/Algorithms/Matching/MatchingCollectionSearcher.swift b/Sources/_StringProcessing/Algorithms/Matching/MatchingCollectionSearcher.swift index eadb46f9e..902d94591 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/MatchingCollectionSearcher.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/MatchingCollectionSearcher.swift @@ -9,7 +9,7 @@ // //===----------------------------------------------------------------------===// -public protocol MatchingCollectionSearcher: CollectionSearcher { +protocol MatchingCollectionSearcher: CollectionSearcher { associatedtype Match func matchingSearch( _ searched: Searched, @@ -18,7 +18,7 @@ public protocol MatchingCollectionSearcher: CollectionSearcher { } extension MatchingCollectionSearcher { - public func search( + func search( _ searched: Searched, _ state: inout State ) -> Range? { @@ -26,7 +26,7 @@ extension MatchingCollectionSearcher { } } -public protocol MatchingStatelessCollectionSearcher: +protocol MatchingStatelessCollectionSearcher: MatchingCollectionSearcher, StatelessCollectionSearcher { func matchingSearch( @@ -38,14 +38,14 @@ public protocol MatchingStatelessCollectionSearcher: extension MatchingStatelessCollectionSearcher { // for disambiguation between the `MatchingCollectionSearcher` and // `StatelessCollectionSearcher` overloads - public func search( + func search( _ searched: Searched, _ state: inout State ) -> Range? { matchingSearch(searched, &state)?.range } - public func matchingSearch( + func matchingSearch( _ searched: Searched, _ state: inout State ) -> (range: Range, match: Match)? { @@ -69,7 +69,7 @@ extension MatchingStatelessCollectionSearcher { return (range, value) } - public func search( + func search( _ searched: Searched, in range: Range ) -> Range? { @@ -79,7 +79,7 @@ extension MatchingStatelessCollectionSearcher { // MARK: Searching from the back -public protocol BackwardMatchingCollectionSearcher: BackwardCollectionSearcher { +protocol BackwardMatchingCollectionSearcher: BackwardCollectionSearcher { associatedtype Match func matchingSearchBack( _ searched: BackwardSearched, @@ -87,7 +87,7 @@ public protocol BackwardMatchingCollectionSearcher: BackwardCollectionSearcher { ) -> (range: Range, match: Match)? } -public protocol BackwardMatchingStatelessCollectionSearcher: +protocol BackwardMatchingStatelessCollectionSearcher: BackwardMatchingCollectionSearcher, BackwardStatelessCollectionSearcher { func matchingSearchBack( @@ -97,14 +97,14 @@ public protocol BackwardMatchingStatelessCollectionSearcher: } extension BackwardMatchingStatelessCollectionSearcher { - public func searchBack( + func searchBack( _ searched: BackwardSearched, in range: Range ) -> Range? { matchingSearchBack(searched, in: range)?.range } - public func matchingSearchBack( + func matchingSearchBack( _ searched: BackwardSearched, _ state: inout BackwardState) -> (range: Range, match: Match)? { diff --git a/Sources/_StringProcessing/Algorithms/Searchers/CollectionSearcher.swift b/Sources/_StringProcessing/Algorithms/Searchers/CollectionSearcher.swift index 78e4fc925..5eb199086 100644 --- a/Sources/_StringProcessing/Algorithms/Searchers/CollectionSearcher.swift +++ b/Sources/_StringProcessing/Algorithms/Searchers/CollectionSearcher.swift @@ -9,7 +9,7 @@ // //===----------------------------------------------------------------------===// -public struct DefaultSearcherState { +struct DefaultSearcherState { enum Position { case index(Searched.Index) case done @@ -19,7 +19,7 @@ public struct DefaultSearcherState { let end: Searched.Index } -public protocol CollectionSearcher { +protocol CollectionSearcher { associatedtype Searched: Collection associatedtype State @@ -30,7 +30,7 @@ public protocol CollectionSearcher { ) -> Range? } -public protocol StatelessCollectionSearcher: CollectionSearcher +protocol StatelessCollectionSearcher: CollectionSearcher where State == DefaultSearcherState { func search( @@ -39,14 +39,14 @@ public protocol StatelessCollectionSearcher: CollectionSearcher } extension StatelessCollectionSearcher { - public func state( + func state( for searched: Searched, in range: Range ) -> State { State(position: .index(range.lowerBound), end: range.upperBound) } - public func search( + func search( _ searched: Searched, _ state: inout State ) -> Range? { @@ -71,7 +71,7 @@ extension StatelessCollectionSearcher { // MARK: Searching from the back -public protocol BackwardCollectionSearcher { +protocol BackwardCollectionSearcher { associatedtype BackwardSearched: BidirectionalCollection associatedtype BackwardState @@ -84,7 +84,7 @@ public protocol BackwardCollectionSearcher { ) -> Range? } -public protocol BackwardStatelessCollectionSearcher: BackwardCollectionSearcher +protocol BackwardStatelessCollectionSearcher: BackwardCollectionSearcher where BackwardState == DefaultSearcherState { func searchBack( @@ -94,14 +94,14 @@ public protocol BackwardStatelessCollectionSearcher: BackwardCollectionSearcher } extension BackwardStatelessCollectionSearcher { - public func backwardState( + func backwardState( for searched: BackwardSearched, in range: Range ) -> BackwardState { BackwardState(position: .index(range.upperBound), end: range.lowerBound) } - public func searchBack( + func searchBack( _ searched: BackwardSearched, _ state: inout BackwardState) -> Range? { guard diff --git a/Sources/_StringProcessing/Algorithms/Searchers/PatternOrEmpty.swift b/Sources/_StringProcessing/Algorithms/Searchers/PatternOrEmpty.swift index ee1b04adf..3b3fe22f9 100644 --- a/Sources/_StringProcessing/Algorithms/Searchers/PatternOrEmpty.swift +++ b/Sources/_StringProcessing/Algorithms/Searchers/PatternOrEmpty.swift @@ -10,14 +10,14 @@ //===----------------------------------------------------------------------===// /// Wraps a searcher that searches for a given pattern. If the pattern is empty, falls back on matching every empty index range exactly once. -public struct PatternOrEmpty { +struct PatternOrEmpty { let searcher: Searcher? } extension PatternOrEmpty: CollectionSearcher { - public typealias Searched = Searcher.Searched + typealias Searched = Searcher.Searched - public struct State { + struct State { enum Representation { case state(Searcher.State) case empty(index: Searched.Index, end: Searched.Index) @@ -27,7 +27,7 @@ extension PatternOrEmpty: CollectionSearcher { let representation: Representation } - public func state( + func state( for searched: Searcher.Searched, in range: Range ) -> State { @@ -40,7 +40,7 @@ extension PatternOrEmpty: CollectionSearcher { } } - public func search( + func search( _ searched: Searched, _ state: inout State ) -> Range? { diff --git a/Tests/RegexBuilderTests/AlgorithmsTests.swift b/Tests/RegexBuilderTests/AlgorithmsTests.swift index 183d247a7..7625af385 100644 --- a/Tests/RegexBuilderTests/AlgorithmsTests.swift +++ b/Tests/RegexBuilderTests/AlgorithmsTests.swift @@ -14,18 +14,20 @@ import _StringProcessing @testable import RegexBuilder class RegexConsumerTests: XCTestCase { - func testMatches() { - let regex = Capture(OneOrMore(.digit)) { 2 * Int($0)! } - let str = "foo 160 bar 99 baz" - XCTAssertEqual(str.matches(of: regex).map(\.result.1), [320, 198]) - } + // FIXME: enable this test when we update the return type of `matches(of:)` + // when SE-0346 is available + // func testMatches() { + // let regex = Capture(OneOrMore(.digit)) { 2 * Int($0)! } + // let str = "foo 160 bar 99 baz" + // XCTAssertEqual(str.matches(of: regex).map(\.result.1), [320, 198]) + // } func testMatchReplace() { func replaceTest( _ regex: R, input: String, result: String, - _ replace: (_MatchResult>) -> String, + _ replace: (Regex.Match) -> String, file: StaticString = #file, line: UInt = #line ) { @@ -38,13 +40,13 @@ class RegexConsumerTests: XCTestCase { int, input: "foo 160 bar 99 baz", result: "foo 240 bar 143 baz", - { match in String(match.result.1, radix: 8) }) + { match in String(match.output.1, radix: 8) }) replaceTest( Regex { int; "+"; int }, input: "9+16, 0+3, 5+5, 99+1", result: "25, 3, 10, 100", - { match in "\(match.result.1 + match.result.2)" }) + { match in "\(match.output.1 + match.output.2)" }) // TODO: Need to support capture history // replaceTest( @@ -57,6 +59,6 @@ class RegexConsumerTests: XCTestCase { Regex { int; "x"; int; Optionally { "x"; int } }, input: "2x3 5x4x3 6x0 1x2x3x4", result: "6 60 0 6x4", - { match in "\(match.result.1 * match.result.2 * (match.result.3 ?? 1))" }) + { match in "\(match.output.1 * match.output.2 * (match.output.3 ?? 1))" }) } } diff --git a/Tests/RegexBuilderTests/CustomTests.swift b/Tests/RegexBuilderTests/CustomTests.swift index 7be95c28c..19c4a3896 100644 --- a/Tests/RegexBuilderTests/CustomTests.swift +++ b/Tests/RegexBuilderTests/CustomTests.swift @@ -64,7 +64,7 @@ func customTest( case .match: result = input.matchWhole(regex)?.output case .firstMatch: - result = input.firstMatch(of: regex)?.result + result = input.firstMatch(of: regex)?.output } XCTAssertEqual(result, match) } @@ -120,9 +120,9 @@ class CustomRegexComponentTests: XCTestCase { return } - XCTAssertEqual(res3.match, "123") - XCTAssertEqual(res3.result.0, "123") - XCTAssertEqual(res3.result.1, "123") + XCTAssertEqual(res3.range, "ab123c".index(atOffset: 2)..<"ab123c".index(atOffset: 5)) + XCTAssertEqual(res3.output.0, "123") + XCTAssertEqual(res3.output.1, "123") let regex4 = Regex { OneOrMore { @@ -135,7 +135,7 @@ class CustomRegexComponentTests: XCTestCase { return } - XCTAssertEqual(res4.result.0, "123") - XCTAssertEqual(res4.result.1, 3) + XCTAssertEqual(res4.output.0, "123") + XCTAssertEqual(res4.output.1, 3) } } diff --git a/Tests/RegexBuilderTests/RegexDSLTests.swift b/Tests/RegexBuilderTests/RegexDSLTests.swift index 50358734d..2762eeb79 100644 --- a/Tests/RegexBuilderTests/RegexDSLTests.swift +++ b/Tests/RegexBuilderTests/RegexDSLTests.swift @@ -704,10 +704,10 @@ class RegexDSLTests: XCTestCase { else { return nil } let result = SemanticVersion( - major: match.result.1, - minor: match.result.2, - patch: match.result.3 ?? 0, - dev: match.result.4.map(String.init)) + major: match.output.1, + minor: match.output.2, + patch: match.output.3 ?? 0, + dev: match.output.4.map(String.init)) return (match.range.upperBound, result) } }