[LSP42] Oberon-2
(この記事はLISP Implementation Advent Calendar 21日目のためのエントリです。)
Oberon-2でLISPを作りました。
https://github.com/zick/Oberon2Lisp

動機
今年の春、訳あって42個のプログラミング言語でLISP処理系を実装することになりました。これはその35個目です。
使った言語が30を超えてからは基本的に「名前を知ってる言語は使ってしまえ」戦略をとっていて、「次は本で見たことがあるModula-3を使おう」と思っていました。しかし、Modula-3の処理系のインストールが上手く行かず失敗。Wikipediaを読むとOberon-2という似たような言語があると書いてあり、こっちは処理系のインストールがうまくいったので使うことにしました。
外観
Oberon-2のプログラムはなかなかインパクトがあります。
PROCEDURE Nreverse(lst: LObj): LObj;
VAR
ret: LObj;
tmp: LObj;
BEGIN
ret := kNil;
LOOP
WITH
lst: Cons DO
tmp := lst.cdr;
lst.cdr := ret;
ret := lst;
ELSE
EXIT
END;
lst := tmp (* This statement can't be in WITH due to type checking. *)
END;
RETURN ret;
END Nreverse;
で、出た〜wwww大文字奴〜wwwwwww
教科書などに載っている古いFORTRANのプログラムみたいです。まあ、FORTRANを元にした言語なので間違ってはいないのですが、90年代に作られた言語なのにキーワードがすべて大文字なのはどうかと思います。でも、シンタックスハイライトがなくてもキーワードがすぐに分かるのはある意味いいかもしれませんね。見るぶんには。タイプするときにはたまったものじゃありません。
オブジェクト
Oberon-2ではレコード型があるので、これでLISPのオブジェクトを表すことにしました。
TYPE LObjDesc = RECORD END; LObj = POINTER TO LObjDesc; NilDesc = RECORD (LObjDesc) END; Nil = POINTER TO NilDesc; ConsDesc = RECORD (LObjDesc) car, cdr: LObj END; Cons = POINTER TO ConsDesc;
実際に使うときはレコードそのものより、そのポインタの方が便利なのでレコードとそのポインタにそれぞれ名前をつけます。レコードは継承関係を作ることが出来ます。上記NreverseのようにWITHを使ってキャストをすることもできます。
レコードの実体をつくるにはNEWを使います。
PROCEDURE MakeCons(a, d: LObj): LObj; VAR cons: Cons; BEGIN NEW(cons); cons.car := a; cons.cdr := d; RETURN cons END MakeCons;
未初期化の変数に対してNEWを呼ぶのがなんだか違和感があります。ちなみに、NEWで作ったレコードはごみ集めによって自動的に回収されます。昔のFORTRANみたいな文法の言語とは思えないくらい気が利いてます。
メソッド
レコードに対してはメソッド(のようなもの)を定義することも出来ます。
TYPE
SubrFnDesc = RECORD END;
SubrFn = POINTER TO SubrFnDesc;
SubrCarDesc = RECORD (SubrFnDesc) END;
SubrCar = POINTER TO SubrCarDesc;
...
SubrDesc = RECORD (LObjDesc) fn: SubrFn END;
Subr = POINTER TO SubrDesc;
...
PROCEDURE (f: SubrFn) Call(args: LObj): LObj;
BEGIN
RETURN MakeError("unknown subr")
END Call;
PROCEDURE (f: SubrCar) Call(args: LObj): LObj;
BEGIN
RETURN SafeCar(SafeCar(args))
END Call;
関数Callは subr.fn.Call(args) のように呼び出します。 fn の型に応じてどの Call が呼ばれるかが決まるという仕組みです。 Call に現れる f はいわゆる this の役割で、好きな名前をつけることが出来ます。
謎
論理積は & で、論理否定は ~ です。しかし論理和はなぜか OR です。意味が分かりません。
無条件ループをつくる LOOP では、それを抜け出す EXIT が使えるのに、条件付きループをつくる WHILE では EXIT が使えません。意味が分かりません。
なんか、色々と古臭いのに、処理系のヘルプを読む限りJITコンパイルをしてるみたいです。逆に意味が分かりません。
小学生並みの感想
大文字が許されるのはFORTRAN 77までだよねー