diff --git a/ppx_2018.md b/ppx_2018.md index 35b4c34..c09be71 100644 --- a/ppx_2018.md +++ b/ppx_2018.md @@ -191,4 +191,96 @@ http://caml.inria.fr/pub/docs/manual-ocaml/intfc.html アトリビュートは存在するASTノードに情報を付加するものですが、 ではエクステンションポイントってなんでしょうか。 -エクステンションポイントは式、型、宣言に置き換える \ No newline at end of file +エクステンションポイントは式、型、宣言に置き換える + +# りろんはわかった、で実際は? + +## PPXはコマンド + +PPXはOCamlのASTを受け取ってASTを返す関数のようなものだという事はわかりました。 +では実際どうやって実装するか。 + +PPXの基本は外部実行コマンドです。例えばあなたの書いたPPXコマンドが`ppx.exe`という名前だとすると、それを利用するには: + +```shell +$ ocamlc -ppx ppx.exe x.ml +``` + +とします。OCamlコンパイラは`x.ml`を読み込んでパースした後、そのASTのバイナリ表現を +一時ファイルに保存して次のコマンドを実行します: + +```shell +ppx.exe ファイル1 ファイル2 +``` + +ファイル1は読み込み元:オリジナルのASTバイナリ表現を保存した一時ファイル。 +ファイル2は書き込み先:PPXが変換したASTのバイナリ表現の保存先です。 + +この形式さえあっていれば、PPXコマンドは何を使っても構いません。 +OCamlのプログラムである必要さえ無い。一番簡単なPPXはshellスクリプトで書けます: + +```shell +#!/bin/sh +cat $1 > $2 +``` + +これを`ppx.sh`とかいう名前にして実行できるようにすれば、 + +```shell +$ ocamlc -ppx ppx.sh x.ml +``` + +でちゃんと動きます。まあ入力をそのまま返すだけですけれど、チェーンすることもできます: + +```shell +$ ocamlc -ppx ./ppx.sh -ppx ./ppx.sh x.ml +``` + +## PPXを作る + +もうすこし複雑な事をしたければさすがにOCamlのコードを書かなければいけません。 +PPXをOCamlで書くにはいくつか方法がありますが、一番単純なものを紹介します。 +(もっと低レベルな方法も使えますが、それは紹介するAPI関数のソースから辿ってください。) + +OCamlのソースコードに`parsing/ast_mapper.mli`というモジュールがあって、 +そこに`Ast_mapper.run_main`という関数があります。これを使います。 + +```ocaml +(* ppx.ml + Compile: ocamlfind ocamlc -o ppx.exe -package compiler-libs.common -linkpkg ppx.ml +*) +let () = + Ast_mapper.run_main + (fun args (* コマンド引数 *) -> + List.iter prerr_endline args; (* 引数を標準エラーに出してみる *) + Ast_mapper.default_mapper) +``` + +コンパイルするには`ppx.ml`という名前で保存して、 + +```shell +$ ocamlfind ocamlc -o ppx.exe -package compiler-libs.common -linkpkg ppx.ml +``` + +とします。`findlib`の警告が出ますが無視して結構です。 + +* `args`はPPXコマンドに与えられた引数です。最後の2つの引数はASTファイルの入出力に使われるのでそれは除外されています +* `Ast_mapper`を始め、OCamlのASTをいじるときには`compiler-libs.common`というパッケージを使います。OCamlコンパイラのパーサ部分のモジュールが入っています。 +* `compiler-libs.common`を使ったコンパイルやリンクのオプションを手で書くの面倒なので`ocamlfind`にやってもらいます。中で何が起こっているか知りたい人は`ocamlfind ocamlc -verbose -o ppx.exe -package compiler-libs.common -linkpkg ppx.ml`してください。 + +`Ast_mapper.default_mapper`って何と言う前に動かしてみます: + +```shell +$ ocamlc -ppx ./ppx.exe x.ml +``` + +`x.ml`がちゃんとしたOCamlのコードであれば、特に何も起こらず、普通にコンパイルできるはずです。 +PPXコマンドに引数を与えて、ちゃんとPPXの関数に引数が渡っているのを確認します: + +```shell +$ ocamlc -ppx "./ppx.exe a -hello" x.ml +a +-hello +``` + +## AST mapperでASTを変更する