1週間ほど前の深夜、ふと Go で連結リスト構造を書いていたら次第に car/cdr 形式になってしまい、気付いたら手が滑って Lisp 処理系を作り始めてしまいました。
なぜかこんな時間から突然 lisp を書き始めてしまった。
— mattn (@mattn_jp) March 26, 2020
初日は深夜だったのでパーサを書いた所で終了。次の日の夕方には四則演算と FizzBuzz が動きました。実は Lisp 処理系を書くのは人生でたぶん4回目くらいで、前回はC言語で書きました。
GitHub - mattn/cisp: Minimal Lisp Interpreter
https://github.com/mattn/cisp
今回のルールとして「過去の自分の実装や他の実装は見ない」というオレオレルールを作ってしまったので幾分時間が掛かってしまった様に思います。テストコードはさすがにいいだろという事で、cisp のテストコードは借りました。マクロを除いて cisp と互換性があります。
今回 Lisp 処理系を書きながらなんとなくやってみたいなと思っていたのが Lisp での非同期処理。Go言語の goroutine と channel を使って通信出来たらどんな物ができるだろう、という研究目的でした。
本日ようやく goroutine/channel が動いたのでブログで公開しておきます。
GitHub - mattn/golisp: Lisp Interpreter
https://github.com/mattn/golisp
(go:make-chan string)
で chan を作る事ができ、(go:chan-send ch "foo")
で送信、(go:chan-recv ch)
で受信する事ができます。また (go (print 1) (print 2))
で goroutine が起動するので、以下の様な channel を使った通信ができます。
(setq time (go:import time))
(let ((ch (go:make-chan string 1)))
(go
(.Sleep time 1e9)
(go:chan-send ch "1")
(.Sleep time 1e9)
(go:chan-send ch "2")
(.Sleep time 1e9)
(go:chan-send ch "3")
(.Sleep time 1e9)
(go:chan-send ch "ダーッ!")
)
(print (car (go:chan-recv ch)))
(print (car (go:chan-recv ch)))
(print (car (go:chan-recv ch)))
(print (car (go:chan-recv ch)))
)
研究の成果としては、面白い動きが確認できたのでまずまずの成果と思います。上記の様に Go のパッケージを import して関数やメソッドを呼び出せる様になっています。例えば乱数を表示するには以下の様に実行します。
(setq time (go:import 'time))
(setq rand (go:import 'math/rand))
(.Seed rand (.UnixNano (.Now time)))
(print (.Int rand))
今の所、実用的ではありませんが、細々とメンテナンスしていってツールを書く程度に使えるまでは持っていきたいと思います。