Hatena::ブログ(Diary)

papamitra RSSフィード

 | 

2011-06-01

[]Compiler Pluginを作ってみた

なにげなくScalaコンパイラのソースを眺めていたときに見つけたコード

// src/compiler/scala/tools/nsc/util/trace.scala
object trace {
  def apply[T](msg: String)(value: T): T = {
    println(msg+value)
    value
  }
  // ...
}

なんてことないけど気の効いたコードだ。このコードいいな、とつぶやいたら

という返事があった。おぉlazy val、その発想はなかった!

どうせなら全てのlazy valにStackTrace出力を埋め込むちょっとウザいCompiler Pluginを作ってみよう思い立った。

ところでCompiler Pluginってなによ?

Compiler Pluginへの入門はこの記事が参考になると思う。

http://blog.takeda-soft.jp/blog/show/399

簡単に言うとコンパイルの各フェイズの間でちょっかいを出すコードが書けてしまうすごい機能。

(「lispマクロに対するOdersky先生の回答だ」みたいなこと言ってた人もいたような気がする)

上の記事ではゼロ除算をコンパイル時に検出するPluginが解説されていたが、Compiler Pluginではコンパイル時に抽象構文木(AST)そのものをいじってしまうこともできる。

そういうPluginの例としてはNonnull-check generatorが紹介されていた。

作ってみた

とりあえず

lazy val hoge = new Hoge()

みたいなのを

lazy val hoge = { Thread.currentThread.getStackTrace foreach println; new Hoge()}

に変換するだけの簡単なCommpilerPluginを作ってみた。

唯一めんどくさそうなのが付加するコードをASTにするところだが、これは@さんが紹介してくれたMkTreeを使えば簡単に得られる。

余談

ここまで「Compiler Pluginって簡単に作れるよ」的な説明をしてきたけど、実際はここに辿りつくまでに大変時間がかかった。というのも何を間違えたかCompiler Pluginを挿し込む場所が、上のコードではparserフェイズの後になっているが、当初typerフェイズの後にしていたからだ。

せっかつくったのでそのコードも載せてみる。

先に挙げたコードとまったく同じ事をしているわけだが、やたら長い。

そしてAST作るのだけでも面倒なのに、さらに各Treeに手で型付けするという苦行(笑。

よっぽどのことがない限り

val runsAfter = List[String]("parser")

をおすすめします。

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。

トラックバック - http://d.hatena.ne.jp/papamitra/20110601/compiler_plugin
 |