Ich versuche, ein Scala-Compiler-Plugin zu schreiben, das extrem allgemeine Code-Generierung erlaubt: etwas wie die Allgemeingültigkeit des C-Präprozessors, aber ein bisschen typsicherer (ich bin mir nicht sicher, ob das ist eine schreckliche Idee, aber es ist eine lustige Übung). Mein idealer Anwendungsfall sieht ungefähr so aus:Pass closure zu Scala-Compiler-Plugin
// 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(...))
Der wichtige Plugin-Code könnte wie folgt aussehen (basierend auf 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
}
...
Jede Idee, wie dies zu tun? Bitte sag nicht "benutze nur Makros"; zumindest in 2.10 sind sie nicht allgemein genug.
BTW, sehe ich zwei Probleme mit dem Ansatz, den ich skizziert habe: 1) Das Compiler-Plugin nimmt eine AST, keine Schließung. Es würde eine Art der Erstellung des Abschlusses benötigen, wahrscheinlich eine Build-Abhängigkeit vom Benutzercode hinzufügen. 2) Der Benutzer hat keinen Zugriff auf scala.reflect.internal.Trees.Tree, nur scala.reflect.runtime.universe.Tree, also müsste das Plugin zwischen den beiden übersetzen.
Es ist definitiv eine schreckliche Idee - aber eine gute Übung;) - Sie sollten darüber nachdenken, in Hacking MakroImpl im Paradies zu suchen. –