Parsley is a modern parser-combinator library for Scala.
The parsley-debug
package allows us to debug parsers using a more comprehensive mechanism, though it
only supports printing out-of-the-box. This repo contains the implementations of different debug-views,
which can be used to visualise the results of a parse. This work was originally a Bachelors Project at
Imperial College London by Fawwaz Abdullah (@mf42-dzh).
Each sub-project contains one view, currently:
parsley-debug-sfx
(@sfx-ui
): A ScalaFX-powered interactive GUI for exploring parser execution trees,FxGUI
.parsley-debug-json
(@json-info
): A JSON string generator,JsonFormatter
andJsonStringFormatter
.
After adding one of these projects as a dependency, use one of the attach
combinators in parsley.debug.combinators
to make a parser render the debugging output with the given view. You can find the views within the package parsley.debug
.
Currently, these views support:
Version | parsley-debug |
---|---|
0.1.0-M1 |
5.0.0-M9 |
snapshot | 5.0-74d27a2-SNAPSHOT + |
The different views work on different platforms:
Version | Scala (JDK8+) | Scala.js (1.16+) | Scala Native (0.5+) |
---|---|---|---|
2.12 | ✔️ | ✔️ | ✔️ |
2.13 | ✔️ | ✔️ | ✔️ |
3.0 | ✔️ | ✔️ | ✔️ |
Version | Scala (JDK8+) | Scala.js (1.16+) | Scala Native (0.5+) |
---|---|---|---|
2.12 | ✔️ | ✔️ | ✔️ |
2.13 | ✔️ | ✔️ | ✔️ |
3.0 | ✔️ | ✔️ | ✔️ |
Version | Scala (JDK8+) | Scala.js (1.16+) | Scala Native (0.5+) |
---|---|---|---|
2.12 | ✔️ | ❌ | ❌ |
2.13 | ✔️ | ❌ | ❌ |
3.0 | ✔️ | ❌ | ❌ |
Assuming you have parsley-debug-sfx
in your dependencies, this is a small example in debugging an arithmetic parser (with either -experimental
for Scala 3 or -Ymacro-annotations
for Scala 2.13):
import parsley.quick.*
import parsley.debug.combinator.*
import parsley.debug.FxGUI
import parsley.expr.{InfixL, Ops, precedence}
@parsley.debuggable
object Maths {
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 (_ - _))
)
lazy val prog: Parsley[List[BigInt]] = many(many(endOfLine) ~> expr)
def main(args: Array[String]): Unit = {
// Attach a graphical debugger to our main `prog` parser.
val debugged = prog.attach(FxGUI)
// Show the parse path for this complicated expression.
// The print is to show the result of the parse.
println(debugged.parse("(1+2)*(4-3)"))
}
}
The UI is shown as follows:
As parsley-debug
itself has PrintView
, then you can also easily switch over to using it from the ScalaFX GUI frontend. Simply change these lines:
- import parsley.debug.FxGUI
+ import parsley.debug.PrintView
...
- val debugged = prog.attach(FxGUI)
+ val debugged = prog.attach(PrintView)
And your output will be something as follows:
prog's parse tree for input:
(1+2)*(4-3)
[ prog (many) ]: ("{1}" [(1,1) -> (1,12)], Success - [ List(3) ])
|
+-[ ~>(1) ]: ("{1}" [(1,1) -> (1,12)], Success - [ 3 ])
| |
| +-[ many ]: ("" [(1,1) -> (1,1)], Success - [ List() ])
| | |
| | +-[ endOfLine ]: ("" [(1,1) -> (1,1)], Failure)
| |
| +-[ expr (infix.left1(1)) ]: ("{1}" [(1,1) -> (1,12)], Success - [ 3 ])
| |
| +-[ infix.left1(1) ]: ("{1}{2}{3}" [(1,1) -> (1,12)], Success - [ 3 ])
| | |
| | +-[ choice(1) ]: ("{1}" [(1,1) -> (1,6)], Success - [ 3 ])
| | | |
[snip!]
And that is it!