39 votes

Où puis-je apprendre à construire des AST pour les macros Scala ?

Où puis-je apprendre à construire les AST que les macros de Scala génèrent ?

Le Scaladoc n'est pas aussi utile que je le voudrais. Par exemple :

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

Mais comment savoir ce qu'est un nœud d'application ? Où puis-je trouver une liste des types de nœuds dans les AST, et comment ils s'assemblent ?

39voto

sschaef Points 20242

Il n'y a pas beaucoup de documentation disponible pour les internes du compilateur, mais les choses qui sont disponibles devraient être suffisantes pour commencer.

Mirko Stocker a écrit son Mémoire de maîtrise sur le refactoring Scala . Dans l'annexe D (p. 95), il décrit l'architecture de l'AST. Il inclut également un aperçu graphique :

Scala AST

Une autre façon de trouver des informations sur l'AST est de regarder directement dans les sources de l'AST. reflect.internal.Trees qui contient l'AST.

Si l'on a besoin de savoir comment un extrait de code source spécifique est représenté en interne, il y a 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(())))

22voto

Eugene Burmako Points 8453

Vous pourriez jeter un coup d'œil à la scaladoc ( http://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#trees ) ou aux diapositives ( http://scalamacros.org/talks/2012-04-28-MetaprogrammingInScala210.pdf (la partie "Apprendre à apprendre").

Voici ce que je fais habituellement. J'ai écrit un simple script appelé parse qui prend le code Scala comme argument et le compile ensuite avec -Xprint:parser -Ystop-after:parser -Yshow-trees-stringified -Yshow-trees-compact ( parse utilise une autre aide script : adhoc-scalac . cliquez ici pour voir ses sources également).

L'avantage de cette approche par rapport à showRaw c'est qu'il n'exige pas que le code vérifie le type. Vous pouvez écrire un petit bout de code, qui fait référence à des variables ou des classes inexistantes, et il s'exécutera quand même avec succès et vous montrera l'AST. Voici un exemple de sortie :

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

Il y a aussi un script appelé typecheck qui fait la même chose, mais s'arrête après typer . C'est parfois utile pour comprendre comment le vérificateur de type transforme exactement les arbres de l'analyseur. Cependant, les boîtes à outils et les macros fonctionnent toutes deux avec des arbres d'analyseurs, c'est pourquoi je me sers de typecheck à des fins de construction d'arbres très rarement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X