モテモテコンパイラプラグイン
自己紹介
●
    @kiris
●
    アリエルの中の人
●
    アリエルだと少数派なScalaユーザー
    ●
        最近はCommon Lisperに押され気味
発表の前に
●
    簡単な質問させてください
発表の前に
●
    簡単な質問させてください
    ●
        ありえるえりあ勉強会の参加は何回目ですか?
発表の前に
●
    簡単な質問させてください
    ●
        ありえるえりあ勉強会の参加は何回目ですか?
    ●
        Scalaプログラムどれくらい書いてますか?
発表の前に
●
    簡単な質問させてください
    ●
        ありえるえりあ勉強会の参加は何回目ですか?
    ●
        Scalaプログラムどれくらい書いてますか?
    ●
        コンパイラプラグインを書いたことはありますか?
発表の前に
●
    簡単な質問させてください
    ●
        ありえるえりあ勉強会の参加は何回目ですか?
    ●
        Scalaプログラムどれくらい書いてますか?
    ●
        コンパイラプラグインを書いたことはありますか?

    ●
        コンパイラプラグインが書けると
        モテそうだと思いませんか?
今日の内容
●
    コンパイラプラグインを書けるようになって
    モテモテになる
    ●
        ゴール「この勉強会中で    告白される」
●
    インスパイア モテモテPHP(WEB+DB PRESS)
    ●
        「PHP連載を通して  モテ      を目指す」
        という大変アグレッシブな企画
    ●
        http://goo.gl/83fcw
対象環境
●
    Scala 2.9.0.fnal 以上
●
    SBT 0.10.0 以上
●
    女子 18歳 以上
    ●
        「スカラちゃん」という処理系があるので代用
本題
コンパイラプラグイン
●
    コンパイル時に独自の処理(フェーズ)を追加で
    きる

●
    例えば
    ●
        型や文法以外のチェックを追加する
    ●
        構文木を変更する
    ●
        .class以外のものを生成する
    ●
        etc

●
    IDEやビルドツールへの依存が無いのも嬉しい
簡単なデモ
実用的そうなの
●
    Alacs
    ●
        コンパイル時にバグになりそうな箇所を探す
●
    ScalaCL
    ●
        コンパイル時にfor文の最適化
    ●
        OpenCLによるGPUの利用
●
    Scala enhanced Strings
    ●
        文字列中に直接 Scala の式を書けるようにする
    ●
        “Length: #{aString.length}.stuf”
使い方
●
    scalac -Xplugin:myplugin.jar Hello.scala
    ●
        $SCALA_HOME/misc/scala-devel/plugins に
        pluginのjarを追加するだけでも可

●
    scalac -Xplugin:myplugin.jar -Xplugin-list
    ●
        プラグイン一覧の表示
コンパイラの流れ
Scalaのコンパイルの流れ
●
    全部で25+2フェーズ
    ●
        scalac -Xshow-phases
構文木作成
1. parser
  ●
      parse source into ASTs, perform simple
      desugaring
  ●
      余談だけどreaderの差し替えも可能らしい
      –   scalac -Xsource-reader MyReader hoge.scala
名前解決とか
2. namer
  ●
      resolve names, attach symbols to named trees
3. packageobjects
4. typer
  ●
      the meat and potatoes: type the trees
5. superaccessors
6. picker
7. refchecks
構文木操作
 8. selectiveanf
   ●
       限定継続用コンパイラプラグイン
 9. liftcode
10. selectivecps
   ●
       限定継続用コンパイラプラグイン
11. uncurry
12. tailcalls
…
20. mixin
中間コード生成
21. cleanup
  ●
      platform-specifc cleanups, generate refective
      calls
22. icode
  ●
      generate portable intermediate code
最適化
23. inliner
   ●
       optimization: do inlining
24. closelim
   ●
       optimization: eliminate uncalled closures
25. dce
   ●
       optimization: eliminate dead code
JVMコード生成・後処理
26. jvm
  ●
      generate JVM bytecode
27. terminal
  ●
      The last phase in the compiler chain
コンパイラプラグインを使うと
$ scalac -Xplugin:myplugin.jar -Xplugin-phases

1. parser
2. myplugin-phase <- 独自のフェーズを追加
3. namer
4. packageobjects
5. typer
...
書いてみよう
プロジェクト構成(SBT)
●
    project_root/
     ●
          build.sbt
     ●
          src/main
           • scala
               ●
                 MyPlugin.scala
               ●
                 MyCompornent.scala
           – resource
               ●
                 scalac-plugin.xml
     ●
          src/test
    ...
build.sbt
name := "My Plugin"
version := "1.0"
organization := "localhost"
scalaVersion := "2.9.0"
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.9.0"
scalac-plugin.xml
<plugin>
 <name>myplugin</name>
 <classname>localhost.MyPlugin</classname>
</plugin>
Plugin
import scala.tools.nsc.Global
import scala.tools.nsc.plugins.Plugin


class MyPlugin(val global: Global) extends Plugin {
    override val name = "myplugin"
    override val description = "myplugin description"
    override val components = List(new MyComponent(global))
}
Component(1/2)
import scala.tools.nsc._
import scala.tools.nsc.plugins.PluginComponent


class MyComponent(val global: Global) extends PluginComponent {
    import global._
    import global.defnitions._


    val runsAfter = List("refchecks")
    val phaseName = "myplugin-phase"


    … // Component(2/2)
}
Component(2/2)
class MyComponent(val global: Global) extends PluginComponent {
        … // Component(1/2)


    def newPhase(prev: Phase) = new StdPhase(prev) {
        def name = phaseName


        override def apply(unit: CompilationUnit): Unit = {
            printf("Hello, motemote world.")
        }
    }
}
どのフェーズを使うべきか?
●
    わりとケースバイケースですが
●   構文木を操作するならparserやnamerの後
●   値をチェックするならrefchecksの後

●
    などがよさそう
参考:各プラグインのフェーズ
●
    コンパイル時にチェックを追加するタイプ
    ●
      DivByZero(チュートリアル) / runAfter = refchecks
    ●
      Var Hunter / runAfter = refchecks
    ●
      WarnBoxingPlugin / runAfter = refchecks
●
    コンパイル時にコードを追加するタイプ
    ●
      atuoproxy-plugin / runAfter = parser
    ●
      Notnull-check generator / runAfter = parser
    ●
      Scala enhanced Strings / runAfter = parser
●
    コンパイル時に別なコードを生成するタイプ
    ●
        ScalaCL / runAfter = namer
    ●
        s2js / runAfter = refchecks
構文木を探索する
class MyComponent(val global: Global) extends PluginComponent {
        …
        def newPhase(prev: Phase): Phase = new StdPhase(prev) {
        override def apply(unit: CompilationUnit): Unit = {
            new ForeachTreeTraverser(onTraverse).traverse(unit.body)
        }
    }
    def onTraverse(tree: Tree): Unit = tree match {
        case Apply(fun, args) => println("traversing application of "+ fun)
        case _ => ()
    }
}
別な構文木に変換する
class MyComponent(val global: Global) extends PluginComponent
                                           with Transform {
    def newTransformer(unit: CompilationUnit) = new MyTransformer
    class MyTransformer extends Transformer {
     override def transform(tree: Tree): Tree = {
         postTransform(super.transform(preTransform(tree)))
     }


     def preTransform(tree: Tree): Tree = tree match {
         case _ => tree
     }
     def postTransform(tree: Tree): Tree = ....
}
構文木を調べる
●
    プログラムの変換結果を表示
    ●
        scalac -Xprint:refchecks Hello.scala
●
    シンタックスツリーの表示
    ●
        scalac -Xprint:refchecks -Yshow-trees Hello.scala
●
    シンタックスツリーをGUIで表示
    ●
        scalac -Ybrowse:refchecks Hello.scala
構文木の作成
●
      case Apply(Select(Select(Select(Ident …
      ●
          やってらんない
●
      MkTreeを使おう
      ●
          http://goo.gl/O3oeh
$ scala MkTree "if (b) 3 else 5"
If(
    Ident("b") // sym=<none>, sym.tpe=<notype>, tpe=null
    Literal(Constant(3))
    Literal(Constant(5))
)
完成したら
●
    sbt package
    ●
        target/scala-2.x.x/myplugin_2.x.x-1.0.jar


●
    これで君もモテモテだ!m9(°д°)
おまけ
おまけ
●
    「モテモテになるコンパイラプラグイン」
    が欲しいと言われて考えてみた

●
    loveletter plugin
    ●
        コンパイルの待ち時間を使って愛の告白
    ●
        コンパイルの各フェーズでメッセージを出力
    ●
        特定の相手にだけに出力されようにする
        –   java.net.InetAddress.getLocalHost()
君に届け
scalatan $ scalac -Xplugin:loveletter-plugin.jar src/test/scala/Hello.scala
君に届け
scalatan $ scalac -Xplugin:loveletter-plugin.jar src/test/scala/Hello.scala
スカラ!スカラ!スカラ!スカラぁぁあああわぁあああああああああああああ
あああああああああん!!!あぁああああ…ああ…あっあっー!あぁああああ
ああ!!!スカラスカラスカラぁああぁわぁああああ!!!あぁクンカクン
カ!クンカクンカ!スーハースーハー!スーハースーハー!いい匂いだなぁ…く
んくんんはぁっ!スカラ・オーダスキーたんの黒色の髪をクンカクンカしたい
お!クンカクンカ!あぁあ!!間違えた!モフモフしたいお!モフモフ!モフ
モフ!髪髪モフモフ!カリカリモフモフ…きゅんきゅんきゅい!!型推論が大
幅強化されたスカラたんかわいかったよぅ!!あぁぁああ…あああ…あっあぁ
ああああ!!ふぁぁあああんんっ!!拡張可能リテラルScala3.0で決まって良
かったねスカラたん!あぁあああああ!かわいい!スカラたん!かわいい!
あっああぁああ!爆速コンパイルも新しいジェネリクスも発表されて嬉し…い
やぁああああああ!!!にゃああああああああん!!ぎゃあああああああ
あ!!ぐあああああああああああ!!!爆速コンパイルなんて現実じゃな
い!!!!あ…型推論の大幅強化も拡張可能リテラルもよく考えたら…ス カ ラ
ち ゃ ん は 現実 じ ゃ な い?にゃあああああああああああああん!!うぁああ
ああああああああ!!そんなぁああああああ!!いやぁぁぁああああああああ
あ!!はぁああああああん!!ローザンヌぅうううう!!この!ちきしょー!
やめてやる!!現実なんかやめ…て…え!?見…てる?表紙絵のスカラちゃん
が僕を見てる?表紙絵のスカラちゃんが僕を見てるぞ!スカラちゃんが僕を見
てるぞ!挿絵のスカラちゃんが僕を見てるぞ!!拡張可能リテラルのスカラ
ちゃんが僕に話しかけてるぞ!!!よかった…世の中まだまだ捨てたモンじゃ
ご静聴ありがとうございました
告白タイム




        質問も一応受けつけます

MoteMote Compiler Plugin

  • 1.
  • 2.
    自己紹介 ● @kiris ● アリエルの中の人 ● アリエルだと少数派なScalaユーザー ● 最近はCommon Lisperに押され気味
  • 3.
    発表の前に ● 簡単な質問させてください
  • 4.
    発表の前に ● 簡単な質問させてください ● ありえるえりあ勉強会の参加は何回目ですか?
  • 5.
    発表の前に ● 簡単な質問させてください ● ありえるえりあ勉強会の参加は何回目ですか? ● Scalaプログラムどれくらい書いてますか?
  • 6.
    発表の前に ● 簡単な質問させてください ● ありえるえりあ勉強会の参加は何回目ですか? ● Scalaプログラムどれくらい書いてますか? ● コンパイラプラグインを書いたことはありますか?
  • 7.
    発表の前に ● 簡単な質問させてください ● ありえるえりあ勉強会の参加は何回目ですか? ● Scalaプログラムどれくらい書いてますか? ● コンパイラプラグインを書いたことはありますか? ● コンパイラプラグインが書けると モテそうだと思いませんか?
  • 8.
    今日の内容 ● コンパイラプラグインを書けるようになって モテモテになる ● ゴール「この勉強会中で 告白される」 ● インスパイア モテモテPHP(WEB+DB PRESS) ● 「PHP連載を通して モテ を目指す」 という大変アグレッシブな企画 ● http://goo.gl/83fcw
  • 9.
    対象環境 ● Scala 2.9.0.fnal 以上 ● SBT 0.10.0 以上 ● 女子 18歳 以上 ● 「スカラちゃん」という処理系があるので代用
  • 10.
  • 11.
    コンパイラプラグイン ● コンパイル時に独自の処理(フェーズ)を追加で きる ● 例えば ● 型や文法以外のチェックを追加する ● 構文木を変更する ● .class以外のものを生成する ● etc ● IDEやビルドツールへの依存が無いのも嬉しい
  • 12.
  • 13.
    実用的そうなの ● Alacs ● コンパイル時にバグになりそうな箇所を探す ● ScalaCL ● コンパイル時にfor文の最適化 ● OpenCLによるGPUの利用 ● Scala enhanced Strings ● 文字列中に直接 Scala の式を書けるようにする ● “Length: #{aString.length}.stuf”
  • 14.
    使い方 ● scalac -Xplugin:myplugin.jar Hello.scala ● $SCALA_HOME/misc/scala-devel/plugins に pluginのjarを追加するだけでも可 ● scalac -Xplugin:myplugin.jar -Xplugin-list ● プラグイン一覧の表示
  • 15.
  • 16.
    Scalaのコンパイルの流れ ● 全部で25+2フェーズ ● scalac -Xshow-phases
  • 17.
    構文木作成 1. parser ● parse source into ASTs, perform simple desugaring ● 余談だけどreaderの差し替えも可能らしい – scalac -Xsource-reader MyReader hoge.scala
  • 18.
    名前解決とか 2. namer ● resolve names, attach symbols to named trees 3. packageobjects 4. typer ● the meat and potatoes: type the trees 5. superaccessors 6. picker 7. refchecks
  • 19.
    構文木操作 8. selectiveanf ● 限定継続用コンパイラプラグイン 9. liftcode 10. selectivecps ● 限定継続用コンパイラプラグイン 11. uncurry 12. tailcalls … 20. mixin
  • 20.
    中間コード生成 21. cleanup ● platform-specifc cleanups, generate refective calls 22. icode ● generate portable intermediate code
  • 21.
    最適化 23. inliner ● optimization: do inlining 24. closelim ● optimization: eliminate uncalled closures 25. dce ● optimization: eliminate dead code
  • 22.
    JVMコード生成・後処理 26. jvm ● generate JVM bytecode 27. terminal ● The last phase in the compiler chain
  • 23.
    コンパイラプラグインを使うと $ scalac -Xplugin:myplugin.jar-Xplugin-phases 1. parser 2. myplugin-phase <- 独自のフェーズを追加 3. namer 4. packageobjects 5. typer ...
  • 24.
  • 25.
    プロジェクト構成(SBT) ● project_root/ ● build.sbt ● src/main • scala ● MyPlugin.scala ● MyCompornent.scala – resource ● scalac-plugin.xml ● src/test ...
  • 26.
    build.sbt name := "MyPlugin" version := "1.0" organization := "localhost" scalaVersion := "2.9.0" libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.9.0"
  • 27.
  • 28.
    Plugin import scala.tools.nsc.Global import scala.tools.nsc.plugins.Plugin classMyPlugin(val global: Global) extends Plugin { override val name = "myplugin" override val description = "myplugin description" override val components = List(new MyComponent(global)) }
  • 29.
    Component(1/2) import scala.tools.nsc._ import scala.tools.nsc.plugins.PluginComponent classMyComponent(val global: Global) extends PluginComponent { import global._ import global.defnitions._ val runsAfter = List("refchecks") val phaseName = "myplugin-phase" … // Component(2/2) }
  • 30.
    Component(2/2) class MyComponent(val global:Global) extends PluginComponent { … // Component(1/2) def newPhase(prev: Phase) = new StdPhase(prev) { def name = phaseName override def apply(unit: CompilationUnit): Unit = { printf("Hello, motemote world.") } } }
  • 31.
    どのフェーズを使うべきか? ● わりとケースバイケースですが ● 構文木を操作するならparserやnamerの後 ● 値をチェックするならrefchecksの後 ● などがよさそう
  • 32.
    参考:各プラグインのフェーズ ● コンパイル時にチェックを追加するタイプ ● DivByZero(チュートリアル) / runAfter = refchecks ● Var Hunter / runAfter = refchecks ● WarnBoxingPlugin / runAfter = refchecks ● コンパイル時にコードを追加するタイプ ● atuoproxy-plugin / runAfter = parser ● Notnull-check generator / runAfter = parser ● Scala enhanced Strings / runAfter = parser ● コンパイル時に別なコードを生成するタイプ ● ScalaCL / runAfter = namer ● s2js / runAfter = refchecks
  • 33.
    構文木を探索する class MyComponent(val global:Global) extends PluginComponent { … def newPhase(prev: Phase): Phase = new StdPhase(prev) { override def apply(unit: CompilationUnit): Unit = { new ForeachTreeTraverser(onTraverse).traverse(unit.body) } } def onTraverse(tree: Tree): Unit = tree match { case Apply(fun, args) => println("traversing application of "+ fun) case _ => () } }
  • 34.
    別な構文木に変換する class MyComponent(val global:Global) extends PluginComponent with Transform { def newTransformer(unit: CompilationUnit) = new MyTransformer class MyTransformer extends Transformer { override def transform(tree: Tree): Tree = { postTransform(super.transform(preTransform(tree))) } def preTransform(tree: Tree): Tree = tree match { case _ => tree } def postTransform(tree: Tree): Tree = .... }
  • 35.
    構文木を調べる ● プログラムの変換結果を表示 ● scalac -Xprint:refchecks Hello.scala ● シンタックスツリーの表示 ● scalac -Xprint:refchecks -Yshow-trees Hello.scala ● シンタックスツリーをGUIで表示 ● scalac -Ybrowse:refchecks Hello.scala
  • 36.
    構文木の作成 ● case Apply(Select(Select(Select(Ident … ● やってらんない ● MkTreeを使おう ● http://goo.gl/O3oeh $ scala MkTree "if (b) 3 else 5" If( Ident("b") // sym=<none>, sym.tpe=<notype>, tpe=null Literal(Constant(3)) Literal(Constant(5)) )
  • 37.
    完成したら ● sbt package ● target/scala-2.x.x/myplugin_2.x.x-1.0.jar ● これで君もモテモテだ!m9(°д°)
  • 38.
  • 39.
    おまけ ● 「モテモテになるコンパイラプラグイン」 が欲しいと言われて考えてみた ● loveletter plugin ● コンパイルの待ち時間を使って愛の告白 ● コンパイルの各フェーズでメッセージを出力 ● 特定の相手にだけに出力されようにする – java.net.InetAddress.getLocalHost()
  • 40.
    君に届け scalatan $ scalac-Xplugin:loveletter-plugin.jar src/test/scala/Hello.scala
  • 41.
    君に届け scalatan $ scalac-Xplugin:loveletter-plugin.jar src/test/scala/Hello.scala スカラ!スカラ!スカラ!スカラぁぁあああわぁあああああああああああああ あああああああああん!!!あぁああああ…ああ…あっあっー!あぁああああ ああ!!!スカラスカラスカラぁああぁわぁああああ!!!あぁクンカクン カ!クンカクンカ!スーハースーハー!スーハースーハー!いい匂いだなぁ…く んくんんはぁっ!スカラ・オーダスキーたんの黒色の髪をクンカクンカしたい お!クンカクンカ!あぁあ!!間違えた!モフモフしたいお!モフモフ!モフ モフ!髪髪モフモフ!カリカリモフモフ…きゅんきゅんきゅい!!型推論が大 幅強化されたスカラたんかわいかったよぅ!!あぁぁああ…あああ…あっあぁ ああああ!!ふぁぁあああんんっ!!拡張可能リテラルScala3.0で決まって良 かったねスカラたん!あぁあああああ!かわいい!スカラたん!かわいい! あっああぁああ!爆速コンパイルも新しいジェネリクスも発表されて嬉し…い やぁああああああ!!!にゃああああああああん!!ぎゃあああああああ あ!!ぐあああああああああああ!!!爆速コンパイルなんて現実じゃな い!!!!あ…型推論の大幅強化も拡張可能リテラルもよく考えたら…ス カ ラ ち ゃ ん は 現実 じ ゃ な い?にゃあああああああああああああん!!うぁああ ああああああああ!!そんなぁああああああ!!いやぁぁぁああああああああ あ!!はぁああああああん!!ローザンヌぅうううう!!この!ちきしょー! やめてやる!!現実なんかやめ…て…え!?見…てる?表紙絵のスカラちゃん が僕を見てる?表紙絵のスカラちゃんが僕を見てるぞ!スカラちゃんが僕を見 てるぞ!挿絵のスカラちゃんが僕を見てるぞ!!拡張可能リテラルのスカラ ちゃんが僕に話しかけてるぞ!!!よかった…世の中まだまだ捨てたモンじゃ
  • 42.
  • 43.
    告白タイム 質問も一応受けつけます