Last active
December 18, 2022 05:25
-
-
Save erica/2763b4b3d77ad597a7dc566ec1bd1226 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
/// Provides NSRegularExpression pattern matching against strings | |
/// in `switch` expressions | |
/// | |
/// Regular expressions are expensive to construct. The built-in | |
/// class cache stores already-constructed pattern instances using | |
/// the pattern string (coerced to `NSString`) as its keys. Modify | |
/// matching options at the `match(_, options:)` call-site if needed. | |
/// | |
/// - Note: This type is implemented as a class as `NSCache` | |
/// is not compatible with Swift structures. Its keys, which | |
/// must also be `AnyObject`, are coerced from `String` to | |
/// `NSString`. | |
public class Regex { | |
/// Store (expensive) existing `Regex` instances | |
public static let regexCache = NSCache<NSString, Regex>() | |
public let regex: NSRegularExpression | |
public var options: NSRegularExpression.Options = [] | |
/// Initializes a `Regex` instance that defaults to | |
/// "no options". Update as needed for case or | |
/// diacritical insensitivity using publicly modifiable | |
/// `options` property. | |
/// | |
/// - parameter regexPattern: A regular expression pattern string | |
/// - parameter options: Regular expression matching options. (See `NSRegularExpression.Options`) | |
public init(_ regexPattern: String, options: NSRegularExpression.Options = []) { | |
self.options = options | |
// Fail loudly if valid regular expression cannot | |
// be constructed | |
self.regex = try! NSRegularExpression(pattern: regexPattern, options: options) | |
Regex.regexCache.setObject(self, forKey: regexPattern as NSString) | |
} | |
/// Create or retrieve a `Regex` instance from the the | |
/// class cache. The instance can than be used with `~=` to | |
/// match against a string. | |
/// | |
/// - parameter pattern: A regular expression pattern string. | |
/// - parameter options: Regular expression matching options. (See `NSRegularExpression.Options`). Defaults to `[]`. | |
/// | |
/// - Note: Existing options are re-used unless they are | |
/// overwritten by a non-empty set. To update options | |
/// to `[]`, access them directly from the cached version | |
/// outside the `match` function. | |
public static func match(_ pattern: String, options: NSRegularExpression.Options = []) -> Regex { | |
if let regex = Regex.regexCache.object(forKey: pattern as NSString) { | |
// print("Reusing") // uncomment to see cache operation | |
// Apply option-reuse policy | |
if options != [] { | |
regex.options = options | |
} | |
return regex | |
} else { | |
// print("Creating") // uncomment to see cache operation | |
let regex = Regex(pattern, options: options) | |
Regex.regexCache.setObject(regex, forKey: pattern as NSString) | |
return regex | |
} | |
} | |
/// Extends pattern matching to use the pattern and | |
/// options stored in the `Regex` matcher | |
public static func ~= ( | |
lhs: Regex, | |
rhs: String | |
) -> Bool { | |
let range = NSRange(location: 0, length: rhs.utf16.count) | |
if let _ = lhs.regex.firstMatch(in: rhs, range: range) { return true } | |
return false | |
} | |
} | |
// For example | |
let str = "Hello, playground" | |
str ~= "Hello" // false | |
str ~= "Hello, playground" // true | |
Regex.match("H.*o") ~= str // true | |
Regex.match("H.*o") ~= "Out of luck" // false | |
// matches | |
switch str { | |
case Regex.match("H.*o"): print("Hello to you!") | |
default: print("Nope") | |
} | |
// does not match | |
switch "Out of luck" { | |
case Regex.match("H.*o"): print("Hello to you!") | |
default: print("Nope") | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment