we have read-evaluated-form, kill read-replacement-character and -string
[sbcl.git] / src / code / print.lisp
index 8e6e0c8..924000b 100644 (file)
@@ -18,8 +18,8 @@
 
 (defvar *print-readably* nil
   #!+sb-doc
-  "If true, all objects will printed readably. If readable printing is
-  impossible, an error will be signalled. This overrides the value of
+  "If true, all objects will be printed readably. If readable printing
+  is impossible, an error will be signalled. This overrides the value of
   *PRINT-ESCAPE*.")
 (defvar *print-escape* t
   #!+sb-doc
@@ -30,7 +30,7 @@
   "Should pretty printing be used?")
 (defvar *print-base* 10.
   #!+sb-doc
-  "the output base for RATIONALs (including integers)")
+  "The output base for RATIONALs (including integers).")
 (defvar *print-radix* nil
   #!+sb-doc
   "Should base be verified when printing RATIONALs?")
   "Should #: prefixes be used when printing symbols with null SYMBOL-PACKAGE?")
 (defvar *print-lines* nil
   #!+sb-doc
-  "the maximum number of lines to print per object")
+  "The maximum number of lines to print per object.")
 (defvar *print-right-margin* nil
   #!+sb-doc
-  "the position of the right margin in ems (for pretty-printing)")
+  "The position of the right margin in ems (for pretty-printing).")
 (defvar *print-miser-width* nil
   #!+sb-doc
   "If the remaining space between the current column and the right margin
@@ -69,7 +69,7 @@
 (defvar *print-pprint-dispatch*)
 #!+sb-doc
 (setf (fdocumentation '*print-pprint-dispatch* 'variable)
-      "the pprint-dispatch-table that controls how to pretty-print objects")
+      "The pprint-dispatch-table that controls how to pretty-print objects.")
 
 (defmacro with-standard-io-syntax (&body body)
   #!+sb-doc
                      ((:pprint-dispatch *print-pprint-dispatch*)
                       *print-pprint-dispatch*))
   #!+sb-doc
-  "Output OBJECT to the specified stream, defaulting to *STANDARD-OUTPUT*"
+  "Output OBJECT to the specified stream, defaulting to *STANDARD-OUTPUT*."
   (output-object object (out-synonym-of stream))
   object)
 
         (push (list variable value) bind)))
     (unless (assoc 'stream bind)
       (push (list 'stream '*standard-output*) bind))
-    `(let ,(nreverse bind)
-       ,@(when ignore `((declare (ignore ,@ignore))))
-       (output-object ,object stream))))
+    (once-only ((object object))
+      `(let ,(nreverse bind)
+         ,@(when ignore `((declare (ignore ,@ignore))))
+         (output-object ,object (out-synonym-of stream))
+         ,object))))
 
 (defun prin1 (object &optional stream)
   #!+sb-doc
           (push variable ignore))
         (push (list variable value) bind)))
     (if bind
-        `(let ,(nreverse bind)
-           ,@(when ignore `((declare (ignore ,@ignore))))
-           (stringify-object ,object))
+        (once-only ((object object))
+          `(let ,(nreverse bind)
+             ,@(when ignore `((declare (ignore ,@ignore))))
+             (stringify-object ,object)))
         `(stringify-object ,object))))
 
 (defun prin1-to-string (object)
 \f
 ;;;; support for the PRINT-UNREADABLE-OBJECT macro
 
+(defun read-unreadable-replacement ()
+  (format *query-io* "~@<Enter an object (evaluated): ~@:>")
+  (finish-output *query-io*)
+  (list (eval (read *query-io*))))
+
 ;;; guts of PRINT-UNREADABLE-OBJECT
 (defun %print-unreadable-object (object stream type identity body)
   (declare (type (or null function) body))
   (when *print-readably*
-    (error 'print-not-readable :object object))
+    (restart-case
+        (error 'print-not-readable :object object)
+      (print-unreadably ()
+        :report "Print unreadably.")
+      (use-value (o)
+        :report "Supply an object to be printed instead."
+        :interactive read-unreadable-replacement
+        (write o :stream stream)
+        (return-from %print-unreadable-object nil))))
   (flet ((print-description ()
            (when type
              (write (type-of object) :stream stream :circle nil
                     :level nil :length nil)
-             (write-char #\space stream))
+             (write-char #\space stream)
+             (pprint-newline :fill stream))
            (when body
-             (pprint-newline :fill stream)
              (funcall body))
            (when identity
              (when (or body (not type))
                               (load-time-value
                                (array-element-type
                                 (make-array 0 :element-type 'character))))))
-                (error 'print-not-readable :object vector))
+                (restart-case
+                    (error 'print-not-readable :object vector)
+                  (print-unreadably ()
+                    :report "Print unreadably."
+                    (let ((*print-readably* nil))
+                      (output-vector vector stream)))
+                  (use-value (o)
+                    :report "Supply an object to be printed instead."
+                    :interactive read-unreadable-replacement
+                    (write o :stream stream))))
                ((or *print-escape* *print-readably*)
                 (write-char #\" stream)
                 (quote-string vector stream)
         (t
          (when (and *print-readably*
                     (not (array-readably-printable-p vector)))
-           (error 'print-not-readable :object vector))
+           (restart-case
+               (error 'print-not-readable :object vector)
+             (print-unreadably ()
+               :report "Print unreadably.")
+             (use-value (o)
+               :report "Supply an object to be printed instead."
+               :interactive read-unreadable-replacement
+               (return-from output-vector (write o :stream stream)))))
          (descend-into (stream)
                        (write-string "#(" stream)
                        (dotimes (i (length vector))
 (defun output-array-guts (array stream)
   (when (and *print-readably*
              (not (array-readably-printable-p array)))
-    (error 'print-not-readable :object array))
+    (restart-case
+        (error 'print-not-readable :object array)
+      (print-unreadably ()
+        :report "Print unreadably.")
+      (use-value (o)
+        :report "Supply an object to be printed instead."
+        :interactive read-unreadable-replacement
+        (return-from output-array-guts (write o :stream stream)))))
   (write-char #\# stream)
   (let ((*print-base* 10)
         (*print-radix* nil))
   ;; FIXME: I think only FORMAT-DOLLARS calls FLONUM-TO-STRING with
   ;; possibly-negative X.
   (setf x (abs x))
-  (cond ((zerop x)
-         ;; Zero is a special case which FLOAT-STRING cannot handle.
-         (if fdigits
-             (let ((s (make-string (1+ fdigits) :initial-element #\0)))
-               (setf (schar s 0) #\.)
-               (values s (length s) t (zerop fdigits) 0))
-             (values "." 1 t t 0)))
-        (t
-         (multiple-value-bind (e string)
-             (if fdigits
-                 (flonum-to-digits x (min (- (+ fdigits (or scale 0)))
-                                          (- (or fmin 0))))
-                 (if (and width (> width 1))
-                     (let ((w (multiple-value-list
-                               (flonum-to-digits x
-                                                 (max 1
-                                                      (+ (1- width)
-                                                         (if (and scale (minusp scale))
-                                                             scale 0)))
-                                                 t)))
-                           (f (multiple-value-list
-                               (flonum-to-digits x (- (+ (or fmin 0)
-                                                         (if scale scale 0)))))))
-                       (cond
-                         ((>= (length (cadr w)) (length (cadr f)))
-                          (values-list w))
-                         (t (values-list f))))
-                     (flonum-to-digits x)))
-           (let ((e (+ e (or scale 0)))
-                 (stream (make-string-output-stream)))
-             (if (plusp e)
-                 (progn
-                   (write-string string stream :end (min (length string)
-                                                         e))
-                   (dotimes (i (- e (length string)))
-                     (write-char #\0 stream))
-                   (write-char #\. stream)
-                   (write-string string stream :start (min (length
-                                                            string) e))
-                   (when fdigits
-                     (dotimes (i (- fdigits
-                                    (- (length string)
-                                       (min (length string) e))))
-                       (write-char #\0 stream))))
-                 (progn
-                   (write-string "." stream)
-                   (dotimes (i (- e))
-                     (write-char #\0 stream))
-                   (write-string string stream)
-                   (when fdigits
-                     (dotimes (i (+ fdigits e (- (length string))))
-                       (write-char #\0 stream)))))
-             (let ((string (get-output-stream-string stream)))
-               (values string (length string)
-                       (char= (char string 0) #\.)
-                       (char= (char string (1- (length string))) #\.)
-                       (position #\. string))))))))
-
-;;; implementation of figure 1 from Burger and Dybvig, 1996.  As the
-;;; implementation of the Dragon from Classic CMUCL (and previously in
-;;; SBCL above FLONUM-TO-STRING) says: "DO NOT EVEN THINK OF
-;;; ATTEMPTING TO UNDERSTAND THIS CODE WITHOUT READING THE PAPER!",
-;;; and in this case we have to add that even reading the paper might
-;;; not bring immediate illumination as CSR has attempted to turn
-;;; idiomatic Scheme into idiomatic Lisp.
+  (multiple-value-bind (e string)
+      (if fdigits
+          (flonum-to-digits x (min (- (+ fdigits (or scale 0)))
+                                   (- (or fmin 0))))
+          (if (and width (> width 1))
+              (let ((w (multiple-value-list
+                        (flonum-to-digits x
+                                          (max 1
+                                               (+ (1- width)
+                                                  (if (and scale (minusp scale))
+                                                      scale 0)))
+                                          t)))
+                    (f (multiple-value-list
+                        (flonum-to-digits x (- (+ (or fmin 0)
+                                                  (if scale scale 0)))))))
+                (cond
+                  ((>= (length (cadr w)) (length (cadr f)))
+                   (values-list w))
+                  (t (values-list f))))
+              (flonum-to-digits x)))
+    (let ((e (if (zerop x)
+                 e
+                 (+ e (or scale 0))))
+          (stream (make-string-output-stream)))
+      (if (plusp e)
+          (progn
+            (write-string string stream :end (min (length string) e))
+            (dotimes (i (- e (length string)))
+              (write-char #\0 stream))
+            (write-char #\. stream)
+            (write-string string stream :start (min (length string) e))
+            (when fdigits
+              (dotimes (i (- fdigits
+                             (- (length string)
+                                (min (length string) e))))
+                (write-char #\0 stream))))
+          (progn
+            (write-string "." stream)
+            (dotimes (i (- e))
+              (write-char #\0 stream))
+            (write-string string stream :end (when fdigits
+                                               (min (length string)
+                                                    (max (or fmin 0)
+                                                         (+ fdigits e)))))
+            (when fdigits
+              (dotimes (i (+ fdigits e (- (length string))))
+                (write-char #\0 stream)))))
+      (let ((string (get-output-stream-string stream)))
+        (values string (length string)
+                (char= (char string 0) #\.)
+                (char= (char string (1- (length string))) #\.)
+                (position #\. string))))))
+
+;;; implementation of figure 1 from Burger and Dybvig, 1996. It is
+;;; extended in order to handle rounding.
+;;;
+;;; As the implementation of the Dragon from Classic CMUCL (and
+;;; previously in SBCL above FLONUM-TO-STRING) says: "DO NOT EVEN
+;;; THINK OF ATTEMPTING TO UNDERSTAND THIS CODE WITHOUT READING THE
+;;; PAPER!", and in this case we have to add that even reading the
+;;; paper might not bring immediate illumination as CSR has attempted
+;;; to turn idiomatic Scheme into idiomatic Lisp.
 ;;;
 ;;; FIXME: figure 1 from Burger and Dybvig is the unoptimized
 ;;; algorithm, noticeably slow at finding the exponent.  Figure 2 has
                                (r r (* r print-base))
                                (m+ m+ (* m+ print-base))
                                (m- m- (* m- print-base)))
-                              ((not (or (< (* (+ r m+) print-base) s)
-                                        (and (not high-ok)
-                                             (= (* (+ r m+) print-base) s))))
+                              ((not (and (plusp (- r m-)) ; Extension to handle zero
+                                         (or (< (* (+ r m+) print-base) s)
+                                             (and (not high-ok)
+                                                  (= (* (+ r m+) print-base) s)))))
                                (values k (generate r s m+ m-)))))))
                    (generate (r s m+ m-)
                      (let (d tc1 tc2)
   (cond (*read-eval*
          (write-string "#." stream))
         (*print-readably*
-         (error 'print-not-readable :object x))
+         (restart-case
+             (error 'print-not-readable :object x)
+           (print-unreadably ()
+             :report "Print unreadably."
+             (let ((*print-readably* nil))
+               (output-float-infinity x stream)))
+           (use-value (o)
+             :report "Supply an object to be printed instead."
+             :interactive read-unreadable-replacement
+             (write o :stream stream))))
         (t
          (write-string "#<" stream)))
   (write-string "SB-EXT:" stream)
   nil)
 
 (defun output-fun (object stream)
-    (let* ((*print-length* 3)  ; in case we have to..
-           (*print-level* 3)  ; ..print an interpreted function definition
-           (name (%fun-name object))
-           (proper-name-p (and (legal-fun-name-p name) (fboundp name)
-                               (eq (fdefinition name) object))))
-      (print-unreadable-object (object stream :identity (not proper-name-p))
-        (format stream "~:[FUNCTION~;CLOSURE~]~@[ ~S~]"
-                (closurep object)
-                name))))
+  (let* ((*print-length* 4)  ; in case we have to..
+         (*print-level* 3)  ; ..print an interpreted function definition
+         (name (%fun-name object))
+         (proper-name-p (and (legal-fun-name-p name) (fboundp name)
+                             (eq (fdefinition name) object))))
+    (print-unreadable-object (object stream :identity (not proper-name-p))
+      (format stream "~:[FUNCTION~;CLOSURE~]~@[ ~S~]"
+              (closurep object)
+              name))))
 \f
 ;;;; catch-all for unknown things