screwlisp proposes kittens

Embeddable Common Lisp External Program Asyncronously - cat(1) echo server

For example if we want to use unix cat(1) without starting a shell as an echo server.

My eev/emacs/ecl setup.

#|
 (setq inferior-lisp-program "ecl")
 (slime)
 (setq eepitch-buffer-name "*slime-repl ECL*")
|#

ext:run-program "cat" '() :wait nil

in so many words.

(locally
    (declare (special *2way* *exit* *proc*))
  (multiple-value-setq
      (*2way* *exit* *proc*)
    (ext:run-program "cat" '() :wait nil)))

We need to preserve the process to interactively ext:external-process-wait for it to finish when we close it.

The process’s input is what we output to it and vice versa

I always find this confusing, but it is clearly correct.

(two-way-stream-output-stream *2way*)
(print "This is only a
test" *)

And let’s read-line from that:

(two-way-stream-input-stream *2way*)
(read-line *) ; ⮕ ""
(read-line **) ; ⮕ ""This is only a"
(read-line ***) ; ⮕ (waits for stream) C-c C-c ABORT <ret>

Note that the cat(1) survives us interactively aborting read-line in lisp. However, partially reading that line loses us the characters for next time we try to read. Instead

Using read-char-no-hang instead

(two-way-stream-output-stream *2way*)
(print "foo
bar
baz
" *)

(two-way-stream-input-stream *2way*)
(loop :for ch := (read-char-no-hang *)
      :while ch
      :collect ch :into chars
      :finally (return (coerce chars 'string)))
#|
" 
"foo
bar
baz
""
|#

as we may have expected (I had a stray newline from before).

Killing the process

(ext:terminate-process *proc*)
(ext:external-process-wait *proc*)
(multiple-value-setq
      (*2way* *exit* *proc*)
    (values))

Conclusions

So an embeddable common lisp ext:external-process running cat(1) functions as a kind of in-memory echo server.

I have always tended to use embeddable common lisp’s multiprocessing and external-process extensions directly when available. They are meant to be similar to lisp machine useages, which I imagine is preserved across compilers. Technically I guess ‘semi-portable’ uiop:launch-program is going to work the same, except arguements to the program are written into the program’s invocation as though one was using a shell (which we are not) instead of ecl’s list of flags. Confusingly uiop:run-program is reserved for syncronous external processes.

I kind of want to use embeddable common lisp anyway, because my insane fairy kitten godmother idea is to arbitrarily transform all C++ programs into kittens.

Fin.

Well, how do you like to use external processes in common lisp - in the mastodon thread.

Please do share this embeddable common lisp example however occurs to you and in whatever way. Since I sometimes stumble around here when I have forgotten how to use arbitrary external programs from inside my lisp image like this, so I imagine you might find it helpful.

screwlisp proposes kittens