Problems with read function in lisp - common-lisp

I'm trying to make a prompt for user input, but each time i call this function, instead of printing the ":", it waits until I press something and after that prints the character ":".
I can't find anything on the web.
(defun MovimientoAdversario ()
(let ((aux))
(format t "~% :")
(setf aux (read))))

Try flushing the output buffers before reading:
(format t "~% :")
(force-output)
(setf aux (read))

Related

Why do executables have different behavior that the repl behavior?

My Setup
I develop common lisp with emacs + slime. My machine is a mac pro M1. And, I use the kitty terminal emulator.
The Situation
When I run my app (see code at the end) in the repl, it works fine.
When I create and run the executable, it's as if the order of the program is different.
For example, I have 5 questions which asks for user input...
(defpackage custom-sender
;; import the exactly the symbols I need from a package
(:import-from :cl-json :encode-json-to-string)
(:use :cl)
(:export :main))
(in-package custom-sender)
(defun main()
(asking-questions))
(defun asking-questions ()
(let ((firstname (prompt-read "What is the firstname of the contact?"))
(email (prompt-read "What is their email?"))
(job (prompt-read "What is the job?"))
(first-line (prompt-read "what is the first line of their address?"))
(sending-account (prompt-read "Which email account do you want to send from? (e2, e3 etc)")))
(status-update "some text ~a" email);; <-- this function is executed BEFORE the "sending-account" question is asked
... ) ;;<-- end of let block
)
(defun status-update (message value)
(format *query-io* (concatenate 'string message "~C~C") value #\linefeed #\linefeed)
(force-output *query-io*))
(defun prompt-read (question)
(format *query-io* "~a: " question)
(read-line *query-io*)
(force-output *query-io*))
The Problem
When running the executable, there are two issues:
The first function (status-update...) is executed before the final prompt read. In the terminal, the question comes up but immediately exits. So I cannot enter anything.
Also... (status-update...) gets the value of email which is NIL. Even though I entered a value inside the terminal.
This entire process runs perfectly in the repl.
Note - I am building the executable with the ASD MAKE process.
(defun prompt-read (question)
(format *query-io* "~a: " question)
(read-line *query-io*)
(force-output *query-io*))
change it to something like this:
(defun prompt-read (question)
(format *query-io* "~a: " question)
(force-output *query-io*)
(read-line *query-io*)
(force-output *query-io*))
Otherwise the prompt may not be visible when the function is waiting for input..., due to a buffering output stream.
Streams may or may not buffer output. For portable programs you'd need to add operations to clear or force output.

Why does it seem that the order of execution is not top->down in this function? [duplicate]

I don't understand why this code behaves differently in different implementations:
(format t "asdf")
(setq var (read))
In CLISP it behaves as would be expected, with the prompt printed followed by the read, but in SBCL it reads, then outputs. I read a bit on the internet and changed it:
(format t "asdf")
(force-output t)
(setq var (read))
This, again, works fine in CLISP, but in SBCL it still reads, then outputs. I even tried separating it into another function:
(defun output (string)
(format t string)
(force-output t))
(output "asdf")
(setq var (read))
And it still reads, then outputs. Am I not using force-output correctly or is this just an idiosyncrasy of SBCL?
You need to use FINISH-OUTPUT.
In systems with buffered output streams, some output remains in the output buffer until the output buffer is full (then it will be automatically written to the destination) or the output buffer is explicity emptied.
Common Lisp has three functions for that:
FINISH-OUTPUT, attempts to ensure that all output is done and THEN returns.
FORCE-OUTPUT, starts the remaining output, but IMMEDIATELY returns and does NOT wait for all output being done.
CLEAR-OUTPUT, tries to delete any pending output.
Also the T in FORCE-OUTPUT and FORMAT are unfortunately not the same.
force-output / finish-output: T is *terminal-io* and NIL is *standard-output*
FORMAT: T is *standard-output*
this should work:
(format t "asdf")
(finish-output nil) ; note the NIL
(setq var (read))

Macro reader for #+

I'm trying to write a formatting program for Common Lisp code, for which I need to tweak the behavior of the reader, e.g. with a reader macro for comments. Currently looking at #+ e.g.
(defun args ()
#+CCL CCL:*UNPROCESSED-COMMAND-LINE-ARGUMENTS*
#+SBCL (cdr *posix-argv*))
Default reader behavior is to discard the currently inactive branch entirely, but for my purposes I need to keep both. I think that means I need a reader macro for #+.
But # is also a prefix to lots of other things. How can I write a reader macro that handles #+ while keeping the default behavior for # everything else?
The # character is a dispatch macro character, meaning you can define a reader macro for the #+ combination without affecting any other reader macros beginning with hash.
You can start from the code below, and customize it to do what you want it to:
;; define the reader function
(defun custom-comment-reader-macro (stream char &optional num)
;; char will be #\+ in our case, and num will be nil.
;; stream will be the code input stream
(declare (ignore char num))
;; this is the default behaviour of #+, you can customize it below
(if (member (intern (string (read stream)) :keyword)
*features*)
(read stream)
(progn (read stream) ;ignore next token
(values)))) ;return nothing
;; tell the reader to use our function when it encounters #+
(set-dispatch-macro-character #\# #\+ #'custom-comment-reader-macro)

Capture output of cl-async:spawn

I was hoping to experiment with cl-async to run a series of external programs with a large combinations of command line arguments. However, I can't figure out how to read the stdout of the processes launched with as:spawn.
I would typically use uiop which makes it easy to capture the process output:
(let ((p (uiop:launch-program ... :output :stream)))
(do-something-else-until-p-is-done)
(format t "~a~%" (read-line (uiop:process-info-output p))))
I've tried both :output :pipe and :output :stream options to as:spawn and executing (as:process-output process-object) in my exit-callback shows the appropriate pipe or async-stream objects but I can't figure out how to read from them.
Can anyone with experience with this library tell how to accomplish this?
So you go to your repl and type:
CL-USER> (documentation 'as:spawn 'function)
And you read whatever comes out (or put your point on the symbol and hit C-c C-d f). If you read it you’ll see that the format for the :input, etc arguments is either :pipe, (:pipe args...), :stream, or (:stream args...) (or some other options). And that :stream behaves similarly to :pipe but gives output of a different type and that for details of args one should look at PIPE-CONNECT so you go and look up the documentation for that. Well it tells you what the options are but it isn’t very useful. What’s the documentation/description of PIPE or STREAM? Well it turns out that pipe is a class and a subclass of STREAMISH. What about PROCESS that’s a class too and it has slots (and accessors) for things like PROCESS-OUTPUT. So what is a good plan for how to figure out what to do next? Here’s a suggestion:
Spawn a long running process (like cat foo.txt -) with :output :stream :input :pipe say
Inspect the result (C-c C-v TAB)
Hopefully it’s an instance of PROCESS. What is it’s output? Inspect that
Hopefully the output is a Gray stream (ASYNC-STREAM). Get it into your repl and see what happens if you try to read from it?
And what about the input? See what type that has and what you can do with it
The above is all speculation. I’ve not tried running any of this but you should. Alternatively go look at the source code for the library. It’s already on your computer and if you can’t find it it’s on GitHub. There are only about half a dozen source files and they’re all small. Just read them and see what you can learn. Or go to the symbol you want to know about and hit M-. to jump straight to its definition. Then read the code. Then see if you can figure out what to do.
I found the answer in the test suite. The output stream can only be processed asynchronously via a read call-back. The following is simple example for posterity
(as:start-event-loop
(lambda ()
(let ((bytes (make-array 0 :element-type '(unsigned-byte 8))))
(as:spawn "./test.sh" '()
:exit-cb (lambda (proc exit-status term-signal)
(declare (ignore proc exit-status term-signal))
(format t "proc output:~%~a"
(babel:octets-to-string bytes)))
:output (list :stream
:read-cb (lambda (pipe stream)
(declare (ignore pipe))
(let ((buf (make-array 128 :element-type '(unsigned-byte 8))))
(loop for n = (read-sequence buf stream)
while (plusp n) do
(setf bytes
(concatenate '(vector (unsigned-byte 8))
bytes
(subseq buf 0 n)))))))))))
with
$ cat test.sh
#!/bin/bash
sleep_time=$((1+$RANDOM%10))
echo "Process $$ will sleep for $sleep_time"
sleep $sleep_time
echo "Process $$ exiting"
yields the expected output

What is the function to exit the complete program in common lisp?

I have some function with loop, each iteration it reads input, on "0" it calls function "exit-and-save", in that function it saves some database and after that I need it to exit the program? What is the command for that? If I use return-from... it just returns from function, if I use return - error, if I use quit, it disconnects from slime. I'm new in common lisp...
(loop for i from 0 to 10
do (progn (format t "~&cycle ~d" i)
(when (> i 5)
(return nil))))
First of all I cannot verify that slime disconnects using (quit), at least not using sbcl at Ubuntu.
CL-USER> (quit)
; Evaluation aborted on NIL.
CL-USER>
"still able to input here"
But if you got some freakish version of slime you could take advantage of the condition system:
(define-condition end-program-condition (simple-error) ())
(defun some-func ()
(error 'end-program-condition))
(defun main-function ()
(handler-case (some-func)
(end-program-condition () "THE END")))
CL-USER> (main-function)
"THE END"
CL-USER> "still can input here"
"still can input here"
It depends on your common lisp implementation, but if using sbcl for example, you could call sb-ext:exit.
Source: http://www.sbcl.org/manual/#Exit

Resources