此文档页面特定于 Scala 2 中发布的功能,这些功能已在 Scala 3 中删除或被替代功能所取代。除非另有说明,此页面中的所有代码示例均假定你使用的是 Scala 2。
Denys Shabalin 实验性
宏和编译器插件中的 AST 操作
准引号主要设计为宏中进行 AST 操作的工具。常见的工作流是使用准引号模式解构参数,然后使用另一个准引号构造重写的结果
// macro that prints the expression code before executing it
object debug {
def apply[T](x: => T): T = macro impl
def impl(c: Context)(x: c.Tree) = { import c.universe._
val q"..$stats" = x
val loggedStats = stats.flatMap { stat =>
val msg = "executing " + showCode(stat)
List(q"println($msg)", stat)
}
q"..$loggedStats"
}
}
// usage
object Test extends App {
def faulty: Int = throw new Exception
debug {
val x = 1
val y = x + faulty
x + y
}
}
// output
executing val x: Int = 1
executing val y: Int = x.+(Test.this.faulty)
java.lang.Exception
...
为了简化与宏的集成,我们还简化了在宏实现中直接使用树,而不是以前可能使用的以 reify 为中心的 Expr
api
// 2.10
object Macro {
def apply(x: Int): Int = macro impl
def impl(c: Context)(x: c.Expr[Int]): c.Expr[Int] = { import c.universe._
c.Expr(q"$x + 1")
}
}
// in 2.11 you can also do it like that
object Macro {
def apply(x: Int): Int = macro impl
def impl(c: Context)(x: c.Tree) = { import c.universe._
q"$x + 1"
}
}
你不再需要使用 c.Expr
包装宏的返回值,也不再需要两次指定参数类型,并且 impl
中的返回类型现在是可选的。
准引号也可以“原样”用于编译器插件,因为反射 API 是编译器 Global
API 的严格子集。
即时编译
借助 ToolBox
API,人们可以在运行时生成、编译和运行 Scala 代码
scala> val code = q"""println("compiled and run at runtime!")"""
scala> val compiledCode = toolbox.compile(code)
scala> val result = compiledCode()
compiled and run at runtime!
result: Any = ()
离线代码生成
借助新的 showCode
“漂亮打印机”,人们可以实现离线代码生成器,该生成器借助准引号执行 AST 操作,然后在将其写入磁盘之前将其序列化为实际源代码
object OfflineCodeGen extends App {
def generateCode() =
q"package mypackage { class MyClass }"
def saveToFile(path: String, code: Tree) = {
val writer = new java.io.PrintWriter(path)
try writer.write(showCode(code))
finally writer.close()
}
saveToFile("myfile.scala", generateCode())
}