Skip to content

Add @RegexComponentBuilder overloads for string processing algorithm. #334

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 2 commits into from
Apr 24, 2022
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
195 changes: 195 additions & 0 deletions Documentation/Evolution/StringProcessingAlgorithms.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,16 @@ extension BidirectionalCollection where SubSequence == Substring {
/// - Returns: `true` if the regex was found in the collection, otherwise
/// `false`.
public func contains(_ regex: some RegexComponent) -> Bool

/// Returns a Boolean value indicating whether the collection contains the
/// given regex.
/// - Parameter content: A closure to produce a `RegexComponent` to search for
/// within this collection.
/// - Returns: `true` if the regex was found in the collection, otherwise
/// `false`.
public func contains(
@RegexComponentBuilder _ content: () -> some RegexComponent
) -> Bool
}
```

Expand All @@ -258,6 +268,15 @@ 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: some RegexComponent) -> Bool

/// Returns a Boolean value indicating whether the initial elements of the
/// sequence are the same as the elements in the specified regex.
/// - Parameter content: A closure to produce a `RegexComponent` to compare.
/// - Returns: `true` if the initial elements of the sequence matches the
/// beginning of `regex`; otherwise, `false`.
public func starts(
@RegexComponentBuilder with content: () -> some RegexComponent
) -> Bool
}
```

Expand Down Expand Up @@ -324,6 +343,15 @@ extension BidirectionalCollection where SubSequence == Substring {
/// - Returns: A new subsequence containing the elements of the collection
/// that does not match `prefix` from the start.
public func trimmingPrefix(_ regex: some RegexComponent) -> SubSequence

/// Returns a new collection of the same type by removing `prefix` from the
/// start.
/// - Parameter _content A closure to produce a `RegexComponent` to be removed.
/// - Returns: A collection containing the elements that does not match
/// `prefix` from the start.
public func trimmingPrefix(
@RegexComponentBuilder _ content: () -> some RegexComponent
) -> SubSequence
}

extension RangeReplaceableCollection
Expand All @@ -332,6 +360,12 @@ extension RangeReplaceableCollection
/// Removes the initial elements that matches the given regex.
/// - Parameter regex: The regex to remove from this collection.
public mutating func trimPrefix(_ regex: some RegexComponent)

/// Removes the initial elements that matches the given regex.
/// - Parameter content: A closure to produce a `RegexComponent` to be removed.
public mutating func trimPrefix(
@RegexComponentBuilder _ content: () -> some RegexComponent
)
}
```

Expand Down Expand Up @@ -365,6 +399,16 @@ extension BidirectionalCollection where SubSequence == Substring {
/// - Returns: A range in the collection of the first occurrence of `regex`.
/// Returns `nil` if `regex` is not found.
public func firstRange(of regex: some RegexComponent) -> Range<Index>?

/// Finds and returns the range of the first occurrence of a given regex
/// within the collection.
/// - Parameter content: A closure to produce a `RegexComponent` to search for
/// within this collection.
/// - Returns: A range in the collection of the first occurrence of regex.
/// Returns `nil` if not found.
public func firstRange(
@RegexComponentBuilder of content: () -> some RegexComponent
) -> Range<Index>?
}
```

Expand All @@ -388,6 +432,16 @@ extension BidirectionalCollection where SubSequence == Substring {
/// - Returns: A collection or ranges in the receiver of all occurrences of
/// `regex`. Returns an empty collection if `regex` is not found.
public func ranges(of regex: some RegexComponent) -> some Collection<Range<Index>>

/// Finds and returns the ranges of the all occurrences of a given sequence
/// within the collection.
/// - Parameter content: A closure to produce a `RegexComponent` to search for
/// within this collection.
/// - Returns: A collection or ranges in the receiver of all occurrences of
/// regex. Returns an empty collection if not found.
public func ranges(
@RegexComponentBuilder of content: () -> some RegexComponent
) -> some Collection<Range<Index>>
}
```

Expand All @@ -401,15 +455,37 @@ extension BidirectionalCollection where SubSequence == Substring {
/// there isn't a match.
public func firstMatch<R: RegexComponent>(of regex: R) -> Regex<R.RegexOutput>.Match?

/// Returns the first match of the specified regex within the collection.
/// - Parameter content: A closure to produce a `RegexComponent` to search for.
/// - Returns: The first match of regex in the collection, or `nil` if
/// there isn't a match.
public func firstMatch<R: RegexComponent>(
@RegexComponentBuilder of content: () -> R
) -> Regex<R.RegexOutput>.Match?

/// Match a regex in its entirety.
/// - Parameter regex: The regex to match against.
/// - Returns: The match if there is one, or `nil` if none.
public func wholeMatch<R: RegexComponent>(of regex: R) -> Regex<R.RegexOutput>.Match?

/// Match a regex in its entirety.
/// - Parameter content: A closure to produce a `RegexComponent` to match against.
/// - Returns: The match if there is one, or `nil` if none.
public func wholeMatch<R: RegexComponent>(
@RegexComponentBuilder of content: () -> R
) -> Regex<R.RegexOutput>.Match?

/// Match part of the regex, starting at the beginning.
/// - Parameter regex: The regex to match against.
/// - Returns: The match if there is one, or `nil` if none.
public func prefixMatch<R: RegexComponent>(of regex: R) -> Regex<R.RegexOutput>.Match?

/// Match part of the regex, starting at the beginning.
/// - Parameter content: A closure to produce a `RegexComponent` to match against.
/// - Returns: The match if there is one, or `nil` if none.
public func prefixMatch<R: RegexComponent>(
@RegexComponentBuilder of content: () -> R
) -> Regex<R.RegexOutput>.Match?
}
```

Expand All @@ -421,6 +497,13 @@ extension BidirectionalCollection where SubSequence == Substring {
/// - Parameter regex: The regex to search for.
/// - Returns: A collection of matches of `regex`.
public func matches<R: RegexComponent>(of regex: R) -> some Collection<Regex<R.RegexOuput>.Match>

/// Returns a collection containing all matches of the specified regex.
/// - Parameter content: A closure to produce a `RegexComponent` to search for.
/// - Returns: A collection of matches of `regex`.
public func matches<R: RegexComponent>(
@RegexComponentBuilder of content: () -> R
) -> some Collection<Regex<R.RegexOutput>.Match>
}
```

Expand Down Expand Up @@ -490,6 +573,23 @@ extension RangeReplaceableCollection where SubSequence == Substring {
subrange: Range<Index>,
maxReplacements: Int = .max
) -> Self where Replacement.Element == Element

/// Returns a new collection in which all occurrences of a sequence matching
/// the given regex are replaced by another collection.
/// - Parameters:
/// - replacement: The new elements to add to the collection.
/// - subrange: The range in the collection in which to search for `regex`.
/// - maxReplacements: A number specifying how many occurrences of the
/// sequence matching `regex` to replace. Default is `Int.max`.
/// - content: A closure to produce a `RegexComponent` to replace.
/// - Returns: A new collection in which all occurrences of subsequence
/// matching `regex` in `subrange` are replaced by `replacement`.
public func replacing<Replacement: Collection>(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not quite sure about these replace ones. With the simplest one that has default value for subrange and maxReplacements, you'd do

let _ = str.replacing(with: "xxx") {
    OneOrMore("a")
    "+"
    OneOrMore("a")
}

But this reads backwards to me and doesn't translate great.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. But seems pretty reasonable given the syntactic benefit it offers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok I'll leave them here for practicality

with replacement: Replacement,
subrange: Range<Index>,
maxReplacements: Int = .max,
@RegexComponentBuilder content: () -> some RegexComponent
) -> Self where Replacement.Element == Element

/// Returns a new collection in which all occurrences of a sequence matching
/// the given regex are replaced by another collection.
Expand All @@ -506,6 +606,21 @@ extension RangeReplaceableCollection where SubSequence == Substring {
maxReplacements: Int = .max
) -> Self where Replacement.Element == Element

/// Returns a new collection in which all occurrences of a sequence matching
/// the given regex are replaced by another collection.
/// - Parameters:
/// - replacement: The new elements to add to the collection.
/// - maxReplacements: A number specifying how many occurrences of the
/// sequence matching `regex` to replace. Default is `Int.max`.
/// - content: A closure to produce a `RegexComponent` to replace.
/// - Returns: A new collection in which all occurrences of subsequence
/// matching `regex` are replaced by `replacement`.
public func replacing<Replacement: Collection>(
with replacement: Replacement,
maxReplacements: Int = .max,
@RegexComponentBuilder content: () -> some RegexComponent
) -> Self where Replacement.Element == Element

/// Replaces all occurrences of the sequence matching the given regex with
/// a given collection.
/// - Parameters:
Expand All @@ -518,6 +633,19 @@ extension RangeReplaceableCollection where SubSequence == Substring {
with replacement: Replacement,
maxReplacements: Int = .max
) where Replacement.Element == Element

/// Replaces all occurrences of the sequence matching the given regex with
/// a given collection.
/// - Parameters:
/// - replacement: The new elements to add to the collection.
/// - maxReplacements: A number specifying how many occurrences of the
/// sequence matching `regex` to replace. Default is `Int.max`.
/// - content: A closure to produce a `RegexComponent` to replace.
public mutating func replace<Replacement: Collection>(
with replacement: Replacement,
maxReplacements: Int = .max,
@RegexComponentBuilder content: () -> some RegexComponent
) -> Self where Replacement.Element == Element

/// Returns a new collection in which all occurrences of a sequence matching
/// the given regex are replaced by another regex match.
Expand All @@ -536,6 +664,24 @@ extension RangeReplaceableCollection where SubSequence == Substring {
maxReplacements: Int = .max,
with replacement: (Regex<R.RegexOutput>.Match) throws -> Replacement
) rethrows -> Self where Replacement.Element == Element

/// Returns a new collection in which all occurrences of a sequence matching
/// the given regex are replaced by another regex match.
/// - Parameters:
/// - subrange: The range in the collection in which to search for `regex`.
/// - maxReplacements: A number specifying how many occurrences of the
/// sequence matching `regex` to replace. Default is `Int.max`.
/// - content: A closure to produce a `RegexComponent` to replace.
/// - replacement: A closure that receives the full match information,
/// including captures, and returns a replacement collection.
/// - Returns: A new collection in which all occurrences of subsequence
/// matching `regex` are replaced by `replacement`.
public func replacing<R: RegexComponent, Replacement: Collection>(
subrange: Range<Index>,
maxReplacements: Int = .max,
@RegexComponentBuilder content: () -> R,
with replacement: (Regex<R.RegexOutput>.Match) throws -> Replacement
) rethrows -> Self where Replacement.Element == Element

/// Returns a new collection in which all occurrences of a sequence matching
/// the given regex are replaced by another collection.
Expand All @@ -552,6 +698,22 @@ extension RangeReplaceableCollection where SubSequence == Substring {
maxReplacements: Int = .max,
with replacement: (Regex<R.RegexOuput>.Match) throws -> Replacement
) rethrows -> Self where Replacement.Element == Element

/// Returns a new collection in which all occurrences of a sequence matching
/// the given regex are replaced by another collection.
/// - Parameters:
/// - maxReplacements: A number specifying how many occurrences of the
/// sequence matching `regex` to replace. Default is `Int.max`.
/// - content: A closure to produce a `RegexComponent` to replace.
/// - replacement: A closure that receives the full match information,
/// including captures, and returns a replacement collection.
/// - Returns: A new collection in which all occurrences of subsequence
/// matching `regex` are replaced by `replacement`.
public func replacing<R: RegexComponent, Replacement: Collection>(
maxReplacements: Int = .max,
@RegexComponentBuilder content: () -> R,
with replacement: (Regex<R.RegexOutput>.Match) throws -> Replacement
) rethrows -> Self where Replacement.Element == Element

/// Replaces all occurrences of the sequence matching the given regex with
/// a given collection.
Expand All @@ -566,6 +728,20 @@ extension RangeReplaceableCollection where SubSequence == Substring {
maxReplacements: Int = .max,
with replacement: (Regex<R.RegexOutput>.Match) throws -> Replacement
) rethrows where Replacement.Element == Element

/// Replaces all occurrences of the sequence matching the given regex with
/// a given collection.
/// - Parameters:
/// - maxReplacements: A number specifying how many occurrences of the
/// sequence matching `regex` to replace. Default is `Int.max`.
/// - content: A closure to produce a `RegexComponent` to replace.
/// - replacement: A closure that receives the full match information,
/// including captures, and returns a replacement collection.
public mutating func replace<R: RegexComponent, Replacement: Collection>(
maxReplacements: Int = .max,
@RegexComponentBuilder content: () -> R,
with replacement: (Regex<R.RegexOutput>.Match) throws -> Replacement
) rethrows where Replacement.Element == Element
}
```

Expand Down Expand Up @@ -613,6 +789,25 @@ extension BidirectionalCollection where SubSequence == Substring {
maxSplits: Int = Int.max,
omittingEmptySubsequences: Bool = true
) -> some Collection<Substring>

/// Returns the longest possible subsequences of the collection, in order,
/// around subsequence that match the given separator regex.
///
/// - Parameters:
/// - maxSplits: The maximum number of times to split the collection,
/// or one less than the number of subsequences to return.
/// - omittingEmptySubsequences: If `false`, an empty subsequence is
/// returned in the result for each consecutive pair of matches
/// and for each match at the start or end of the collection. If
/// `true`, only nonempty subsequences are returned.
/// - separator: A closure to produce a `RegexComponent` to be split upon.
/// - Returns: A collection of substrings, split from this collection's
/// elements.
public func split(
maxSplits: Int = Int.max,
omittingEmptySubsequences: Bool = true,
@RegexComponentBuilder separator: () -> some RegexComponent
) -> some Collection<Substring>
}
```

Expand Down