parser4k.log.kt Maven / Gradle / Ivy
package parser4k
import java.util.LinkedList
fun Parser.with(parserId: String, log: ParsingLog) = Parser { input ->
log.before(parserId, input)
val output = [email protected](input)
log.after(parserId, output)
output
}
class ParsingLog(private val onEvent: (ParsingEvent) -> Unit = { println(it.toDebugString()) }) {
private val idStack = LinkedList()
private val inputStack = LinkedList()
internal fun before(parserId: String, input: Input) {
idStack.push(parserId)
inputStack.push(input)
onEvent(BeforeParsing(input, stackTrace()))
}
internal fun after(parserId: String, output: Output?) {
onEvent(AfterParsing(inputStack.peek(), output, stackTrace()))
inputStack.pop()
idStack.pop().let { id ->
require(id == parserId) { "Expected id '$parserId' but was '$id'" }
}
}
private fun stackTrace(): List =
idStack.zip(inputStack)
.map { (id, input) -> StackFrame(id, input.offset) }
.asReversed()
}
fun List.print() = forEach { println(it.toDebugString()) }
sealed class ParsingEvent
data class BeforeParsing(val input: Input, val stackTrace: List) : ParsingEvent()
data class AfterParsing(val input: Input, val output: Output?, val stackTrace: List) : ParsingEvent()
data class StackFrame(val parserId: String, val offset: Int)
fun ParsingEvent.toDebugString() =
when (this) {
is BeforeParsing -> "${input.string()} ${stackTrace.string()}"
is AfterParsing<*> -> "${input.string()} ${stackTrace.string()} -- ${if (output == null) "no match" else input.diff(output.nextInput)}"
}
private fun Input.diff(that: Input) = value.substring(this.offset, that.offset)
private fun List.string() = joinToString(" ") { it.parserId + ":" + it.offset }
private fun Input.string() = "\"$value\""