Skip to content
Merged
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
51 changes: 51 additions & 0 deletions proposals/0351-regex-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [`RegexComponent` protocol](#regexcomponent-protocol)
- [Concatenation](#concatenation)
- [Capture](#capture)
- [Mapping Output](#mapping-output)
- [Reference](#reference)
- [Alternation](#alternation)
- [Repetition](#repetition)
Expand Down Expand Up @@ -557,6 +558,56 @@ extension TryCapture {

</details>

### Mapping Output

In addition to transforming individual captures within a regex, you can also map the output of an entire regex to a different output type. You can use the `mapOutput(_:)` methods to reorder captures, flatten nested optionals, or create instances of a custom type.

This example shows how you can transform the output of a regex with three capture groups into an instance of a custom `SemanticVersion` type, matching strings such as `"1.0.0"` or `"1.0"`:

```swift
struct SemanticVersion: Equatable {
var major, minor, patch: Int
}

let semverRegex = Regex {
TryCapture(OneOrMore(.digit)) { Int($0) }
"."
TryCapture(OneOrMore(.digit)) { Int($0) }
Optionally {
"."
TryCapture(OneOrMore(.digit)) { Int($0) }
}
}.map { _, c1, c2, c3 in
SemanticVersion(major: c1, minor: c2, patch: c3 ?? 0)
}

let semver1 = "1.11.4".firstMatch(of: semverRegex)?.output
// semver1 == SemanticVersion(major: 1, minor: 11, patch: 4)
let semver2 = "0.6".firstMatch(of: semverRegex)?.output
// semver2 == SemanticVersion(major: 0, minor: 6, patch: 0)
```

<details>
<summary>API definition</summary>

Note: This extension is defined in the standard library, not the `RegexBuilder` module.

```swift
extension Regex {
/// Returns a regex that transforms its matches using the given closure.
///
/// When you call `mapOutput(_:)` on a regex, you change the type of
/// output available on each match result. The `body` closure is called
/// when each match is found to transform the result of the match.
///
/// - Parameter body: A closure for transforming the output of this
/// regex.
/// - Returns: A regex that has `NewOutput` as its output type.
func mapOutput<NewOutput>(_ body: @escaping (Output) -> NewOutput) -> Regex<NewOutput>
}
```
</details>

### Reference

Reference is a feature that can be used to achieve named captures and named backreferences from textual regexes. Simply state what type the reference will hold on to and you can use it later once you've matched a string to get back a specific capture. Note the type you pass to reference will be whatever the result of a capture's transform is. A capture with no transform always has a reference type of `Substring`.
Expand Down