27 votes

Quelle est la façon la plus simple d'utiliser reify (obtenir un AST de) une expression dans Scala?

Je suis à la recherche d'alternatives à l' -print ou javap comme un moyen de déterminer ce que le compilateur fait en Scala. Avec la nouvelle réflexion/macros bibliothèque, reify me semble un bon candidat pour que, comme indiqué dans retronym du macrocosmes' desugar. Il montre même comment on l'habitude de le faire, pré-M4.

La question est donc, quel est le plus court/la chose la plus facile, je peux taper sur Scala REPL pour obtenir de l'AST, une expression, post-Scala 2.10.0-M4?

50voto

sschaef Points 20242

Beaucoup de choses déjà définis dans le paquet scala.reflect.mirror ont déménagé 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"))))

En outre, il est possible de vérifier si une chaîne de caractères contenant certains Scala code est valide Scala expression et - mieux encore - certains d'évaluation:

Edit. Dans 2.10.0-RC1 certaines méthodes d' ToolBox ont été renommés. parseExpr est maintenant juste parse, et runExpr est maintenant appelé 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)

Le plus compliqué, c'est ici la crue de l'arbre de la représentation d'une expression. Lorsque l'on veut utiliser les macros les macros doivent être définis de la même manière, comme indiqué par showRaw. Mais avec certaines méthodes d'assistance, il est possible de définir quelques pas afin de laid à la recherche macro implémentations:

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

Mais nous arrivons maintenant à des problèmes avec path-dependent-types - nous avons à écrire leurs chemins explicitement si nous ne voulons pas de les importer.

2voto

Mark Simon Points 15

Il y a une amélioration de la nouvelle version de 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] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@78dc5f15

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)
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X