When calling (read "(+ 1 2) (+ 2 3)")
, I get (+ 1 2)
, as expected. How can I get the next expression, namely (+ 2 3)
?
3 Answers
"(+ 1 2) (+ 2 3)"
is not a stream, it is a string. Strings do not keep track of what has been read already and what should be read next, so if you call read
on this string multiple times you will always get the first expression.
You probably want to read from a buffer instead. Calling read
on a buffer will move point
in that buffer as it goes, allowing the next call to read
to pick up where the first left off.
See chapter 20.2 Input Streams in the Elisp manual for more information.
-
The question may well be an X-Y question. What is it that you're really trying to do? (Maybe @db48x has guessed correctly what you want to know, in which case great.) But
read
reads a single expression from its argument (a string in this case), and you passed it a string with two expressions. It read an expression from the string you gave it, and it returned that expression,(+ 1 2)
.– DrewCommented May 17, 2022 at 1:25 -
In fairness the signature is
(read &optional STREAM)
and "STREAM or the value of ‘standard-input’ may be: ... a string (takes text from string, starting at the beginning)". That "starting at the beginning" part is the key here of course, as you've pointed out.– philsCommented May 17, 2022 at 5:55 -
Thanks for the link to X-Y question. I am prompting the user with a minibuffer, and asking them to input s-expressions. So they might say
(+ 1 2)
and I want to read that and store it as s-expression. But I also want to support(+ 1 2) (+ 2 3)
in the minibuffer as well so that I may read in multiple s-expressions at once.– B BCommented May 17, 2022 at 20:45
If you want to read all the expressions from the string "(+ 1 2) (+ 2 3)"
just wrap it by an extra pair of parantheses to represent a list, i.e., read "((+ 1 2) (+ 2 3))"
instead.
The following Elisp snippets give you a hint how to do that:
(let ((str "(+ 1 2) (+ 2 3)"))
(read (concat "(" str ")")))
or
(let ((str "(+ 1 2) (+ 2 3)"))
(read (format "(%s)" str)))
Thereafter, you can iterate over the list with dolist
or cl-loop for ... in ... do
.
I slightly modified the answer in How should you read a Lisp file as Lisp for processing without condition-case? to get
(defun read-multiple (string)
"Read STRING and return a list of its Lisp forms."
(with-temp-buffer
(let ((buf (current-buffer))
forms)
(insert string)
(goto-char (point-min))
(while (ignore-errors (push (read buf) forms)))
(nreverse forms))))