1

作为宏的一部分,我想操作部分函数的案例定义。

为此,我使用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
}    

编译器将偏函数的 case 定义模式转换如下(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))))))

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

4

2 回答 2

2

首先,有一种特殊的 quasiquotes 专门用于 CaseDefs,称为cq

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以在此问题得到修复时得到通知。

于 2013-08-27T14:37:48.550 回答
0

在 Den Shabalin 指出,quasiquotes 不能在这个特定的设置中使用之后,我设法找到了一个与偏函数的 case 定义的模式相匹配的模式。

关键问题是,我们要匹配的构造函数(在我们的示例中Data)存储在TypeTree节点Apply中。匹配包裹在 a 中的树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节点,我们现在可以检查该节点的符号是否是我们正在寻找的符号。

于 2013-09-25T14:48:56.503 回答