Created
September 14, 2019 18:01
-
-
Save kaqu/365dcb5cf410aee3934da9ffc6a0b732 to your computer and use it in GitHub Desktop.
BytesParser based on https://pointfree.co parser video series
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 | |
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