I'm trying to write a Scala compiler plugin that will allow extremely general code generation: something like the generality of the C preprocessor, but a bit more typesafe (I'm not sure if this is a terrible idea, but it's a fun exercise). My ideal use case looks something like this:
// User code. This represents some function that might take some args
// and outputs an abstract syntax tree.
def createFooTree(...): scala.reflect.runtime.universe.Tree = ...
// Later user code (maybe separate compilation?). Here the user generates
// code programmatically using the function call to |createFooTree| and inserts
// the code using insertTree.
insertTree(createFooTree(...))
The important plugin code might look like this (based on this):
class InsertTreeComponent(val global: Global)
extends PluginComponent
with TypingTransformers {
import global._
import definitions._
override val phaseName = "insertTree"
override val runsRightAfter = Some("parser")
override val runsAfter = runsRightAfter.toList
override val runsBefore = List[String]("typer")
def newPhase(prev: Phase): StdPhase = new StdPhase(prev) {
def apply(unit: CompilationUnit) {
val onTransformer = new TypingTransformer(unit) {
override def transform(tree: Tree): Tree = tree match {
case orig @ Apply(
function,
// |treeClosure| is the closure we passed, which should
// evaluate to a Tree (albeit a runtime Tree).
// The function.toString bit matches anything that looks like a
// function call with a function called |insertTree|.
treeClosure) if (function.toString == "insertTree") => {
// This function evaluates and returns the Tree, inserting it
// into the call site as automatically-generated code.
// Unfortunately, the following line isn't valid.
eval(treeClosure): Tree
}
...
Any idea how to do this? Please don't say "just use macros"; at least in 2.10, they aren't general enough.
BTW, I see two problems with the approach I've outlined: 1) The compiler plugin takes an AST, not a closure. It would need some way of creating the closure, probably adding a build dependency on the user code. 2) The user doesn't have access to scala.reflect.internal.Trees.Tree, only scala.reflect.runtime.universe.Tree, so the plugin would need to translate between the two.