-
-
Save chriseidhof/ea9e1de6321ef0f25ad9794df5aeefae to your computer and use it in GitHub Desktop.
Playground for Self-sliced Collections: eat/seek/peek
This file contains hidden or 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
// Eat/seek/peek | |
extension Collection where SubSequence == Self, Element: Equatable { | |
mutating func eat(_ element: Element) -> Bool { | |
guard let f = first, f == element else { return false } | |
eat() | |
return true | |
} | |
mutating func eat(asserting on: Element) -> Element { | |
let e = eat() | |
assert(on == e) | |
return e | |
} | |
mutating func eat(until element: Element) -> SubSequence { | |
return eat(until: { $0 == element }) | |
} | |
func peek(is element: Element) -> Bool { | |
if let f = self.first, f == element { return true } | |
return false | |
} | |
} | |
extension Collection where SubSequence == Self { | |
mutating func eat() -> Element { | |
defer { self = self.dropFirst() } | |
return peek() | |
} | |
mutating func eat(_ n: Int) -> SubSequence { | |
let (pre, rest) = self.seek(n) | |
self = rest | |
return pre | |
} | |
mutating func eat(until f: (Element) -> Bool) -> SubSequence { | |
let (pre, rest) = self.seek(until: f) | |
self = rest | |
return pre | |
} | |
mutating func eat(while cond: (Element) -> Bool) -> SubSequence { | |
let (result, newSelf) = seek(until: { !cond($0) }) | |
self = newSelf | |
return result | |
} | |
mutating func eat(oneOf cond: (Element) -> Bool) -> Element? { | |
guard let element = first, cond(element) else { return nil } | |
self = dropFirst() | |
return element | |
} | |
func peek() -> Element { | |
return self.first! | |
} | |
func peek(_ n: Int) -> Element { | |
assert(n > 0 && self.count >= n) | |
return self.dropFirst(n).peek() | |
} | |
func seek(_ n: Int) -> (prefix: SubSequence, rest: SubSequence) { | |
return (self.prefix(n), self.dropFirst(n)) | |
} | |
func seek(until f: (Element) -> Bool) -> (prefix: SubSequence, rest: SubSequence) { | |
guard let point = self.index(where: f) else { | |
return (self[...], self[endIndex...]) | |
} | |
return (self[..<point], self[point...]) | |
} | |
// ... seek(until: Element), seek(through:), seek(until: Set<Element>), seek(through: Set<Element>) ... | |
} | |
let string = "a𝔹cdề\u{301}\u{302}f𝔾h⒤jklmnoᕉ𝙌Ⓡş" | |
var scanner = string[...] | |
scanner.eat(/*om nom nom*/) // a | |
scanner // bcdefghijklmnoᕉ𝙌Ⓡş | |
scanner.eat(3) // 𝔹cd | |
scanner.eat(asserting: "\u{0065}\u{0302}\u{0300}\u{0301}\u{0302}") // ề́̂ | |
scanner.peek() // f | |
scanner.peek(3) // ⒤ | |
scanner.seek(3) // (prefix: f𝔾h, rest: ⒤jklmnoᕉ𝙌Ⓡş) | |
let (pre, rest) = scanner.seek(until: { $0 == "o" }) // (prefix: f𝔾h, rest: ⒤jklmnoᕉ𝙌Ⓡş) | |
pre // f𝔾h⒤jklmn | |
rest // oᕉ𝙌Ⓡş | |
var theRestInCodeUnits = Array(scanner.utf8)[...] | |
theRestInCodeUnits.eat(5) // [102, 240, 157, 148, 190] | |
theRestInCodeUnits // ... the rest ... | |
let nom = scanner.eat(until: { $0 == "o" }) | |
nom // f𝔾h⒤jklmn | |
scanner // oᕉ𝙌Ⓡş | |
print("done") | |
// A small CSV-like parser | |
func parseField(input: inout Substring) -> Substring { | |
let quote: Character = "\"" | |
if input.eat(quote) { | |
var contents = input.eat(until: quote) | |
input.eat(asserting: quote) | |
while input.eat(quote) { // double quote is an escaped quote | |
contents.append(quote) | |
contents.append(contentsOf: input.eat(until: quote)) | |
input.eat(asserting: quote) | |
} | |
return contents | |
} else { | |
return input.eat(until: { $0 == "," }) | |
} | |
} | |
func parseLine(input: inout Substring) -> [Substring]? { | |
guard !input.isEmpty else { return nil } | |
var result: [Substring] = [] | |
while !input.isEmpty, !input.peek(is: "\n") { | |
result.append(parseField(input: &input)) | |
if input.eat(",") { | |
continue | |
} else if input.peek(is: "\n") || input.isEmpty { | |
break | |
} else { | |
fatalError() | |
} | |
} | |
input.eat("\n") | |
return result | |
} | |
// This function is just an experiment, ignore... | |
func many<Coll, Result>(input: Coll, one: (inout Coll) -> Result?) -> [Result] { | |
var result: [Result] = [] | |
var remainder = input | |
while let next = one(&remainder) { | |
result.append(next) | |
} | |
return result | |
} | |
var sample = "123,\"hello, \"\" test\"\n\"two\",three" | |
many(input: sample[...], one: parseLine) | |
sample | |
extension Collection where SubSequence == Self { | |
/// Tries to parse using `parse`, but if it fails, the current state will be reset. | |
mutating func attempt<A>(_ parse: (inout Self) -> A?) -> A? { | |
let previousState = self | |
guard let result = parse(&self) else { | |
self = previousState | |
return nil | |
} | |
return result | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment