2013-05-08 54 views
2

有人可以幫助我理解允許AST在scala中進行修改的轉換對象嗎?AST轉換對象如何工作?

網絡上有很多例子,但我很難理解遞歸是如何發生的。

E.g.如果我有這樣的AST:

def foo = { 
true 
} 

def foo = true 

並希望將其轉換爲

def foo = { 
println("foo") 
true 
} 

如何轉換功能應該像。在這一點上我有這樣的事情:

override def transform(tree: Tree) = tree match { 
case defdef @ DefDef(_,_,_,_,_,rhs) => rhs match{ 
    case b: Block => treeCopy.Block(b, createPrintln :: b.stats, b.expr)   
    case _ => //Manage functions without block 
} 
case t => super.transform(t) 
} 

private def createPrintln = Apply(Select(Ident("System.out"), newTermName("println")), List(Literal(Constant("foo")))) 

但不工作,誠實我只是將我在例子中看到,但對樹正在建設想不通。對「傻瓜」的解釋將不勝感激。

[編輯]

塞尼亞例子是好的,但有一個稍微複雜一點的樹一樣,當我仍然停留:

imports bla.bla 
class MyObject{ 

    val x = 0 
    def foo = true 
    def foo2 = { 1 } 
} 
object MyObject 

預期結果:

import bla.bla 
class MyObject{ 

    val x = 0 
    def foo = { println("foo"); true } 
    def foo2 = { println("foo"); 1 } 
} 
object MyObject 
+0

@senia對於熟悉Scala AST的人來說,這可能是顯而易見的,但對我來說不是,我想了解更多:什麼是'treeCopy'? 'transform'方法覆蓋或實現什麼類或接口? (可以在答案中的代碼更完整或鏈接到相關文檔?) – Suma 2015-05-14 12:09:31

回答

5

你的原Tree是這樣的:

scala> import scala.reflect.runtime.universe._ 
import scala.reflect.runtime.universe._ 

scala> showRaw{ reify { def foo = { true } }.tree } 
res0: String = Block(List(DefDef(Modifiers(), newTermName("foo"), List(), List(), TypeTree(), Literal(Constant(true)))), Literal(Constant(()))) 

無外Block

scala> showRaw{ reify { def foo = { true } }.tree match { case Block(List(defdef), _) => defdef } } 
res1: String = DefDef(Modifiers(), newTermName("foo"), List(), List(), TypeTree(), Literal(Constant(true))) 

所以你rhs變量不是Block

所以,你應該用Block(createPrintln, t)

更換//Manage functions without block我猜你transform方法應該返回DefDef,不Block

def addPrintln(t: Tree): Block = t match { 
    case b :Block => treeCopy.Block(b, createPrintln :: b.stats, b.expr) 
    case t => Block(createPrintln, t) 
} 

override def transform(tree: Tree) = tree match { 
    case defdef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => 
    treeCopy.DefDef(defdef, mods, name, tparams, vparamss, tpt, addPrintln(rhs)) 
    case t => super.transform(t) 
} 

測試:

scala> def createPrintln = Apply(Select(Ident("System.out"), newTermName("println")), List(Literal(Constant("foo")))) 
createPrintln: reflect.runtime.universe.Apply 

scala> def addPrintln(t: Tree): Block = t match { 
    | case b :Block => treeCopy.Block(b, createPrintln :: b.stats, b.expr) 
    | case t => Block(createPrintln, t) 
    | } 
addPrintln: (t: reflect.runtime.universe.Tree)reflect.runtime.universe.Block 

scala> def transform(tree: Tree) = tree match { 
    | case defdef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => 
    |  treeCopy.DefDef(defdef, mods, name, tparams, vparamss, tpt, addPrintln(rhs)) 
    | } 
transform: (tree: reflect.runtime.universe.Tree)reflect.runtime.universe.DefDef 

scala> val defdef = reify { def foo = { true } }.tree match { case Block(List(defdef), _) => defdef } 
defdef: reflect.runtime.universe.Tree = def foo = true 

scala> transform(defdef) 
res0: reflect.runtime.universe.DefDef = 
def foo = { 
    System.out.println("foo"); 
    true 
} 

UPD:

super.transform調用父類的實現。有關代碼,請參見internal implementation

+0

Tnx senia,您對重構addPrintln的建議是一個很好的建議。如果我有一個更復雜的樹,我想忽略所有不是defdef的東西,會發生什麼?爲了重新創建新的樹,變換方法應該如何改變?我已經看到,在許多例子中,當匹配不是應該被修改的表達式時,它們在super.transform(tree)之類的轉換中重複出現,但是我不知道如何在遞歸之後重新創建樹。 https://github.com/retronym/macrocosm/blob/171be7e/src/main/scala/com/github/retronym/macrocosm/Macrocosm.scala#L129(看看變換) – rsan 2013-05-08 06:57:02

+0

編輯的問題,以顯示問題,我忽略了你的變換方法,但當樹長大時會變得混亂。對不起,我希望不要懶惰,並試圖得到一個簡單的答案,我真的不明白這個東西:( – rsan 2013-05-08 07:04:04

+0

這不是一個遞歸調用。這是一個父母[實施]的呼籲(https://github.com/scala /scala/blob/a702b780bb4b6e6684fc39a82119600a027a01b0/src/reflect/scala/reflect/api/Trees.scala#L3014)。 – senia 2013-05-08 07:04:50