treehugger lets you treat Scala code as data (case classes), and data as code (treehugger DSL) so you can code while you code.
For example, here’s treehugger DSL for Hello World:
scala> import treehugger.forest._, definitions._, treehuggerDSL._
import treehugger.forest._
import definitions._
import treehuggerDSL._
scala> val tree: Tree = Predef_println APPLY LIT("Hello, world!")
[1m[34mtree[0m: [1m[32mtreehugger.forest.Tree[0m = Apply(Ident(println),List(Literal(Constant(Hello, world!))))
scala> treeToString(tree)
[1m[34mres0[0m: [1m[32mString[0m = println("Hello, world!")
The result of the tree
shows the structure,
and treeToString(tree)
shows the Scala source code represented by the tree.
Because it is responsible for formatting of the String
, your code generating code would be more clear using treehugger.
Let’s omit the import statements, and println
statements from here. Here’s how treehugger DSL defines classes:
scala> :paste
// Entering paste mode (ctrl-D to finish)
object sym {
val IntQueue = RootClass.newClass("IntQueue")
val BasicIntQueue = RootClass.newClass("BasicIntQueue")
val buf = BasicIntQueue.newValue("buf")
}
val tree1 = CLASSDEF(sym.IntQueue) withFlags(Flags.ABSTRACT) := BLOCK(
DEF("get", IntClass),
PROC("put") withParams(PARAM("x", IntClass))
)
val tree2 = CLASSDEF(sym.BasicIntQueue) withParents(sym.IntQueue) := BLOCK(
VAL(sym.buf) withFlags(Flags.PRIVATE) :=
NEW(ArrayBufferClass TYPE_OF IntClass),
DEF("get", IntClass) := REF(sym.buf) DOT "remove" APPLY(),
PROC("put") withParams(PARAM("x", IntClass)) := BLOCK(
REF(sym.buf) INFIX("+=") APPLY REF("x")
)
)
// Exiting paste mode, now interpreting.
defined object sym
[1m[34mtree1[0m: [1m[32mtreehugger.forest.ClassDef[0m = ClassDef(Modifiers(abstract, , Map()),Modifiers(, , Map()),IntQueue,List(),List(),Template(List(),ValDef(Modifiers(private, , Map()),Ident(_),EmptyTree),List(DefDef(Modifiers(, , Map()),get,List(),List(),TypeTree(),EmptyTree), ProcDef(Modifiers(, , Map()),put,List(),List(List(ValDef(Modifiers(<param>, , Map()),Typed(Ident(x),TypeTree()),EmptyTree))),EmptyTree))))
[1m[34mtree2[0m: [1m[32mtreehugger.forest.ClassDef[0m = ClassDef(Modifiers(, , Map()),Modifiers(, , Map()),BasicIntQueue,List(),List(),Template(List(TypeTree()),ValDef(Modifiers(private, , Map()),Ident(_),EmptyTree),List(ValDef(Modifiers(private, , Map()),Ident(buf),New(TypeTree())), DefDef(Modifiers(, , Map()),get,List(),List(),TypeTree(),Ap...
Let’s print them out:
scala> treeToString(tree1)
[1m[34mres1[0m: [1m[32mString[0m =
abstract class IntQueue {
def get: Int
def put(x: Int)
}
scala> treeToString(tree2)
[1m[34mres3[0m: [1m[32mString[0m =
class BasicIntQueue extends IntQueue {
private val buf = new scala.collection.mutable.ArrayBuffer[Int]
def get: Int = buf.remove()
def put(x: Int) {
buf += x
}
}
Notice some of the symbols were defined upfront so we could avoid typing in string names.
To be clear where the credit is due, the majority of treehugger’s code is borrowed from the Scala compiler (scalac
) written by Martin Odersky, Paul Phillips, and others. treehugger just took it further to fit its needs.