副作用も必要
副作用は竹内関数(たらいまわし)のように値だけが問題ならば必要ないのですが、何らしかの表示をしようとすると画面表示のための副作用が必要となります。ところが子Lispの出力はPipeで親Lispにつながっています。このままでは出力を画面表示することができません。さて、どうしましょうか?
親Lisp経由
ChatGPTと相談しつつ、いくつかの方法を検討しました。Pipeの代わりにソケットを使う、あるいは共有メモリが使えば標準出力が子Lispにおいても使えます。しかし、そうすると全面的に書き直さないといけません。Pipeを使う方法はお手軽です。
そこで、Pipeを使いつつ画面表示のためのプロトコルを決めて、親Lispの標準出力により画面出力をすることにしました。アスキーコードx02が使えることがわかりました。これだと他に影響を与えません。x02で挟まれた文字を見つけたらはそれは標準出力に書き出すこととしました。親Lisp側の recieve_from_pipeを書き換えました。次のように改良してあります。
int read_from_pipe(int n)
{
char buffer[256];
int i,j;
// set nonblock mode
int flags = fcntl(pipe_c2p[n][R], F_GETFL, 0);
fcntl(pipe_c2p[n][R], F_SETFL, flags | O_NONBLOCK);
int bytes_read;
reread:
// wait until get result
while ((bytes_read = read(pipe_c2p[n][R], buffer, 256)) == -1 && errno == EAGAIN);
buffer[bytes_read] = '\0';
if (buffer[0] == '\x02'){
i = 1;
rewrite:
while(buffer[i] != '\x02' && i < 256){
putc(buffer[i],stdout);
i++;
}
i++;
if (buffer[i] == '\x02'){
i++;
goto rewrite;
}
else if (buffer[i] == '\0'){
/* still not recieve result */
for(i=0;i<256;i++){
buffer[i] = 0;
}
goto reread;
}
else {
/* already recieved result */
j = 0;
while(buffer[i] != '\0'){
buffer[j] = buffer[i];
i++;
j++;
}
buffer[j] = '\0';
}
}
return(make_str(buffer));
}
format関数の独自拡張
format関数に独自拡張を加えることとしました。(format (standard-output) str ...) のstrの部分で~!で囲まれた部分につきアスキーコードのx02で挟むこととしました。
例
(format (standard-output) "~!Hello World~! )
テスト
並列構文のmp-execを使って試しました。1秒まって子Lispからメッセージを出力するものです。
(defun bar (x y)
(mp-exec (uoo x) (uoo y)))
(defun uoo (x)
(format (standard-output) "~! test1 ~A ~%~!" x)
(finish-output (standard-output))
(sleep 1)
(format (standard-output) "~! test2 ~A ~%~!" x)
(finish-output (standard-output))
(sleep 1)
t)
Easy-ISLisp Ver3.92
> (mp-create 2)
T
> (load "./tests/para.lsp")
T
> (bar 1 2)
test1 1
test2 1
test1 2
test2 2
T
>
実装
Easy-ISLispはOSSです。修正BSDライセンスのもとで自由にお使いください。