Posted 2015-04-08 04:29:01 GMT

最近、ぼんやりする日々ですが、Twitterを眺めていた所、Eclipse Common Lisp のソースコードがGitHubで公開されたとのニュース!

個人的には、知る人ぞ知る謎の処理系 No. 1だったので、早速GitHbからコードをチェックアウトしてビルドに挑戦してみました。

Eclipse Common Lisp とは

その前に、Eclipse Common Lispとは何かということですが、Eclipse Common Lispは、Elwood Corporationが1997年に発売したLispからCへのトランスレータ方式のCommon Lisp処理系で、

ANSI Common Lisp 準拠

MOP装備

Common LispのプログラムをCのコードとして出力でき、CからCommon Lispを呼んだり、Common LispからCを呼んだりできる

Unicode対応

お値段 $300 と商用処理系としては非常に安価(後に$500、処理系ソース付きで$5,000)

等々が特長の処理系です。

Cへの変換というとKCL系統(GCL、ECL等々)が有名ですが、Eclipse CLは、新規に開発されたもので、KCLとは全く別物になります。

1997年に最初のバージョン1.0のアナウンスがされ、

1998年に、バージョン1.1のアナウンスもされましたが、

しかし、その後、残念ながら、Elwood Corporation はCommon Lisp事業から撤退したのか、それ切りになってしまったようです。

私が、Eclipse CL について知ったのは、Eclipse CLのプロジェクトリーダーであった、Howard Stearns氏が自身のウェブページにマニュアルを掲載したのが切っ掛けでしたが、このような処理系があったことと、開発が継続されなかったことを残念に思っていました。

MOP装備のANSI Common Lisp 処理系は一人で作れる(らしい)

Howard Stearns氏の経歴のページによれば、ANSI Common Lisp処理系を丸ごと作ったと書いてあるので、どうも一人でこの処理系を作ったのではないかと思われます。

ソースを眺めてみてもそんな雰囲気です。恐しや!!

眺めた所では、どうやら、Eclipse CLの処理系のCのソースはCommon Lispで生成しているようで、それには、Allegro CL 4.3が主に使われていたようです。

ビルドしてみる

さて、早速ビルドに挑戦してみましょう。まず、

から、コードを拾ってきます。

他に必要なものとしては、libgc(Boehm GC)が必要になります。

Linux等では、パッケージでインストールできることが殆どだと思います。

git clone して、

$ cd eclipse-lisp/c $ make

これだけでビルドできちゃうのか!と思いましたが、Blake McBride氏がいまどきのOSでも動くようにファイルシステム周り等を修正してくれているようです。ありがたや!!

さて make すると eclipse というバイナリができますが、これが処理系の本体です。

$ eclipse-lisp/c/eclipse Initializing: intern-ext intern-int intern-add list kernel mop-init types-run control-run symbol clases clos-run clos-define clos-seq common mop class-meth methods gfunc method-init predicates arithmetic conv hash type-ops type-seq method-comb type-mops sequence seq-mod search sort control numbers trig num-conv array string struct-run character tree package c-pkg resource env condition set alist bit-array bignum bits equalp stream file-stream comp-stream circle init reader printer format pretty print-object doc describer miscel random pathname file enclose dispatch parameters pkg signals dev-meth env-comp common-comp prog-comp cont-comp cont-comp2 macro macro-comp opts number-comp list-comp clos-comp pretty-comp struct-comp cond-comp more-compile env-comp loop directives describe debug default-pp walk-util walk-top walk-special literal evaluation c file-walk c-walk prim-decs Eclipse Common Lisp Version 1.1 Copyright (c) 1997, 1998 Elwood Corporation, Oak Creek, WI, USA. Lisp Top Level. Options: 0: Print help. [:HELP] 1: Exit Lisp. [:EXIT] >

こんな感じの起動画面です。

ちょっと古いOSでもビルドを試す

上記では、 make だけでしたが、本来は、 make install するようです。これも試してみましたが、こちらは、まだ対応していない様子。

一応想定している処理を探って環境を合せてみましたが、 libgc5.0 のソースを eclipse-lisp/c/gc 以下に展開すれば、 make install が上手く行くようです。

ちなみに、試したOSは、Knoppix (Kernel 2.4.27) ですが、こっちの方が若干調子が良いような気がしないでもありません。

しかし、まだ違いを詳しくは調べていません。

fib を定義してみよう

さて、おなじみの fib を定義してみます。

( n ) if ( < n 2 ) n + 1- n fib - n 2 fib defun fib

ちなみに改行が入っているとセグフォしたりするので、まだ色々不安定なようです。

すると、

#include <eclipse.h> clObject cl1MINUS_FUNC ( clProto ) , clCharpSimpleBaseString __P ( ( clCharp ) ) , clExtraArgs ( clProto ) , clIntern ( clProto ) , clMissingArgs ( clProto ) , clPkg ( clProto ) ; clObject cl1MINUS_FUNC, clCharpSimpleBaseString __P, clExtraArgs, clIntern, clMissingArgs, clPkg extern clObject clADD, clLT, clSUBT; static clObject I_1, I_2, PKG_USER, STR_FIB__0, STR_USER__1, usrFIB; clObject usrFib clVdecl(_ap) { clObject n; { clBeginParse(_ap); clSetq(n, (_clVp(_ap) ? clVpop(_ap) : clMissingArgs(I_1, clEOA))); if (_clVp(_ap)) clExtraArgs(clVargs(_ap), clEOA); clEndParse(_ap); } { clObject L_test; { clObject L_0; clSetq(L_0, n); clSetq(L_test, clFuncallFunction(clSymbolFunctionValue(clLT), L_0, I_2, clEOA)); } if (clTrue(L_test)) return(clValues1(n)); else { clObject L_1, L_0; { clObject L_0__R1; { clObject L_0__R2; clSetq(L_0__R2, n); clSetq(L_0__R1, cl1MINUS_FUNC(L_0__R2, clEOA)); } clSetq(L_0, clFuncallFunction(clFdefinition(usrFIB, clEOA), L_0__R1, clEOA)); } { clObject L_0__R1; { clObject L_0__R2; clSetq(L_0__R2, n); clSetq(L_0__R1, clFuncallFunction(clSymbolFunctionValue(clSUBT), L_0__R2, I_2, clEOA)); } clSetq(L_1, clFuncallFunction(clFdefinition(usrFIB, clEOA), L_0__R1, clEOA)); } return(clFuncallFunction(clSymbolFunctionValue(clADD), L_0, L_1, clEOA)); } } } void clLoader __P((void)) { clDbind(clstarPACKAGEstar); clDbind(clstarREADTABLEstar); clDbind(clstarLOAD_TRUENAMEstar); clDbind(clstarLOAD_PATHNAMEstar); clSetq(I_1, clIntFixnum(1)); clSetq(I_2, clIntFixnum(2)); clSetq(STR_FIB__0, clCharpSimpleBaseString("FIB")); clSetq(STR_USER__1, clCharpSimpleBaseString("USER")); clSetq(PKG_USER, clPkg(STR_USER__1, clEOA)); clSetq(usrFIB, clIntern(STR_FIB__0, PKG_USER, clEOA)); clMakeClosure(0, usrFib, clNULL_HOOK); clUnwind(4);

こんな感じのものが出力されます。

Eclipse CL の日本語の扱い

Eclipse CLのマニュアルには、UCS-4が使えるということなのですが、具体的にはどうなっているのか確認してみました。

とりあえず、

char-code-limit

なので、確かにUCS-4の範囲はカバーしています。

"おはよう日本!" という文字列を文字コードのリストにすると、 (12362 12399 12424 12358 26085 26412 33) になりますが、

map ' map ' ( 12362 12399 12424 12358 26085 26412 33 ) string #' code-char list #' char-code

という風に、リスト→文字列→リスト、は元に戻ります。

しかし、エンコードは一体何を使っていることになってるのかということで、ファイルに書き出してみました。

ASCIIの範囲外で書き出したい場合(UCS-2/4で書き出したい場合)は、 :external-format には、 :ucs を指定します。

with-open-file ( out "ohayo.txt" :direction :output :if-exists :supersede :external-format :ucs ) map nil lambda ( c ) princ code-char c out ( 12362 12399 12424 12358 26085 26412 33 ) nil

の結果として、 ohayo.txt の中身は、

00000000: 4a30 0000 6f30 0000 8830 0000 4630 0000 J0..o0...0..F0.. 00000010: e565 0000 2c67 0000 2100 0000 .e..,g..!...

となっていました。

4e300000 をひっくり返すと、 0000304a => 12362(お) なのでリトルエンディアンのようです。

ということで、SBCLからファイルを読んでみましたが、

utf-32leとして読めることは読めるようです。

2000年辺りは、まだ多言語対応も揺れていた時期かなと思いますが、ゴージャスにUCS-4採用に舵を切ったCommon Lisp処理系は、Eclipse CL位かなと思います。

みつけたバグ

with-hash-table-iterator

ハッシュテーブルのイテレータの実装に使われている内部関数がどういう訳か実装されていません。

コードはあるのですが、他のホスト処理系からクロスコンパイルする場合に使われるもののようです。

とはいえ、 maphash 等は動くので、 with-hash-table-iterator のコードが何故この状態なのかは謎です。

この影響として、 with-hash-table-iterator を利用する、 with-package-iterator や、更にそれを利用する、 do-symbols 等が動きません。

とりあえず、 do-symbols 位なら、

でしのげます。

この辺りは、クラスを多用しているのが結構特徴的というか、ANSI CL制定後にスクラッチで書かれた処理系ならではという感じはします。

defpackage

:intern オプションが使えません。

が、

に展開されるので、 intern にリストが渡ってエラーになるようです。

defpackage で :intern オプションはあまり使うこともないので、バグが埋もれていたのではないかと思い ますが、偶然見付けてしまいました。

こんな所もにまたなんとなく、フルスクラッチで書かれたんだなあということを感じてしまいます。

まとめ

等々、まとまりもなく書き散らかしてしまいましたが、Eclipse CLの詳細は付属のマニュアルに書かれていますので、そちらを参照してみてください。

Eclipse CLは非常にポテンシャルがある処理系だと思っているので、今後開発が有志によって進んで行くことを期待しています!

■

