2012-06-15 14 views
33

Estoy buscando alternativas a -print o javap como una forma de averiguar qué está haciendo el compilador en Scala. Con la nueva biblioteca de reflexión/macros, reify parece un buen candidato para eso, como se muestra en macrocosm de retronym desugar. Incluso muestra cómo se solía hacer eso, antes de M4.¿Cuál es la forma más fácil de usar reify (obtener un AST de) una expresión en Scala?

Entonces la pregunta es, ¿cuál es la cosa más corta/más fácil que puedo escribir en el REPL de Scala para obtener el AST para una expresión, post-Scala 2.10.0-M4?

+2

Más fácil que 'reflect.runtime.universe.reify (para (i <- 1 a 10) rendimiento i * 2) .tree'? –

+2

@TravisBrown Suena suficientemente bueno, pero solo puedo aceptar respuestas, no comentarios. :-) –

Respuesta

58

Muchas cosas previamente definidos en el paquete scala.reflect.mirror se han trasladado a 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")))) 

Además, es posible comprobar si una cadena que contiene un código Scala es una expresión válida Scala y - aún mejor - hacer algunas evaluación:

Editar. En 2.10.0-RC1 algunos métodos de ToolBox han cambiado de nombre. parseExpr ahora es solo parse, y runExpr ahora se llama 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) 

Lo más complicado aquí es la representación en árbol sin procesar de una expresión. Cuando uno quiere usar macros, las macros deben definirse de la misma manera que se muestra en showRaw. Sin embargo, con algunos métodos de ayuda que es posible definir algunos no tan feos que buscan implementaciones macro:

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 

Pero ahora llegamos en problemas con dependientes de la trayectoria de tipo - tenemos que escribir sus caminos de forma explícita si no queremos importación ellos.

+0

¡Respuesta impresionante! ¡Fuiste más allá del llamado del deber! :-) –

+1

@ DanielC.Sobral: entré y luché contra compiladores, reflexiones y macros, pero todavía no los he superado del todo. ;) – sschaef

+0

Puede usar el: modo de potencia en REPL solo para evitar algunas importaciones. De todos modos, gran respuesta. – jeslg

3

Hay mejorar de nueva versión 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) 
Cuestiones relacionadas