2013-04-12 8 views
8

ソースのテキストにアクセスできるScalaマクロが既にあるのですか?たとえば、私はこのようなコードを書きたい:実行時にソースコードテキストにアクセスするマクロ

val list = List(1, 2, 3) 
val (text, sum) = (list.sum).withSource{(source, sum) => (source, sum)} 
// would return ("list.sum", 6) 
(list.sum).withSource{(source, sum) => println(s"$source: $sum"} 
// prints list.sum: 6 
+1

私はちょうど次のライブラリに類似の機能(およびその他)があることを知りました:https://github.com/retronym/macrocosm; 'log'、' trace'、 'desugar'を参照してください... – huynhjl

答えて

8

あなたは本当に十分であるソースコードやTreeをしたいですか?

あなたはこのよう Contextprefixを使用することができ Treeについては

:私はちょうど源と値を表示し、値を返すために直接withSourceを再利用することができませんでした

scala> val (x, y) = (1, 2) 
x: Int = 1 
y: Int = 2 

scala> {x + y}.withSource{ (s, r) => s"$s = $r" } 
res15: String = x.+(y) = 3 

scala> val list = List(1, 2, 3) 
list: List[Int] = List(1, 2, 3) 

scala> val (text, sum) = (list.sum).withSource{(source, sum) => (source, sum)} 
text: String = list.sum[Int](math.this.Numeric.IntIsIntegral) 
sum: Int = 6 

scala> (list.sum).withSource{(source, sum) => println(s"$source: $sum")} 
$line38.$read.$iw.$iw.$iw.list.sum[Int](math.this.Numeric.IntIsIntegral): 6 
+1

これは私が書いたものなので、ソースが最適でしょう。しかし、ツリーのトップレベルは十分かもしれません。 – huynhjl

+0

'implicit'キーワードを削除することはできないようですが、' WithSource'を参照するメソッドを持つ 'WithSourceHelper'の暗黙のサブクラスを作成します...制限があるかどうか知っていますか?私はマクロの内容を最小限に保ち、 'withSource'の周りにラッパーを提供できることを期待していました。例えば、私はこれを念頭に置いていました: '暗黙のクラスLogValue [T](t:T)はWithSourceHelper(t){def logvalue = withSource {(s、t)=> {println(s); t}} ' – huynhjl

+0

私は明らかにあなたに賞金を授与するのに24時間必要です... – huynhjl

1

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

implicit class WithSourceHelper[T](source: T) { 
    def withSource[R](f: (String, T) => R): R = macro withSourceImpl[T, R] 
} 

def withSourceImpl[T, R](c: Context)(f: c.Expr[(String, T) => R]): c.Expr[R] = { 
    import c.universe.{reify, Apply} 

    val source = c.prefix.tree match { 
    case Apply(_, List(s)) => s 
    case _ => c.abort(c.enclosingPosition, "can't find source") 
    } 

    reify{ f.splice.apply(c.literal(source.toString).splice, c.Expr[T](source).splice) } 
} 

使い方。 withSourceマクロは同じオブジェクト自体からは利用できません(そのため、わずかに変更されたバージョンのwithSourceをそのファイルに追加することはできません)。WithSourceHelperのサブクラスからwithSourceを呼び出して継承を通じて再利用を制限できません。

誰かが興味がある場合は、ソースの値をログに記録して残りの計算が行われるように値を返すだけで、Seniaの答えを補うことができます。

def logValueImpl[T](c: Context): c.Expr[T] = { 
    import c.universe._ 
    val source = c.prefix.tree match { 
    case Apply(_, List(s)) => s 
    case _ => c.abort(c.enclosingPosition, "can't find source") 
    } 
    val freshName = newTermName(c.fresh("logValue$")) 
    val valDef = ValDef(Modifiers(), freshName, TypeTree(source.tpe), source) 
    val ident = Ident(freshName) 
    val print = reify{ 
    println(c.literal(show(source)).splice + ": " + c.Expr[T](ident).splice) } 
    c.Expr[T](Block(List(valDef, print.tree), ident)) 
} 

次に暗黙の変換としてdef p = macro Debug.logValueImpl[T]と定義します。

List(1, 2, 3).reverse.p.head 
// prints: immutable.this.List.apply[Int](1, 2, 3).reverse: List(3, 2, 1) 

面白い部分は、私はそれを2回適用することができるということです:

List(1, 2, 3).reverse.p.p 

そして、それはlogValueImplマクロが何をしたか、私が表示されます:

{ 
    val logValue$7: List[Int] = immutable.this.List.apply[Int](1, 2, 3).reverse; 
    Predef.println("immutable.this.List.apply[Int](1, 2, 3).reverse: ".+(logValue$7)); 
    logValue$7 
} 

私は、このように使用することができます他のマクロでもうまくいくようです:

f"float ${1.3f}%3.2f; str ${"foo".reverse}%s%n".p` 
//prints: 
{ 
    val arg$1: Float = 1.3; 
    val arg$2: Any = scala.this.Predef.augmentString("foo").reverse; 
    scala.this.Predef.augmentString("float %3.2f; str %s%%n").format(arg$1, arg$2) 
}: float 1.30; str oof%n 

showの代わりにshowRawを使用した場合、他のマクロの書き方を理解するのに便利な拡張マクロのツリーも見ることができます。

関連する問題