scala macros scala-macros

¿Dónde puedo aprender sobre la construcción de AST para macros de Scala?



scala-macros (2)

¿Dónde puedo aprender cómo construir los AST que generan las macros de Scala?

El Scaladoc no es tan útil como me gustaría. Por ejemplo:

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

¿Pero cómo averiguo qué es un nodo Apply? ¿Dónde puedo encontrar una lista de los tipos de nodos en AST y cómo encajan?


No hay mucha documentación para las partes internas del compilador disponible, pero las cosas que están disponibles deberían ser suficientes para comenzar.

Mirko Stocker , ha escrito su tesis de maestría sobre Scala Refactoring . En el Apéndice D (página 95) describe la arquitectura de la AST. Incluye también una descripción gráfica:

Otra forma de encontrar información sobre el AST es mirar directamente a las fuentes de reflect.internal.Trees , que contiene el AST.

Si necesita averiguar cómo se representa internamente un fragmento de código fuente específico, hay una 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(())))


Puede echar un vistazo al scaladoc ( http://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#trees ) o en las diapositivas ( http://scalamacros.org/talks/2012-04-28-MetaprogrammingInScala210.pdf , la parte "Aprenda a aprender").

Esto es lo que suelo hacer. Escribí un script simple llamado parse , que toma el código Scala como argumento y luego lo compila con -Xprint:parser -Ystop-after:parser -Yshow-trees-stringified -Yshow-trees-compact (el parse usa otro script auxiliar: adhoc-scalac . haga clic aquí para ver sus fuentes también).

La ventaja de este enfoque sobre showRaw es que no requiere el código para comprobar el tipo. Puede escribir un pequeño fragmento de código, que se refiere a variables o clases inexistentes, y todavía se ejecutará correctamente y le mostrará el AST. Aquí hay un ejemplo de salida:

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

También hay un script llamado typecheck , que hace lo mismo, pero se detiene después de typer . Eso a veces es útil para entender cómo exactamente el contador de tipos transforma los árboles del analizador. Sin embargo, tanto las cajas de herramientas como las macros funcionan con árboles de análisis sintáctico, por lo que muy pocas veces utilizo la typecheck de typecheck para la construcción de árboles.