2012-12-07 8 views
8

MethodSymbolをScala 2.10のメソッド定義ツリー(DefDef)の左側にするのに便利な方法はありますか?メソッドシンボルとボディからメソッド定義ツリーを作成する

たとえば、特性のインスタンスを取り、その特性のすべてのメソッドをいくつかのデバッグ機能でラップするマクロを作成するとします。私は、次のように記述することができます。

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

object WrapperExample { 
    def wrap[A](a: A): A = macro wrap_impl[A] 

    def wrap_impl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = { 
    import c.universe._ 

    val wrapped = weakTypeOf[A] 
    val f = Select(reify(Predef).tree, "println") 

    val methods = wrapped.declarations.collect { 
     case m: MethodSymbol if !m.isConstructor => DefDef(
     Modifiers(Flag.OVERRIDE), 
     m.name, 
     Nil, Nil, 
     TypeTree(), 
     Block(
      Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil), 
      Select(a.tree, m.name) 
     ) 
    ) 
    }.toList 

    //... 
} 

私は形質を実装する新しい匿名クラスでこれらのメソッドをこだわりの退屈な業務を省かきたし、そのクラス-あなたのインスタンスを作成すると、「あなたの場合、完全な作業例hereを見つけることができます興味がある。

今私は、たとえば、これを書くことができます。

scala> trait X { def foo = 1; def bar = 'a } 
defined trait X 

scala> val x = new X {} 
x: X = [email protected] 

scala> val w: X = WrapperExample.wrap[X](x) 
w: X = [email protected] 

scala> w.foo 
Calling: foo 
res0: Int = 1 

scala> w.bar 
Calling: bar 
res1: Symbol = 'a 

をだから、それは動作しますが、形質がアクセス修飾子、注釈付きパラメータリストを持つメソッドを持っている場合にのみ、非常に簡単に例 - それはないだろう、

私が本当に望むのは、新しいボディにメソッドシンボルとツリーをとり、DefDefを返す関数です。私は手で1書き始めましたが、それはこのように手間のかかるものの多くが含ま:、迷惑な冗長、およびエラーが発生しやすい

List(if (method.isImplicit) Some(Flag.IMPLICIT) else None, ...) 

。新しいReflection APIでこれを行うには、より良い方法がいくつかありますか?

答えて

4

私の知る限りでは、シンボルから定義するツリーへの標準的な方法はありません。

おそらくc.enclosingRun.unitsを繰り返して、あなたが行くにつれてunit.bodyツリーのそれぞれに戻ります。 symbolがシンボルに等しいDefDefが表示されている場合は、目的地に到着しています。 UP。それを再利用する前に定義するツリーduplicateを忘れないでください!

この手法は、世界で最も便利なものではありませんが、機能するはずです。

+0

ありがとう(と+1)、私がラップしようとしている特性がライブラリからのものなら、どうすればよいでしょうか?その場合、私は上記のアプローチに悩まされています。 –

+1

ああ、私はあなたが意味するものを参照してください。このAPIはhttps://github.com/scalamacros/kepler/blob/0acb8a30c379f268e8a3e1340504530493a1a1dc/src/reflect/scala/reflect/api/Trees.scala#L2480で使用できます。 2.10.1で廃止されましたが、実装方法についてはhttps://github.com/scalamacros/kepler/blob/0acb8a30c379f268e8a3e1340504530493a1a1dc/src/reflect/scala/reflect/internal/Trees.scala#L975をご覧ください。 。 –

2

次の方法をお試しください。

val methods = wrapped.declarations.collect { 
    case m: MethodSymbol if !m.isConstructor => DefDef(
    Modifiers(Flag.OVERRIDE), 
    m.name, 
    m.typeParams.map(TypeDef(_)), 
    m.paramss.map(_.map(ValDef(_))), 
    TypeTree(m.returnType), 
    Block(
     Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil), 
     m.paramss.foldLeft(Select(a.tree.duplicate, m.name): Tree)((prev, params) => 
     Apply(prev, params.map(p => Ident(p.name))) 
    ) 
    ) 
) 
}.toList 
+0

同じことをする 'DefDef(sym、tree)'もありますが、このメソッドは 'TypeDef(sym)'と 'ValDef(sym)'とともに廃止されました。 UP。あなたのコードは既存の修飾子を省略しているようだから、同じことはしません。 –

+0

OPの要点への私のコメントもご覧ください:https://gist.github.com/4234441。あなたのコードは、そのコードと同じ微妙な問題があります。 –

+0

ありがとうEugene。それに応じて私の答えを更新しました。これは、厄介なコンパイラクラッシュを伴う私のマクロのいくつかの問題を説明するかもしれません。 – Leo

関連する問題