2010年5月19日水曜日

[newLISP]今日の無題

自作したnewlisp.elの挙動がおかしかった原因が少し分かったのでメモ。

newlispのREPLはデフォルトで行編集ライブラリ(readline)が使えるが、 複数行を入力する場合は[cmd]~[/cmd]タグでS式を囲まなければいけないのが newlispの面倒な仕様。

newlisp.elの newlisp-eval関数はその辺を考慮してプロセスに文字列を 送るようにしているのだけど、なぜか効果が無いようでREPLバッファに 入力が溜まってしまう現象がよく起きていた。


(defun newlisp-eval (str-sexp)
 "Eval newlisp s-expression."
 (interactive "snewLISP Eval: ")
 (let ((proc (newlisp-process)))
   (labels ((sendln (str)
              (comint-send-string proc (concat str "\n"))))
     (cond
       ((string-match "\n" str-sexp)   ; 複数行の場合
        (sendln "[cmd]")
        (sendln str-sexp)
        (sendln "[/cmd]"))
       (:else
        (sendln str-sexp))))
   (newlisp-show-repl t)))

newlispのソースを眺めたり、gdbでちまちまとステップ実行してみて気づいたのは

  • 1行目は readline関数
  • 1行目が[cmd]タグならば残りは fgets関数

というトリッキー(?)な方法でREPLを回しているらしいこと (newlisp.cのmain関数とexecuteCommandLine関数を参照)。

readlineもfgetsも基本的には標準入力(stdin)から読み込むのだが、 どうもこの辺のバッファリングでコケている気がする。 試しにfgetではなく[cmd]タグ以降もreadline関数で読み込みをしてみると 入力が溜まる問題は解決するみたい。

以下パッチ。

--- newlisp_orig.c  2010-05-19 08:00:00.000000000 +0900
+++ newlisp.c   2010-05-19 09:00:00.000000000 +0900
@@ -993,7 +993,8 @@
void executeCommandLine(char * command, UINT outDevice, STREAM * cmdStream)
{
STREAM stream;
-char buff[MAX_LINE];
+  /* char buff[MAX_LINE]; */
+  char *buff;
int batchMode;

batchMode = (memcmp(command, "[cmd]", 5) == 0);
@@ -1033,7 +1034,11 @@
if(cmdStream != NULL && batchMode)
   {
   openStrStream(cmdStream, 1024, TRUE);
+#ifdef READLINE
+      while ((buff = readline("")) != NULL)
+#else
   while(fgets(buff, MAX_LINE - 1, IOchannel) != NULL)
+#endif
       {
       if(memcmp(buff, "[/cmd]", 6) == 0)
           {
@@ -1048,6 +1053,10 @@
           return;
           }
       writeStreamStr(cmdStream, buff, 0);
+#ifdef READLINE
+          writeStreamStr(cmdStream, "\n", 1);
+          freeMemory(buff);
+#endif
       }
   closeStrStream(cmdStream);
   if(!demonMode)  exit(1);

…けれども上記のパッチではサーバモード(-d オプション)が働かなくなって しまうため解決した事にはなってない。困った。

0 件のコメント:

コメントを投稿