空想犬猫記

※当日記では、犬も猫も空想も扱っておりません。(旧・エト記)

etolisp 進捗 (17) 〜 user interface, test framework

Macro も大体動くようになって(まだまだ細かいところで問題は山積みであるが)いよいよ次は構造体や CLOS の実装を試してみようという気になってきた。ただそのためにはシンボル操作周りの関数が不足しているので,いくつか実装する必要がある。

一つ一つ実装していってもいいのだが,それだとモチベーション管理が難しいので,テストケースを先に書いて,fancy なテスト結果を表示するフレームワークを作ろうと画策。ここで役立つのが nice-catch だ。etolisp では C++ の例外が拾えるので,etolisp のランタイムエラーや,実装レベルの例外が catch できてしまう。いかがなものかと思われるけど,これによって,まだ実装のないエラーを引き起こすコードのテストを書いて,実行して,成功/失敗の統計をとるといったことが etolisp 上でできる。stub を作らなくても実装がなくてもテストが書けるしテスト動く。もちろん,厳密な意味では信頼できないんだけど,大抵は上手く機能すると思う。

(defmacro nice-caught (&body body)
  `(progn (nice-catch 'error ,@body)
          *last-nice-catch-result*))

(defmacro test-no-exception (&body body)
  `(if (null (nice-caught (progn ,@body)))
       (format t "passed ... ~A~%" '(progn ,@body))
       (format t "FAILED ... ~A~%" '(progn ,@body))))

(test-no-exception (boundp 'x))
(test-no-exception (symbol-name 'x))

まず手始めに,上のようなコードで試してみた。*last-nice-catch-result* は etolisp で定義されているグローバル変数で,nice-catch で掴まえた例外が入っていて,例外が発生しなければ nil という仕様。取りあえずの実行結果は以下のような感じ。*1

卵か先か鶏が先かという話になりそうだけど,class や package の実装ができてき次第,少しずつ素敵なフレームワークに育てていこうと思う。

> ./etolisp ../test/test-framework.lisp
ERRR: IMPLEMENT_ME
ERRR: boundp failed.
ERRR: cons::eval failed.
ERRR: progn failed.
ERRR: cons::eval failed.
FAILED ... (progn (boundp (quote x)))
passed ... (progn (symbol-name (quote x)))

素敵なテスト結果の表示には素敵な UI が必要だ。それだけでなく,将来 etolisp を色んなアプリケーション,デバイスに組み込んで使うということを意識すると,今のうちに UI を抽象化しておくのがよいと思われたので,全ての I/O を UserInterface クラスを介して行う仕様に変更。runtime 初期化時に UI オブジェクトを渡すようにした。もしかしたら要らないかも知れないけど…。

*1:format の ~: に対応すれば,もう少し cons 量を減らせるのだが,現状はやむなくこの形。