Próbuję napisać wtyczkę kompilatora Scala, która pozwoli na generowanie generalnie bardzo generalnego kodu: coś takiego jak ogólność preprocesora C, ale trochę więcej typówafe (nie jestem pewien, czy to jest straszny pomysł, ale to zabawne ćwiczenie). Mój idealny przypadek użycia wygląda mniej więcej tak:Przejście do wtyczki kompilatora Scala
// 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(...))
Ważną kod wtyczki może wyglądać następująco (w oparciu o 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
}
...
Każdy pomysł jak to zrobić? Proszę nie mówić "po prostu używaj makr"; przynajmniej w 2.10, nie są one wystarczająco ogólne.
BTW, widzę dwa problemy z podejściem, które opisałem: 1) Wtyczka kompilatora przyjmuje AST, a nie zamknięcie. Będzie potrzebował jakiegoś sposobu na zamknięcie, prawdopodobnie dodając zależność od kodu użytkownika. 2) Użytkownik nie ma dostępu do scala.reflect.internal.Trees.Tree, tylko scala.reflect.runtime.universe.Tree, więc wtyczka musiałaby przetłumaczyć między tymi dwoma.
To zdecydowanie przerażający pomysł - ale wspaniałe ćwiczenie;) - należy myśleć o tym, jak zajrzeć do makrofotografii w raju. –