25

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

Drew
  • 75,699
  • 9
  • 109
  • 225
kjo
  • 3,145
  • 14
  • 42
  • 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 Jan 28 '16 at 12:35

3 Answers3

30

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!\")")
Constantine
  • 9,072
  • 1
  • 34
  • 49
6

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

npostavs
  • 9,033
  • 1
  • 21
  • 53
Tobias
  • 32,569
  • 1
  • 34
  • 75
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.

Chris
  • 143
  • 5