Dill (Debugging Interactively in the Parsley Library) is a cross-platform, visual, interactive debugger to be used with the parser combinator library for Scala, Parsley.
- View the debug tree, input string and the parser code
- Download trees and share them with friends!
- Debug multiple instances of
Parsleysimultaneously - Breakpoint compatibility
- State reference manipulation
Dill is distributed as a binary executable and can be installed below according to your machine's operating system from the releases page.
To build the Dill debugger on your own machine, go to building.
You will first need to install / build the Dill debugging application onto your machine. Then once the application has started, you are ready to start sending it debug information from within Parsley:
- First, ensure that your project has the
remote-viewproject as a dependency (you will of course need to have theParsleylibrary as a dependency too). - Import the
RemoteViewobject fromparsley.debug. - Then on the parser which you would like to debug, attach
RemoteView.dillbefore callingparse().
test.sc: the following scala script uses Parsley 5.0.0-M14
//> using repository sonatype-s01:snapshots
//> using dep com.github.j-mie6::parsley:5.0.0-M14
//> using dep com.github.j-mie6::parsley-debug:5.0.0-M14
//> using dep com.github.j-mie6::parsley-debug-remote::0.1-49c36c0-SNAPSHOT
//> using options -experimental
import parsley.debug.*
import parsley.state.*
import parsley.quick.*
import parsley.syntax.character.{charLift, stringLift}
import parsley.debug.combinator.*
import scala.annotation.experimental
@experimental @parsley.debuggable
object Parser {
import parsley.character.digit
import parsley.expr.{InfixL, Ops, precedence}
/* Expression parsing */
val natural: Parsley[Int] = digit.foldLeft1(0)((n, d) => n * 10 + d.asDigit)
val hello: Parsley[Unit] = ('h' ~> ("ello" | "i") ~> " world!").void
val int: Parsley[BigInt] = satisfy(_.isDigit).foldLeft1(BigInt(0))((acc, c) => acc * 10 + c.asDigit)
lazy val expr: Parsley[BigInt] = precedence[BigInt](int, char('(') ~> expr <~ char(')'))(
Ops(InfixL)(char('*') as (_ * _), char('/') as (_ / _)),
Ops(InfixL)(char('+') as (_ + _), char('-') as (_ - _))
)
/* Breakpoints in a short sequence */
lazy val seq = (string("a").break(ExitBreak) ~> string("b")).break(ExitBreak) ~> string("c")
/* Iterative example */
lazy val xyxyxy = many("x" ~> "y")
/* Double XML tags */
val openTag = (atomic('<' <~ notFollowedBy('/')))
val aTagLeft = openTag ~> "a" <~ '>'
val bTagLeft = openTag ~> 'b' <~ '>'
val xml = aTagLeft.fillRef { r1 =>
object Ref1Codec extends RefCodec {
type A = String
val ref: Ref[A] = r1
val codec: Codec[A] = summon[Codec[String]]
}
bTagLeft.fillRef { r2 =>
object Ref2Codec extends RefCodec {
type A = Char
val ref: Ref[A] = r2
val codec: Codec[A] = summon[Codec[Char]]
}
char(' ').break(ExitBreak, Ref1Codec, Ref2Codec) ~> ("</" ~> r2.get.flatMap(char) <~ '>')
} <~> "</" ~> r1.get.flatMap(string) <~ '>'
}
}
Parser.expr.attach(RemoteView.dill).parse("(3+1)-(2*4)")
Parser.seq.attach(RemoteView.dill).parse("abcd")
Parser.xyxyxy.attach(RemoteView.dill).parse("xyxyxyxyxyxyxyxyxyx")
Parser.xml.attach(RemoteView.dill).parse("<a><b> </B></A>") // Fails unless "A" and "B" are passed back by the user
To run this snippet, simply run scala test.sc.
You will then be able to view a representation of the abstract syntax tree generated by Parsley:
The RemoteView object creates instances of a generic RemoteView interface which sends debug trees to a target over HTTP. To create an instance for Dill running locally attach RemoteView.dill, or if hosted externally attach RemoteView.dill(address).
The frontend of the application is written using ScalaJS and Laminar, and uses the sbt build system, the frontend compiles down to a single JavaScript file located in ./static. The backend uses the Tauri package to host the frontend, and the Rocket package to host a server to receive the tree from Parsley. We use npm to manage the various packages.
To run the project, execute sbt run:
This will install the node packages required to build the project, build the front and backend, and then start the generated executable.
To run the project in development mode, execute:
sbt ~buildFrontendto start the sbt frontend development server.sbt runBackendin a different terminal to start theTauriapp in development mode.
This will cause a quick-reload when any of the source files are modified.
If you encounter a bug when using Dill, please try to make a self contained example: this will help to identify the issue.
Then, create a new issue - if possible, please include screenshots and a description of the bug.
- The
remote-viewbackend forparsley-debugposts the debug tree from the parser to theRocketHTTP server running within theDilldebugger. - The
Rocketserver transforms and passes off a representation of the debug tree to theTauriapplication inRust. Taurithen emits the debug tree to the front end.- Upon receiving of the tree, the frontend renders the tree on the screen.
We provide a Dockerfile that lets you modify and test the project without installing additional dependencies.
After building an image, you can start a container using the command docker run -it --rm -e DISPLAY=:0 -v /tmp/.X11-unix:/tmp/.X11-unix -p 2222:22 <IMAGE_ID>. You must be using a Linux-based terminal or have an X-Server installed on your machine to support GUI forwarding.
This will launch the container with SSH access and X-Forwarding enabled, allowing the GUI to display on your local machine. Once the container is running, you can SSH into it using ssh -X -p 2222 root@localhost, then navigate to the Dill root directory with cd /home.
Before running the project, ensure that Rust's package manager is set to the latest version by running rustup default stable. Once this is done, you can follow the commands listed in building to work on Dill.
Alternatively, if you prefer working on the code outside the container, you can edit files locally and copy them into the container using: sbt dockerBuild.
