[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までだよねー