Skip to content

Instantly share code, notes, and snippets.

@chriseidhof
Forked from milseman/eat_seek_peek.swift
Last active May 13, 2018 23:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chriseidhof/ea9e1de6321ef0f25ad9794df5aeefae to your computer and use it in GitHub Desktop.
Save chriseidhof/ea9e1de6321ef0f25ad9794df5aeefae to your computer and use it in GitHub Desktop.
Playground for Self-sliced Collections: eat/seek/peek
// 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