「JavaScriptで仮想機械の勉強をしましょう (その2)」で定義した命令セットを動くようにしてみました。ただし、NFCALL(ネイティブ関数の呼び出し)はまだやっていません。プログラムの終了命令を忘れていたので追加しました。
- EXIT -- 正常終了
- ERROR -- 異常終了
命令コード(OPコード)の定義は次のようになります。
/* == 命令コード一覧 == */ /* 定数の命令 */ var CONST = 00; // 定数nをスタックに積む /* 算術演算の命令 */ var ADD = 10; // 足し算 var SUB = 11; // 引き算 var MUL = 12; // 掛け算 var DIV = 13; // 割り算 var REM = 15; // 割り算の余り /* 述語/関係の命令 */ var EQ = 20; // スタックトップと二番目が等しいかどうか var LT = 21; // スタックトップ < 二番目 かどう /* 論理演算の命令 */ var AND = 30; // 論理アンド var OR = 31; // 論理オア var NOT = 32; // 論理ノット /* 流れ制御の命令 */ var $$$ = 40; // ラベルを付ける var JMP = 41; // ラベルの場所にジャンプ var IFT = 42; // スタックトップが真だったらラベルの場所にジャンプ var EXIT = 44; // プログラムの終了 var ERROR = 45; // エラーによるプログラムの終了 /* ブロックスコープと変数の命令 */ var BOPEN = 50; // 新しいブロックを作成 var BCLOSE = 51; // 現在のブロックを破棄 var REF = 52; // 変数値をスタックに積む var ASSIGN = 54; // 変数にスタックトップを代入、スタックトップはそのまま var ALLOC = 55; // 変数を確保(値は未定義) /* スタック操作の命令 */ var DISCARD = 60; // スタックトップを捨てる var DUP = 61; // スタックトップをコピー var XCHG = 62; // スタックトップと二番目を入れ換える /* ネイティブ関数呼び出しの命令 */ var NFCALL = 70; // 名前で示される関数を呼ぶ
この命令セットによって、簡単なプログラムを書いてみました。
// 2 + 3 を計算する。
[
CONST, 2,
CONST, 3,
ADD,
EXIT
],// x^2 + y^2 を計算する。
[
REF, "x",
DUP,
MUL,
REF, "y",
DUP,
MUL,
ADD,
EXIT
],// スタックトップの絶対値を求める。
[
DUP,
CONST, 0,
LT,
IFT, "Neg",
EXIT,$$$, "Neg",
CONST, 0,
XCHG,
SUB,
EXIT
],// スタックトップの階乗を計算する。
[
DUP,
CONST, 1,
LT,
IFT, "Error",
// 準備
ASSIGN, "k",// 計算開始
$$$, "Start",
REF, "k",
CONST, 1,
SUB,
DUP,
CONST, 0,
// N, k-1, k-1, 0
EQ,
IFT, "End",
// N, k-1
ASSIGN, "k", // k := k - 1
MUL, // N*(k-1),
JMP, "Start",
$$$, "Error",
ERROR, "NonPositiveArgument",$$$, "End",
DISCARD,
EXIT
],// 変数n, m(ただし、n ≦ m)に対して、
// その最大公約数を求める。
[
$$$, "ConfirmArgs",
REF, "m",
REF, "n",
LT,
NOT,
IFT, "Check",
ERROR, "BadArgument",$$$, "Check",
REF, "n",
CONST, 0,
EQ,
IFT, "End",$$$, "DoStep",
REF, "m",
REF, "n",
ASSIGN, "x",
REM,
ASSIGN, "n",
DISCARD,
REF, "x",
ASSIGN, "m",
DISCARD,
JMP, "Check",$$$, "End",
REF, "m",
EXIT
],
次のURL(冒頭にも示した)で、上記のサンプルプログラムを実行できます。