Skip to content

Commit 65ef2ae

Browse files
itingliumilseman
authored andcommitted
Expose matches, ranges and split (#304)
* Expose `matches`, `ranges` and `split` Publicize these API per the String Processing Algorithms proposal. The proposed ones return generic `Collection`, empowered by SE-0346. For now we'll wrap the results with a concrete `Array` until the language feature is ready. Co-authored-by: Michael Ilseman <michael.ilseman@gmail.com>
1 parent 115a937 commit 65ef2ae

File tree

6 files changed

+69
-30
lines changed

6 files changed

+69
-30
lines changed

Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift

+27-2
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,24 @@ extension BidirectionalCollection {
175175
// MARK: Fixed pattern algorithms
176176

177177
extension Collection where Element: Equatable {
178-
// FIXME: Replace `RangesCollection` when SE-0346 is enabled
179178
func ranges<S: Sequence>(
180179
of other: S
181180
) -> RangesCollection<ZSearcher<Self>> where S.Element == Element {
182181
ranges(of: ZSearcher(pattern: Array(other), by: ==))
183182
}
183+
184+
// FIXME: Return `some Collection<Range<Index>>` for SE-0346
185+
/// Finds and returns the ranges of the all occurrences of a given sequence
186+
/// within the collection.
187+
/// - Parameter other: The sequence to search for.
188+
/// - Returns: A collection of ranges of all occurrences of `other`. Returns
189+
/// an empty collection if `other` is not found.
190+
@available(SwiftStdlib 5.7, *)
191+
public func ranges<S: Sequence>(
192+
of other: S
193+
) -> [Range<Index>] where S.Element == Element {
194+
ranges(of: ZSearcher(pattern: Array(other), by: ==)).map { $0 }
195+
}
184196
}
185197

186198
extension BidirectionalCollection where Element: Equatable {
@@ -217,8 +229,8 @@ extension BidirectionalCollection where Element: Comparable {
217229
// MARK: Regex algorithms
218230

219231
extension BidirectionalCollection where SubSequence == Substring {
220-
// FIXME: Replace `RangesCollection` when SE-0346 is enabled
221232
@available(SwiftStdlib 5.7, *)
233+
@_disfavoredOverload
222234
func ranges<R: RegexComponent>(
223235
of regex: R
224236
) -> RangesCollection<RegexConsumer<R, Self>> {
@@ -231,4 +243,17 @@ extension BidirectionalCollection where SubSequence == Substring {
231243
) -> ReversedRangesCollection<RegexConsumer<R, Self>> {
232244
rangesFromBack(of: RegexConsumer(regex))
233245
}
246+
247+
// FIXME: Return `some Collection<Range<Index>>` for SE-0346
248+
/// Finds and returns the ranges of the all occurrences of a given sequence
249+
/// within the collection.
250+
/// - Parameter regex: The regex to search for.
251+
/// - Returns: A collection or ranges in the receiver of all occurrences of
252+
/// `regex`. Returns an empty collection if `regex` is not found.
253+
@available(SwiftStdlib 5.7, *)
254+
public func ranges<R: RegexComponent>(
255+
of regex: R
256+
) -> [Range<Index>] {
257+
Array(ranges(of: RegexConsumer(regex)))
258+
}
234259
}

Sources/_StringProcessing/Algorithms/Algorithms/Split.swift

+25-10
Original file line numberDiff line numberDiff line change
@@ -233,16 +233,24 @@ extension BidirectionalCollection where Element: Equatable {
233233
// MARK: Fixed pattern algorithms
234234

235235
extension Collection where Element: Equatable {
236-
// FIXME: Replace `SplitCollection` when SE-0346 is enabled
236+
@_disfavoredOverload
237+
func split<S: Sequence>(
238+
by separator: S
239+
) -> SplitCollection<ZSearcher<Self>> where S.Element == Element {
240+
split(by: ZSearcher(pattern: Array(separator), by: ==))
241+
}
242+
243+
// FIXME: Return `some Collection<SubSequence>` for SE-0346
237244
/// Returns the longest possible subsequences of the collection, in order,
238245
/// around elements equal to the given separator.
239246
/// - Parameter separator: The element to be split upon.
240247
/// - Returns: A collection of subsequences, split from this collection's
241248
/// elements.
242-
func split<S: Sequence>(
249+
@available(SwiftStdlib 5.7, *)
250+
public func split<S: Sequence>(
243251
by separator: S
244-
) -> SplitCollection<ZSearcher<Self>> where S.Element == Element {
245-
split(by: ZSearcher(pattern: Array(separator), by: ==))
252+
) -> [SubSequence] where S.Element == Element {
253+
Array(split(by: ZSearcher(pattern: Array(separator), by: ==)))
246254
}
247255
}
248256

@@ -282,12 +290,7 @@ extension BidirectionalCollection where Element: Comparable {
282290

283291
@available(SwiftStdlib 5.7, *)
284292
extension BidirectionalCollection where SubSequence == Substring {
285-
// FIXME: Replace `SplitCollection` when SE-0346 is enabled
286-
/// Returns the longest possible subsequences of the collection, in order,
287-
/// around elements equal to the given separator.
288-
/// - Parameter separator: A regex describing elements to be split upon.
289-
/// - Returns: A collection of substrings, split from this collection's
290-
/// elements.
293+
@_disfavoredOverload
291294
func split<R: RegexComponent>(
292295
by separator: R
293296
) -> SplitCollection<RegexConsumer<R, Self>> {
@@ -299,4 +302,16 @@ extension BidirectionalCollection where SubSequence == Substring {
299302
) -> ReversedSplitCollection<RegexConsumer<R, Self>> {
300303
splitFromBack(by: RegexConsumer(separator))
301304
}
305+
306+
// FIXME: Return `some Collection<Substring>` for SE-0346
307+
/// Returns the longest possible subsequences of the collection, in order,
308+
/// around elements equal to the given separator.
309+
/// - Parameter separator: A regex describing elements to be split upon.
310+
/// - Returns: A collection of substrings, split from this collection's
311+
/// elements.
312+
public func split<R: RegexComponent>(
313+
by separator: R
314+
) -> [SubSequence] {
315+
Array(split(by: RegexConsumer(separator)))
316+
}
302317
}

Sources/_StringProcessing/Algorithms/Matching/MatchReplace.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ extension RangeReplaceableCollection where SubSequence == Substring {
139139
var result = Self()
140140
result.append(contentsOf: self[..<index])
141141

142-
for match in self[subrange]._matches(of: regex)
142+
for match in self[subrange].matches(of: regex)
143143
.prefix(maxReplacements)
144144
{
145145
result.append(contentsOf: self[index..<match.range.lowerBound])

Sources/_StringProcessing/Algorithms/Matching/Matches.swift

+8-7
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,8 @@ extension BidirectionalCollection {
184184
// MARK: Regex algorithms
185185

186186
extension BidirectionalCollection where SubSequence == Substring {
187-
// FIXME: Replace `MatchesCollection` when SE-0346 is enabled
188-
/// Returns a collection containing all matches of the specified regex.
189-
/// - Parameter regex: The regex to search for.
190-
/// - Returns: A collection of matches of `regex`.
191187
@available(SwiftStdlib 5.7, *)
188+
@_disfavoredOverload
192189
func matches<R: RegexComponent>(
193190
of regex: R
194191
) -> MatchesCollection<RegexConsumer<R, Self>> {
@@ -202,10 +199,14 @@ extension BidirectionalCollection where SubSequence == Substring {
202199
matchesFromBack(of: RegexConsumer(regex))
203200
}
204201

205-
// FIXME: Replace the returned value as `some Collection<Regex<R.Output>.Match>
206-
// when SE-0346 is enabled
202+
// FIXME: Return `some Collection<Regex<R.Output>.Match> for SE-0346
203+
/// Returns a collection containing all matches of the specified regex.
204+
/// - Parameter regex: The regex to search for.
205+
/// - Returns: A collection of matches of `regex`.
207206
@available(SwiftStdlib 5.7, *)
208-
func _matches<R: RegexComponent>(of r: R) -> [Regex<R.RegexOutput>.Match] {
207+
public func matches<R: RegexComponent>(
208+
of r: R
209+
) -> [Regex<R.RegexOutput>.Match] {
209210
let slice = self[...]
210211
var start = self.startIndex
211212
let end = self.endIndex

Tests/RegexBuilderTests/AlgorithmsTests.swift

+5-7
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,11 @@ import _StringProcessing
1515

1616
@available(SwiftStdlib 5.7, *)
1717
class RegexConsumerTests: XCTestCase {
18-
// FIXME: enable this test when we update the return type of `matches(of:)`
19-
// when SE-0346 is available
20-
// func testMatches() {
21-
// let regex = Capture(OneOrMore(.digit)) { 2 * Int($0)! }
22-
// let str = "foo 160 bar 99 baz"
23-
// XCTAssertEqual(str.matches(of: regex).map(\.result.1), [320, 198])
24-
// }
18+
func testMatches() {
19+
let regex = Capture(OneOrMore(.digit)) { 2 * Int($0)! }
20+
let str = "foo 160 bar 99 baz"
21+
XCTAssertEqual(str.matches(of: regex).map(\.output.1), [320, 198])
22+
}
2523

2624
func testMatchReplace() {
2725
func replaceTest<R: RegexComponent>(

Tests/RegexTests/AlgorithmsTests.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,13 @@ class RegexConsumerTests: XCTestCase {
163163
XCTAssertEqual(s2.replacing(regex, with: ""), "")
164164

165165
XCTAssertEqual(
166-
s._matches(of: regex).map(\.0),
166+
s.matches(of: regex).map(\.0),
167167
["aaa", "aaaaaa", "aaaaaaaaaa"])
168168
XCTAssertEqual(
169-
s1._matches(of: regex).map(\.0),
169+
s1.matches(of: regex).map(\.0),
170170
["aaaaaa", "aaaaaaaaaa"])
171171
XCTAssertEqual(
172-
s2._matches(of: regex).map(\.0),
172+
s2.matches(of: regex).map(\.0),
173173
["aa"])
174174
}
175175
}

0 commit comments

Comments
 (0)