scala - seguridad - ¿qué es el ast y para qué sirve?
¿Cuál es la forma más fácil de usar reify(obtener un AST de) una expresión en Scala? (2)
Estoy buscando alternativas a la -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 ser un buen candidato para eso, como se muestra en el macroáz de retronym ''s desugar
. Incluso muestra cómo se solía hacer eso, antes de M4.
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?
Hay una mejora de la nueva versión 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)
Muchas cosas previamente definidas en el paquete scala.reflect.mirror
han movido 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 verificar si una cadena que contiene algún código de Scala es una expresión de Scala válida y, aún mejor, hacer alguna evaluación:
Editar . En 2.10.0-RC1 algunos métodos de ToolBox
han sido renombrados. parseExpr
ahora se acaba de 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] = 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)
Lo más complicado aquí es la representación en árbol sin formato de una expresión. Cuando uno quiere usar macros, las macros deben definirse de la misma manera que se muestra con showRaw
. Pero con algunos métodos de ayuda es posible definir algunas implementaciones de macro no tan feas:
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 tenemos problemas con los tipos dependientes de la ruta: debemos escribir sus rutas explícitamente si no queremos importarlas.