如何匹配Scala宏中部分函数大小写定义的模式?

时间:2013-08-27 11:01:46

标签: scala macros scala-2.10 scala-macros

作为宏的一部分,我想操纵部分函数的大小写定义。

为此,我使用Transformer来操作部分函数的案例定义,并使用Traverser来检查案例定义的模式:

def myMatchImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)
    (expr: c.Expr[A])(patterns: c.Expr[PartialFunction[A, B]]): c.Expr[B] = {
  import c.universe._

  val transformer = new Transformer {
    override def transformCaseDefs(trees: List[CaseDef]) = trees map {
      case caseDef @ CaseDef(pattern, guard , body) => {
        // println(show(pattern))
        val traverser = new Traverser {
          override def traverse(tree: Tree) = tree match {
            // match against a specific pattern
          }
        }
        traverser.traverse(pattern)
      }
    }
  }

  val transformedPartialFunction = transformer.transform(patterns.tree)

  c.Expr[B](q"$transformedPartialFunction($expr)")
}

现在让我们假设,我想要匹配的有趣数据由类Data(它是对象Example的一部分)表示:

case class Data(x: Int, y: String)

现在调用下面示例中的宏

abstract class Foo
case class Bar(data: Data) extends Foo
case class Baz(string: String, data: Data) extends Foo

def test(foo: Foo) = myMatch(foo){
  case Bar(Data(x,y))    => y
  case Baz(_, Data(x,y)) => y
}    

部分函数的大小写定义的模式由编译器转换如下(Foo,Bar和Baz类也是对象Example的成员):

(data: Example.Data)Example.Bar((x: Int, y: String)Example.Data((x @ _), (y @ _)))
(string: String, data: Example.Data)Example.Baz(_, (x: Int, y: String)Example.Data((x @ _), (y @ _)))

这是在上面的宏中打印模式的结果(使用show),原始的抽象语法树(使用showRaw打印)如下所示:

Apply(TypeTree().setOriginal(Select(This(newTypeName("Example")), Example.Bar)), List(Apply(TypeTree().setOriginal(Select(This(newTypeName("Example")), Example.Data)), List(Bind(newTermName("x"), Ident(nme.WILDCARD)), Bind(newTermName("y"), Ident(nme.WILDCARD))))))

Apply(TypeTree().setOriginal(Select(This(newTypeName("Example")), Example.Baz)), List(Ident(nme.WILDCARD), Apply(TypeTree().setOriginal(Select(This(newTypeName("Example")), Example.Data)), List(Bind(newTermName("x"), Ident(nme.WILDCARD)), Bind(newTermName("y"), Ident(nme.WILDCARD))))))

如何编写与这些树匹配的模式引用?

2 个答案:

答案 0 :(得分:2)

首先,专门针对名为cq的CaseDefs有一种特殊的quasiquotes:

override def transformCaseDefs(trees: List[CaseDef]) = trees map {
  case caseDef @ cq"$pattern if $guard => $body" => ...
}

其次,您应该使用pq来解构模式:

pattern match {
   case pq"$name @ $nested" => ...
   case pq"$extractor($arg1, $arg2: _*)" => ...
   ...
}

如果您对用于模式匹配的树的内部感兴趣,则它们是由patvarTransformer

中定义的TreeBuilder.scala创建的

另一方面,如果你正在使用UnApply树(在类型检查后生成),我有一个坏消息:quasiquotes目前不支持它们。如果修复了此问题,请关注SI-7789以获得通知。

答案 1 :(得分:0)

Den Shabalin指出,在这个特定的设置中不能使用quasiquotes,我设法找到一个与部分函数的案例定义模式相匹配的模式。

关键问题是,我们要匹配的构造函数(在我们的示例中为Data)存储在TypeTree节点的Apply中。匹配TypeTree中包含的树有点棘手,因为此类(TypeTree())的唯一提取器对此特定任务不是很有帮助。相反,我们必须使用original方法选择包装树:

override def transform(tree: Tree) = tree match {
  case Apply(constructor @ TypeTree(), args) => constructor.original match {
    case Select(_, sym) if (sym == newTermName("Data")) => ...
  }
}

在我们的用例中,包装树是Select节点,我们现在可以检查此节点的符号是否是我们要查找的节点。