2013-04-12 27 views
8

Kaynağın metnine erişmemi sağlayan bir Scala makrosunun bulunması zaten mümkün mü? Mesela böyle kod yazmak istiyorum:Çalışma zamanında kaynak kodu metnine erişmek için makro

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

Şu kitaplıkta benzer işlevlerin (ve daha fazlasının) bulunduğunu öğrendim: https://github.com/retronym/macrocosm; 'log', 'trace', 'desugar' ... 'a bakın. – huynhjl

cevap

8

Eğer gerçekten bir kaynak kodunu istiyorsunuz ya Tree yeterli mi?

Kullanımı
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) } 
} 

:

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

Kaynak en iyisi olurdu, çünkü yazdıklarım buydu. Ancak ağacın üst seviyesi yeterince iyi olabilir. – huynhjl

+0

'implicit' anahtar sözcüğünü kaldırmak mümkün değil ve bunun yerine "withSourceHelper" in bir "impactit" alt sınıfını oluşturun, "withSource" ile ilgili bir yöntem var ... Bir sınırlama olup olmadığını biliyor musunuz? Makro malzemeyi en aza indirmeyi umuyordum ve 'withSource' etrafında sarıcı sağlayabiliyordum. Örneğin bunu aklımda tutmuştum: 'örtük sınıf LogValue [T] (t: T), WithSourceHelper (t) {def logvalue = withSource {(s, t) => {println (s); t}} ' – huynhjl

+0

Size ödül vermek için 24 saate ihtiyacım var. – huynhjl

1

Sadece kaynak ve değer yazdırmak için doğrudan withSource yeniden mümkün değildi ve değer döndürmek

Tree için böyle Context ait prefix kullanabilirsiniz . withSource makrosu, aynı nesnenin kendisinden kullanılamaz (bu yüzden, bu dosyada benim küçük bir değiştirilmiş sürümümüzü değiştiremiyorum) ve WithSourceHelper alt sınıfından withSource numaralı telefonu arayamıyorum, bu da miras yoluyla yeniden kullanılabilir.

Herkesin ilgisini çekmesi durumunda, Senia'nın cevabı yalnızca değeri kaynakla günlüğe kaydetme ve değeri geri döndürme işleminin geri kalanının gerçekleşmesi için geri göndermesi için bir tamamlayıcıdır.

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)) 
} 

Sonra def p = macro Debug.logValueImpl[T] örtülü bir dönüşüm olarak tanımlar.

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

komik kısmı iki kez uygulayabilirsiniz olmasıdır: Sonra bu gibi kullanabilirsiniz

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

Ve logValueImpl makro ne yaptığını gösterecektir:

{ 
    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 
} 

Diğer makrolarla da çalışıyor gibi görünüyor:

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 yerine showRaw kullansaydım daha da ilginç bir şekilde, genişletilmiş makronun ağacını bile görebilirim, bu da diğer makroların nasıl yazılacağını anlamak için kullanışlı olabilir.

İlgili konular