Skip to content

Nominalize API names #271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions Documentation/Evolution/RegexTypeOverview.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# Regex Type and Overview

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

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.

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").
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").

`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:

Expand Down Expand Up @@ -278,14 +277,14 @@ func processEntry(_ line: String) -> Transaction? {
*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.


### Algorithms, algorithms everywhere
### Regex-powered algorithms

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.

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


### Onward Unicode
### Unicode handling

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).

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

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

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

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

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

/// The result of matching a regex against a string.
///
Expand All @@ -344,19 +343,19 @@ public struct Regex<Output> {
@dynamicMemberLookup
public struct Match {
/// The range of the overall match
public let range: Range<String.Index>
public var range: Range<String.Index> { get }

/// The produced output from the match operation
public var output: Output
public var output: Output { get }

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

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

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

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


### `Regex<Match, Captures>` instead of `Regex<Output>`

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.
Expand Down
4 changes: 2 additions & 2 deletions Sources/Exercises/Participants/RegexParticipant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private func graphemeBreakPropertyData<RP: RegexComponent>(
forLine line: String,
using regex: RP
) -> GraphemeBreakEntry? where RP.Output == (Substring, Substring, Substring?, Substring) {
line.matchWhole(regex).map(\.output).flatMap(extractFromCaptures)
line.wholeMatch(of: regex).map(\.output).flatMap(extractFromCaptures)
}

private func graphemeBreakPropertyDataLiteral(
Expand All @@ -80,7 +80,7 @@ private func graphemeBreakPropertyDataLiteral(
private func graphemeBreakPropertyData(
forLine line: String
) -> GraphemeBreakEntry? {
line.matchWhole {
line.wholeMatch {
TryCapture(OneOrMore(.hexDigit)) { Unicode.Scalar(hex: $0) }
Optionally {
".."
Expand Down
24 changes: 12 additions & 12 deletions Sources/RegexBuilder/Match.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,29 @@
import _StringProcessing

extension String {
public func matchWhole<R: RegexComponent>(
@RegexComponentBuilder _ content: () -> R
public func wholeMatch<R: RegexComponent>(
@RegexComponentBuilder of content: () -> R
) -> Regex<R.Output>.Match? {
matchWhole(content())
wholeMatch(of: content())
}

public func matchPrefix<R: RegexComponent>(
@RegexComponentBuilder _ content: () -> R
public func prefixMatch<R: RegexComponent>(
@RegexComponentBuilder of content: () -> R
) -> Regex<R.Output>.Match? {
matchPrefix(content())
prefixMatch(of: content())
}
}

extension Substring {
public func matchWhole<R: RegexComponent>(
@RegexComponentBuilder _ content: () -> R
public func wholeMatch<R: RegexComponent>(
@RegexComponentBuilder of content: () -> R
) -> Regex<R.Output>.Match? {
matchWhole(content())
wholeMatch(of: content())
}

public func matchPrefix<R: RegexComponent>(
@RegexComponentBuilder _ content: () -> R
public func prefixMatch<R: RegexComponent>(
@RegexComponentBuilder of content: () -> R
) -> Regex<R.Output>.Match? {
matchPrefix(content())
prefixMatch(of: content())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//
//===----------------------------------------------------------------------===//

// FIXME: What even is this? Can we delete this whole thing?
struct RegexConsumer<
R: RegexComponent, Consumed: BidirectionalCollection
> where Consumed.SubSequence == Substring {
Expand All @@ -24,7 +25,7 @@ extension RegexConsumer {
func _matchingConsuming(
_ consumed: Substring, in range: Range<String.Index>
) -> (upperBound: String.Index, match: Match)? {
guard let result = try! regex._match(
guard let result = try! regex.regex._match(
consumed.base,
in: range, mode: .partialFromFront
) else { return nil }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ extension BidirectionalCollection where SubSequence == Substring {
/// - Returns: The first match of `regex` in the collection, or `nil` if
/// there isn't a match.
public func firstMatch<R: RegexComponent>(
of regex: R
of r: R
) -> Regex<R.Output>.Match? {
let slice = self[...]
return try? regex.firstMatch(in: slice.base)
return try? r.regex.firstMatch(in: slice.base)
}
}
7 changes: 5 additions & 2 deletions Sources/_StringProcessing/Algorithms/Matching/Matches.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,17 @@ extension BidirectionalCollection where SubSequence == Substring {

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

var result = [Regex<R.Output>.Match]()
while start < end {
guard let match = try? regex._firstMatch(slice.base, in: start..<end) else {
guard let match = try? regex._firstMatch(
slice.base, in: start..<end
) else {
break
}
result.append(match)
Expand Down
36 changes: 22 additions & 14 deletions Sources/_StringProcessing/Regex/Match.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,18 @@ extension Regex.Match {
}
}

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

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

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

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

Expand Down Expand Up @@ -153,18 +153,26 @@ extension RegexComponent {
}

extension String {
public func matchWhole<R: RegexComponent>(_ regex: R) -> Regex<R.Output>.Match? {
try? regex.matchWhole(self)
public func wholeMatch<R: RegexComponent>(
of r: R
) -> Regex<R.Output>.Match? {
try? r.regex.wholeMatch(in: self)
}
public func matchPrefix<R: RegexComponent>(_ regex: R) -> Regex<R.Output>.Match? {
try? regex.matchPrefix(self)
public func prefixMatch<R: RegexComponent>(
of r: R
) -> Regex<R.Output>.Match? {
try? r.regex.prefixMatch(in: self)
}
}
extension Substring {
public func matchWhole<R: RegexComponent>(_ regex: R) -> Regex<R.Output>.Match? {
try? regex.matchWhole(self)
}
public func matchPrefix<R: RegexComponent>(_ regex: R) -> Regex<R.Output>.Match? {
try? regex.matchPrefix(self)
public func wholeMatch<R: RegexComponent>(
of r: R
) -> Regex<R.Output>.Match? {
try? r.regex.wholeMatch(in: self)
}
public func prefixMatch<R: RegexComponent>(
of r: R
) -> Regex<R.Output>.Match? {
try? r.regex.prefixMatch(in: self)
}
}
10 changes: 5 additions & 5 deletions Tests/RegexBuilderTests/CustomTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func customTest<Match: Equatable>(
let result: Match?
switch call {
case .match:
result = input.matchWhole(regex)?.output
result = input.wholeMatch(of: regex)?.output
case .firstMatch:
result = input.firstMatch(of: regex)?.output
}
Expand Down Expand Up @@ -167,7 +167,7 @@ class CustomRegexComponentTests: XCTestCase {
// TODO: Why is Radix optional?

do {
guard let m = try hexRegex.matchWhole("123aef.345") else {
guard let m = try hexRegex.wholeMatch(in: "123aef.345") else {
XCTFail()
return
}
Expand All @@ -180,7 +180,7 @@ class CustomRegexComponentTests: XCTestCase {
}

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

do {
guard let m = try addressRegex.matchWhole("0x1234567f") else {
guard let m = try addressRegex.wholeMatch(in: "0x1234567f") else {
XCTFail()
return
}
Expand All @@ -213,7 +213,7 @@ class CustomRegexComponentTests: XCTestCase {
}

do {
_ = try addressRegex.matchWhole("0xdeadbeef")
_ = try addressRegex.wholeMatch(in: "0xdeadbeef")
XCTFail()
} catch let e as Poison {
XCTAssertEqual(e, Poison())
Expand Down
2 changes: 1 addition & 1 deletion Tests/RegexBuilderTests/MotivationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ private func process(
_ line: String,
using regex: Regex<(Substring, Substring, Substring, Substring, Substring)>
) -> Transaction? {
guard let output = try? regex.matchWhole(line),
guard let output = try? regex.wholeMatch(in: line),
let kind = Transaction.Kind(output.1)
else {
return nil
Expand Down
Loading