Function: eshell-do-eval

eshell-do-eval is a byte-compiled function defined in esh-cmd.el.gz.

Signature

(eshell-do-eval FORM &optional SYNCHRONOUS-P)

Documentation

Evaluate form, simplifying it as we go.

Unless SYNCHRONOUS-P is non-nil, throws eshell-defer if it needs to be finished later after the completion of an asynchronous subprocess.

Source Code

;; Defined in /usr/src/emacs/lisp/eshell/esh-cmd.el.gz
(defun eshell-do-eval (form &optional synchronous-p)
  "Evaluate form, simplifying it as we go.
Unless SYNCHRONOUS-P is non-nil, throws `eshell-defer' if it needs to
be finished later after the completion of an asynchronous subprocess."
  (cond
   ((not (listp form))
    (list 'quote (eval form)))
   ((memq (car form) '(quote function))
    form)
   (t
    ;; skip past the call to `eshell-do-eval'
    (when (eq (car form) 'eshell-do-eval)
      (setq form (cadr (cadr form))))
    ;; expand any macros directly into the form.  This is done so that
    ;; we can modify any `let' forms to evaluate only once.
    (if (macrop (car form))
        (let ((exp (copy-tree (macroexpand form))))
	  (eshell-manipulate (format-message "expanding macro `%s'"
					     (symbol-name (car form)))
	    (setcar form (car exp))
	    (setcdr form (cdr exp)))))
    (let ((args (cdr form)))
      (cond
       ((eq (car form) 'while)
        ;; `copy-tree' is needed here so that the test argument
	;; doesn't get modified and thus always yield the same result.
	(when (car eshell-command-body)
	  (cl-assert (not synchronous-p))
	  (eshell-do-eval (car eshell-command-body))
	  (setcar eshell-command-body nil)
	  (setcar eshell-test-body nil))
	(unless (car eshell-test-body)
          (setcar eshell-test-body (copy-tree (car args))))
	(while (cadr (eshell-do-eval (car eshell-test-body) synchronous-p))
	  (setcar eshell-command-body
                  (if (cddr args)
                      `(progn ,@(copy-tree (cdr args)))
                    (copy-tree (cadr args))))
	  (eshell-do-eval (car eshell-command-body) synchronous-p)
	  (setcar eshell-command-body nil)
          (setcar eshell-test-body (copy-tree (car args))))
	(setcar eshell-command-body nil))
       ((eq (car form) 'if)
        ;; `copy-tree' is needed here so that the test argument
	;; doesn't get modified and thus always yield the same result.
	(if (car eshell-command-body)
	    (progn
	      (cl-assert (not synchronous-p))
	      (eshell-do-eval (car eshell-command-body)))
	  (unless (car eshell-test-body)
            (setcar eshell-test-body (copy-tree (car args))))
	  (setcar eshell-command-body
                  (copy-tree
                   (if (cadr (eshell-do-eval (car eshell-test-body)
                                             synchronous-p))
                       (cadr args)
                     (car (cddr args)))))
	  (eshell-do-eval (car eshell-command-body) synchronous-p))
	(setcar eshell-command-body nil)
	(setcar eshell-test-body nil))
       ((eq (car form) 'setcar)
	(setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p))
	(eval form))
       ((eq (car form) 'setcdr)
	(setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p))
	(eval form))
       ((eq (car form) 'let)
	(if (not (eq (car (cadr args)) 'eshell-do-eval))
	    (eshell-manipulate "evaluating let args"
	      (dolist (letarg (car args))
		(if (and (listp letarg)
			 (not (eq (cadr letarg) 'quote)))
		    (setcdr letarg
			    (list (eshell-do-eval
				   (cadr letarg) synchronous-p)))))))
        (cl-progv
            (mapcar (lambda (binding) (if (consp binding) (car binding) binding))
                    (car args))
            ;; These expressions should all be constants now.
            (mapcar (lambda (binding) (if (consp binding) (eval (cadr binding))))
                    (car args))
	  (eshell-do-eval (macroexp-progn (cdr args)) synchronous-p)))
       ((memq (car form) '(catch condition-case unwind-protect))
	;; `condition-case' and `unwind-protect' have to be
	;; handled specially, because we only want to call
	;; `eshell-do-eval' on their first form.
	;;
	;; NOTE: This requires obedience by all forms which this
	;; function might encounter, that they do not contain
	;; other special forms.
	(unless (eq (car form) 'unwind-protect)
	  (setq args (cdr args)))
	(unless (eq (caar args) 'eshell-do-eval)
	  (eshell-manipulate "handling special form"
	    (setcar args `(eshell-do-eval ',(car args) ,synchronous-p))))
	(eval form))
       ((eq (car form) 'setq)
	(if (cddr args) (error "Unsupported form (setq X1 E1 X2 E2..)"))
        (eshell-manipulate "evaluating arguments to setq"
          (setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p)))
	(list 'quote (eval form)))
       (t
	(if (and args (not (memq (car form) '(run-hooks))))
	    (eshell-manipulate
		(format-message "evaluating arguments to `%s'"
				(symbol-name (car form)))
	      (while args
		(setcar args (eshell-do-eval (car args) synchronous-p))
		(setq args (cdr args)))))
	(cond
	 ((eq (car form) 'progn)
	  (car (last form)))
	 ((eq (car form) 'prog1)
	  (cadr form))
	 (t
	  ;; If a command desire to replace its execution form with
	  ;; another command form, all it needs to do is throw the new
	  ;; form using the exception tag `eshell-replace-command'.
	  ;; For example, let's say that the form currently being
	  ;; eval'd is:
	  ;;
	  ;;   (eshell-named-command "hello")
	  ;;
	  ;; Now, let's assume the 'hello' command is an Eshell alias,
	  ;; the definition of which yields the command:
	  ;;
	  ;;   (eshell-named-command "echo" (list "Hello" "world"))
	  ;;
	  ;; What the alias code would like to do is simply substitute
	  ;; the alias form for the original form.  To accomplish
	  ;; this, all it needs to do is to throw the substitution
	  ;; form with the `eshell-replace-command' tag, and the form
	  ;; will be replaced within the current command, and
	  ;; execution will then resume (iteratively) as before.
	  ;; Thus, aliases can even contain references to asynchronous
	  ;; sub-commands, and things will still work out as they
	  ;; should.
	  (let* (result
                 (new-form
                  (catch 'eshell-replace-command
                    (ignore
                     (setq result (eval form))))))
	    (if new-form
		(progn
		  (eshell-manipulate "substituting replacement form"
		    (setcar form (car new-form))
		    (setcdr form (cdr new-form)))
		  (eshell-do-eval form synchronous-p))
              (if-let (((memq (car form) eshell-deferrable-commands))
                       ((not eshell-current-subjob-p))
                       (procs (eshell-make-process-pair result)))
                  (if synchronous-p
		      (eshell/wait (cdr procs))
		    (eshell-manipulate "inserting ignore form"
		      (setcar form 'ignore)
		      (setcdr form nil))
		    (throw 'eshell-defer procs))
                (list 'quote result))))))))))))