The question pretty much says it all: I have a string containing the source code for a valid Elisp expression, and I would like to evaluate it.
(In Python, for example, the expression eval("1 - 2 + 3")
evaluates to 2.)
Evaluating a string of elisp code is a two-stage process: you need to parse the string using read-from-string
and then evaluate the resulting Lisp expression with eval
.
(defun my-eval-string (string)
"Evaluate elisp code stored in a string."
(eval (car (read-from-string string))))
Now (my-eval-string "(+ 1 2)")
evaluates to 3
.
Edit:
As pointed out by @lunaryorn, read-from-string
reads the first expression only, so this should be better:
(defun my-eval-string (string)
(eval (car (read-from-string (format "(progn %s)" string)))))
Edit 2:
To evaluate elisp code for side effects one could also use with-temp-buffer
and eval-buffer
(eval-buffer
always returns nil
).
(defun my-eval-string-for-side-effects (string)
"Evaluate a string of elisp code for side effects."
(with-temp-buffer
(insert string)
(eval-buffer)))
(my-eval-string-for-side-effects "(message \"hello!\")")
with-temp-buffer
is less than ideal because it will mess up with all buffer-related calls, e.g. buffer-file-name
, ...
Commented
Nov 3, 2017 at 11:19
The answer of Constantine is okay.
Just to provide a slight modification:
(defun my-eval-string (str)
"Read and evaluate all forms in str.
Return the results of all forms as a list."
(let ((next 0)
ret)
(condition-case err
(while t
(setq ret (cons (funcall (lambda (ret)
(setq next (cdr ret))
(eval (car ret)))
(read-from-string str next))
ret)))
(end-of-file))
(nreverse ret)))
(my-eval-string "1 2 3 (+ 3 1)")
The last form returns the list (1 2 3 4)
.
In my case I wanted to evaluate elisp in a string only if it actually was valid elisp. Otherwise I just wanted the string as-is.
(defun maybe-eval-string (string)
"Maybe evaluate elisp in a given STRING."
(or (ignore-errors
(eval (car (read-from-string (format "(progn %s)" string))))
) string))
(print (maybe-eval-string "(format \"hello, %s\" \"world\")"))
(print (maybe-eval-string "hello, world"))
Output:
"hello, world"
"hello, world"
Full credit to @Constantine's answer. I just wrapped it in an or
and ignore-errors
but I though it was handy enough function to post as an answer.
(calc-eval "1 - 2 + 3")
fits your python example better even if this is not valid elisp. If you do not yet require thecalc
package you need to load it before with(require 'calc)
. (I know that this does not answer your question. Hence it is formulated as comment.)