Skip to content

Instantly share code, notes, and snippets.

@davbeck
Created November 22, 2023 03:38
Show Gist options
  • Save davbeck/d62fc38579889f092b191a9f76166c79 to your computer and use it in GitHub Desktop.
Save davbeck/d62fc38579889f092b191a9f76166c79 to your computer and use it in GitHub Desktop.
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