先日、プログラミング言語というにはあまりにお粗末な IMP という言語を紹介して、それを Objective Caml で書いてみました。今日は、同じ言語を JavaScript で書き直しましたという話題です。すでに慣れている Objective Caml の場合には 4 時間で書けたのですけれども、JavaScript の場合には構文解析器生成系のツール探しやら、Windows 用に実装されたツールの Mac OS X への移植、そのツールの改造をしていたので三日もかかってしまいました。また、(不慣れにもかかわらず)JavaScript の特徴を生かしたインタプリタを書こうと思って、Objective Caml版とは少し違う構造に変更したので、その部分でも時間がかかりました。ようやく動いたので紹介します。今回は型宣言が不要ということで200行にまで短縮しました。
今回は全部でぴったり200行。前回の Objective Caml の300行もそうなんですが、狙ったわけではなく偶然です。ソースコードを見ていただければ分るけれど、不自然なところないですよ。IMP インタプリタの行数 100 の法で閉じているらしいです。
今回の実装の特徴には以下の点があります。
– 例外オブジェクトの toString を書くことで Objective Caml での実装にあった handle 関数に相当するものをなくした。
– Objective Caml での実装では単項、二項演算子の意味を保存するために unaries, binaries という表を用意しましたが、これらは整数や論理値を表すプロトタイプをハッシュ表になぞらえて登録しました。
– インタプリタの状態を表す変数束縛にもオブジェクトを用いました。ハッシュ表が言語機構に組込まれているととても便利です。
インタプリタの心臓部は以下のようにかなりすっきりと書けました。まずは、式の評価の部分です。
var imp_eval = function (e) {
var t = e.t;
if (t === ‘Int’) return imp_int(e.v);
else if (t === ‘ID’) return imp_state[e.name];
else if (t === ‘Unary’) return e.e[e.op]();
else if (t === ‘Binary’)
return imp_eval(e.e1)[e.op](imp_eval(e.e2));
else throw Exception.create({ kind: ‘Unknown expression type’, e: e });
};
次はコマンドの処理の部分です。
var imp_exec = function (c) {
var t = c.t;
if (t === ‘Define’ || t === ‘Assign’)
imp_state[c.name] = imp_eval(c.e);
else if (t === ‘If’)
(c.e[imp_eval(c.e[0]).v ? 1 : 2]).forEach(imp_exec);
else if (t === ‘While’) {
if (imp_eval(c.e[0]).v) {
c.e[1].forEach(imp_exec);
imp_exec(c);
}
} else if (t === ‘Print’) print(imp_eval(c.e));
else if (t === ‘Dump’) print(imp_state);
else throw Exception.create({ kind: ‘Unknown command type’, e: e });
};
[[zip を公開:http://ken-wakita.net/files/langs-js.zip%5D%5Dしました。ほとんどデバッグしてないのでバグだらけではないかと思います。。。