13

如果我有一个宏,其中tranforms代码例如:

  (src: a.b.c.TestEntity) =>
    {
      z.y.TestTable(None)
    }

为了匹配该 AST 的 None 部分,我可以使用提取器,例如:

  object NoneExtractor {
    def unapply(t: Tree): Boolean = t match {
      case Select(Ident(scala), none) if scala.encoded == "scala" && none.encoded == "None" => true
      case _ => false
    }
  }

由于showRawAST 的 None 部分看起来像:

Select(Ident(scala), None)

但是,如果我想编写一个单元测试,NoneExtractor我不想编译和重建宏并将测试托管在宏正在编译的项目中。我想对宏项目中的提取器进行单元测试,这表明运行时反射是一种方法:

val t = reify {

  (src: a.b.c.TestEntity) =>
    {
      z.y.TestTable(None)
    }

}.tree 

然而,这棵树完全不同,在showRaw那棵树中,None 看起来像:

Ident(scala.None)

这对于编写负面测试和检查我的宏的错误处理来说是个坏消息。您不能使用来自另一个项目的宏为宏编写负面测试,因为代码不会编译(并且您不能调试带有编译错误的负面测试)。

为什么像 None 这样基本的东西的表示在编译时反射和运行时反射之间如此不同?有没有办法在宏项目中创建可测试的树片段,它与编译时反射期间传递给宏的 AST 相同?

4

1 回答 1

0

要解决这种不一致,您可以在模式匹配中使用即将到来的quasiqoutes。它们抽象出 AST,因此可以使用两种表示形式(AST 无论如何都是编译器特定的,Scala 目前是一种单一的编译器语言,但依赖编译器的内部表示并不是那么好):

case q"_root_.scala.None" => ...

将匹配两个 AST。您也可以创建树,q"_root_.scala.None"这样您就不必担心表示。当 scala 2.11 发布 quasiquotes 时,Reify 将被淘汰。要在 scala 2.10 中使用准引号,您可以使用宏天堂

这是关于 scala quasiquotes 的一个很好的 WIP 指南

于 2014-04-07T12:41:07.363 回答