作为宏的一部分,我想操纵部分函数的大小写定义。
为此,我使用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))))))
如何编写与这些树匹配的模式引用?
答案 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
节点,我们现在可以检查此节点的符号是否是我们要查找的节点。