From 62b03c84efec7ee5e42deed840a9899626d6e032 Mon Sep 17 00:00:00 2001 From: Jamie Willis Date: Sat, 28 Dec 2024 15:24:18 +0000 Subject: [PATCH] feat(debug): reworked ConsolePrettyPrinter with new methods --- .../frontend/ConsolePrettyPrinter.scala | 68 +++++++++++++++--- .../frontend/internal/consolepretty.scala | 71 ------------------- 2 files changed, 57 insertions(+), 82 deletions(-) delete mode 100644 parsley-debug/shared/src/main/scala/parsley/debugger/frontend/internal/consolepretty.scala diff --git a/parsley-debug/shared/src/main/scala/parsley/debugger/frontend/ConsolePrettyPrinter.scala b/parsley-debug/shared/src/main/scala/parsley/debugger/frontend/ConsolePrettyPrinter.scala index 42a0aeb43..8fae1d6b0 100644 --- a/parsley-debug/shared/src/main/scala/parsley/debugger/frontend/ConsolePrettyPrinter.scala +++ b/parsley-debug/shared/src/main/scala/parsley/debugger/frontend/ConsolePrettyPrinter.scala @@ -5,15 +5,11 @@ */ package parsley.debugger.frontend -import parsley.debugger.DebugTree -import parsley.debugger.frontend.internal.consolepretty.* +import java.io.{OutputStream, PrintStream} -private [frontend] sealed class ConsolePrettyPrinterImpl private[frontend] (ioF: String => Unit) extends ReusableFrontend { - override private [debugger] def process(input: => String, tree: => DebugTree): Unit = { - ioF(s"${tree.parserName}'s parse tree for input:\n\n$input\n\n") - ioF(tree.pretty + "\n") - } -} +import scala.annotation.tailrec + +import parsley.debugger.{DebugTree, ParseAttempt} /** A console pretty-printer for the debugger. * @@ -22,10 +18,60 @@ private [frontend] sealed class ConsolePrettyPrinterImpl private[frontend] (ioF: * * @since 5.0.0 */ -object ConsolePrettyPrinter extends ConsolePrettyPrinterImpl(println(_)) { - /** Create a string pretty-printer that takes an arbitrary impure string function. +object ConsolePrettyPrinter extends ConsolePrettyPrinter(Console.out) { + /** Create a string pretty-printer that outputs to an arbitrary place * * @since 5.0.0 */ - def apply(ioF: String => Unit): ReusableFrontend = new ConsolePrettyPrinterImpl(ioF) + def apply(out: OutputStream): ReusableFrontend = apply(new PrintStream(out)) + /** Create a string pretty-printer that outputs to an arbitrary place + * + * @since 5.0.0 + */ + def apply(out: PrintStream): ReusableFrontend = new ConsolePrettyPrinter(out) +} + +private [frontend] sealed class ConsolePrettyPrinter private[frontend] (out: PrintStream) extends ReusableFrontend { + override private [debugger] def process(input: => String, tree: => DebugTree): Unit = { + out.println(s"${tree.parserName}'s parse tree for input:\n\n$input\n\n") + pretty(tree) + } + + private def bury(str: String, withMark: Boolean, indents: Vector[String]): Unit = out.println { + if (indents.isEmpty) str + else if (withMark) s"${indents.init.mkString}+-$str" + else s"${indents.mkString}$str" + } + + private def pretty(dt: DebugTree): Unit = pretty(dt, Vector.empty) + + private def pretty(dt: DebugTree, indents: Vector[String]): Unit = { + val uname = + if (dt.parserName != dt.internalName) + s"${dt.parserName} (${dt.internalName}${if (dt.childNumber.isDefined) s" (${dt.childNumber.get})" else ""})" + else + s"${dt.internalName}${if (dt.childNumber.isDefined) s" (${dt.childNumber.get})" else ""}" + val results = dt.parseResults.map(printParseAttempt).mkString + + bury(s"[ $uname ]: $results", withMark = true, indents) + printChildren(dt, indents, dt.nodeChildren.toList) + } + + // Print a parse attempt in a human-readable way. + private def printParseAttempt(attempt: ParseAttempt): String = { + val status = if (attempt.success) s"Success - [ ${attempt.result.get} ]" else "Failure" + s"""(\"${attempt.rawInput}\" [${attempt.fromPos} -> ${attempt.toPos}], $status)""" + } + + // Print all the children, remembering to add a blank indent for the last child. + @tailrec private def printChildren(dt: DebugTree, indents: Vector[String], children: List[(String, DebugTree)]): Unit = children match { + case List((_, t)) => + bury("|", withMark = false, indents) + pretty(t, indents :+ " ") + case (_, t) :: xs => + bury("|", withMark = false, indents) + pretty(t, indents :+ "| ") + printChildren(dt, indents, xs) + case Nil => + } } diff --git a/parsley-debug/shared/src/main/scala/parsley/debugger/frontend/internal/consolepretty.scala b/parsley-debug/shared/src/main/scala/parsley/debugger/frontend/internal/consolepretty.scala deleted file mode 100644 index 3bc1ae01c..000000000 --- a/parsley-debug/shared/src/main/scala/parsley/debugger/frontend/internal/consolepretty.scala +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2020 Parsley Contributors - * - * SPDX-License-Identifier: BSD-3-Clause - */ -package parsley.debugger.frontend.internal - -import scala.annotation.tailrec -import scala.collection.mutable - -import parsley.debugger.{DebugTree, ParseAttempt} - -private [debugger] object consolepretty { - // Utility class for aiding in the toString method for debug trees. - private [frontend] case class PrettyPrintHelper(acc: mutable.StringBuilder, indents: Vector[String]) { - // Indent a string with the given indenting delimiters. - def bury(str: String, withMark: Boolean = true): Unit = { - val pretty = - if (indents.isEmpty) str - else if (withMark) indents.init.mkString + "+-" + str - else indents.mkString + str - - acc.append(pretty + "\n") - } - - // Add a new indent delimiter to the current helper instance. - // The accumulator is shared between new instances. - def addIndent: PrettyPrintHelper = PrettyPrintHelper(acc, indents :+ "| ") - - // Adds a two-blank-space indent instead for the last child of a node. - def addBlankIndent: PrettyPrintHelper = PrettyPrintHelper(acc, indents :+ " ") - } - - implicit final class TreePrinter(val dt: DebugTree) extends AnyVal { - def pretty: String = { - val acc = new mutable.StringBuilder - prettyPrint(PrettyPrintHelper(acc, Vector.empty)) - acc.init.toString - } - - private [consolepretty] def prettyPrint(helper: PrettyPrintHelper): Unit = { - val uname = - if (dt.parserName != dt.internalName) - s"${dt.parserName} (${dt.internalName}${if (dt.childNumber.isDefined) s" (${dt.childNumber.get})" else ""})" - else - s"${dt.internalName}${if (dt.childNumber.isDefined) s" (${dt.childNumber.get})" else ""}" - val results = dt.parseResults.map(printParseAttempt).mkString - - helper.bury(s"[ $uname ]: $results") - printChildren(helper, dt.nodeChildren.toList) - } - - // Print a parse attempt in a human-readable way. - private [TreePrinter] def printParseAttempt(attempt: ParseAttempt): String = { - val status = if (attempt.success) s"Success - [ ${attempt.result.get} ]" else "Failure" - s"""(\"${attempt.rawInput}\" [${attempt.fromPos} -> ${attempt.toPos}], $status)""" - } - - // Print all the children, remembering to add a blank indent for the last child. - @tailrec private def printChildren(helper: PrettyPrintHelper, children: List[(String, DebugTree)]): Unit = children match { - case (_, t) :: Nil => - helper.bury("|", withMark = false) - t.prettyPrint(helper.addBlankIndent) - case (_, t) :: xs => - helper.bury("|", withMark = false) - t.prettyPrint(helper.addIndent) - printChildren(helper, xs) - case Nil => - } - } -}