2013-05-07 20 views
8

如何獲得一個scala宏以替換方法調用?如何獲得一個scala宏以替換方法調用

我的目標是創建一個名爲ToStringAdder的特徵。假設我有一個具有此特徵的對象x,那麼當我呼叫x.add(any)時,我希望宏實際調用x.add(any, string),其中字符串是AST的字符串表示形式。 (當'any'是一個函數時,這樣我可以有很好的tostring)。

除了Expecty,我所看到的所有這些例子都有效地使用了靜態方法調用:不使用調用宏的對象。 Expecty有以下方法,它給了我一個關於如何檢測'隱式this'的線索,但是我找不到在reify調用中引用它的方法。

private[this] def recordAllValues(expr: Tree): Tree = expr match { 
    case New(_) => expr // only record after ctor call 
    case Literal(_) => expr // don't record 
    // don't record value of implicit "this" added by compiler; couldn't find a better way to detect implicit "this" than via point 
    case Select([email protected](_), y) if getPosition(expr).point == getPosition(x).point => expr 
    case _ => recordValue(recordSubValues(expr), expr) 
    } 

那麼我該如何去取代調用宏的對象的調用。我此刻的代碼如下,它是需要進行排序

trait ToStringAdder { 
    def add(param: Any): Any = macro ToStringAdder.toStringAndValueImpl 
    def add(param: Any, toStringBasedOnAST: String): Any ; //This is the actual method I want the above method call to be replaced by 
} 

object ToStringAdder { 
    def toStringAndValueImpl(c: Context)(param: c.Expr[Any]): c.Expr[Unit] = { 
    import c.universe._ 
    val paramRep = show(param.tree) 
    val paramRepTree = Literal(Constant(paramRep)) 
    val paramRepExpr = c.Expr[String](paramRepTree) 
    //need to put something here 
    reify { c.someMethodCall("something to represent the method any", param.splice, paramRepExpr.splice) } 
    } 
} 
+0

這是,如果我理解正確的一個非常好的問題。我相信這一點是,如果函數上的toString返回一個唯一的AST,則可以在'Function'上實現一個相等運算符。然後可以做一些很棒的事情,比如不用擔心List中的函數重複 - 只需調用toSet。 – samthebest 2013-12-01 12:45:42

回答

4

你可以得到TreeToStringAdder實例作爲c.prefix在物化調用的代碼。

試試這個:

reify { c.Expr[ToStringAdder](c.prefix.tree).splice.add(param.splice, c.literal(paramRep).splice) } 

證明它的工作原理:

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

import scala.language.experimental.macros 
import reflect.macros.Context 

trait ToStringAdder { 
    def add(param: Any): Any = macro ToStringAdder.toStringAndValueImpl 
    def add(param: Any, toStringBasedOnAST: String): Any ; //This is the actual method I want the above method call to be replaced by 
} 

object ToStringAdder { 
    def toStringAndValueImpl(c: Context)(param: c.Expr[Any]): c.Expr[Any] = { 
    import c.universe._ 
    val paramRep = show(param.tree) 
    reify { (c.Expr[ToStringAdder](c.prefix.tree)).splice.add(param.splice, c.literal(paramRep).splice) } 
    } 
} 


// Exiting paste mode, now interpreting. 

import scala.language.experimental.macros 
import reflect.macros.Context 
defined trait ToStringAdder 
defined module ToStringAdder 

scala> class ToStringAdder1 extends ToStringAdder { 
    | def add(param: Any, toStringBasedOnAST: String): Any = s"param: $param \ntoStringBasedOnAST: $toStringBasedOnAST" 
    | } 
defined class ToStringAdder1 

scala> new ToStringAdder1().add((i: Int) => i*2) 
res0: Any = 
param: <function1> 
toStringBasedOnAST: ((i: Int) => i.*(2)) 
+0

感謝您的快速響應。我收到消息「增值不是org.autotdd.scalamacros.ToStringAdder的成員」。而控制/空間代碼洞察並不顯示任何添加選項。我是否需要輸入演員或其他內容? – 2013-05-07 10:31:21

+0

@StaveEscura:我在我的答案中添加了代碼示例。代碼中有一個錯誤:'c.Expr [Unit]'而不是'c.Expr [Any]'作爲toStringAndValueImpl的結果類型。 – senia 2013-05-07 10:44:12

+0

這只是工作。我非常感謝幫助! – 2013-05-07 11:06:19