Side effect operator
ULazdins opened this issue · 2 comments
ULazdins commented
💡 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)
}
}
stephencelis commented
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.
stephencelis commented
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!