diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift index 2a1ef72a2..bb86f4676 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Contains.swift @@ -12,10 +12,10 @@ // MARK: `CollectionSearcher` algorithms extension Collection { - func contains( + func _contains( _ searcher: Searcher ) -> Bool where Searcher.Searched == Self { - firstRange(of: searcher) != nil + _firstRange(of: searcher) != nil } } @@ -36,7 +36,7 @@ extension Collection where Element: Equatable { } extension BidirectionalCollection where Element: Comparable { - func contains(_ other: C) -> Bool + func _contains(_ other: C) -> Bool where C.Element == Element { if #available(SwiftStdlib 5.7, *) { @@ -70,6 +70,6 @@ extension BidirectionalCollection where SubSequence == Substring { /// `false`. @available(SwiftStdlib 5.7, *) public func contains(_ regex: R) -> Bool { - contains(RegexConsumer(regex)) + _contains(RegexConsumer(regex)) } } diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift b/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift index 42703827e..ad5c535d4 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/FirstRange.swift @@ -12,7 +12,7 @@ // MARK: `CollectionSearcher` algorithms extension Collection { - 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 regex: R) -> Range? { - firstRange(of: RegexConsumer(regex)) + _firstRange(of: RegexConsumer(regex)) } @available(SwiftStdlib 5.7, *) - func lastRange(of regex: R) -> Range? { - lastRange(of: RegexConsumer(regex)) + 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 33a9748ac..9f3c50c7c 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift @@ -157,7 +157,7 @@ extension ReversedRangesCollection: Sequence { // MARK: `CollectionSearcher` algorithms extension Collection { - 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 { - func rangesFromBack( + func _rangesFromBack( of searcher: S ) -> ReversedRangesCollection where S.BackwardSearched == Self { ReversedRangesCollection(base: self, searcher: searcher) @@ -175,10 +175,10 @@ extension BidirectionalCollection { // MARK: Fixed pattern algorithms extension Collection where Element: Equatable { - func ranges( + func _ranges( of other: C ) -> RangesCollection> where C.Element == Element { - ranges(of: ZSearcher(pattern: Array(other), by: ==)) + _ranges(of: ZSearcher(pattern: Array(other), by: ==)) } // FIXME: Return `some Collection>` for SE-0346 @@ -191,7 +191,7 @@ extension Collection where Element: Equatable { public func ranges( of other: C ) -> [Range] where C.Element == Element { - ranges(of: ZSearcher(pattern: Array(other), by: ==)).map { $0 } + Array(_ranges(of: other)) } } @@ -207,12 +207,12 @@ extension BidirectionalCollection where Element: Equatable { } extension BidirectionalCollection where Element: Comparable { - func ranges( + func _ranges( of other: C ) -> RangesCollection>> where C.Element == Element { - ranges(of: PatternOrEmpty(searcher: TwoWaySearcher(pattern: Array(other)))) + _ranges(of: PatternOrEmpty(searcher: TwoWaySearcher(pattern: Array(other)))) } // FIXME @@ -231,17 +231,17 @@ extension BidirectionalCollection where Element: Comparable { extension BidirectionalCollection where SubSequence == Substring { @available(SwiftStdlib 5.7, *) @_disfavoredOverload - func ranges( + func _ranges( of regex: R ) -> RangesCollection> { - ranges(of: RegexConsumer(regex)) + _ranges(of: RegexConsumer(regex)) } @available(SwiftStdlib 5.7, *) - func rangesFromBack( + func _rangesFromBack( of regex: R ) -> ReversedRangesCollection> { - rangesFromBack(of: RegexConsumer(regex)) + _rangesFromBack(of: RegexConsumer(regex)) } // FIXME: Return `some Collection>` for SE-0346 @@ -255,6 +255,6 @@ extension BidirectionalCollection where SubSequence == Substring { public func ranges( of regex: R ) -> [Range] { - Array(ranges(of: RegexConsumer(regex))) + Array(_ranges(of: regex)) } } diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift index 217fb90d6..daf726fe7 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift @@ -12,7 +12,7 @@ // MARK: `CollectionSearcher` algorithms extension RangeReplaceableCollection { - func replacing( + func _replacing( _ searcher: Searcher, with replacement: Replacement, subrange: Range, @@ -26,7 +26,7 @@ extension RangeReplaceableCollection { var result = Self() result.append(contentsOf: self[..( + func _replacing( _ searcher: Searcher, with replacement: Replacement, maxReplacements: Int = .max ) -> Self where Searcher.Searched == SubSequence, Replacement.Element == Element { - replacing( + _replacing( searcher, with: replacement, subrange: startIndex..( _ searcher: Searcher, with replacement: Replacement, maxReplacements: Int = .max ) where Searcher.Searched == SubSequence, Replacement.Element == Element { - self = replacing( + self = _replacing( searcher, with: replacement, maxReplacements: maxReplacements) @@ -84,7 +84,7 @@ extension RangeReplaceableCollection where Element: Equatable { subrange: Range, maxReplacements: Int = .max ) -> Self where C.Element == Element, Replacement.Element == Element { - replacing( + _replacing( ZSearcher(pattern: Array(other), by: ==), with: replacement, subrange: subrange, @@ -136,37 +136,37 @@ extension RangeReplaceableCollection where Element: Equatable { extension RangeReplaceableCollection where Self: BidirectionalCollection, Element: Comparable { - func replacing( + func _replacing( _ other: C, with replacement: Replacement, subrange: Range, maxReplacements: Int = .max ) -> Self where C.Element == Element, Replacement.Element == Element { - replacing( + _replacing( PatternOrEmpty(searcher: TwoWaySearcher(pattern: Array(other))), with: replacement, subrange: subrange, maxReplacements: maxReplacements) } - func replacing( + func _replacing( _ other: C, with replacement: Replacement, maxReplacements: Int = .max ) -> Self where C.Element == Element, Replacement.Element == Element { - replacing( + _replacing( other, with: replacement, subrange: startIndex..( + mutating func _replace( _ other: C, with replacement: Replacement, maxReplacements: Int = .max ) where C.Element == Element, Replacement.Element == Element { - self = replacing( + self = _replacing( other, with: replacement, subrange: startIndex.., maxReplacements: Int = .max ) -> Self where Replacement.Element == Element { - replacing( + _replacing( RegexConsumer(regex), with: replacement, subrange: subrange, diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift b/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift index ab465c382..412197557 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Split.swift @@ -34,7 +34,7 @@ struct SplitCollection { maxSplits: Int, omittingEmptySubsequences: Bool) { - self.ranges = base.ranges(of: searcher) + self.ranges = base._ranges(of: searcher) self.maxSplits = maxSplits self.omittingEmptySubsequences = omittingEmptySubsequences } @@ -183,7 +183,7 @@ struct ReversedSplitCollection { } init(base: Base, searcher: Searcher) { - self.ranges = base.rangesFromBack(of: searcher) + self.ranges = base._rangesFromBack(of: searcher) } } diff --git a/Sources/_StringProcessing/Algorithms/Algorithms/StartsWith.swift b/Sources/_StringProcessing/Algorithms/Algorithms/StartsWith.swift index 2f45a734b..c8aaf9126 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/StartsWith.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/StartsWith.swift @@ -12,7 +12,7 @@ // MARK: `CollectionConsumer` algorithms extension Collection { - 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 { - func ends(with consumer: C) -> Bool + func _ends(with consumer: C) -> Bool where C.Consumed == SubSequence { consumer.consumingBack(self[...]) != nil @@ -30,18 +30,18 @@ extension BidirectionalCollection { // MARK: Fixed pattern algorithms extension Collection where Element: Equatable { - func starts(with prefix: C) -> Bool + func _starts(with prefix: C) -> Bool where C.Element == Element { - starts(with: FixedPatternConsumer(pattern: prefix)) + _starts(with: FixedPatternConsumer(pattern: prefix)) } } extension BidirectionalCollection where Element: Equatable { - func ends(with suffix: C) -> Bool + func _ends(with suffix: C) -> Bool where C.Element == Element { - ends(with: FixedPatternConsumer(pattern: suffix)) + _ends(with: FixedPatternConsumer(pattern: suffix)) } } @@ -56,10 +56,10 @@ 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: R) -> Bool { - starts(with: RegexConsumer(regex)) + _starts(with: RegexConsumer(regex)) } - func ends(with regex: R) -> Bool { - ends(with: RegexConsumer(regex)) + 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 7411236da..2e955507b 100644 --- a/Sources/_StringProcessing/Algorithms/Algorithms/Trim.swift +++ b/Sources/_StringProcessing/Algorithms/Algorithms/Trim.swift @@ -12,7 +12,7 @@ // MARK: `CollectionConsumer` algorithms extension Collection { - 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 { - 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 - 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 { - 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,24 +64,24 @@ extension BidirectionalCollection { } extension BidirectionalCollection where SubSequence == Self { - mutating func trimSuffix( + mutating func _trimSuffix( _ consumer: Consumer ) where Consumer.Consumed == SubSequence { _ = consumer.consumeBack(&self) } - mutating func trim( + mutating func _trim( _ consumer: Consumer ) where Consumer.Consumed == Self { - trimPrefix(consumer) - trimSuffix(consumer) + _trimPrefix(consumer) + _trimSuffix(consumer) } } extension RangeReplaceableCollection where Self: BidirectionalCollection { @_disfavoredOverload - mutating func trimSuffix( + mutating func _trimSuffix( _ consumer: Consumer ) where Consumer.Consumed == Self { @@ -91,11 +91,11 @@ extension RangeReplaceableCollection where Self: BidirectionalCollection { } @_disfavoredOverload - mutating func trim( + mutating func _trim( _ consumer: Consumer ) where Consumer.Consumed == Self { - trimSuffix(consumer) - trimPrefix(consumer) + _trimSuffix(consumer) + _trimPrefix(consumer) } } @@ -137,49 +137,49 @@ extension RangeReplaceableCollection { } extension BidirectionalCollection { - func trimmingSuffix( + func _trimmingSuffix( while predicate: @escaping (Element) -> Bool ) -> SubSequence { - trimmingSuffix(ManyConsumer(base: PredicateConsumer(predicate: predicate))) + _trimmingSuffix(ManyConsumer(base: PredicateConsumer(predicate: predicate))) } - func trimming( + func _trimming( while predicate: @escaping (Element) -> Bool ) -> SubSequence { - trimming(ManyConsumer(base: PredicateConsumer(predicate: predicate))) + _trimming(ManyConsumer(base: PredicateConsumer(predicate: predicate))) } } extension BidirectionalCollection where SubSequence == Self { - mutating func trimSuffix( + mutating func _trimSuffix( while predicate: @escaping (Element) -> Bool ) { - trimSuffix(ManyConsumer( + _trimSuffix(ManyConsumer( base: PredicateConsumer(predicate: predicate))) } - 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) - trimSuffix(consumer) + _trimPrefix(consumer) + _trimSuffix(consumer) } } extension RangeReplaceableCollection where Self: BidirectionalCollection { @_disfavoredOverload - mutating func trimSuffix( + mutating func _trimSuffix( while predicate: @escaping (Element) -> Bool ) { - trimSuffix(ManyConsumer(base: PredicateConsumer(predicate: predicate))) + _trimSuffix(ManyConsumer(base: PredicateConsumer(predicate: predicate))) } @_disfavoredOverload - 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) - trimSuffix(consumer) + _trimPrefix(consumer) + _trimSuffix(consumer) } } @@ -197,7 +197,7 @@ extension Collection where Element: Equatable { public func trimmingPrefix( _ prefix: Prefix ) -> SubSequence where Prefix.Element == Element { - trimmingPrefix(FixedPatternConsumer(pattern: prefix)) + _trimmingPrefix(FixedPatternConsumer(pattern: prefix)) } } @@ -211,7 +211,7 @@ extension Collection where SubSequence == Self, Element: Equatable { public mutating func trimPrefix( _ prefix: Prefix ) where Prefix.Element == Element { - trimPrefix(FixedPatternConsumer(pattern: prefix)) + _trimPrefix(FixedPatternConsumer(pattern: prefix)) } } @@ -226,39 +226,39 @@ extension RangeReplaceableCollection where Element: Equatable { public mutating func trimPrefix( _ prefix: Prefix ) where Prefix.Element == Element { - trimPrefix(FixedPatternConsumer(pattern: prefix)) + _trimPrefix(FixedPatternConsumer(pattern: prefix)) } } extension BidirectionalCollection where Element: Equatable { - func trimmingSuffix( + func _trimmingSuffix( _ suffix: Suffix ) -> SubSequence where Suffix.Element == Element { - trimmingSuffix(FixedPatternConsumer(pattern: suffix)) + _trimmingSuffix(FixedPatternConsumer(pattern: suffix)) } - func trimming( + func _trimming( _ pattern: Pattern ) -> SubSequence where Pattern.Element == Element { - trimming(FixedPatternConsumer(pattern: pattern)) + _trimming(FixedPatternConsumer(pattern: pattern)) } } extension BidirectionalCollection where SubSequence == Self, Element: Equatable { - mutating func trimSuffix( + mutating func _trimSuffix( _ suffix: Suffix ) where Suffix.Element == Element { - trimSuffix(FixedPatternConsumer(pattern: suffix)) + _trimSuffix(FixedPatternConsumer(pattern: suffix)) } - mutating func trim( + mutating func _trim( _ pattern: Pattern ) where Pattern.Element == Element { let consumer = FixedPatternConsumer(pattern: pattern) - trimPrefix(consumer) - trimSuffix(consumer) + _trimPrefix(consumer) + _trimSuffix(consumer) } } @@ -266,19 +266,19 @@ extension RangeReplaceableCollection where Self: BidirectionalCollection, Element: Equatable { @_disfavoredOverload - mutating func trimSuffix( + mutating func _trimSuffix( _ prefix: Suffix ) where Suffix.Element == Element { - trimSuffix(FixedPatternConsumer(pattern: prefix)) + _trimSuffix(FixedPatternConsumer(pattern: prefix)) } @_disfavoredOverload - mutating func trim( + mutating func _trim( _ pattern: Pattern ) where Pattern.Element == Element { let consumer = FixedPatternConsumer(pattern: pattern) - trimPrefix(consumer) - trimSuffix(consumer) + _trimPrefix(consumer) + _trimSuffix(consumer) } } @@ -292,17 +292,17 @@ extension BidirectionalCollection where SubSequence == Substring { /// `prefix` from the start. @available(SwiftStdlib 5.7, *) public func trimmingPrefix(_ regex: R) -> SubSequence { - trimmingPrefix(RegexConsumer(regex)) + _trimmingPrefix(RegexConsumer(regex)) } @available(SwiftStdlib 5.7, *) - func trimmingSuffix(_ regex: R) -> SubSequence { - trimmingSuffix(RegexConsumer(regex)) + func _trimmingSuffix(_ regex: R) -> SubSequence { + _trimmingSuffix(RegexConsumer(regex)) } @available(SwiftStdlib 5.7, *) - func trimming(_ regex: R) -> SubSequence { - trimming(RegexConsumer(regex)) + func _trimming(_ regex: R) -> SubSequence { + _trimming(RegexConsumer(regex)) } } @@ -313,37 +313,37 @@ extension RangeReplaceableCollection /// - Parameter regex: The regex to remove from this collection. @available(SwiftStdlib 5.7, *) public mutating func trimPrefix(_ regex: R) { - trimPrefix(RegexConsumer(regex)) + _trimPrefix(RegexConsumer(regex)) } @available(SwiftStdlib 5.7, *) - mutating func trimSuffix(_ regex: R) { - trimSuffix(RegexConsumer(regex)) + mutating func _trimSuffix(_ regex: R) { + _trimSuffix(RegexConsumer(regex)) } @available(SwiftStdlib 5.7, *) - mutating func trim(_ regex: R) { + mutating func _trim(_ regex: R) { let consumer = RegexConsumer(regex) - trimPrefix(consumer) - trimSuffix(consumer) + _trimPrefix(consumer) + _trimSuffix(consumer) } } extension Substring { @available(SwiftStdlib 5.7, *) - mutating func trimPrefix(_ regex: R) { - trimPrefix(RegexConsumer(regex)) + mutating func _trimPrefix(_ regex: R) { + _trimPrefix(RegexConsumer(regex)) } @available(SwiftStdlib 5.7, *) - mutating func trimSuffix(_ regex: R) { - trimSuffix(RegexConsumer(regex)) + mutating func _trimSuffix(_ regex: R) { + _trimSuffix(RegexConsumer(regex)) } @available(SwiftStdlib 5.7, *) - mutating func trim(_ regex: R) { + mutating func _trim(_ regex: R) { let consumer = RegexConsumer(regex) - trimPrefix(consumer) - trimSuffix(consumer) + _trimPrefix(consumer) + _trimSuffix(consumer) } } diff --git a/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift b/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift index 4342391af..2b6dd1704 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift @@ -12,7 +12,7 @@ // MARK: `CollectionSearcher` algorithms extension Collection { - func firstMatch( + func _firstMatch( of searcher: S ) -> _MatchResult? where S.Searched == Self { var state = searcher.state(for: self, in: startIndex..( of regex: R ) -> _MatchResult>? { - firstMatch(of: RegexConsumer(regex)) + _firstMatch(of: RegexConsumer(regex)) } @available(SwiftStdlib 5.7, *) diff --git a/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift b/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift index 206d68554..38224e30f 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift @@ -12,7 +12,7 @@ // MARK: `MatchingCollectionSearcher` algorithms extension RangeReplaceableCollection { - func replacing< + func _replacing< Searcher: MatchingCollectionSearcher, Replacement: Collection >( _ searcher: Searcher, @@ -28,7 +28,7 @@ extension RangeReplaceableCollection { var result = Self() result.append(contentsOf: self[..( _ searcher: Searcher, @@ -49,14 +49,14 @@ extension RangeReplaceableCollection { ) rethrows -> Self where Searcher.Searched == SubSequence, Replacement.Element == Element { - try replacing( + try _replacing( searcher, with: replacement, subrange: startIndex..( _ searcher: Searcher, @@ -65,7 +65,7 @@ extension RangeReplaceableCollection { ) rethrows where Searcher.Searched == SubSequence, Replacement.Element == Element { - self = try replacing( + self = try _replacing( searcher, with: replacement, maxReplacements: maxReplacements) @@ -76,13 +76,13 @@ extension RangeReplaceableCollection { extension RangeReplaceableCollection where SubSequence == Substring { @available(SwiftStdlib 5.7, *) - func replacing( + func _replacing( _ regex: R, with replacement: (_MatchResult>) throws -> Replacement, subrange: Range, maxReplacements: Int = .max ) rethrows -> Self where Replacement.Element == Element { - try replacing( + try _replacing( RegexConsumer(regex), with: replacement, subrange: subrange, @@ -90,12 +90,12 @@ extension RangeReplaceableCollection where SubSequence == Substring { } @available(SwiftStdlib 5.7, *) - func replacing( + func _replacing( _ regex: R, with replacement: (_MatchResult>) throws -> Replacement, maxReplacements: Int = .max ) rethrows -> Self where Replacement.Element == Element { - try replacing( + try _replacing( regex, with: replacement, subrange: startIndex..( + mutating func _replace( _ regex: R, with replacement: (_MatchResult>) throws -> Replacement, maxReplacements: Int = .max ) rethrows where Replacement.Element == Element { - self = try replacing( + self = try _replacing( regex, with: replacement, maxReplacements: maxReplacements) diff --git a/Sources/_StringProcessing/Algorithms/Matching/Matches.swift b/Sources/_StringProcessing/Algorithms/Matching/Matches.swift index f038616fe..293520735 100644 --- a/Sources/_StringProcessing/Algorithms/Matching/Matches.swift +++ b/Sources/_StringProcessing/Algorithms/Matching/Matches.swift @@ -166,7 +166,7 @@ extension ReversedMatchesCollection: Sequence { // MARK: `CollectionSearcher` algorithms extension Collection { - 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 { - func matchesFromBack( + func _matchesFromBack( of searcher: S ) -> ReversedMatchesCollection where S.BackwardSearched == Self { ReversedMatchesCollection(base: self, searcher: searcher) @@ -186,17 +186,17 @@ extension BidirectionalCollection { extension BidirectionalCollection where SubSequence == Substring { @available(SwiftStdlib 5.7, *) @_disfavoredOverload - func matches( + func _matches( of regex: R ) -> MatchesCollection> { - matches(of: RegexConsumer(regex)) + _matches(of: RegexConsumer(regex)) } @available(SwiftStdlib 5.7, *) - func matchesFromBack( + func _matchesFromBack( of regex: R ) -> ReversedMatchesCollection> { - matchesFromBack(of: RegexConsumer(regex)) + _matchesFromBack(of: RegexConsumer(regex)) } // FIXME: Return `some Collection.Match> for SE-0346 @@ -213,14 +213,22 @@ extension BidirectionalCollection where SubSequence == Substring { let regex = r.regex var result = [Regex.Match]() - while start < end { + while start <= end { guard let match = try? regex._firstMatch( slice.base, in: start.. let (criticalIndex, periodOfSecondPart) = pattern._criticalFactorization(<) let periodIsExact = pattern[criticalIndex...] .prefix(periodOfSecondPart) - .ends(with: pattern[.. where Input.Element: Equatable { let captureList: CaptureList let referencedCaptureOffsets: [ReferenceID: Int] let namedCaptureOffsets: [String: Int] + + var initialOptions: MatchingOptions } extension MEProgram: CustomStringConvertible { diff --git a/Sources/_StringProcessing/MatchingOptions.swift b/Sources/_StringProcessing/MatchingOptions.swift index c60f08d17..a5daa3f73 100644 --- a/Sources/_StringProcessing/MatchingOptions.swift +++ b/Sources/_StringProcessing/MatchingOptions.swift @@ -54,6 +54,13 @@ extension MatchingOptions { stack[stack.count - 1].apply(sequence) _invariantCheck() } + + // @testable + /// Returns true if the options at the top of `stack` are equal to those + /// for `other`. + func _equal(to other: MatchingOptions) -> Bool { + stack.last == other.stack.last + } } // MARK: Matching behavior API @@ -127,6 +134,7 @@ extension MatchingOptions { } } +// MARK: - Implementation extension MatchingOptions { /// An option that changes the behavior of a regular expression. fileprivate enum Option: Int { diff --git a/Sources/_StringProcessing/PrintAsPattern.swift b/Sources/_StringProcessing/PrintAsPattern.swift index 601447968..3f620030f 100644 --- a/Sources/_StringProcessing/PrintAsPattern.swift +++ b/Sources/_StringProcessing/PrintAsPattern.swift @@ -469,7 +469,7 @@ extension PrettyPrinter { extension String { // TODO: Escaping? fileprivate var _quoted: String { - "\"\(self.replacing("\"", with: "\\\""))\"" + "\"\(self._replacing("\"", with: "\\\""))\"" } } diff --git a/Sources/_StringProcessing/Regex/ASTConversion.swift b/Sources/_StringProcessing/Regex/ASTConversion.swift index 79a515033..d025893b8 100644 --- a/Sources/_StringProcessing/Regex/ASTConversion.swift +++ b/Sources/_StringProcessing/Regex/ASTConversion.swift @@ -13,15 +13,7 @@ extension AST { var dslTree: DSLTree { - return DSLTree( - root.dslTreeNode, options: globalOptions?.dslTreeOptions) - } -} - -extension AST.GlobalMatchingOptionSequence { - var dslTreeOptions: DSLTree.Options { - // TODO: map options - return .init() + return DSLTree(root.dslTreeNode) } } diff --git a/Sources/_StringProcessing/Regex/Core.swift b/Sources/_StringProcessing/Regex/Core.swift index 29d2267b2..5d2101afe 100644 --- a/Sources/_StringProcessing/Regex/Core.swift +++ b/Sources/_StringProcessing/Regex/Core.swift @@ -91,6 +91,18 @@ extension Regex { self.tree = tree } } + + /// The set of matching options that applies to the start of this regex. + /// + /// Note that the initial options may not apply to the entire regex. For + /// example, in this regex, only case insensitivity (`i`) and Unicode scalar + /// semantics (set by API) apply to the entire regex, while ASCII character + /// classes (`P`) is part of `initialOptions` but not global: + /// + /// let regex = /(?i)(?P:\d+\s*)abc/.semanticLevel(.unicodeScalar) + var initialOptions: MatchingOptions { + program.loweredProgram.initialOptions + } } @available(SwiftStdlib 5.7, *) @@ -102,6 +114,6 @@ extension Regex { @_spi(RegexBuilder) public init(node: DSLTree.Node) { - self.program = Program(tree: .init(node, options: nil)) + self.program = Program(tree: .init(node)) } } diff --git a/Sources/_StringProcessing/Regex/DSLTree.swift b/Sources/_StringProcessing/Regex/DSLTree.swift index 91f102999..8ca6dce8d 100644 --- a/Sources/_StringProcessing/Regex/DSLTree.swift +++ b/Sources/_StringProcessing/Regex/DSLTree.swift @@ -14,11 +14,9 @@ @_spi(RegexBuilder) public struct DSLTree { var root: Node - var options: Options? - init(_ r: Node, options: Options?) { + init(_ r: Node) { self.root = r - self.options = options } } diff --git a/Sources/_StringProcessing/Regex/Match.swift b/Sources/_StringProcessing/Regex/Match.swift index 8172e993b..e2c6e1a87 100644 --- a/Sources/_StringProcessing/Regex/Match.swift +++ b/Sources/_StringProcessing/Regex/Match.swift @@ -154,13 +154,17 @@ extension Regex { var low = inputRange.lowerBound let high = inputRange.upperBound - while low < high { + while true { if let m = try _match(input, in: low..= high { return nil } + if regex.initialOptions.semanticLevel == .graphemeCluster { + input.formIndex(after: &low) + } else { + input.unicodeScalars.formIndex(after: &low) + } } - return nil } } diff --git a/Tests/RegexTests/AlgorithmsInternalsTests.swift b/Tests/RegexTests/AlgorithmsInternalsTests.swift index f0d556744..31e082bce 100644 --- a/Tests/RegexTests/AlgorithmsInternalsTests.swift +++ b/Tests/RegexTests/AlgorithmsInternalsTests.swift @@ -24,7 +24,7 @@ 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 last = str._lastRange(of: r) let (expectFirst, expectLast) = ( str.index(atOffset: 0)..] = string[...].ranges(of: regex)[...].map(string.offsets(of:)) XCTAssertEqual(actualCol, expected, file: file, line: line) + let matchRanges = string.matches(of: regex).map { string.offsets(of: $0.range) } + XCTAssertEqual(matchRanges, expected, file: file, line: line) + let firstRange = string.firstRange(of: regex).map(string.offsets(of:)) XCTAssertEqual(firstRange, expected.first, file: file, line: line) } @@ -332,6 +335,11 @@ class AlgorithmTests: XCTestCase { XCTAssertEqual( s2.matches(of: regex).map(\.0), ["aa"]) + + XCTAssertEqual( + s2.matches(of: try Regex("a*?")).map { s2.offsets(of: $0.range) }, [0..<0, 1..<1, 2..<2]) + XCTAssertEqual( + s2.ranges(of: try Regex("a*?")).map(s2.offsets(of:)), [0..<0, 1..<1, 2..<2]) } func testSwitches() { diff --git a/Tests/RegexTests/CompileTests.swift b/Tests/RegexTests/CompileTests.swift index eab46dca0..9e94a886a 100644 --- a/Tests/RegexTests/CompileTests.swift +++ b/Tests/RegexTests/CompileTests.swift @@ -88,4 +88,51 @@ extension RegexTests { try testCompilationEquivalence(row) } } + + func testCompileInitialOptions() throws { + func expectInitialOptions( + _ regex: Regex, + _ optionSequence: AST.MatchingOptionSequence, + file: StaticString = #file, + line: UInt = #line + ) throws { + var options = MatchingOptions() + options.apply(optionSequence) + + XCTAssertTrue( + regex.program.loweredProgram.initialOptions._equal(to: options), + file: file, line: line) + } + + func expectInitialOptions( + _ pattern: String, + _ optionSequence: AST.MatchingOptionSequence, + file: StaticString = #file, + line: UInt = #line + ) throws { + let regex = try Regex(pattern) + try expectInitialOptions(regex, optionSequence, file: file, line: line) + } + + try expectInitialOptions(".", matchingOptions()) + try expectInitialOptions("(?i)(?-i).", matchingOptions()) + + try expectInitialOptions("(?i).", matchingOptions(adding: [.caseInsensitive])) + try expectInitialOptions("(?i).(?-i)", matchingOptions(adding: [.caseInsensitive])) + + try expectInitialOptions( + "(?im)(?s).", + matchingOptions(adding: [.caseInsensitive, .multiline, .singleLine])) + try expectInitialOptions(".", matchingOptions()) + try expectInitialOptions( + "(?im)(?s).(?u)", + matchingOptions(adding: [.caseInsensitive, .multiline, .singleLine])) + + try expectInitialOptions( + "(?i:.)", + matchingOptions(adding: [.caseInsensitive])) + try expectInitialOptions( + "(?i:.)(?m:.)", + matchingOptions(adding: [.caseInsensitive])) + } } diff --git a/Tests/RegexTests/MatchTests.swift b/Tests/RegexTests/MatchTests.swift index 89a645375..cb40562c7 100644 --- a/Tests/RegexTests/MatchTests.swift +++ b/Tests/RegexTests/MatchTests.swift @@ -1612,5 +1612,15 @@ extension RegexTests { // TODO: Add test for grapheme boundaries at start/end of match + func testCase() { + let regex = try! Regex(#".\N{SPARKLING HEART}."#) + let input = "🧟‍♀️💖🧠 or 🧠💖☕️" + let characterMatches = input.matches(of: regex) + XCTAssertEqual(characterMatches.map { $0.0 }, ["🧟‍♀️💖🧠", "🧠💖☕️"]) + + let scalarMatches = input.matches(of: regex.matchingSemantics(.unicodeScalar)) + let scalarExpected: [Substring] = ["\u{FE0F}💖🧠", "🧠💖☕"] + XCTAssertEqual(scalarMatches.map { $0.0 }, scalarExpected) + } }