2012-06-15 20 views
33

Szukam alternatyw dla -print lub javap jako sposób ustalenia, co kompilator robi w Scali. Z nową biblioteką refleksów/makr, reify wydaje się być dobrym kandydatem do tego, jak pokazano w macrocosm retronym's macrocosm 's desugar. Pokazuje nawet, jak się to robiło, pre-M4.Jaki jest najprostszy sposób użycia reify (uzyskać AST) wyrażenie w Scala?

Pytanie brzmi, jaka jest najkrótsza/najłatwiejsza rzecz, jaką mogę wpisać w REPL Scala, aby uzyskać AST do wyrażenia, po Scala 2.10.0-M4?

+2

łatwiejsze niż 'reflect.runtime.universe.reify (dla (i <- 1 do 10) wydajność i * 2) .tree'? –

+2

@TravisBrown Brzmi wystarczająco dobrze, ale mogę tylko akceptować odpowiedzi, a nie komentarze. :-) –

Odpowiedz

58

Wiele rzeczy wcześniej zdefiniowanych w pakiecie scala.reflect.mirror zostały przeniesione do 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")))) 

Ponadto możliwe jest, aby sprawdzić, czy ciąg zawierający niektóre kodu Scala jest poprawnym wyrażeniem Scala i - jeszcze lepiej - zrobić kilka ocena:

Edytuj. W 2.10.0-RC1 zmieniono niektóre metody ToolBox. parseExpr jest teraz tylko parse, a runExpr nazywa się teraz 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) 

Najbardziej skomplikowana rzecz to surowe przedstawienie drzewa wyrażenia. Gdy chce się używać makr, makra muszą być zdefiniowane w taki sam sposób, jak pokazano w showRaw. Ale z niektórych metod pomocniczych możliwe jest zdefiniowanie niektórych nie tak brzydkie wyglądające implementacje makro:

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 

ale teraz dochodzimy do problemów ze ścieżką zależne od typów - mamy napisać swoje ścieżki wyraźnie, jeśli nie chcą importu im.

+0

Niesamowita odpowiedź! Wyszedłeś ponad obowiązek! :-) –

+1

@ DanielC.Sobral: Wszedłem do środka i walczyłem z kompilatorami, odbiciami i makrami, ale wciąż nie pokonałem ich całkowicie. ;) – sschaef

+0

Możesz użyć trybu: power w REPL, aby uniknąć importu. W każdym razie świetna odpowiedź. – jeslg

3

Jest udoskonalenie nowej wersji 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) 
Powiązane problemy