Scala Compiler Plugin

Scala Compiler Pluginの書き方と用途について、調べてみました。今のところ大きく分けて、コードのチェック/Scalaソースの補助/外部ソースコードのジェネレータとして使うことが多いようです。

Scalaでは、コンパイル時に独自の処理を走らせることができて、この機構をCompiler Pluginとして提供しています。

クイックスタートは本家サイトにのっていて、すぐに試せると思います。
Writing Scala Compiler Plugins

簡単に訳すと、

val amount = five / 0

のように、ゼロでの割り算をコンパイル時にエラー検出するプラグインを作るには、DivByZero.scalaをコンパイルして、

 ■scalac-plugin.xml

 <plugin>
  <name>divbyzero</name>
  <classname>localhost.DivByZero</classname>
 </plugin>

と一緒にjarして、

 >scalac -Xplugin:divbyzero.jar Test.scala

とやると、該当行がコンパイルエラーになる。というもの。このとおりやってみると動きます。プラグインがうまくインストールできてるか確認するには、次のようにします。

 >scalac -Xplugin:divbyzero.jar -Xplugin-list
 divbyzero - checks for division by zero
 continuations - applies selective cps conversion

※continuationsは、scala2.8のデフォルトプラグインのようです。
何をやるものかは、Onion開発しつつ、PEGEXを開発する日記:限定継続を実現するScalaコンパイラプラグインを試してみるに説明がありました。

DivByZeroの形がCompiler Pluginの定型のようで、

 //Plugin Component(複数指定可)
 val components = List[PluginComponent](aComponent,bComponent)

 object aComponent extends PluginComponent {
  //どのフェーズの後に、
  val runsAfter = List[String]("refchecks");
  //何をする。
  def newPhase(_prev: Phase) = new StdPhase(_prev){
    def apply(unit: CompilationUnit) {
     // CompilationUnit や Globalを操作して何かをする。
    }
  }
 }

コンパイルフェーズは、以下のコマンドで出力されるいずれかの値です。

 > scalac -Xshow-phases

プラグインは、これらのフェーズに「独自のフェーズを追加する」という考え方になるようです。各フェーズとコンパイラプラグインの関係はこちらにドキュメントがあります。
Scala Compiler Phase and Plug-In Initialization

どんなときに使う?

上記の本家では、"ほんの一例"として

* コンパイル時のチェックを追加したいとき(DivByZeroのように)
* よく使うAPIを最適化したいとき
* Scalaの文法を書き換えたいとき(ただし依存関係など注意してね)

などを挙げています。

どんなプラグインがすでにある?

■コンパイル時にいろいろチェックをするタイプ

varhunter (Step by step Scala Compiler plug-in)
var(可変変数)を使ったら即アウトにするプラグイン(カッケーw

noboxing-plugin
アノテーションを使ってboxingを検出してくれるプラグイン

Scala Compiler Plugin for Unique References
アノテーションを使って重複参照を排除するプラグイン

■コンパイル時にコードに機能追加するタイプ

autoproxy-plugin
別オブジェクトへプロキシメソッドを自動追加してくれるプラグイン

Nonnull-check generator
パラメータに@Nonnullをつけると、"if( param == null ) throw new Exception" をコードに付与してくれるプラグイン。

■コンパイル時に別のコードを自動生成するタイプ

avro-scala-compiler-plugin
Apache Avro用のシリアライズクラスを生成してくれるプラグイン

s2js
ScalaソースをJavaScriptソースに変換してくれるプラグイン


何に使うんだこれ!?っていうのも正直ありますが、特にNonnull-checkなどは、コンパイラわからない私でも理解しやすかったりして、上記のサンプルのコードを見てみるとなんとかなるかもしれないっという気にはなります。
ちゃんと実装しようと思ったらもちろんコンパイラの生成物を理解しなければならないと思いますが ;x; 、いろいろと用途はありそう。

ちょっと思い立っただけですが、たとえば、

・nullを返してしまう可能性のあるメソッドを検出して警告を挙げる(Optionの利用を促す)。
・自由にコーディングしてしまいがちなScalaでチーム開発する際のルール付けとか、やばそうなコードの検出に使えないかな?
・APTの代替策。

などと想像は膨らみます。

追加:Scala東北情報

by @ScalaTohoku(thx!)

過去にScalaWatchで以下の plugin がでてきてました。

* ScalaCL Compiler Plugin
for 式や collection の foldLeft 等を while ループに変換する。

* Scala enhanced Strings plugin
文字列中に直接 Scala の式を書けるようにする。

* Beans Scalac Plugin

* a compiler plugin which generates cross-linked HTML files of the source files.


同じカテゴリのエントリ
1.Lift再入門 / 8.javascriptからsubmitできない / 7.Ajax Form / 6.Radio、Checkboxについて / 5.行列型の編集FORM / 4.サーバーサイドバリデーションとサーバサイド関数 / 3.ログインFORM - S.param使ったら負け / 2.Snippetメソッドとして許される型 / sbt0.12.xで依存jar抽出タスク / scala2.10+lift2.5+NetBeans7.2 / Scalaで入門関数プログラミング / reactive-webを試してみました / Lift2.2M1のテンプレート機能 / Scala Compiler Plugin / View Bound/Context Bound / ScalaZa01参加してきました / Akka Frameworkチュートリアルの次 / Akka Frameworkチュートリアルその2 / Akka Frameworkチュートリアル / LiftでJCaptcha / Url Rewrite Filter / sbt-android-plugin / Android SDK for Scala / 祝Lift2.0リリース / Liftの携帯対応まとめ / Scala2.8への移行 / Lift 2.0-scala280-SNAPSHOT/sbt0.7.1 / Scalaお絵かき環境 - Kojo / Lift+Quartzでバッチ / Scala&Liftを採用した理由 / Liftでdate_select系ヘルパーを作る / LiftでAjax / LiftのSubmitかしこい / lift-mapperのpaginateを使う / snippetをspecする / Lift Mapperを拡張する / LiftのDBをMySQLに / Liftプロジェクト環境を整える / Scala本読み比べてみました / NetBeans6.7&scala / じつはScalaはライトな言語 / Scalaバザ~ル / lift1.0所感 / specsを読む / implicit def / ScalaならNetBeansがサイコー / scala勉強会@東北がスタート / それでも俺はLiftをやるってのか / Scala&Liftセットアップ / ブログリニューアル /
コメント

コメントしてください

closed.