在Scala中使用表达式的reify(获取AST)的最简单方法是什么? [英] What's the easiest way to use reify (get an AST of) an expression in Scala?

查看:113
本文介绍了在Scala中使用表达式的reify(获取AST)的最简单方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找-printjavap的替代方法,以弄清编译器在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆