-
-
Save SintraWorks/2c8a3928ae3a8c92f7ddc93633585dc6 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>() | |
private(set) var regex: NSRegularExpression | |
public var options: NSRegularExpression.Options { | |
get { return Regex.regexCache.object(forKey: "\(self.regex.pattern)" as NSString)!.options } | |
/// - Note: If the cached regex already sports these | |
/// options, there is no need to update. | |
set(options) { | |
if self.regex.options == options { | |
//print("not changing options") // uncomment to see cache operation | |
return | |
} | |
//print("Recreating pattern: \"\(self.regex.pattern)\" with options: \(options.rawValue)") // uncomment to see cache operation | |
self.regex = try! NSRegularExpression(pattern: self.regex.pattern, options: options) | |
} | |
} | |
/// Initializes a `Regex` instance that defaults to | |
/// "no options". Update as needed for case or | |
/// diacritical insensitivity using the 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 = []) { | |
// 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 then 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 `[]`. | |
public static func match(_ pattern: String, options: NSRegularExpression.Options? = nil) -> Regex { | |
if let regex = Regex.regexCache.object(forKey: "\(pattern)" as NSString) { | |
if let requestedOptions = options { | |
//print("matching, with options: \(String(describing: requestedOptions))") // uncomment to see cache operation | |
regex.options = requestedOptions | |
return regex | |
} else { | |
//print("matching, reusing pattern: \"\(pattern)\" options: \(String(describing: regex.regex.options))") // uncomment to see cache operation | |
return regex | |
} | |
} else { | |
let options: NSRegularExpression.Options = options ?? [] | |
//print("matching, creating with pattern: \"\(pattern)\" options: \(String(describing: options))") // 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") | |
} | |
print("") | |
let pattern = "sailor" | |
let regex = Regex(pattern, options: [.caseInsensitive]) | |
print(Regex.match(pattern) ~= "sailor") // true | |
print(Regex.match(pattern) ~= "Sailor") // true | |
print(Regex.match(pattern) ~= "SAILOR") // true | |
print("") | |
regex.options = [] | |
print(Regex.match(pattern) ~= "sailor") // true | |
print(Regex.match(pattern) ~= "Sailor") // false | |
print(Regex.match(pattern) ~= "SAILOR") // false | |
print("") | |
print(Regex.match(pattern, options: [.caseInsensitive]) ~= "sailor") // true | |
print(Regex.match(pattern) ~= "Sailor") // true | |
print(Regex.match(pattern) ~= "SAILOR") // true | |
print("") | |
Regex.regexCache.object(forKey: pattern as NSString)?.options = [.caseInsensitive] | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment