简体   繁体   中英

Where can I learn about constructing AST's for Scala macros?

Where I can learn how to construct the AST's that Scala's macros generate?

The Scaladoc isn't as helpful as I'd like. For example:

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

But how do I figure out what an Apply node is? Where can I find a list of the node types in AST's, and how they fit together?

There isn't a lot of documentation for the internals of the compiler available, but the things that are available should be enough to get started.

Mirko Stocker , has written his Master Thesis about Scala Refactoring . In Appendix D (p. 95) he describes the architecture of the AST. It includes also a graphical overview:

Scala AST

Another way to find information about the AST is to look directly into the sources of reflect.internal.Trees , which contains the AST.

If one needs to find out how a specific source code snippet is represented internally there is 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(())))

You could take a look at the scaladoc ( http://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#trees ) or at the slides ( http://scalamacros.org/talks/2012-04-28-MetaprogrammingInScala210.pdf , the "Learn to learn" part).

Here's what I usually do. I wrote a simple script called parse , which takes Scala code as an argument and then compiles it with -Xprint:parser -Ystop-after:parser -Yshow-trees-stringified -Yshow-trees-compact ( parse uses another helper script: adhoc-scalac . click here to see its sources as well).

The advantage this approach has over showRaw is that it doesn't require the code to typecheck. You could write a small snippet of code, which refers to non-existent variables or classes, and it still will successfully run and show you the AST. Here's an example of output:

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))))))))

There's also a script called typecheck , which does the same, but stops after typer . That's sometimes useful to understand how exactly the typechecker transforms the parser trees. However, both toolboxes and macros work with parser trees, so I use typecheck for tree construction purposes very rarely.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM