39

我在哪里可以学习如何构建 Scala 的宏生成的 AST?

Scaladoc 没有我想的那么有用。例如:

abstract def Apply(sym: Universe.Symbol, args: Universe.Tree*): Universe.Tree
A factory method for Apply nodes.

但是我如何弄清楚 Apply 节点是什么?我在哪里可以找到 AST 中的节点类型列表,以及它们如何组合在一起?

4

2 回答 2

40

没有很多关于编译器内部的文档可用,但可用的东西应该足以开始。

Mirko Stocker撰写了关于 Scala 重构的硕士论文。在附录 D(第 95 页)中,他描述了 AST 的架构。它还包括一个图形概述:

斯卡拉 AST

查找有关 AST 信息的另一种方法是直接查看包含 AST的reflect.internal.Trees的来源。

如果需要找出特定的源代码片段在内部是如何表示的,则有reify

scala> import reflect.runtime.universe._
import reflect.runtime.universe._

scala> showRaw(reify{val i = 0}.tree)
res8: String = Block(List(ValDef(Modifiers(), newTermName("i"), TypeTree(),
  Literal(Constant(0)))), Literal(Constant(())))
于 2013-02-09T17:08:25.073 回答
22

您可以查看 scaladoc ( http://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#trees ) 或幻灯片 ( http://scalamacros.org/talks/ 2012-04-28-MetaprogrammingInScala210.pdf,“学习学习”部分)。

这是我通常做的事情。我编写了一个名为 的简单脚本parse,它将 Scala 代码作为参数,然后用它编译-Xprint:parser -Ystop-after:parser -Yshow-trees-stringified -Yshow-trees-compactparse使用另一个帮助脚本:adhoc-scalac单击此处也可以查看其源代码)。

这种方法的优势showRaw在于它不需要代码进行类型检查。您可以编写一小段代码,它引用不存在的变量或类,它仍然会成功运行并向您显示 AST。这是一个输出示例:

09:26 ~$ parse 'class C { def x = 2 }'
[[syntax trees at end of parser]]// Scala source: tmp36sVGp
package <empty> {
  class C extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    def x = 2
  }
}
PackageDef(Ident(TermName("<empty>")), List(ClassDef(Modifiers(), TypeName("C"), List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), emptyValDef, List(DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))), DefDef(Modifiers(), TermName("x"), List(), List(), TypeTree(), Literal(Constant(2))))))))

还有一个名为 的脚本typecheck,它的作用相同,但在 之后停止typer。这有时有助于理解类型检查器如何准确地转换解析器树。但是,工具箱和宏都适用于解析器树,因此我typecheck很少将其用于树构造目的。

于 2013-02-10T08:30:43.240 回答