学習計画 3-1 でリファクタリング

学習計画 3-1 でリファクタリングしてみた。
注意していないと自分が vm を書いているつもりなのか、ただの Lisp の関数を書いているのか、マクロを書いているのか分からなくなりそうだ。

ちょっと考えかたを変えて、コンパイラはS式を vm の命令のみで作られた関数(ラムダ式)に変換することにしてみた。こうすると実行時は funcall で関数を呼びだすだけになる。PC(プログラムカウンタ)の増加はどこでやるかまだちょっとあやしい。

今の作りを整理すると、

  • スタックマシンに対する命令は defmethod で定義し、そこでは最小限の演算だけ使う。PCの操作含む。
  • コンパイラはS式を vm の命令だけを使う関数の列(コード)に変換する。
  • 実行時は無限ループで淡々と関数を実行する。
  • todo: PC がコードの範囲外を指したら終了。(格好悪い)


結局コンパイラがやることというのは、複雑な命令を単純な命令の組み合わせへ変換する、単なる式の変形にすぎないのだろうか。
だとするとそれは Lisp のマクロがやっていることだよなぁ。
勉強になっているようななっていないような不可思議な気分だ。


対話的に vm を動かせると理解しやすいかと期待していたが、そもそも CLOS を使う必要など無かったかも…。

(defun compile-sexp (sexp)
  (cond
   ((null sexp) nil)
   ((numberp sexp) (list (lambda (vm) (vm-push vm sexp))))
   ((and (consp sexp) (= 3 (length sexp)))
    (let ((fn (ecase (car sexp)
		(+ #'vm-add)
		(- #'vm-sub)
		(* #'vm-mul)
		(> #'vm-gt)
		(< #'vm-lt)
		;; todo.
		)))
      (append (append (compile-sexp (second sexp))
		      (compile-sexp (third sexp))) (list fn))))
   (t (error "unexpected sexp:~a" sexp))))

(defun exec (sexp)
  (format t ";; expect: ~a" (eval sexp))
  (let ((vm (make-instance 'vm)))  
    (let ((codes (concatenate 'vector (compile-sexp sexp))))
      (setf (codes vm) codes)
      (with-slots (pc fp codes) vm
	(loop while (< pc (length codes)) ;; 無限ループ
	    do (funcall (aref codes pc) vm)) ;; 実行
	vm))))