Как я могу повторно использовать поддеревья определения (AST) в макросе?
Я работаю во встроенном DSL Scala, и макросы становятся основным инструментом для достижения моих целей. Я получаю сообщение об ошибке при попытке повторно использовать поддерево из входящего выражения макроса в полученное. Ситуация довольно сложная, но (я надеюсь) я упростил ее для ее понимания.
Предположим, у нас есть этот код:
val y = transform {
val x = 3
x
}
println(y) // prints 3
где «трансформировать» это задействованный макрос. Хотя может показаться, что он абсолютно ничего не делает, он действительно преобразовывает показанный блок в это выражение:
3 match { case x => x }
Это делается с помощью этой реализации макроса:
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
}
}
Заметить, чтоxNam соответствует имени переменной,xVal соответствует его связанному значению и, наконец,xExp соответствует выражению, содержащему переменную. Хорошо, если я распечатаю необработанное дерево xExp, я получаюIdent(newTermName("x"))и это именно то, что установлено в случае RHS. Поскольку выражение может быть изменено (например, x + 2 вместо x), это не является правильным решением для меня. То, что я хочу сделать, это повторно использовать дерево xExp (см. Комментарий xExp), изменяя при этом «x» apos; значение (это определение во входном выражении, но будет выходной переменной LHS в выходном), но оно запускает длинную ошибку, обобщенную в:
symbol value x does not exist in org.habla.main.Main$delayedInit$body.apply); see the error output for details.
Мое текущее решение состоит в разборе xExp для установки всех Ident на новые, но это полностью зависит от внутренних компонентов компилятора и, таким образом, временного обходного пути. Очевидно, что xExp поставляется вместе с дополнительной информацией, которую предлагает showRaw. Как я могу очистить этот xExp для разрешения & x; роль переменной случая? Кто-нибудь может объяснить всю картину этой ошибки?
PS: я безуспешно пытался использовать семейство замещающих * методов изTreeApi но мне не хватает основ, чтобы понять его значение.