是否已经或者是否可以使用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
答案 0 :(得分:8)
您真的想要源代码还是Tree
就足够了?
对于Tree
,您可以使用prefix
Context
这样的内容:
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 :(得分:1)
我无法直接重用withSource
来打印源和值并返回值。 withSource
宏不能从同一个对象本身使用(因此我不能只在该文件中添加稍微修改后的withSource版本)并且我无法从{{的子类调用withSource
1}},通过继承限制重用。
如果有人感兴趣,这里是Senia回答的补充,只需用源记录值并返回值,这样就可以进行其余的计算。
WithSourceHelper
然后我将其定义为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)
它会告诉我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
而不是showRaw
,我甚至可以看到扩展宏的树,这可能会很方便地弄清楚如何编写其他宏。