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

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

问题描述

我正在寻找 -printjavap 的替代方案,以此来确定编译器在 Scala 中的作用.使用新的反射/宏库,reify 似乎是一个很好的候选者,如 retronym 的 所示macrocosm脱糖.它甚至显示了人们过去是如何做到这一点的,在 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 2.10.0-M4 之后,我可以在 Scala 的 REPL 上输入的最短/最简单的东西是什么以获得表达式的 AST?

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 现在只是 parserunExpr 现在被称为 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天全站免登陆