Skip to content

Instantly share code, notes, and snippets.

@kaqu
Created September 14, 2019 18:01
Show Gist options
  • Save kaqu/365dcb5cf410aee3934da9ffc6a0b732 to your computer and use it in GitHub Desktop.
Save kaqu/365dcb5cf410aee3934da9ffc6a0b732 to your computer and use it in GitHub Desktop.
BytesParser based on https://pointfree.co parser video series
import Foundation
public extension Data {
func parse<T>(with parser: Parser<T>) -> T? {
self.withUnsafeBytes { (bufferPointer) -> T? in
var slice: Slice<Array<Byte>> = .init(bufferPointer)
return parser.parse(&slice)
}
}
}
public struct Parser<A> {
let parse: (inout Slice<Array<Byte>>) -> A?
}
public extension Parser {
static var never: Parser {
return Parser { _ in nil }
}
static func always<A>(_ a: A) -> Parser<A> {
return Parser<A> { _ in a }
}
static func literal(_ literal: Byte...) -> Parser<Void> {
return .init { bytes in
guard zip(bytes.lazy, literal.lazy).lazy.prefix(while: ==).count == literal.count else { return nil }
bytes.removeFirst(literal.count)
return Void()
}
}
static func literal(_ literal: Array<Byte>) -> Parser<Void> {
return .init { bytes in
guard zip(bytes.lazy, literal.lazy).lazy.prefix(while: ==).count == literal.count else { return nil }
bytes.removeFirst(literal.count)
return Void()
}
}
}
public extension Parser {
func map<B>(_ f: @escaping (A) -> B) -> Parser<B> {
return Parser<B> { bytes -> B? in
self.parse(&bytes).map(f)
}
}
func flatMap<B>(_ f: @escaping (A) -> Parser<B>) -> Parser<B> {
return Parser<B> { bytes -> B? in
var localBytes = bytes
let matchA = self.parse(&localBytes)
let parserB = matchA.map(f)
guard let matchB = parserB?.parse(&localBytes) else { return nil }
bytes = localBytes
return matchB
}
}
}
public func zip<A, B>(_ a: Parser<A>, _ b: Parser<B>) -> Parser<(A, B)> {
return Parser<(A, B)> { bytes -> (A, B)? in
var localBytes = bytes
guard let matchA = a.parse(&localBytes) else { return nil }
guard let matchB = b.parse(&localBytes) else { return nil }
bytes = localBytes
return (matchA, matchB)
}
}
public func zip<A, B, C>(
_ a: Parser<A>,
_ b: Parser<B>,
_ c: Parser<C>
) -> Parser<(A, B, C)> {
return zip(a, zip(b, c))
.map { a, bc in (a, bc.0, bc.1) }
}
public func zip<A, B, C, D>(
_ a: Parser<A>,
_ b: Parser<B>,
_ c: Parser<C>,
_ d: Parser<D>
) -> Parser<(A, B, C, D)> {
return zip(a, zip(b, c, d))
.map { a, bcd in (a, bcd.0, bcd.1, bcd.2) }
}
public func zip<A, B, C, D, E>(
_ a: Parser<A>,
_ b: Parser<B>,
_ c: Parser<C>,
_ d: Parser<D>,
_ e: Parser<E>
) -> Parser<(A, B, C, D, E)> {
return zip(a, zip(b, c, d, e))
.map { a, bcde in (a, bcde.0, bcde.1, bcde.2, bcde.3) }
}
public func zip<A, B, C, D, E, F>(
_ a: Parser<A>,
_ b: Parser<B>,
_ c: Parser<C>,
_ d: Parser<D>,
_ e: Parser<E>,
_ f: Parser<F>
) -> Parser<(A, B, C, D, E, F)> {
return zip(a, zip(b, c, d, e, f))
.map { a, bcdef in (a, bcdef.0, bcdef.1, bcdef.2, bcdef.3, bcdef.4) }
}
public func zip<A, B, C, D, E, F, G>(
_ a: Parser<A>,
_ b: Parser<B>,
_ c: Parser<C>,
_ d: Parser<D>,
_ e: Parser<E>,
_ f: Parser<F>,
_ g: Parser<G>
) -> Parser<(A, B, C, D, E, F, G)> {
return zip(a, zip(b, c, d, e, f, g))
.map { a, bcdefg in (a, bcdefg.0, bcdefg.1, bcdefg.2, bcdefg.3, bcdefg.4, bcdefg.5) }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment