7d0cd39ee703

Don't shit the bed when `load`ing a file with errors

Refactored the evaluator a bit so the file loading middleware can just use the
same evaluation machinery and get all the error handling and stream shuttling
for free.
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Sat, 09 Apr 2016 21:45:42 +0000
parents 3e9db801d234
children 9d3d9514dbd8
branches/tags (none)
files nrepl.asd src/middleware/eval.lisp src/middleware/load-file.lisp

Changes

--- a/nrepl.asd	Sat Apr 09 21:13:26 2016 +0000
+++ b/nrepl.asd	Sat Apr 09 21:45:42 2016 +0000
@@ -28,9 +28,9 @@
                   :components ((:file "core")
                                (:file "describe")
                                (:file "documentation")
-                               (:file "load-file")
                                (:file "macroexpand")
                                (:file "eval")
+                               (:file "load-file")
                                (:file "session")))
                  (:file "server")))))
 
--- a/src/middleware/eval.lisp	Sat Apr 09 21:13:26 2016 +0000
+++ b/src/middleware/eval.lisp	Sat Apr 09 21:45:42 2016 +0000
@@ -14,26 +14,31 @@
   (do ((data "" (flex:octets-to-string
                   (flex:get-output-stream-sequence from-stream)
                   :external-format :utf-8)))
-    ((and (not (open-stream-p from-stream))
-          (equal data ""))
-     nil)
+      ((and (not (open-stream-p from-stream))
+            (equal data ""))
+       nil)
     (when (not (equal data ""))
       (respond message (make-map "status" '("ok")
                                  stream-name data)))
     (sleep 0.1)))
 
 (defun get-forms (code)
-  "Get all lisp forms from the given hunk of code.
+  "Get all lisp forms from `code`.
 
-   Signal an evaluation-error if the input is mangled.
+  If `code` is a string, the forms will be read out of it, and an
+  `evaluation-error` signaled if the input is mangled.
+
+  If `code` is anything else it will just be returned as-is.
 
-   "
-  (handler-case
-    (read-all-from-string code)
-    (error (e)
-           (error 'evaluation-error
-                  :text "Malformed input!"
-                  :orig e))))
+  "
+  (if (stringp code)
+    (handler-case
+        (read-all-from-string code)
+      (error (e)
+             (error 'evaluation-error
+                    :text "Malformed input!"
+                    :orig e)))
+    code))
 
 (defun clean-backtrace (backtrace)
   (format nil "~{~A~^~%~}"
@@ -64,9 +69,17 @@
                                         #-sbcl "dunno"))))))
       (eval form))))
 
-(define-middleware wrap-eval "eval" message
-  (let* ((code (fset:lookup message "code"))
-         (captured-out (flex:make-in-memory-output-stream))
+(defun evaluate-forms (message forms)
+  "Evaluate each form in `forms` and shuttle back the responses.
+
+  `forms` can be a string, in which case the forms will be read out of it, or
+  a ready-to-go list for actual forms.
+
+  Other middlewares (e.g. `load-file`) can use this function to evaluate things
+  and send the results back to the user.
+
+  "
+  (let* ((captured-out (flex:make-in-memory-output-stream))
          (captured-err (flex:make-in-memory-output-stream))
          (*standard-output*
            (flex:make-flexi-stream captured-out :external-format :utf-8))
@@ -89,14 +102,17 @@
                (lambda () (shuttle-stream stream desc message))
                :name (format nil "NREPL ~A writer" desc))))
       (unwind-protect
-        (progn
-          (make-shuttle-thread captured-out "stdout")
-          (make-shuttle-thread captured-err "stderr")
-          (handler-case
-            (progn
-              (loop for form in (get-forms code) do (eval-form form))
-              (respond message (make-map "status" '("done"))))
-            (evaluation-error (e) (error-respond e))))
+          (progn
+            (make-shuttle-thread captured-out "stdout")
+            (make-shuttle-thread captured-err "stderr")
+            (handler-case
+                (progn
+                  (loop :for form :in (get-forms forms) :do (eval-form form))
+                  (respond message (make-map "status" '("done"))))
+              (evaluation-error (e) (error-respond e))))
         (close captured-out)
         (close captured-err)))))
 
+(define-middleware wrap-eval "eval" message
+  (evaluate-forms message (fset:lookup message "code")))
+
--- a/src/middleware/load-file.lisp	Sat Apr 09 21:13:26 2016 +0000
+++ b/src/middleware/load-file.lisp	Sat Apr 09 21:45:42 2016 +0000
@@ -2,6 +2,4 @@
 
 (define-middleware wrap-load-file "load-file" message
   (let ((path (fset:lookup message "path")))
-    (load path)
-    (respond message (make-map "status" '("done")
-                               "value" "T"))))
+    (evaluate-forms message (list `(load ,path)))))