Skip to content

Commit ce458eb

Browse files
authored
Nominalize API names (#271)
Go from matchWhole -> wholeMatch(in:), which is more consistent with firstMatch etc.
1 parent 19adde4 commit ce458eb

File tree

10 files changed

+94
-82
lines changed

10 files changed

+94
-82
lines changed

Documentation/Evolution/RegexTypeOverview.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
# Regex Type and Overview
32

43
- Authors: [Michael Ilseman](https://github.com/milseman)
@@ -225,7 +224,7 @@ func processEntry(_ line: String) -> Transaction? {
225224

226225
The result builder allows for inline failable value construction, which participates in the overall string processing algorithm: returning `nil` signals a local failure and the engine backtracks to try an alternative. This not only relieves the use site from post-processing, it enables new kinds of processing algorithms, allows for search-space pruning, and enhances debuggability.
227226

228-
Swift regexes describe an unambiguous algorithm, were choice is ordered and effects can be reliably observed. For example, a `print()` statement inside the `TryCapture`'s transform function will run whenever the overall algorithm naturally dictates an attempt should be made. Optimizations can only elide such calls if they can prove it is behavior-preserving (e.g. "pure").
227+
Swift regexes describe an unambiguous algorithm, where choice is ordered and effects can be reliably observed. For example, a `print()` statement inside the `TryCapture`'s transform function will run whenever the overall algorithm naturally dictates an attempt should be made. Optimizations can only elide such calls if they can prove it is behavior-preserving (e.g. "pure").
229228

230229
`CustomMatchingRegexComponent`, discussed in [String Processing Algorithms][pitches], allows industrial-strength parsers to be used a regex components. This allows us to drop the overly-permissive pre-parsing step:
231230

@@ -278,14 +277,14 @@ func processEntry(_ line: String) -> Transaction? {
278277
*Note*: Details on how references work is discussed in [Regex Builders][pitches]. `Regex.Match` supports referring to _all_ captures by position (`match.1`, etc.) whether named or referenced or neither. Due to compiler limitations, result builders do not support forming labeled tuples for named captures.
279278

280279

281-
### Algorithms, algorithms everywhere
280+
### Regex-powered algorithms
282281

283282
Regexes can be used right out of the box with a variety of powerful and convenient algorithms, including trimming, splitting, and finding/replacing all matches within a string.
284283

285284
These algorithms are discussed in [String Processing Algorithms][pitches].
286285

287286

288-
### Onward Unicode
287+
### Unicode handling
289288

290289
A regex describes an algorithm to be ran over some model of string, and Swift's `String` has a rather unique Unicode-forward model. `Character` is an [extended grapheme cluster](https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) and equality is determined under [canonical equivalence](https://www.unicode.org/reports/tr15/#Canon_Compat_Equivalence).
291290

@@ -310,12 +309,12 @@ public struct Regex<Output> {
310309
/// Match a string in its entirety.
311310
///
312311
/// Returns `nil` if no match and throws on abort
313-
public func matchWhole(_ s: String) throws -> Regex<Output>.Match?
312+
public func wholeMatch(in s: String) throws -> Regex<Output>.Match?
314313

315314
/// Match part of the string, starting at the beginning.
316315
///
317316
/// Returns `nil` if no match and throws on abort
318-
public func matchPrefix(_ s: String) throws -> Regex<Output>.Match?
317+
public func prefixMatch(in s: String) throws -> Regex<Output>.Match?
319318

320319
/// Find the first match in a string
321320
///
@@ -325,17 +324,17 @@ public struct Regex<Output> {
325324
/// Match a substring in its entirety.
326325
///
327326
/// Returns `nil` if no match and throws on abort
328-
public func matchWhole(_ s: Substring) throws -> Regex<Output>.Match?
327+
public func wholeMatch(in s: Substring) throws -> Regex<Output>.Match?
329328

330329
/// Match part of the string, starting at the beginning.
331330
///
332331
/// Returns `nil` if no match and throws on abort
333-
public func matchPrefix(_ s: Substring) throws -> Regex<Output>.Match?
332+
public func prefixMatch(in s: Substring) throws -> Regex<Output>.Match?
334333

335334
/// Find the first match in a substring
336335
///
337336
/// Returns `nil` if no match is found and throws on abort
338-
public func firstMatch(_ s: Substring) throws -> Regex<Output>.Match?
337+
public func firstMatch(in s: Substring) throws -> Regex<Output>.Match?
339338

340339
/// The result of matching a regex against a string.
341340
///
@@ -344,19 +343,19 @@ public struct Regex<Output> {
344343
@dynamicMemberLookup
345344
public struct Match {
346345
/// The range of the overall match
347-
public let range: Range<String.Index>
346+
public var range: Range<String.Index> { get }
348347

349348
/// The produced output from the match operation
350-
public var output: Output
349+
public var output: Output { get }
351350

352351
/// Lookup a capture by name or number
353-
public subscript<T>(dynamicMember keyPath: KeyPath<Output, T>) -> T
352+
public subscript<T>(dynamicMember keyPath: KeyPath<Output, T>) -> T { get }
354353

355354
/// Lookup a capture by number
356355
@_disfavoredOverload
357356
public subscript(
358357
dynamicMember keyPath: KeyPath<(Output, _doNotUse: ()), Output>
359-
) -> Output
358+
) -> Output { get }
360359
// Note: this allows `.0` when `Match` is not a tuple.
361360

362361
}
@@ -482,6 +481,7 @@ We're also looking for more community discussion on what the default type system
482481

483482
The actual `Match` struct just stores ranges: the `Substrings` are lazily created on demand. This avoids unnecessary ARC traffic and memory usage.
484483

484+
485485
### `Regex<Match, Captures>` instead of `Regex<Output>`
486486

487487
The generic parameter `Output` is proposed to contain both the whole match (the `.0` element if `Output` is a tuple) and captures. One alternative we have considered is separating `Output` into the entire match and the captures, i.e. `Regex<Match, Captures>`, and using `Void` for for `Captures` when there are no captures.

Sources/Exercises/Participants/RegexParticipant.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ private func graphemeBreakPropertyData<RP: RegexComponent>(
6363
forLine line: String,
6464
using regex: RP
6565
) -> GraphemeBreakEntry? where RP.Output == (Substring, Substring, Substring?, Substring) {
66-
line.matchWhole(regex).map(\.output).flatMap(extractFromCaptures)
66+
line.wholeMatch(of: regex).map(\.output).flatMap(extractFromCaptures)
6767
}
6868

6969
private func graphemeBreakPropertyDataLiteral(
@@ -80,7 +80,7 @@ private func graphemeBreakPropertyDataLiteral(
8080
private func graphemeBreakPropertyData(
8181
forLine line: String
8282
) -> GraphemeBreakEntry? {
83-
line.matchWhole {
83+
line.wholeMatch {
8484
TryCapture(OneOrMore(.hexDigit)) { Unicode.Scalar(hex: $0) }
8585
Optionally {
8686
".."

Sources/RegexBuilder/Match.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,29 @@
1212
import _StringProcessing
1313

1414
extension String {
15-
public func matchWhole<R: RegexComponent>(
16-
@RegexComponentBuilder _ content: () -> R
15+
public func wholeMatch<R: RegexComponent>(
16+
@RegexComponentBuilder of content: () -> R
1717
) -> Regex<R.Output>.Match? {
18-
matchWhole(content())
18+
wholeMatch(of: content())
1919
}
2020

21-
public func matchPrefix<R: RegexComponent>(
22-
@RegexComponentBuilder _ content: () -> R
21+
public func prefixMatch<R: RegexComponent>(
22+
@RegexComponentBuilder of content: () -> R
2323
) -> Regex<R.Output>.Match? {
24-
matchPrefix(content())
24+
prefixMatch(of: content())
2525
}
2626
}
2727

2828
extension Substring {
29-
public func matchWhole<R: RegexComponent>(
30-
@RegexComponentBuilder _ content: () -> R
29+
public func wholeMatch<R: RegexComponent>(
30+
@RegexComponentBuilder of content: () -> R
3131
) -> Regex<R.Output>.Match? {
32-
matchWhole(content())
32+
wholeMatch(of: content())
3333
}
3434

35-
public func matchPrefix<R: RegexComponent>(
36-
@RegexComponentBuilder _ content: () -> R
35+
public func prefixMatch<R: RegexComponent>(
36+
@RegexComponentBuilder of content: () -> R
3737
) -> Regex<R.Output>.Match? {
38-
matchPrefix(content())
38+
prefixMatch(of: content())
3939
}
4040
}

Sources/_StringProcessing/Algorithms/Consumers/RegexConsumer.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
//
1010
//===----------------------------------------------------------------------===//
1111

12+
// FIXME: What even is this? Can we delete this whole thing?
1213
struct RegexConsumer<
1314
R: RegexComponent, Consumed: BidirectionalCollection
1415
> where Consumed.SubSequence == Substring {
@@ -24,7 +25,7 @@ extension RegexConsumer {
2425
func _matchingConsuming(
2526
_ consumed: Substring, in range: Range<String.Index>
2627
) -> (upperBound: String.Index, match: Match)? {
27-
guard let result = try! regex._match(
28+
guard let result = try! regex.regex._match(
2829
consumed.base,
2930
in: range, mode: .partialFromFront
3031
) else { return nil }

Sources/_StringProcessing/Algorithms/Matching/FirstMatch.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ extension BidirectionalCollection where SubSequence == Substring {
5555
/// - Returns: The first match of `regex` in the collection, or `nil` if
5656
/// there isn't a match.
5757
public func firstMatch<R: RegexComponent>(
58-
of regex: R
58+
of r: R
5959
) -> Regex<R.Output>.Match? {
6060
let slice = self[...]
61-
return try? regex.firstMatch(in: slice.base)
61+
return try? r.regex.firstMatch(in: slice.base)
6262
}
6363
}

Sources/_StringProcessing/Algorithms/Matching/Matches.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,17 @@ extension BidirectionalCollection where SubSequence == Substring {
202202

203203
// FIXME: Replace the returned value as `some Collection<Regex<R.Output>.Match>
204204
// when SE-0346 is enabled
205-
func _matches<R: RegexComponent>(of regex: R) -> [Regex<R.Output>.Match] {
205+
func _matches<R: RegexComponent>(of r: R) -> [Regex<R.Output>.Match] {
206206
let slice = self[...]
207207
var start = self.startIndex
208208
let end = self.endIndex
209+
let regex = r.regex
209210

210211
var result = [Regex<R.Output>.Match]()
211212
while start < end {
212-
guard let match = try? regex._firstMatch(slice.base, in: start..<end) else {
213+
guard let match = try? regex._firstMatch(
214+
slice.base, in: start..<end
215+
) else {
213216
break
214217
}
215218
result.append(match)

Sources/_StringProcessing/Regex/Match.swift

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,18 @@ extension Regex.Match {
8181
}
8282
}
8383

84-
extension RegexComponent {
84+
extension Regex {
8585
/// Match a string in its entirety.
8686
///
8787
/// Returns `nil` if no match and throws on abort
88-
public func matchWhole(_ s: String) throws -> Regex<Output>.Match? {
88+
public func wholeMatch(in s: String) throws -> Regex<Output>.Match? {
8989
try _match(s, in: s.startIndex..<s.endIndex, mode: .wholeString)
9090
}
9191

9292
/// Match part of the string, starting at the beginning.
9393
///
9494
/// Returns `nil` if no match and throws on abort
95-
public func matchPrefix(_ s: String) throws -> Regex<Output>.Match? {
95+
public func prefixMatch(in s: String) throws -> Regex<Output>.Match? {
9696
try _match(s, in: s.startIndex..<s.endIndex, mode: .partialFromFront)
9797
}
9898

@@ -106,14 +106,14 @@ extension RegexComponent {
106106
/// Match a substring in its entirety.
107107
///
108108
/// Returns `nil` if no match and throws on abort
109-
public func matchWhole(_ s: Substring) throws -> Regex<Output>.Match? {
109+
public func wholeMatch(in s: Substring) throws -> Regex<Output>.Match? {
110110
try _match(s.base, in: s.startIndex..<s.endIndex, mode: .wholeString)
111111
}
112112

113113
/// Match part of the string, starting at the beginning.
114114
///
115115
/// Returns `nil` if no match and throws on abort
116-
public func matchPrefix(_ s: Substring) throws -> Regex<Output>.Match? {
116+
public func prefixMatch(in s: Substring) throws -> Regex<Output>.Match? {
117117
try _match(s.base, in: s.startIndex..<s.endIndex, mode: .partialFromFront)
118118
}
119119

@@ -153,18 +153,26 @@ extension RegexComponent {
153153
}
154154

155155
extension String {
156-
public func matchWhole<R: RegexComponent>(_ regex: R) -> Regex<R.Output>.Match? {
157-
try? regex.matchWhole(self)
156+
public func wholeMatch<R: RegexComponent>(
157+
of r: R
158+
) -> Regex<R.Output>.Match? {
159+
try? r.regex.wholeMatch(in: self)
158160
}
159-
public func matchPrefix<R: RegexComponent>(_ regex: R) -> Regex<R.Output>.Match? {
160-
try? regex.matchPrefix(self)
161+
public func prefixMatch<R: RegexComponent>(
162+
of r: R
163+
) -> Regex<R.Output>.Match? {
164+
try? r.regex.prefixMatch(in: self)
161165
}
162166
}
163167
extension Substring {
164-
public func matchWhole<R: RegexComponent>(_ regex: R) -> Regex<R.Output>.Match? {
165-
try? regex.matchWhole(self)
166-
}
167-
public func matchPrefix<R: RegexComponent>(_ regex: R) -> Regex<R.Output>.Match? {
168-
try? regex.matchPrefix(self)
168+
public func wholeMatch<R: RegexComponent>(
169+
of r: R
170+
) -> Regex<R.Output>.Match? {
171+
try? r.regex.wholeMatch(in: self)
172+
}
173+
public func prefixMatch<R: RegexComponent>(
174+
of r: R
175+
) -> Regex<R.Output>.Match? {
176+
try? r.regex.prefixMatch(in: self)
169177
}
170178
}

Tests/RegexBuilderTests/CustomTests.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func customTest<Match: Equatable>(
6262
let result: Match?
6363
switch call {
6464
case .match:
65-
result = input.matchWhole(regex)?.output
65+
result = input.wholeMatch(of: regex)?.output
6666
case .firstMatch:
6767
result = input.firstMatch(of: regex)?.output
6868
}
@@ -167,7 +167,7 @@ class CustomRegexComponentTests: XCTestCase {
167167
// TODO: Why is Radix optional?
168168

169169
do {
170-
guard let m = try hexRegex.matchWhole("123aef.345") else {
170+
guard let m = try hexRegex.wholeMatch(in: "123aef.345") else {
171171
XCTFail()
172172
return
173173
}
@@ -180,7 +180,7 @@ class CustomRegexComponentTests: XCTestCase {
180180
}
181181

182182
do {
183-
_ = try hexRegex.matchWhole("123aef❗️345")
183+
_ = try hexRegex.wholeMatch(in: "123aef❗️345")
184184
XCTFail()
185185
} catch let e as Abort {
186186
XCTAssertEqual(e, Abort())
@@ -202,7 +202,7 @@ class CustomRegexComponentTests: XCTestCase {
202202
}
203203

204204
do {
205-
guard let m = try addressRegex.matchWhole("0x1234567f") else {
205+
guard let m = try addressRegex.wholeMatch(in: "0x1234567f") else {
206206
XCTFail()
207207
return
208208
}
@@ -213,7 +213,7 @@ class CustomRegexComponentTests: XCTestCase {
213213
}
214214

215215
do {
216-
_ = try addressRegex.matchWhole("0xdeadbeef")
216+
_ = try addressRegex.wholeMatch(in: "0xdeadbeef")
217217
XCTFail()
218218
} catch let e as Poison {
219219
XCTAssertEqual(e, Poison())

Tests/RegexBuilderTests/MotivationTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ private func process(
199199
_ line: String,
200200
using regex: Regex<(Substring, Substring, Substring, Substring, Substring)>
201201
) -> Transaction? {
202-
guard let output = try? regex.matchWhole(line),
202+
guard let output = try? regex.wholeMatch(in: line),
203203
let kind = Transaction.Kind(output.1)
204204
else {
205205
return nil

0 commit comments

Comments
 (0)