Como posso reutilizar subárvores de definição (AST) em uma macro?
Eu estou trabalhando em um DSL embarcado Scala e macros estão se tornando uma ferramenta principal para alcançar meus objetivos. Eu estou recebendo um erro ao tentar reutilizar uma subárvore da expressão de macro de entrada para o resultante. A situação é bastante complexa, mas (espero) a simplifiquei para sua compreensão.
Suponha que tenhamos este código:
val y = transform {
val x = 3
x
}
println(y) // prints 3
onde 'transformar' é a macro envolvida. Embora pareça que não faz absolutamente nada, está realmente transformando o bloco mostrado nesta expressão:
3 match { case x => x }
Isso é feito com essa implementação de macro:
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
}
}
Notar quexNam corresponde ao nome da variável,xVal corresponde ao seu valor associado e finalmentexExp corresponde com a expressão que contém a variável. Bem, se eu imprimir a árvore bruta xExp eu receboIdent (newTermName ("x"))e é exatamente isso que é definido no caso RHS. Como a expressão pode ser modificada (por exemplo x + 2 em vez de x), esta não é uma solução válida para mim. O que eu quero fazer é reutilizar a árvore xExp (veja o comentário xExp) enquanto altera o significado 'x' (é uma definição na expressão de entrada, mas será uma variável LHS de caso na saída), mas lança uma erro longo resumido em:
symbol value x does not exist in org.habla.main.Main$delayedInit$body.apply); see the error output for details.
Minha solução atual consiste na análise do xExp para sustentar todos os Idents com novos, mas é totalmente dependente dos componentes internos do compilador e, portanto, de uma solução temporária. É óbvio que o xExp vem junto com mais informações que o oferecido pelo showRaw. Como posso limpar esse xExp para permitir que 'x' role a variável case? Alguém pode explicar toda a imagem desse erro?
PS: Eu tenho tentado, sem sucesso, usar a família do método substituto * doTreeApi mas estou sentindo falta do básico para entender suas implicações.