28

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.)

1
  • 2
    Note, (calc-eval "1 - 2 + 3") fits your python example better even if this is not valid elisp. If you do not yet require the calc 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.)
    – Tobias
    Commented Jan 28, 2016 at 12:35

3 Answers 3

32

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!\")")
1
  • 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
7

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).

1

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.