Вопрос по scala, macros, scala-2.10 – Какой самый простой способ использовать reify (получить AST) выражения в Scala?

36

Я смотрю на альтернативы-print или жеjavap как способ выяснить, что делает компилятор в Scala. С новой библиотекой отражений / макросов,reify кажется хорошим кандидатом для этого, как показано в ретронимемакрокосм& APOS; sdesugar, Это даже показывает, как раньше это делали, до M4.

Таким образом, вопрос в том, что самое короткое / простое, что я могу набрать в REPL Scala, чтобы получить AST для выражения, после Scala 2.10.0-M4?

@ TravisBrown Звучит достаточно хорошо, но я могу принимать только ответы, а не комментарии. :-) Daniel C. Sobral
Легче чемreflect.runtime.universe.reify(for (i <- 1 to 10) yield i * 2).tree? Travis Brown

Ваш Ответ

2   ответа
62

Много вещей, ранее определенных в упаковкеscala.reflect.mirror переехали в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 и, что еще лучше, выполнить некоторую оценку:

Edit, В 2.10.0-RC1 некоторые методыToolBox были переименованы.parseExpr сейчас простоparse, а такжеrunExpr сейчас называется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] = [email protected]

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, Но с помощью некоторых вспомогательных методов можно определить некоторые не очень уродливые реализации макросов:

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

Но теперь у нас возникают проблемы с типами, зависящими от пути, - мы должны явно указывать их пути, если не хотим их импортировать.

Вы можете использовать режим: power в REPL, чтобы избежать импорта. Во всяком случае, отличный ответ.
На самом деле, похоже, что Даниэль опубликовал отдельный вопрос для этого:stackoverflow.com/questions/14695609/….
ТотshowRaw Функция, которую я искал около 15 минут.
@ DanielC.Sobral: Я вошел внутрь и боролся против компиляторов, отражений и макросов, но я до сих пор не победил их полностью. ;)
Отличный ответ! Вы вышли за пределы служебного долга! :-) Daniel C. Sobral
3

Улучшение новой версии Scala

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] = [email protected]

scala> val tree = tb.parseExpr("1 to 3 map (_+1)")
<console>:10: error: value parseExpr is not a member of scala.tools.reflect.ToolBox[reflect.runtime.universe.type]
       val tree = tb.parseExpr("1 to 3 map (_+1)")
                     ^

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)

Похожие вопросы