defineをmacroで実装した
私がRubyで書いているLisp方言、 [Nendo]の開発状況続き。
lambdaとmacroの可変長引数のサポートできたので、それを利用してdefineもNendo上のmacroで実装した。 いままでは、defineをRubyで実装していたが、Rubyでのリスト処理が煩雑だったのだ。macroでスッキリ書けた。 初期化ライブラリの init.nnd の冒頭に定義した。
(set! define
(macro (arg . body)
(if (not (pair? arg))
(cons 'set!
(cons arg
body))
(cons 'set!
(cons (car arg)
(list
(cons 'lambda
(cons (cdr arg)
body))))))))
ここでは、使える部品が少ないので、定義は非常に冗長になってしまう。 quasiquoteはまだ使えないし、appendはdefineを使って定義したいのでこうならざるを得ないのかな。 ブートストラップとはそういうものですよ、多分。
ところで、昔、仕事で組込CPUのブートローダーをアセンブラでたくさん書いた事を思い出す。 組込CPUのブートストラップは、ROM RAMチェックが完了するまではスタック(RAM)が使えないので少ないレジスタを使いまわしてメモリチェックをしないといけない。 RAMチェックが完了して初めてスタックポインタを設定し、 C言語のエントリポイントである _main を呼ぶことができる。 _mainまで来ればC言語で書けるので一気に楽になるのだ。
Nendoの初期化ライブラリ init.nnd も同様にビルディングブロックが積みあがるみたいに、後になればなる程、コードが簡潔になっていくのだろう。 condもmacro定義したけど、この段階でもまだまだ使える部品が少なくて冗長だ。
(define cond
(macro lst
(define (caseblock elem . elseblock)
(let ((condition (car elem))
(body (cdr elem)))
(append
(list 'if
(if (eq? 'else condition)
true
condition)
(cons 'begin body))
(if (< 0 (length elseblock))
elseblock
'()))))
(define (cond_iter lst)
(if (eq? 0 (length lst))
'()
(if (eq? 1 (length lst))
(caseblock (car lst) '())
(caseblock (car lst)
(cond_iter (cdr lst))))))
(cond_iter lst)))
次はこのcondを使ってor と and を定義してみよう。 もうそろそろ ミニLisp処理系を実装している人とブートストラップの話をしてみたいなぁ。