在Scala中使用表达式的reify(获取AST)的最简单方法是什么? [英] What's the easiest way to use reify (get an AST of) an expression in Scala?
问题描述
我正在寻找-print
或javap
的替代方法,以弄清编译器在Scala中正在做什么.有了新的反射/宏库,reify
似乎是一个不错的选择,如Retronym的 macrocosm 所示的desugar
.它甚至显示了M4之前的原型是怎么做到的.
I'm looking at alternatives to -print
or javap
as a way of figuring out what the compiler is doing in Scala. With the new reflection/macros library, reify
seems a good candidate for that, as shown in retronym's macrocosm's desugar
. It even shows how one used to do that, pre-M4.
问题是,我可以在Scala的REPL上键入最短/最简单的方法来获取表达式的AST(在Scala 2.10.0-M4之后)?
So the question is, what's the shortest/easiest thing I can type on Scala's REPL to get the AST for an expression, post-Scala 2.10.0-M4?
推荐答案
以前在软件包scala.reflect.mirror
中定义的很多东西都已移至scala.reflect.runtime.universe
:
A lot of things previously defined in package scala.reflect.mirror
have moved to scala.reflect.runtime.universe
:
scala> import scala.reflect.runtime.{universe => u}
import scala.reflect.runtime.{universe=>u}
scala> val expr = u reify { 1 to 3 map (_+1) }
expr: reflect.runtime.universe.Expr[scala.collection.immutable.IndexedSeq[Int]] = Expr[scala.collection.immutable.IndexedSeq[Int]](scala.this.Predef.intWrapper(1).to(3).map(((x$1) => x$1.$plus(1)))(immutable.this.IndexedSeq.canBuildFrom))
scala> u show expr.tree
res57: String = scala.this.Predef.intWrapper(1).to(3).map(((x$1) => x$1.$plus(1)))(immutable.this.IndexedSeq.canBuildFrom)
scala> u showRaw expr.tree
res58: String = Apply(Apply(Select(Apply(Select(Apply(Select(Select(This(newTypeName("scala")), newTermName("Predef")), newTermName("intWrapper")), List(Literal(Constant(1)))), newTermName("to")), List(Literal(Constant(3)))), newTermName("map")), List(Function(List(ValDef(Modifiers(<param> <synthetic>), newTermName("x$1"), TypeTree(), EmptyTree)), Apply(Select(Ident(newTermName("x$1")), newTermName("$plus")), List(Literal(Constant(1))))))), List(Select(Select(This(newTypeName("immutable")), newTermName("IndexedSeq")), newTermName("canBuildFrom"))))
此外,可以检查包含某些Scala代码的字符串是否是有效的Scala表达式,并且-更好的是-进行一些评估:
Furthermore it is possible to check if a string containing some Scala code is a valid Scala expression and - even better - do some evaluation:
修改.在2.10.0-RC1中,ToolBox
的某些方法已重命名. parseExpr
现在只是parse
,而runExpr
现在称为eval
.
Edit. In 2.10.0-RC1 some methods of ToolBox
have been renamed. parseExpr
is now just parse
, and runExpr
is now called eval
.
scala> import scala.tools.reflect.ToolBox
import scala.tools.reflect.ToolBox
scala> import scala.reflect.runtime.{currentMirror => m}
import scala.reflect.runtime.{currentMirror=>m}
scala> val tb = m.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@9293709
scala> val tree = tb.parse("1 to 3 map (_+1)")
tree: tb.u.Tree = 1.to(3).map(((x$1) => x$1.$plus(1)))
scala> val eval = tb.eval(tree)
eval: Any = Vector(2, 3, 4)
这里最复杂的是表达式的原始树表示.当要使用宏时,必须按showRaw
所示的相同方式定义宏.但是,通过一些辅助方法,可以定义看起来不太丑陋的宏实现:
The most complicated thing here is the raw tree representation of an expression. When one wants to use macros, the macros have to be defined the same way as shown by showRaw
. But with some helper methods it is possible to define some not so ugly looking macro implementations:
object IntMacro {
import language.experimental.macros
import scala.reflect.makro.Context
import scala.reflect.NameTransformer.encode
def isEven(i: Int): Boolean = macro isEvenImpl
def isEvenImpl(c: Context)(i: c.Expr[Int]): c.Expr[Boolean] = {
import c.universe._
implicit val cc: c.type = c
val `x = i%2` = Apply(Select(i.tree, op("%")), const(2))
val `x == 0` = Apply(Select(`x = i%2`, op("==")), const(0))
c.Expr(`x == 0`)
}
def op(s: String)(implicit c: Context): c.universe.TermName =
c.universe.newTermName(encode(s))
def const(a: Any)(implicit c: Context): List[c.universe.Literal] =
List(c.universe.Literal(c.universe.Constant(a)))
}
scala> import IntMacro._
import IntMacro._
scala> isEven(2)
res60: Boolean = true
scala> isEven(3)
res61: Boolean = false
但是现在我们遇到了路径依赖类型的问题-如果我们不想导入它们,我们必须显式地编写它们的路径.
But now we come in problems with path-dependent-types - we have to write their paths explicitly if we want not import them.
这篇关于在Scala中使用表达式的reify(获取AST)的最简单方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!