【发布时间】:2012-06-26 13:48:48
【问题描述】:
我正在使用 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 以用新的 Idents 替换所有 Idents,但它完全依赖于编译器内部结构,因此是一种临时解决方法。很明显,xExp 附带了 showRaw 提供的更多信息。如何清理该 xExp 以允许“x”扮演案例变量的角色?谁能解释这个错误的全貌?
PS:我一直在尝试使用 TreeApi 中的替代*方法系列,但没有成功,但我缺少了解其含义的基础知识。
【问题讨论】:
-
resetAllAttrs工作了吗? -
是的,确实如此。它在显示的代码中以及在具有多个变量“更改”的非常复杂的树中工作。
-
您在
c.Expr结果、block或某些选定的树上调用了resetAllAttrs? -
我现在没有测试,但我相信在 XExp 上应用重置就足够了(重置整个 c.Expr 肯定有效)。我没有尝试在输入块上应用该方法。
-
我建议使用
resetLocalAttrs,因为它的破坏性要小得多:github.com/scala/scala/pull/3305
标签: scala macros abstract-syntax-tree scala-2.10