JavaScript による HSP インタプリタ HSP on JS

HSP on JS は JavaScript による HSPインタプリタの実装です。
CodeReposリポジトリを置いて開発しています。

対応している機能

まず、 GUI 描画命令はありません。 mes 命令を実行したら textarea に出力されるだけです。将来的に HTML5 Canvas を使って GUI 描画命令も対応できたらいいだろうなーとは思っています。

  • ラベル型、文字列型、実数型、整数型、struct型
  • goto, gosub, repeat 〜 loop などの基本的な制御命令
  • 配列変数、多次元配列
  • wait 命令(ブラウザを停止させません)
  • ユーザ定義命令・関数
  • モジュール変数
  • その他一部の命令・関数

に対応しています。

仕組み

  1. hspcmp を実行して結果の AX を JSON で返す CGIAjax でアクセス
  2. AX のコードを独自の命令列に変換
  3. 命令列をJavaScriptソースコードに変換
  4. 実行

という流れです。

命令列

mes "foo" は

PUSH <<StrValue:foo>>
CALL_BUILTIN_CMD <9, 15, 1>

コンパイルされます。
PUSH <> は文字列 foo をスタックに積む、 CALL_BUILTIN_CMD <9, 15, 1> は mes 命令を一つの引数で呼び出すという意味です。

a = b + c は

PUSH_VAR <0, 0> ← a を表す
PUSH_VAR <1, 0> ← b を表す
PUSH_VAR <2, 0> ← c を表す
ADD <>
SETVAR <1>

コンパイルされます。PUSH_VAR は一つ目の命令オペランドが変数 ID 、二つ目が配列の添字の数です。どれも配列の添字は指定されていませんので添字の数は 0 です。
ADD はスタックから二つポップして足し算した結果をスタックに積みます。
SETVAR は命令オペランドの数だけスタックからpopしてそれを、さらに一つ pop して得る変数に代入します。配列の連続代入のときにはこの命令オペランドが 2 以上になります。

たとえば、 a = 1, 2, 3 は

PUSH_VAR <0, 0>
PUSH <<IntValue:1>>
PUSH <<IntValue:2>>
PUSH <<IntValue:3>>
SETVAR <3>

となります。

AX のコードをそのまま実行せずに独自の命令列しているわけ

AX のコードは解釈するときにパーサーのような再帰的な処理を行います。再帰的な処理で実行していると、 Javascript の非同期処理後に実行を再開することが難しいのです。だから、再帰的な処理をせずにすむ独自の命令列にしています。
非同期処理とは、 wait の実装につかう setTimeout や、ファイル読み込みの実装に使う XMLHttpRequest などのことを指します。

追記 2008-11-11T17:54:19+09:00

モジュール変数に対応したのと、実行のステップが増えてJavaScriptソースコードへの変換を行うようになったので変更。