我想使用Scala宏在树中找到所有可能的序列创建。
val l = List(1, 2)
val v = Vector(1, 2)
val ab = ArrayBuffer(1, 2)
val s = Seq(1, 2)
但以下匹配不起作用:
case Apply(TypeApply(Select(path, Name("apply")), _), args) if path.tpe <:< weakTypeOf[SeqFactory[Any]] => ...
同样,我需要按序列索引查找所有访问:
val v = Vector(1, 2)
val one = v(0)
路径上的或所有'apply'方法调用,即path.tpe&lt;:&lt; ???? [SEQ [_]
如何写这张支票? 这不编译:
case Apply(Select(path, Name("apply")), List(idx)) if path.tpe <:< typeOf[Seq[_]]
答案 0 :(得分:5)
我不确定你到底尝试了什么,但是一眼就看出你至少丢失了两件:你需要使用Traverser
来遍历树的所有后代,并且你需要对每个候选树进行类型检查,以确保它能够让你知道你有一个SeqFactory
的应用程序。
例如,这是一个快速实现,它在编译时打印一个类中的所有序列创建树:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object SeqSearch {
def printCreatesInClass = macro printCreatesInClass_impl
def printCreatesInClass_impl(c: Context) = {
import c.universe._
import scala.collection.generic.SeqFactory
val factorySym = c.typeOf[SeqFactory[Seq]].typeSymbol
def isCreation(tree: Tree) = c.typeCheck(tree) match {
case Apply(TypeApply(Select(factory, name), _), _) if
factory.tpe.baseClasses.contains(factorySym) &&
name == newTermName("apply") => true
case _ => false
}
object printCreates extends Traverser {
override def traverse(tree: Tree) = tree match {
case application @ Apply(_, args) if isCreation(application) =>
println("Matched create: " + application)
super.traverseTrees(args)
case _ => super.traverse(tree)
}
}
printCreates(c.enclosingClass)
c.literalUnit
}
}
它的工作原理如下:
scala> class Foo {
| SeqSearch.printCreatesInClass
| val l = List(1, 2)
| val v = Vector(1, 2)
| val ab = collection.mutable.ArrayBuffer(1, 2)
| val s = Seq(1, 2)
| }
Matched create: List(1, 2)
Matched create: Vector(1, 2)
Matched create: collection.mutable.ArrayBuffer(1, 2)
Matched create: Seq(1, 2)
defined class Foo
查找访问类似 - 只需将以下方法添加到上述对象:
def printAccessesInClass = macro printAccessesInClass_impl
def printAccessesInClass_impl(c: Context) = {
import c.universe._
def isAccess(tree: Tree) = c.typeCheck(tree) match {
case Apply(Select(seq, name), _) if
seq.tpe <:< typeOf[Seq[Any]] &&
name == newTermName("apply") => true
case _ => false
}
object printAccesses extends Traverser {
override def traverse(tree: Tree) = tree match {
case application @ Apply(_, args) if isAccess(application) =>
println("Matched access: " + application)
super.traverseTrees(args)
case _ => super.traverse(tree)
}
}
printAccesses(c.enclosingClass)
c.literalUnit
}
然后:
scala> class Foo {
| SeqSearch.printCreatesInClass
| SeqSearch.printAccessesInClass
| val xs = List(1, 2, 3)
| val xh = xs(0)
| }
Matched create: List(1, 2, 3)
Matched access: xs(0)
defined class Foo
调整Traverser
的副作用以捕获您感兴趣的任何信息,而不是仅仅在编译时打印树,应该很容易。