Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
581 views
in Technique[技术] by (71.8m points)

macros - What's the easiest way to use reify (get an AST of) an expression in Scala?

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.

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?

question from:https://stackoverflow.com/questions/11055210/whats-the-easiest-way-to-use-reify-get-an-ast-of-an-expression-in-scala

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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"))))

Furthermore it is possible to check if a string containing some Scala code is a valid Scala expression and - even better - do some evaluation:

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)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...