新たなフィーチャーを実装するために Groovy の構文を拡張する、ということがよいアイデアに思えることもある (複数回の代入がいい例だ)。 だけど、ほとんどの場合文法規則にキーワードを追加することはできないし、新しい概念を表現するための構文を作ることもできない。 でも AST 変換を使えば文法規則を変えることなく革新的なアイデアに挑戦することができる。
Groovy コンパイラが Groovy スクリプトや Groovy クラスをコンパイルするとき、ソースコードはいくつかの過程を経てメモリ上に完全構文木 (Concrete Syntax Tree) を構築してそれを抽象構文木 (Abstract Syntax Tree) に変換する。 AST 変換はコンパイルプロセスによって AST がバイトコードに翻訳される前に開発者が介入できるようにするためのものだ。
AST 変換によって非常に柔軟で強力で実行時性能のペナルティの無いコンパイル時メタプログラミングが言語レベルで可能になった。
AST 変換はアノテーションで利用できる。 アノテーションによってあなたのクラスがコンパイル時に AST 変換をするための印を付けるのだ。 状況によるがコンパイラフェーズの段階に応じた AST プロセッサが使われる。
次に示す標準的なアノテーションは AST 変換される。
- Standard Annotations
- Bindable and Vetoable transformation
- Building AST Guide
- Category and Mixin transformations
- Compiler Phase Guide
- Delegate transformation
- Immutable AST Macro
- Immutable transformation
- Lazy transformation
- Newify transformation
- PackageScope transformation
- Singleton transformation
Grape は @Grab によって自分自身を変換する。
変換には 2 種類ある。グローバル変換とローカル変換だ。
グローバル変換はコンパイラがコードをコンパイルする際ならどんな変換も適用される。 コンパイル済みのグローバル変換を実装したクラスとサービスロケーターファイル (META-INF/services/org.codehaus.groovy.transform.ASTTransformation) は、コンパイラのクラスパス上にある JAR ファイルに配置する。 グローバル変換クラスには引数無しのコンストラクタと org.codehaus.groovy.transform.ASTTransformationinterface の実装が必要である。 グローバル変換はそれぞれのソースコードをコンパイルするたびに実行されるので、時間のかかりそうなこと (AST を全てスキャンするなど) はしないのがマナーである。 コンパイラができる限り速く動けるようにしよう。
ローカル変換は局所的なコード辺を対象として適用される。 ローカル変換クラスの実装には他のアノテーションを再利用できる。そして org.codehaus.groovy.transform.ASTTransformation を実装しなければならない。
- Building AST Guide - decide how best to create AST
- Compiler Phase Guide - decide on the compiler phase in which to perform the work
- implementing a Global AST Transformations
- implementing a local AST transformation