我在 Scala 嵌入式 DSL 中工作,宏正在成为实现我的目的的主要工具。尝试将传入宏表达式中的子树重用到结果中时出现错误。情况相当复杂,但是(我希望)我已经简化了它以便理解。
假设我们有这样的代码:
val y = transform {
val x = 3
x
}
println(y) // prints 3
其中 'transform' 是所涉及的宏。尽管它看起来似乎什么也没做,但它确实将显示的块转换为这个表达式:
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”扮演案例变量的角色?谁能解释这个错误的全貌?
PS:我一直在尝试使用TreeApi中的替代 * 方法系列,但没有成功,但我缺少了解其含义的基础知识。