Created
November 22, 2023 03:38
-
-
Save davbeck/d62fc38579889f092b191a9f76166c79 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 | |
// Foundation.CharacterSet should be called "UnicodeScalarSet" | |
// this operates on actual Characters | |
// TODO: Equatable doesn't work right: for instance defining a range vs defining a set with the same values will not be equal | |
indirect enum CharacterSet: Sendable, Hashable { | |
case closedRange(ClosedRange<Character>) | |
case set(Set<Character>) | |
case inverted(CharacterSet) | |
case union(CharacterSet, CharacterSet) | |
case intersection(CharacterSet, CharacterSet) | |
case symmetricDifference(CharacterSet, CharacterSet) | |
init(_ range: ClosedRange<Character>) { | |
self = .closedRange(range) | |
} | |
init(_ characters: Set<Character>) { | |
self = .set(characters) | |
} | |
var inverted: CharacterSet { | |
.inverted(self) | |
} | |
} | |
extension CharacterSet: ExpressibleByArrayLiteral { | |
init(arrayLiteral elements: Character...) { | |
self = .set(.init(elements)) | |
} | |
} | |
extension CharacterSet: SetAlgebra { | |
typealias Element = Character | |
init() { | |
self = .set([]) | |
} | |
private mutating func withSet(_ work: (inout Set<Character>) -> Void) { | |
if case let .set(set) = self { | |
var set = set | |
work(&set) | |
self = .set(set) | |
} else { | |
var set = Set<Character>() | |
work(&set) | |
self = .union(.set(set), self) | |
} | |
} | |
func contains(_ member: Character) -> Bool { | |
switch self { | |
case .closedRange(let closedRange): | |
return closedRange.contains(member) | |
case .set(let set): | |
return set.contains(member) | |
case .inverted(let characterSet): | |
return !characterSet.contains(member) | |
case .union(let a, let b): | |
return a.contains(member) || b.contains(member) | |
case .intersection(let a, let b): | |
return a.contains(member) && b.contains(member) | |
case .symmetricDifference(let a, let b): | |
let aContains = a.contains(member) | |
let bContains = b.contains(member) | |
return (aContains || bContains) && aContains != bContains | |
} | |
} | |
@discardableResult | |
mutating func insert(_ newMember: __owned Character) -> (inserted: Bool, memberAfterInsert: Self.Element) { | |
if case let .set(set) = self { | |
var set = set | |
let result = set.insert(newMember) | |
self = .set(set) | |
return result | |
} else if !self.contains(newMember) { | |
self = .union(.set([newMember]), self) | |
return (true, newMember) | |
} else { | |
return (false, newMember) | |
} | |
} | |
mutating func remove(_ member: Character) -> Character? { | |
if case let .set(set) = self { | |
var set = set | |
let result = set.remove(member) | |
self = .set(set) | |
return result | |
} else if self.contains(member) { | |
self = .intersection(self, .set([member]).inverted) | |
return member | |
} else { | |
return nil | |
} | |
} | |
mutating func update(with newMember: __owned Character) -> Character? { | |
self.insert(newMember) | |
return newMember | |
} | |
func union(_ other: __owned CharacterSet) -> CharacterSet { | |
.union(self, other) | |
} | |
func intersection(_ other: CharacterSet) -> CharacterSet { | |
.intersection(self, other) | |
} | |
func symmetricDifference(_ other: __owned CharacterSet) -> CharacterSet { | |
.symmetricDifference(self, other) | |
} | |
mutating func formUnion(_ other: __owned CharacterSet) { | |
self = self.union(other) | |
} | |
mutating func formIntersection(_ other: CharacterSet) { | |
self = self.intersection(other) | |
} | |
mutating func formSymmetricDifference(_ other: __owned CharacterSet) { | |
self = self.symmetricDifference(other) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment