[LSP42] SquirrelとIcon
(この記事はLISP Implementation Advent Calendar 20日目のためのエントリです。)
SquirrelとIconでLISPを作りました。
https://github.com/zick/SquirreLisp
https://github.com/zick/IconLisp
動機
今年の春、訳あって42個のプログラミング言語でLISP処理系を実装することになりました。これはその33〜34個目です。
Squirrelはゲーム用のいわゆる組み込み言語です。Luaと同様の用途と聞いてそれなら簡単に違いないと思って使いました。
IconはWikipediaで見つけ、面白そうだったので選びました。マニュアルを見ると分かりますが、びっくりするくらいコンパクトな言語です。
Squirrelの思い出
外観
Squirrelのプログラムはこんな感じの見た目をしています。
class Nil { } kNil <- Nil(); class Cons { constructor(a, d) { car = a; cdr = d; } car = kNil; cdr = kNil; } function nreverse(lst) { local ret = kNil; while (lst instanceof Cons) { local tmp = lst.cdr; lst.cdr = ret; ret = lst; lst = tmp; } return ret; }
そう。テキトーに書いても動くやつです。何も考えずに口を半開きにしてヨダレを垂れ流しながらでも書けるような簡単な言語です。やったー!
標準入力の罠
しかし、標準入力から一行取得しようとしたところでいきなりハマりました。そんな関数がライブラリにないのです。それどころか標準入力から一文字読み込むという関数もありません。使えそうなのはSquirrelのプログラムをロードするという関数。「シェルスクリプトと上手く連携させてユーザが文字列を入力するたびにファイルに書き込んでそれをロードして......」などと複雑なものを書き終えた後に、Squirrelの文字は8-bit整数で、標準入力から8-bit整数を読み込むとで文字の読み込みができる事に気付きました。マニュアルに注釈くらいは書いて欲しいです。
最速伝説
標準入力からの文字列の取得は戸惑いましたが、あとはマニュアルをほぼ読まずにテキトーに書いても、ほぼ想像通りに動いたのであっという間にLISPが完成しました。Squirrelはエラーメッセージが貧弱で分かり難いという問題点もありましたが、ほとんどエラーに遭遇しなかったので今回はあまり問題にならず。恐らく42言語中、もっとも早くLISPが完成しました。
Iconの思い出
外観
procedure の終わりには end がつくのでbegin/end系の言語かと思いきや、ブロックは中括弧{}で表します。
procedure makeCons(a, d) local obj obj := table() obj["tag"] := "cons" obj["car"] := a obj["cdr"] := d return obj end procedure nreverse(lst) local ret, tmp ret := kNil while lst["tag"] == "cons" do { tmp := lst["cdr"] lst["cdr"] := ret ret := lst lst := tmp } return ret end
あと、久しぶりに連想配列でLISPのデータを表す方針を取りました。
Goal-directed execution
Iconの一番面白いところは goal-directed execution と呼ばれる機能でしょう。Goalの条件式の値は true/false ではなく success/failure の二種類の値を取り、式の一部が失敗したらその時点で全体が失敗します。
Wikipediaには次のような例が載っています。
while write(read())
これは write(read())
が成功し続ける限りそれを繰り返します。 read()
はEOFを読み込むと失敗するので、そのときは「式の一部の失敗」となり、writeが呼ばれる前に式全体が失敗し、ループが終わります。
私のLISPでも同じ仕組を使ってみました。
while (exp := read1(read())) do { write(printObj(eval(exp[1], g_env))) writes("> ") }
この goal-directed execution はなかなかに面白い機能だとは思うのですが、簡単なLISPを作るくらいだと使いドコロがほとんどなかったのが残念で仕方ありません。暇があればもっと色々と遊んでみたいところです。あとIconの日本語版Wikipediaはあまりにも間違った記述が多いので英語版を読むことをおすすめします。
range
Iconは部分文字列を取得するために str[start:end]
のような書き方ができます。このindexは1-originで、exclusive (end-1番目の要素まで含む。end番目は含めない) です。この1-originとexclusiveの組み合わせはどうも違和感があります。多くの言語は0-originとexclusiveの組み合わせか1-originとinclusive (end番目の要素を含む) の組み合わせだと私の直感が言っています。inclusiveだとサイズ0のrangeを表現できないのでIconはexclusiveにしたのではないかと思いますが、それなら0-originにして欲しいです。あと、終端までのrangeを取得する方法が str[start:0]
というのもちょっと違和感があります。
小学生並みの感想
何はともあれ型を書かなくて良い言語は最高だと思いました。
[…] ただけあって、Pikeは使いやすかったです。テキトーに書いてもなんだかよく分からないけど動く。Squirrel並みの速さでLISPを完成させることが出来ました。それ故に思い出が少ないです。 […]