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.

questionAnswers(1)

yourAnswerToTheQuestion