読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

アセンブリでLISP開発日誌(完)

asm lisp

バグ山ほど出しながらも、なんとかLispを動かせた。ここから先はアセンブリでのVMが主目的では無いので、単にAmeLisp開発日誌となるでしょう。なったらいいな。

リポジトリの名前も変えた。

github.com

振り返り

前回からやったことを思い出してみる。

まずアセンブリで、コンパイラを動かすためにVM命令を追加していった。CMP命令をいっぱい書いてウーンとなったので、ディスパッチテーブル方式にした。しかしアセンブリのマクロに不慣れで同じVM命令を3箇所書く羽目になってるので、これはどうにかしたい。

併せて、VM/S側でAmeLisp→S-CODEコンパイラを作っていった。ここで思いついたことがあって、コンパイラも一緒にSECDマシン上で動くなら、コンパラのディスパッチテーブルを動的にいじることができれば、マクロと同じことができるんじゃないかと思った。

とりあえずクロージャと連想リストで(!)かなりチープな実装を行って、動的にマクロに対する操作を行えるようになった。M.Hiroi氏の解説に出てくるDEFM命令をアセンブリでがんばって書く必要が無くなったのもかなり楽だった。そのかわりVM/S上で動くコンパイラがめちゃくちゃ遅くなった。

その後GCのバグが出たので必死に追跡し、VM/SとVM/Aで命令に乖離が起きていたのでVM/S側を近づける形で修正。ここは後に作ったVM/Aの設計の方が優れていることに気づいたから。何回も同じもの作るの、学習にはとても良いと実感した。

そして今日、quasiquote周りの単純なバグを潰して、ついにVM/A上でAmeLispによるCompilerを使ったREPLが動いた!いやー感動した。最後のは単純なバグだったけど、バグがVM/S、VM/A、Compiler/AmeLisp(Scheme用版)、Compiler/AmeLisp(純粋?AmeLisp版)、どの層にあるのか全然見当つかなくて、かなりの長期戦を覚悟した。地道にやっていこうと思い、自動テスト・自動ビルドを整備して、一つずつテスト書いて……とやってるうちにアレ?と思って探索したらわかった。いつか整備しようと思ってたので良かったです。

以下のコードが動く。かっこいい。(コメント機能はまだない)

; ディスパッチテーブルの登録名一覧を出力
; *compiler-patterns*は単に第一引数のシンボルでディスパッチする関数。
; Poor man's object.
> (*compiler-patterns* 'keys)
;=> ((const undef) (const false) (const true) (const nil) symbol atom (special-form let*) (special-form letr) (special-form let) (special-form cond) (special-form or) (special-form and) (special-form quasiquote) (special-form defmac) (special-form defn) (special-form do) (special-form set!) (special-form apply) (special-form fn) (special-form def) (special-form if) (special-form quote) (primitive error) (primitive show-SECD) (primitive garbage-collect) (primitive write-byte) (primitive read-byte) (primitive print) (primitive read) (primitive <=) (primitive >=) (primitive <) (primitive >) (primitive =) (primitive undef?) (primitive null?) (primitive nil?) (primitive fn?) (primitive number?) (primitive symbol?) (primitive pair?) (primitive not) (primitive eq?) (primitive dec) (primitive inc) (primitive mod) (primitive /) (primitive *) (primitive 0) (primitive +) (primitive set-cdr!) (primitive set-car!) (primitive cons) (primitive cdr) (primitive car) (primitive stop-vm) (primitive run-vm) (primitive exit) normal-apply)

; special-formだけを取り出してみる。

; まずクロージャで、carを比較するツールを
> (defn car-eq? [x] (fn [xs] (and (pair? xs) (eq? (car xs) x))))
;=> car-eq?

; select。Smalltalk好きなんで……
> (select (*compiler-patterns* 'keys) (car-eq? 'special-form))
;=> ((special-form let*) (special-form letr) (special-form let) (special-form cond) (special-form or) (special-form and) (special-form quasiquote) (special-form defmac) (special-form defn) (special-form do) (special-form set!) (special-form apply) (special-form fn) (special-form def) (special-form if) (special-form quote))

; defnのコンパイルパターンの、コンパイル関数を取り出してみる。
; ('(special-form defn) . (match-fn . compile-fn)) ) という形で入っているので
; まずパターン(cdr部)を取り出す
> (def pattern-defn (*compiler-patterns* 'get '(special-form defn)))
; => pattern-defn
> pattern-defn
;=> (#<fn:13DD5E0> . #<fn:13DD5C0>

; コンパイラ(さらにcdr部)を取り出す
> (def compiler-defn (cdr pattern-defn))
;=> compiler-defn
> compiler-defn
;=> #<fn:13DD5E0>

; コンパイル時にメッセージを出すようラッピングして
>(defn compiler-defn-wrapped args
   (print '(COMPILING DEFN!))
   (apply compiler-defn args))
;=> compiler-defn-wrapped

; コンパイラとして設定しなおす
>(*compiler-patterns* 'set 
   '(special-form defn)
   (cons (car pattern-defn) compiler-defn-wrapped))
;=> ((special-form defn) #<fn:20EB5E0> . #<fn:20A4D60>)

; defnフォームを評価すると
> (defn id [x] x)
(COMPILING DEFN!)
;=> id

継続でバックトラックできるようにしたりすると更にカッコイイんじゃないでしょうか。ムフフ。

今後

とりえあず書いてる最中に、いくつかのVM命令で値を返してない可能性があることに気づいた。修正していきましょう。

それからは、まずテストを書いていってハーネスを打ち込んでいきたい。バグ潰してしっかり動けばVM/A上でAmeLispをコンパイルできるはず。

その後なんだけど、方針を再確認すると

  • とにかくシンプルに作る
  • かっこいいことはまた今度にする

それに加えて、

  • 自分の遊び場にする
  • Smalltalkのように「極端に遅延結合な動的システム」を目指す
  • スピードは完全に無視
  • 空間効率も最初は無視

というところ。

次の目標は

  • VM/Aに継続、限定継続を実装する(VM/S上で動いてるのでたぶん大丈夫)
  • Copying GCに切り替えて文字列・配列・バイト配列を扱えるようにする
  • Windowsで動かす

というところかな。ヤルゾ〜

Lispについて

Smalltalkしばらく触って得たあの感覚は本当に素晴らしくて、それ以降ずっと影響を受け続けている。なんだけど、でもやっぱ、S式とリストとクロージャ作りまくるスタイルが大好きなんだよな〜〜と思った。完全に手に馴染んでいる。

開発中それを強く感じたので、なんでだろう?とちょっと自問したら思い出した。たま〜に本買ってはすぐわからなくなって積むワナビーだった中学生の頃、数少ない、ちゃんと目を通して手でも動かしたのがリスト遊びというLispの本だった。これは面白かったなあ。中高生の頃影響されたものはずっと好きでい続ける、みたいな話あるけどそれだろうか。

広告を非表示にする