使用Scala宏在树中查找所有可能的序列创建

时间:2013-07-31 12:02:38

标签: scala scala-2.10 scala-macros

我想使用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[_]]

1 个答案:

答案 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的副作用以捕获您感兴趣的任何信息,而不是仅仅在编译时打印树,应该很容易。