2

我想使用 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)

或所有“应用”方法调用路径,其中 path.tpe <:< ????[Seq[_]

这张支票怎么写?这不编译:

case Apply(Select(path, Name("apply")), List(idx)) if path.tpe <:< typeOf[Seq[_]]
4

1 回答 1

5

我不确定您到底尝试了什么,但乍一看似乎您至少缺少两部分:您需要使用 aTraverser遍历树的所有后代,并且需要对每个候选树进行类型检查以确保它被脱糖到你可以告诉你有一个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以捕获您感兴趣的任何信息应该很容易,而不仅仅是在编译时打印树。

于 2013-07-31T13:28:10.713 回答