Skip to content

Commit

Permalink
refactor(debug)!: changed the type of rules to be partial function
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mie6 committed Dec 28, 2024
1 parent 5af2af5 commit 233e38c
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,39 @@ import parsley.internal.deepembedding.frontend.LazyParsley
import parsley.internal.deepembedding.frontend.debugger.{TaggedWith, Named}
import parsley.internal.deepembedding.backend.debugger.{CheckDivergence, Debugging}

/** This object contains the two main debug combinators, `attachDebugger` and `attachWithFrontend`.
/** This object contains the combinators for attaching debuggers to parsers.
*
* @since 4.5.0
*/
object combinator {
// By default, we don't want closures stored as themselves.
private [debugger] val defaultRules: Seq[Any => Boolean] = Seq(_.isInstanceOf[Function[_, _]])
/** Returns true for any function type, which we ideally don't store as a string */
val DefaultStringRules: PartialFunction[Any, Boolean] = {
case _ : Function1[_, _] => true
case _ : Function2[_, _, _] => true
case _ : Function3[_, _, _, _] => true
case _ : Function4[_, _, _, _, _] => true
case _ : Function5[_, _, _, _, _, _] => true
case _ : Function6[_, _, _, _, _, _, _] => true
case _ : Function7[_, _, _, _, _, _, _, _] => true
case _ : Function8[_, _, _, _, _, _, _, _, _] => true
case _ : Function9[_, _, _, _, _, _, _, _, _, _] => true
case _ : Function10[_, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function11[_, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function12[_, _, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function13[_, _, _, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function14[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => true
case _ : Function22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => true
}

/** Shorthand representation of a pair of a tree extraction function and a debugged parser. */
private type DebuggedPair[+A] = (() => DebugTree, Parsley[A])
private [parsley] type DebuggedPair[+A] = (() => DebugTree, Parsley[A])

/** Attaches a debugger to a parser, returning a reference to the debug tree produced by
* the parser's parse tree formed as it runs.
Expand Down Expand Up @@ -77,7 +100,7 @@ object combinator {
* @tparam A Output type of original parser.
* @return A pair of the finalised tree, and the instrumented parser.
*/
private [parsley] def attachDebugger[A](parser: Parsley[A], toStringRules: Seq[Any => Boolean]): DebuggedPair[A] = {
private [parsley] def attachDebugger[A](parser: Parsley[A], toStringRules: PartialFunction[Any, Boolean]): DebuggedPair[A] = {
val context: DebugContext = new DebugContext(toStringRules)

val attached: LazyParsley[A] = TaggedWith.tagRecursively(parser.internal, new Debugging(context))
Expand Down Expand Up @@ -110,7 +133,7 @@ object combinator {
* @tparam A Output type of original parser.
* @return A pair of the finalised tree, and the instrumented parser.
*/
private [parsley] def attachDebugger[A](parser: Parsley[A]): DebuggedPair[A] = attachDebugger[A](parser, defaultRules)
private [parsley] def attachDebugger[A](parser: Parsley[A]): DebuggedPair[A] = attachDebugger[A](parser, DefaultStringRules)

// $COVERAGE-OFF$
/* Create a closure that freshly attaches a debugger to a parser every time it is called.
Expand All @@ -126,7 +149,7 @@ object combinator {
*
* @return Generator closure for debugged versions of the input parser.
*/
//private def attachReusable[A](parser: Parsley[A], toStringRules: Seq[Any => Boolean]): () => DebuggedPair[A] = () => attachDebugger[A](parser, toStringRules)
//private def attachReusable[A](parser: Parsley[A], toStringRules: PartialFunction[Any, Boolean]): () => DebuggedPair[A] = () => attachDebugger[A](parser, toStringRules)

/* Create a closure that freshly attaches a debugger to a parser every time it is called.
* This is used for creating debugged parsers that can be used as children to multiple parent
Expand Down Expand Up @@ -171,7 +194,7 @@ object combinator {
* @return A modified parser which will ask the frontend to process the produced debug tree after
* a call to [[Parsley.parse]] is made.
*/
def attach[A](parser: Parsley[A], frontend: DebugFrontend, toStringRules: Seq[Any => Boolean]): Parsley[A] = {
def attach[A](parser: Parsley[A], frontend: DebugFrontend, toStringRules: PartialFunction[Any, Boolean]): Parsley[A] = {
val (tree, attached) = attachDebugger(parser, toStringRules)

// Ideally, this should run 'attached', and render the tree regardless of the parser's success.
Expand Down Expand Up @@ -204,7 +227,7 @@ object combinator {
* a call to [[Parsley.parse]] is made.
*
*/
def attach[A](parser: Parsley[A], frontend: DebugFrontend): Parsley[A] = attach[A](parser, frontend, defaultRules)
def attach[A](parser: Parsley[A], frontend: DebugFrontend): Parsley[A] = attach[A](parser, frontend, DefaultStringRules)

/** Create a closure that freshly attaches a debugger and a tree-processing frontend to a parser every
* time it is called.
Expand All @@ -213,7 +236,7 @@ object combinator {
*
* @return Generator closure for frontend-debugged versions of the input parser.
*/
def attachReusable[A](parser: Parsley[A], frontend: =>ReusableFrontend, toStringRules: Seq[Any => Boolean]): () => Parsley[A] = {
def attachReusable[A](parser: Parsley[A], frontend: =>ReusableFrontend, toStringRules: PartialFunction[Any, Boolean]): () => Parsley[A] = {
() => attach(parser, frontend, toStringRules)
}

Expand All @@ -230,7 +253,7 @@ object combinator {
* has more information on its usage.
* @return Generator closure for frontend-debugged versions of the input parser.
*/
def attachReusable[A](parser: Parsley[A], frontend: =>ReusableFrontend): () => Parsley[A] = attachReusable[A](parser, frontend, defaultRules)
def attachReusable[A](parser: Parsley[A], frontend: =>ReusableFrontend): () => Parsley[A] = attachReusable[A](parser, frontend, DefaultStringRules)

/* Attach a debugger and an implicitly-available frontend in which the debug tree should be
* processed with.
Expand All @@ -240,7 +263,7 @@ object combinator {
* [[parsley.debugger.combinator$.attachWithFrontend[A](parser:parsley\.Parsley[A],frontend:parsley\.debugger\.frontend\.DebugFrontend,toStringRules* attachWithFrontend]]
* for more information.
*/
/*def attach[A](parser: Parsley[A], toStringRules: Seq[Any => Boolean])(implicit frontend: DebugFrontend): Parsley[A] = {
/*def attach[A](parser: Parsley[A], toStringRules: PartialFunction[Any, Boolean])(implicit frontend: DebugFrontend): Parsley[A] = {
attach(parser, frontend, toStringRules)
}*/

Expand Down Expand Up @@ -270,16 +293,16 @@ object combinator {

/** Dot accessor versions of the combinators. */
implicit class DebuggerOps[A](par: Parsley[A]) {
//def attachDebugger(toStringRules: Seq[Any => Boolean]): DebuggedPair[A] = combinator.attachDebugger(par, toStringRules)
//def attachReusable(toStringRules: Seq[Any => Boolean]): () => DebuggedPair[A] = combinator.attachReusable(par, toStringRules)
def attach(frontend: DebugFrontend, toStringRules: Seq[Any => Boolean]): Parsley[A] = combinator.attach(par, frontend, toStringRules)
def attachReusable(frontend: =>ReusableFrontend, toStringRules: Seq[Any => Boolean]): () => Parsley[A] =
//def attachDebugger(toStringRules: PartialFunction[Any, Boolean]): DebuggedPair[A] = combinator.attachDebugger(par, toStringRules)
//def attachReusable(toStringRules: PartialFunction[Any, Boolean]): () => DebuggedPair[A] = combinator.attachReusable(par, toStringRules)
def attach(frontend: DebugFrontend, toStringRules: PartialFunction[Any, Boolean]): Parsley[A] = combinator.attach(par, frontend, toStringRules)
def attachReusable(frontend: =>ReusableFrontend, toStringRules: PartialFunction[Any, Boolean]): () => Parsley[A] =
combinator.attachReusable(par, frontend, toStringRules)
//def attach(toStringRules: Seq[Any => Boolean])(implicit frontend: DebugFrontend): Parsley[A] = combinator.attach(par, toStringRules)
//def attach(toStringRules: PartialFunction[Any, Boolean])(implicit frontend: DebugFrontend): Parsley[A] = combinator.attach(par, toStringRules)
//def attachDebugger: DebuggedPair[A] = combinator.attachDebugger(par, defaultRules)
//def attachReusable: () => DebuggedPair[A] = combinator.attachReusable(par, defaultRules)
def attach(frontend: DebugFrontend): Parsley[A] = combinator.attach(par, frontend, defaultRules)
def attachReusable(frontend: =>ReusableFrontend): () => Parsley[A] = combinator.attachReusable(par, frontend, defaultRules)
def attach(frontend: DebugFrontend): Parsley[A] = combinator.attach(par, frontend, DefaultStringRules)
def attachReusable(frontend: =>ReusableFrontend): () => Parsley[A] = combinator.attachReusable(par, frontend, DefaultStringRules)
//def attach(implicit frontend: DebugFrontend): Parsley[A] = combinator.attach(par, defaultRules)
def named(name: String): Parsley[A] = combinator.named(par, name)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ import scala.collection.mutable
import org.typelevel.scalaccompat.annotation.unused
import parsley.XAssert
import parsley.debugger.ParseAttempt
import parsley.debugger.combinator.defaultRules
import parsley.internal.deepembedding.frontend.LazyParsley

// Class used to hold details about a parser being debugged.
// This is normally held as a value inside an implicit variable.
// Anything caught by the toStringRules will have a parse result of that type toString-ed for memory
// efficiency.
private [parsley] class DebugContext(val toStringRules: Seq[Any => Boolean] = defaultRules) {
private [parsley] class DebugContext(private val toStringRules: PartialFunction[Any, Boolean]) {
// Create a new dummy root of the tree that will act as filler for the rest of the tree to build
// off of (as there is no "nil" representation for the tree... other than null, which should be
// avoided in Scala wherever possible).
Expand All @@ -30,6 +29,8 @@ private [parsley] class DebugContext(val toStringRules: Seq[Any => Boolean] = de
def pushPos(offset: Int, line: Int, col: Int): Unit = checkStack.prepend((offset, line, col))
def popPos(): (Int, Int, Int) = checkStack.remove(0)

def shouldString(x: Any): Boolean = toStringRules.applyOrElse[Any, Boolean](x, _ => false)

// Tracks where we are in the parser callstack.
private val builderStack = mutable.ListBuffer[TransientDebugTree](dummyRoot)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ private [internal] class AddAttemptAndLeave(dbgCtx: DebugContext) extends Instr
dbgCtx.addParseAttempt {
val res =
if (success) Some(ctx.stack.upeek match {
case f if dbgCtx.toStringRules.exists(_(f)) => f.toString // Closures and lambdas are expensive!
case x => x
case f if dbgCtx.shouldString(f) => f.toString // Closures and lambdas are expensive!
case x => x
}) else None
new ParseAttempt(inp = input, fof = prevOffset, tof = currentOff, fps = prevPos, tps = (ctx.line, ctx.col), scs = success, res = res)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import parsley.internal.deepembedding.backend.debugger.Debugging
class DebuggerUsageSpec extends ParsleyTest {
import DebuggerUsageSpec.Arithmetic
"the Debugged internal frontend class" should "not allow nesting of Debugged nodes" in {
val factory = new Debugging(new DebugContext())
val factory = new Debugging(new DebugContext(parsley.debugger.combinator.DefaultStringRules))
try {
val _ = new TaggedWith(factory)(new TaggedWith(factory)(fresh(()).internal, null, None), null, None)
fail("Debugged nodes have been nested")
Expand All @@ -33,7 +33,7 @@ class DebuggerUsageSpec extends ParsleyTest {
}

it should "preserve the prettified names of the parsers" in {
val factory = new Debugging(new DebugContext())
val factory = new Debugging(new DebugContext(parsley.debugger.combinator.DefaultStringRules))
new TaggedWith(factory)(named(fresh(()), "foo").internal, null, None).debugName shouldBe "foo"
new TaggedWith(factory)(fresh(()).internal, null, None).debugName shouldBe "fresh"
new TaggedWith(factory)(fresh(()).internal, null, Some("bar")).debugName shouldBe "bar"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class RenameSpec extends ParsleyTest {

it should "pass through Debugged parsers and get the inner parser's name" in {
val symbolic = new <**>
val debugged = new TaggedWith[Any](new Debugging(new DebugContext()))(symbolic, symbolic, None)
val debugged = new TaggedWith[Any](new Debugging(new DebugContext(parsley.debugger.combinator.DefaultStringRules)))(symbolic, symbolic, None)

Renamer.nameOf(None, debugged) shouldBe "<**>"
}
Expand Down

0 comments on commit 233e38c

Please sign in to comment.