Jak mogę ponownie wykorzystać poddrzewa definicji (AST) w makrze?
Pracuję w osadzonym DSL Scala, a makra stają się głównym narzędziem do osiągnięcia moich celów. Otrzymuję błąd podczas próby ponownego użycia poddrzewa z przychodzącego wyrażenia makro do wynikowego. Sytuacja jest dość złożona, ale (mam nadzieję) uprościłem ją dla zrozumienia.
Załóżmy, że mamy ten kod:
val y = transform {
val x = 3
x
}
println(y) // prints 3
gdzie „transformacja” jest zaangażowanym makro. Chociaż może się wydawać, że absolutnie nic nie robi, to naprawdę przekształca pokazany blok w to wyrażenie:
3 match { case x => x }
Wykonuje się to za pomocą tej makropolecenia:
def transform(c: Context)(block: c.Expr[Int]): c.Expr[Int] = {
import c.universe._
import definitions._
block.tree match {
/* {
* val xNam = xVal
* xExp
* }
*/
case Block(List(ValDef(_, xNam, _, xVal)), xExp) =>
println("# " + showRaw(xExp)) // prints Ident(newTermName("x"))
c.Expr(
Match(
xVal,
List(CaseDef(
Bind(xNam, Ident(newTermName("_"))),
EmptyTree,
/* xExp */ Ident(newTermName("x")) ))))
case _ =>
c.error(c.enclosingPosition, "Can't transform block to function")
block // keep original expression
}
}
Zauważ, żexNam odpowiada nazwie zmiennej,xVal odpowiada jej powiązanej wartości i wreszciexExp odpowiada wyrażeniu zawierającemu zmienną. Cóż, jeśli wydrukuję surowe drzewo xExp, otrzymamIdent (newTermName ("x"))i dokładnie tak jest w przypadku RHS. Ponieważ wyrażenie może zostać zmodyfikowane (na przykład x + 2 zamiast x), nie jest to dla mnie poprawne rozwiązanie. Chcę ponownie użyć drzewa xExp (zobacz komentarz xExp), zmieniając znaczenie „x” (jest to definicja w wyrażeniu wejściowym, ale będzie to zmienna LHS w przypadku wyjścia), ale uruchamia długi błąd podsumowany w:
symbol value x does not exist in org.habla.main.Main$delayedInit$body.apply); see the error output for details.
Moje obecne rozwiązanie polega na analizowaniu xExp, aby obsłużyć wszystkie Identy nowymi, ale jest to całkowicie zależne od wewnętrznych elementów kompilatora, a więc tymczasowego obejścia. Jest oczywiste, że xExp pojawia się wraz z większą ilością informacji, które oferuje showRaw. Jak mogę wyczyścić xExp, aby zezwolić „x” na przypisanie zmiennej case? Czy ktoś może wyjaśnić cały obraz tego błędu?
PS: Próbowałem bezskutecznie użyć rodziny metod zastępczych * zTreeApi ale brakuje mi podstaw, aby zrozumieć jego implikacje.