Я работаю со встроенным 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», означающее (, это определение во входном выражении, но будет переменной case LHS в выходном. ), но запускает длинную ошибку, описанную в:
symbol value x does not exist in org.habla.main.Main$delayedInit$body.apply); see the error output for details.
Мое текущее решение состоит в анализе xExp для замены всех идентификаторов новыми, но это полностью зависит от внутренних компонентов компилятора и, следовательно, временного обходного пути. Очевидно, что xExp содержит больше информации, чем предлагает showRaw.Как я могу очистить этот xExp, чтобы разрешить «x» роль переменной case? Может ли кто-нибудь объяснить всю картину этой ошибки?
PS :Я безуспешно пытался использовать заменяющее семейство методов *из TreeApi , но мне не хватает основ, чтобы понять его значение.