この記事は言語処理系 Advent Calender3日目の記事です。
1日目がJVMバイトコードの記事でしたので、今日はJasmin形式からclassファイルを作成する段階のお話をしようと思います。
jasc[1]はOCamlで作成したJVMバイトコードアセンブラです。Jasmin[2]とコンパチブルな機能を持ちます。全てのJasminの機能を持っているわけではありませんが、joose0コンパイラのバックエンドとして動作するレベルの機能を有しています。joose言語(JVM言語のコンパイラ)とペアで必要な機能を追加して行くと良いだろうと考えていました。jascはHaxe[3]のjavalib[4]を参考にしています。言い換えればjascはHaxeのJavalibにバイトコード操作を加え、Jasminのフロントエンドを加えた物です。
モチベーション
Scalaのコンパイルが遅いのでOCamlで再実装したら良いだろうとの思いで作り始めました。
コンパイラを作成するには、まずバイトコードの操作が出来ないといけません。
OPAMのJavalib[5]は、バイトコードが扱える拡張がされていました。しかし、高レベル表現を持ち、いくつかバグがあり、またWindowsでOPAMが動かなかったのでコンパイル出来ませんでした。Javalibの元を辿るとHaxeの作者の Nicolas Cannasse により作成されていました。HaxeにもJavalibがあります。extlibもHaxeの作者が関わっていたようです。HaxeコンパイラはWindowsで動作させる為にextlibやjavalibを内蔵しています。そこでjascもextlibも内蔵することにしました。
jascはOPAMのJavalibの高レベル表現をなくしてスリムにしたものに、Jasminのフロントエンドを加えた、あるいはHaxeのJavaLibにバイトコード操作機能とJasminのフロントエンドを加えた物です。
ライセンス
LGPLです。
理由はjavalibやextライブラリがLGPLだからです。
これは自由に使えたほうがよいと考えているのでMITライセンスにしたいところです。
性能
OCamlのバイトコードでもネイティブでも高速に起動します。
extlibは内部では破壊的な操作も行うなどして高速化されています。
OPAM上のJavaLibは高レベルインターフェイスを持っていて多段階のパスがありましたがjascでは高レベルインターフェイスを無くす事で高速化を図りました。
構文解析は伝統的なocamllex, ocamlyaccを用いており高速です。
ただし性能は測定していません。<ちゃんと測りましょう。
ジャンプ動作の関係で配列に値を保存していますが、これは他のデータ構造にしたほうが速くなる事はあるかもしれません。文字列を生成するよりも直接バイトコードを操作したほうが高速でしょうが、やはり測定してみないと分かりません。
構成
JCodeモジュールがバイトコードを表し、JDataモジュールがクラスファイルを表します。
-
ext 便利で高速なライブラリです。
- ext/IO.ml IO周りの便利なライブラリです。
- ext/enum.ml extlibの基本アルゴリズムが含まれています。iterとかfoldとかです。
- ext/extList.ml リスト周りの便利で高速なライブラリです。
- ext/extString.ml 文字列周りの便利で高速なライブラリです。
- ext/pMap.ml 便利で高速なMapです。
-
jCode.ml バイトコードを表します。
- jCodeReader.ml バイトコードの読み込みを行います。
- jCodeWriter.ml バイトコードの書き出しを行います。
- jCodePP.ml バイトコードのプリティプリントを行います。
-
jData.ml クラスファイルを表します。
- jReader.ml クラスファイルの読み込みを行います。
- jWriter.ml クラスファイルの書き出しを行います。
- jDataPP.ml クラスファイルのプリティプリントを行います。
- parser.mly Jasminの構文解析器です。
- lexer.mll Jasminの字句解析器です。
- jasc.ml メインルーチンです。
$ wc *.ml*
134 610 3310 jCode.ml
203 1311 11216 jCodePP.ml
214 1432 7822 jCodeReader.ml
269 1583 9175 jCodeWriter.ml
174 988 5469 jData.ml
322 1628 13818 jDataPP.ml
655 3313 20561 jReader.ml
569 2377 18794 jWriter.ml
67 246 1671 jasc.ml
379 2390 19584 lexer.mll
1440 6297 50643 parser.mly
4426 22175 162063 total
extlibは省くと、4426行で、パーサが大きいです。
parserとlexerを省くと、2607行です。parserはまだまだ発展途上です。
ちなみにextlibは2114行です。
$ wc ext/*.ml
796 3231 17062 ext/IO.ml
376 1358 7018 ext/enum.ml
508 2307 10204 ext/extList.ml
237 1037 5024 ext/extString.ml
197 1174 5391 ext/pMap.ml
2114 9107 44699 total
アドレスから見ても分かる通りあまり本気ではないはずだったのですが、結構大きなプロジェクトになって来ていますね。これように、リポジトリ分けるか。
今後
MinCamlのJVM実装からjascを呼び出せば、より高速に動作させる事が出来るかもしれません(必要な機能がなければ作ればいいのです)。また、直接ライブラリを呼べば高速に出来るでしょう。ライブラリのインターフェイスが不満なら、OPAMのJavalibの挑戦のように、より洗練されたインターフェイスを作る事に繋がるでしょう。