inf-js で v8 を使う

emacsJavaScript のコードを記述するのであれば js2-mode が便利であることはよく知られている。
http://code.google.com/p/js2-mode/
詳しいことはあらためてここで取り上げる必要もないだろう。 一般的な作法に則ってインストールするだけで特に躓くこともない。
さて、書いたら実行したいわけだが、 Lisp 系言語でよくやるように対話的なモードがないものかと探してみたところ、 inf-js を発見した。
http://github.com/bkyle/inf-js/tree/master
デフォルトでは Rhino を使うようになっている。 私は今流行の v8 を使おうとドキュメントに従って設定を開始したわけだが、色々と素直にいかなかったので経緯を記録しておくことにする。 予め断わっておくが、アドホックな処置である。

設定

まず私自身が行った設定を示す。 以下を .emacs に追記した。

(defvar js-program-name "d:\\bin\\v8\\shell --shell")
(defvar inferior-js-first-prompt-pattern "^> *")
(defvar inferior-js-prompt-pattern "^> *")

(autoload 'run-js "inf-js" nil t)
(autoload 'inf-js-keys "inf-js" nil)
(add-hook 'js2-mode-hook
          '(lambda ()
             (inf-js-keys)))

js-program-name は処理系のパスなので各人の環境によって変わるだろう。

プロンプト

まず第一に躓いたのは run-js を実行しても応答が無いということだ。
原因はすぐにわかった。 プロンプトを出力した後にフラッシュしていなかったので、プロンプトがバッファに残ったままになってしまい、 emacs 側は待ちぼうけになっていたのだ。
単純に標準出力をフラッシュすれば解決する。

Index: samples/shell.cc
===================================================================
--- samples/shell.cc    (revision 2226)
+++ samples/shell.cc    (working copy)
@@ -223,7 +223,7 @@
   static const int kBufferSize = 256;
   while (true) {
     char buffer[kBufferSize];
-    printf("> ");
+    printf("> "); fflush(stdout);
     char* str = fgets(buffer, kBufferSize, stdin);
     if (str == NULL) break;
     v8::HandleScope handle_scope;

構文エラー

v8 はあくまでもアプリケーションに組み込んで使うことを想定したものだ。 ソースツリーの中にある shell.cc が sample ディレクトリの中にあることからもわかるように、シェルはあまり作り込まれていない。
どうやら一行入力するごとにパーサに渡しているようで、中途半端なところで改行してもその時点で一区切りと見做してしまう。 簡単な例を挙げよう。 以下を入力してみるとよい。

function test () {
}

開き中括弧の後に改行を入力した時点でエラーが出力される。

(shell):-1: SyntaxError: Unexpected end of input

これは emacs から利用する場合に限ったものではない。 だが、関数定義を何もかも一行に詰め込むのは非現実的である以上、何らかの対応が必要だ。
本来ならば v8 の側に手を加えるのがまともな解決だろうが、簡易的な解決方法として emacs から v8 に送るときに改行を全部取り除くという方法をとることにした。 具体的には以下のコードを .emacs に追記した。

(defadvice comint-send-region
  (around v8-send-region (process start end))
  (let ((oldbuf (current-buffer)))
    (with-temp-buffer
      (insert-buffer-substring oldbuf start end)
      (goto-char (point-min))
      (replace-string "\n" "")
      (let ((start (point-min))
            (end (point-max)))
        ad-do-it))))

(add-hook 'inferior-js-mode-hook
          '(lambda ()
             (ad-activate 'comint-send-region)))

アドバイスをバッファローカルにする方法がわからなかったのでこのままだと他の inferior-mode に影響してしまうが、ここではコンセプトを表わしたものと捉えて御容赦を。 よりよい方法があれば御教示頂きたい。
なお、 JavaScript では行末のセミコロンの省略を許しているが、そのような記述をしている場合には単純に改行を削除してしまうこの方法では駄目だ。 私自身は省略しない派なので問題ないのだが…

一行の大きさ

さて、以上の対応で小さなテストコードが通るようになったのだが、ある程度大きいコード片を送るとやはり同じようなエラーになる。
よくよく shell.cc を見たところ…

char* str = fgets(buffer, kBufferSize, stdin);

一行のサイズに上限があった!
kBufferSize は 256 になっているので、充分に大きいサイズに変更すれば実用上はなんとかなるだろう。 どの程度が充分なサイズかは人によるのでなんとも言えないが、普段編集しているファイルの大きさを基準にすれば足りないということは無いと思う。
Document ID: d16bc5a94495f57ad78d1a66cd045667