pointfreeco/swift-parsing

Side effect operator

ULazdins opened this issue · 2 comments

💡 a suggestion

Adding a side-effect operator. I made one for myself to add the ability to inspect and debug the parsing. An example usage:

let debugPrint: EffectCallback = { output, rest in
    print("I'm debugging here >>> \(output != nil ? "success" : "fail") `\(rest)`, \(String(describing: output))")
}

let user = Int.parser()
    .skip(StartsWith(","))
    .take(Prefix { $0 != "," })
    .skip(StartsWith(","))
    .effect(debugPrint) // ==> It looks like I'm failing here, let's check
    .take(Bool.parser())
    .map { User(id: $0, name: String($1), isAdmin: $2) }

var input = "1,Blob=true"[...]
user.parse(&input) // => fails here

The suggested form:

extension Parsers {
    public struct Effect<Upstream>: Parser where Upstream: Parser {
        public typealias EffectCallback = (_ output: Upstream.Output?, _ rest: Upstream.Input) -> Void
        
        let upstream: Upstream
        let effect: EffectCallback
        
        public init(upstream: Upstream, effect: @escaping EffectCallback) {
            self.upstream = upstream
            self.effect = effect
        }
        
        public func parse(_ input: inout Upstream.Input) -> Upstream.Output? {
            let output: (output: Output?, rest: Input) = upstream.parse(input)
            self.effect(output.output, output.rest)
            return output.output
        }
    }
}

extension Parser {
    @inlinable
    public func effect(_ effect: @escaping Parsers.Effect<Self>.EffectCallback) -> Parsers.Effect<Self> {
        .init(upstream: self, effect: effect)
    }
}

We definitely want to improve debugging and error handling, and a parser like the one you suggested would be a great start.

nom, a popular parser combinator library for Rust, offers the ability to trace parsing through a composition of parsers: https://github.com/Geal/nom/blob/master/doc/error_management.md#debugging-parsers It would be awesome to see if we could do something similar.

I'm going to convert this to a discussion for now. Will be a better place to discuss iterations on such an operator and debugging in general!