2013-02-09 17 views
38

Gdzie mogę dowiedzieć się, jak konstruować AST, które generują makra Scala?Gdzie mogę się dowiedzieć, jak budować AST dla makr Scala?

Scaladoc nie jest tak pomocny, jak bym chciał. Na przykład:

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

Ale jak mogę dowiedzieć się, co to jest węzeł Apply? Gdzie mogę znaleźć listę typów węzłów w AST i w jaki sposób pasują do siebie?

Odpowiedz

39

Nie ma zbyt wiele dokumentacji dla wewnętrznych elementów kompilatora, ale rzeczy, które są dostępne, powinny wystarczyć.

Mirko Stocker, napisał swoją Master Thesis about Scala Refactoring. W dodatku D (s. 95) opisuje architekturę AST. Obejmuje on także graficzny przegląd:

Scala AST

Innym sposobem na znalezienie informacji o AST jest patrzeć bezpośrednio do źródeł reflect.internal.Trees, zawierający AST.

Jeśli trzeba, aby dowiedzieć się, w jaki sposób specyficzny kod źródłowy fragment jest reprezentowane wewnętrznie jest 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(()))) 
+0

Dzięki! Ta teza jest wspaniałym zasobem. – Bill

+5

Dziękuję :-) Mam nadzieję, że nie jest jeszcze zbyt przestarzały ... –

+0

Jak możemy narysować taki wykres? – Freewind

22

Można spojrzeć na scaladoc (http://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#trees) lub na slajdy (http://scalamacros.org/talks/2012-04-28-MetaprogrammingInScala210.pdf, tym " Naucz się uczyć "części".

Oto, co zwykle robię. Napisałem prosty skrypt o nazwie parse, który przyjmuje kod Scala jako argument, a następnie kompiluje go z -Xprint:parser -Ystop-after:parser -Yshow-trees-stringified -Yshow-trees-compact (parse używa innego skryptu pomocniczego: adhoc-scalac. click here, aby zobaczyć również jego źródła).

Zaletą tego podejścia jest ponad showRaw jest to, że nie wymaga kodu do sprawdzania typu. Możesz napisać mały fragment kodu, który odnosi się do nieistniejących zmiennych lub klas, i nadal będzie z powodzeniem działał i pokazuje AST. Oto przykład wyjścia:

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

Jest też skrypt o nazwie typecheck, który robi to samo, ale zatrzymuje się po typer. Czasami warto zrozumieć, jak dokładnie typechecker przekształca drzewa parserów. Jednak zarówno skrzynki narzędziowe, jak i makra działają z parserami, więc bardzo rzadko używam do budowy drzewa typecheck.

+0

Dzięki, Eugene! Połączenie skalpów jest bardzo pomocne. – Bill

Powiązane problemy