2012-06-15 5 views
33

Ich suche Alternativen zu -print oder javap als eine Möglichkeit herauszufinden, was der Compiler in Scala tut. Mit der neuen Reflektions-/Makro-Bibliothek scheint reify ein guter Kandidat dafür zu sein, wie in macrocosm von Retronym desugar gezeigt wird. Es zeigt sogar, wie man das vor M4 gemacht hat.Was ist der einfachste Weg, um einen Ausdruck in Scala zu verwenden, um einen AST zu erhalten?

Also die Frage ist, was ist die kürzeste/einfachste Sache, die ich auf Scalas REPL tippen kann, um den AST für einen Ausdruck zu erhalten, Post-Scala 2.10.0-M4?

+2

Einfacher als 'reflect.runtime.universe.reify (für (i <- 1 bis 10) Ausbeute i * 2) .tree'? –

+2

@TravisBrown Hört sich gut an, aber ich kann nur Antworten akzeptieren, keine Kommentare. :-) –

Antwort

58

Viele Dinge vorher im Paket definiert scala.reflect.mirror zu scala.reflect.runtime.universe bewegt hat:

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

Das Weiteren ist es möglich, wenn ein String-Code enthält einige Scala zu prüfen, ein gültiger Scala Ausdruck ist und - noch besser - einige tun Bewertung:

Bearbeiten. In 2.10.0-RC1 wurden einige Methoden von ToolBox umbenannt. parseExpr ist jetzt nur parse, und runExpr heißt jetzt 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) 

Die komplizierteste Sache hier ist die rohe Baumdarstellung eines Ausdrucks. Wenn Makros verwendet werden sollen, müssen die Makros genau so definiert werden, wie es in showRaw angezeigt wird. Aber mit einigen Helfermethoden ist es möglich, einige nicht so hässlich makro-Implementierungen zu definieren:

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 

Aber jetzt kommen wir in Probleme mit wegabhängigen-Typen - müssen wir ihre Wege explizit schreiben, wenn wir nicht importieren wollen Sie.

+0

Super Antwort! Du bist über den Ruf der Pflicht hinaus gegangen! :-) –

+1

@ DanielC.Sobral: Ich ging hinein und kämpfte gegen Compiler, Reflektionen und Makros, aber ich habe sie immer noch nicht komplett geschlagen. ;) – sschaef

+0

Sie können den: Power-Modus in der REPL verwenden, nur um einige Importe zu vermeiden. Wie auch immer, gute Antwort. – jeslg

3

Es gibt verbessern neue Scala Version

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) 
Verwandte Themen