JavaScriptでおもちゃのLispを作ろう(5)〜組込み関数編〜
前回までに組込み関数を一切定義していないピュアなLisp処理系ができました。今回は必要最小限の組込み関数を実装します。
/* * 関数に名前をつけて登録する。引数lazyがtrueのときは、関数の引数を評価しない。 * スペシャルフォームのときtrueをセットするようにする。 */ function define(name, func, lazy) { var proc = new Procedure(name, func, lazy); var sym = intern(name); sym.value = proc; } function progn(exp) { var rv = NIL; for (var p = exp; !atom(p); p = cdr(p)) { rv = eval(car(p)); } return rv; } function append(x, y) { if (x === NIL) { return y; } else { return cons(car(x), append(cdr(x), y)); } } define("quote", function (x) { return x; }, true); define("lambda", function (x) { return cons(LAMBDA, x); }, true); define("cons", function (x) { return cons(car(x), cadr(x)); }); define("car", function (x) { return caar(x); }); define("cdr", function (x) { return cdar(x); }); define("caar", function (x) { return caar(car(x)); }); define("cadr", function (x) { return cadr(car(x)); }); define("cdar", function (x) { return cdar(car(x)); }); define("cddr", function (x) { return cddr(car(x)); }); define("null", function (x) { return (car(x) === NIL) ? T : NIL; }); define("eq", function (x) { return (car(x) === cadr(x)) ? T : NIL; }); define("equal", function (x) { return equal(car(x), cadr(x)) ? T : NIL; }); define("list", function (x) { return x; }); define("progn", function (x) { return progn(x); }, true); define("append", function (x) { return append(car(x), cadr(x)); });
condなどの制御文に相当するものも同様に実装できます。
define("cond", function (x) { for (var p = x; !atom(p); p = cdr(p)) { var v = eval(caar(p)); if (v !== NIL) { return (cdar(p) === NIL) ? v : progn(cdar(p)); } } return NIL; }, true); define("setq", function (x) { var v = NIL; for (var sym = x, val = cdr(x); !atom(sym); sym = cddr(sym), val = cddr(val)) { v = setValue(car(sym), eval(car(val))); } return v; }, true); define("defun", function (x) { var sym = intern(car(x)); sym.value = cons(LAMBDA, cdr(x)); return sym; }, true);
これまで、動作確認は端末ベースでJavaScriptが実行できるSpiderMonkeyやRhinoのエンジンを使っていましたが、次回はブラウザ上で実行するための対話環境を実装します。ブラウザ上で動作するようになったら、逐次、全ソースを公開していく予定です。