X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Fprint.lisp;h=cff78d0eeab33fdad22eab92e1b0db88aea3118a;hb=15d6e7c9a2c3234f95dfe278046fa2fee1b0c007;hp=da66712c97ecba5384c69ee21a927ae1a84b8678;hpb=31361af9eb64344f521abbb245ea784c76c746e5;p=sbcl.git diff --git a/src/code/print.lisp b/src/code/print.lisp index da66712..cff78d0 100644 --- a/src/code/print.lisp +++ b/src/code/print.lisp @@ -23,52 +23,53 @@ *PRINT-ESCAPE*.") (defvar *print-escape* T #!+sb-doc - "Flag which indicates that slashification is on. See the manual") + "Should we print in a reasonably machine-readable way? (possibly + overridden by *PRINT-READABLY*)") (defvar *print-pretty* nil ; (set later when pretty-printer is initialized) #!+sb-doc - "Flag which indicates that pretty printing is to be used") + "Should pretty printing be used?") (defvar *print-base* 10. #!+sb-doc - "The output base for integers and rationals.") + "the output base for RATIONALs (including integers)") (defvar *print-radix* nil #!+sb-doc - "This flag requests to verify base when printing rationals.") + "Should base be verified when printing RATIONALs?") (defvar *print-level* nil #!+sb-doc - "How many levels deep to print. Unlimited if null.") + "How many levels should be printed before abbreviating with \"#\"?") (defvar *print-length* nil #!+sb-doc - "How many elements to print on each level. Unlimited if null.") + "How many elements at any level should be printed before abbreviating + with \"...\"?") (defvar *print-circle* nil #!+sb-doc - "Whether to worry about circular list structures. See the manual.") + "Should we use #n= and #n# notation to preserve uniqueness in general (and + circularity in particular) when printing?") (defvar *print-case* :upcase #!+sb-doc - "What kind of case the printer should use by default") + "What case should the printer should use default?") (defvar *print-array* t #!+sb-doc - "Whether the array should print its guts out") + "Should the contents of arrays be printed?") (defvar *print-gensym* t #!+sb-doc - "If true, symbols with no home package are printed with a #: prefix. - If false, no prefix is printed.") + "Should #: prefixes be used when printing symbols with null SYMBOL-PACKAGE?") (defvar *print-lines* nil #!+sb-doc - "The maximum number of lines to print. If NIL, unlimited.") + "the maximum number of lines to print per object") (defvar *print-right-margin* nil #!+sb-doc - "The position of the right margin in ems. If NIL, try to determine this - from the stream in use.") + "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 is less than this, then print using ``miser-style'' output. Miser style conditional newlines are turned on, and all indentations are turned off. If NIL, never use miser mode.") -(defvar *print-pprint-dispatch* nil - #!+sb-doc - "The pprint-dispatch-table that controls how to pretty print objects. See - COPY-PPRINT-DISPATH, PPRINT-DISPATCH, and SET-PPRINT-DISPATCH.") +(defvar *print-pprint-dispatch*) +#!+sb-doc +(setf (fdocumentation '*print-pprint-dispatch* 'variable) + "the pprint-dispatch-table that controls how to pretty-print objects") (defmacro with-standard-io-syntax (&body body) #!+sb-doc @@ -93,10 +94,11 @@ *READ-DEFAULT-FLOAT-FORMAT* SINGLE-FLOAT *READ-EVAL* T *READ-SUPPRESS* NIL - *READTABLE* the standard readtable." - `(%with-standard-io-syntax #'(lambda () ,@body))) + *READTABLE* the standard readtable" + `(%with-standard-io-syntax (lambda () ,@body))) (defun %with-standard-io-syntax (function) + (declare (type function function)) (let ((*package* (find-package "COMMON-LISP-USER")) (*print-array* t) (*print-base* 10) @@ -156,7 +158,7 @@ #!+sb-doc "Output a mostly READable printed representation of OBJECT on the specified STREAM." - (let ((*print-escape* T)) + (let ((*print-escape* t)) (output-object object (out-synonym-of stream))) object) @@ -164,8 +166,8 @@ #!+sb-doc "Output an aesthetic but not necessarily READable printed representation of OBJECT on the specified STREAM." - (let ((*print-escape* NIL) - (*print-readably* NIL)) + (let ((*print-escape* nil) + (*print-readably* nil)) (output-object object (out-synonym-of stream))) object) @@ -215,18 +217,21 @@ #!+sb-doc "Return the printed representation of OBJECT as a string with slashification on." - (stringify-object object t)) + (let ((*print-escape* t)) + (stringify-object object))) (defun princ-to-string (object) #!+sb-doc "Return the printed representation of OBJECT as a string with slashification off." - (stringify-object object nil)) + (let ((*print-escape* nil) + (*print-readably* nil)) + (stringify-object object))) ;;; This produces the printed representation of an object as a string. ;;; The few ...-TO-STRING functions above call this. (defvar *string-output-streams* ()) -(defun stringify-object (object &optional (*print-escape* *print-escape*)) +(defun stringify-object (object) (let ((stream (if *string-output-streams* (pop *string-output-streams*) (make-string-output-stream)))) @@ -240,21 +245,19 @@ ;;; 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)) (flet ((print-description () (when type (write (type-of object) :stream stream :circle nil :level nil :length nil) - (when (or body identity) - (write-char #\space stream) - (pprint-newline :fill stream))) + (write-char #\space stream)) (when body (funcall body)) (when identity - (when body - (write-char #\space stream) - (pprint-newline :fill stream)) + (when (or body (not type)) + (write-char #\space stream)) (write-char #\{ stream) (write (get-lisp-obj-address object) :stream stream :radix nil :base 16) @@ -288,31 +291,35 @@ ;;; marker, it is incremented. (defvar *circularity-counter* nil) -;;; Check to see whether OBJECT is a circular reference, and return something -;;; non-NIL if it is. If ASSIGN is T, then the number to use in the #n= and -;;; #n# noise is assigned at this time. Note: CHECK-FOR-CIRCULARITY must -;;; be called *EXACTLY* once with ASSIGN T, or the circularity detection noise -;;; will get confused about when to use #n= and when to use #n#. If this -;;; returns non-NIL when ASSIGN is T, then you must call HANDLE-CIRCULARITY -;;; on it. If you are not using this inside a WITH-CIRCULARITY-DETECTION, -;;; then you have to be prepared to handle a return value of :INITIATE which -;;; means it needs to initiate the circularity detection noise. See the -;;; source for info on how to do that. +;;; Check to see whether OBJECT is a circular reference, and return +;;; something non-NIL if it is. If ASSIGN is T, then the number to use +;;; in the #n= and #n# noise is assigned at this time. +;;; If ASSIGN is true, reference bookkeeping will only be done for +;;; existing entries, no new references will be recorded! +;;; +;;; Note: CHECK-FOR-CIRCULARITY must be called *exactly* once with +;;; ASSIGN true, or the circularity detection noise will get confused +;;; about when to use #n= and when to use #n#. If this returns non-NIL +;;; when ASSIGN is true, then you must call HANDLE-CIRCULARITY on it. +;;; If CHECK-FOR-CIRCULARITY returns :INITIATE as the second value, +;;; you need to initiate the circularity detection noise, e.g. bind +;;; *CIRCULARITY-HASH-TABLE* and *CIRCULARITY-COUNTER* to suitable values +;;; (see #'OUTPUT-OBJECT for an example). (defun check-for-circularity (object &optional assign) (cond ((null *print-circle*) ;; Don't bother, nobody cares. nil) ((null *circularity-hash-table*) - :initiate) + (values nil :initiate)) ((null *circularity-counter*) (ecase (gethash object *circularity-hash-table*) ((nil) - ;; First encounter. + ;; first encounter (setf (gethash object *circularity-hash-table*) t) ;; We need to keep looking. nil) ((t) - ;; Second encounter. + ;; second encounter (setf (gethash object *circularity-hash-table*) 0) ;; It's a circular reference. t) @@ -323,24 +330,25 @@ (let ((value (gethash object *circularity-hash-table*))) (case value ((nil t) - ;; If NIL, we found an object that wasn't there the first time - ;; around. If T, exactly one occurance of this object appears. - ;; Either way, just print the thing without any special - ;; processing. Note: you might argue that finding a new object - ;; means that something is broken, but this can happen. If - ;; someone uses the ~@<...~:> format directive, it conses a - ;; new list each time though format (i.e. the &REST list), so - ;; we will have different cdrs. + ;; If NIL, we found an object that wasn't there the + ;; first time around. If T, this object appears exactly + ;; once. Either way, just print the thing without any + ;; special processing. Note: you might argue that + ;; finding a new object means that something is broken, + ;; but this can happen. If someone uses the ~@<...~:> + ;; format directive, it conses a new list each time + ;; though format (i.e. the &REST list), so we will have + ;; different cdrs. nil) (0 (if assign (let ((value (incf *circularity-counter*))) - ;; First occurance of this object. Set the counter. + ;; first occurrence of this object: Set the counter. (setf (gethash object *circularity-hash-table*) value) value) t)) (t - ;; Second or later occurance. + ;; second or later occurrence (- value))))))) ;;; Handle the results of CHECK-FOR-CIRCULARITY. If this returns T then @@ -371,55 +379,55 @@ ;;;; OUTPUT-OBJECT -- the main entry point -(defvar *pretty-printer* nil - #!+sb-doc - "The current pretty printer. Should be either a function that takes two - arguments (the object and the stream) or NIL to indicate that there is - no pretty printer installed.") +;;; Objects whose print representation identifies them EQLly don't +;;; need to be checked for circularity. +(defun uniquely-identified-by-print-p (x) + (or (numberp x) + (characterp x) + (and (symbolp x) + (symbol-package x)))) ;;; Output OBJECT to STREAM observing all printer control variables. (defun output-object (object stream) (labels ((print-it (stream) (if *print-pretty* - (if *pretty-printer* - (funcall *pretty-printer* object stream) - (let ((*print-pretty* nil)) - (output-ugly-object object stream))) + (sb!pretty:output-pretty-object object stream) (output-ugly-object object stream))) (check-it (stream) - (let ((marker (check-for-circularity object t))) - (case marker - (:initiate - (let ((*circularity-hash-table* - (make-hash-table :test 'eq))) - (check-it (make-broadcast-stream)) - (let ((*circularity-counter* 0)) - (check-it stream)))) - ((nil) - (print-it stream)) - (t - (when (handle-circularity marker stream) - (print-it stream))))))) - (cond ((or (not *print-circle*) - (numberp object) - (characterp object) - (and (symbolp object) (symbol-package object) t)) - ;; If it a number, character, or interned symbol, we do not - ;; want to check for circularity/sharing. + (multiple-value-bind (marker initiate) + (check-for-circularity object t) + ;; initialization of the circulation detect noise ... + (if (eq initiate :initiate) + (let ((*circularity-hash-table* + (make-hash-table :test 'eq))) + (check-it (make-broadcast-stream)) + (let ((*circularity-counter* 0)) + (check-it stream))) + ;; otherwise + (if marker + (when (handle-circularity marker stream) + (print-it stream)) + (print-it stream)))))) + (cond (;; Maybe we don't need to bother with circularity detection. + (or (not *print-circle*) + (uniquely-identified-by-print-p object)) (print-it stream)) - ((or *circularity-hash-table* - (consp object) - (typep object 'instance) - (typep object '(array t *))) - ;; If we have already started circularity detection, this - ;; object might be a sharded reference. If we have not, - ;; then if it is a cons, a instance, or an array of element - ;; type t it might contain a circular reference to itself - ;; or multiple shared references. + (;; If we have already started circularity detection, this + ;; object might be a shared reference. If we have not, then + ;; if it is a compound object it might contain a circular + ;; reference to itself or multiple shared references. + (or *circularity-hash-table* + (compound-object-p object)) (check-it stream)) (t (print-it stream))))) +;;; a hack to work around recurring gotchas with printing while +;;; DEFGENERIC PRINT-OBJECT is being built +;;; +;;; (hopefully will go away naturally when CLOS moves into cold init) +(defvar *print-object-is-disabled-p*) + ;;; Output OBJECT to STREAM observing all printer control variables ;;; except for *PRINT-PRETTY*. Note: if *PRINT-PRETTY* is non-NIL, ;;; then the pretty printer will be used for any components of OBJECT, @@ -437,15 +445,15 @@ ;; a method on an external symbol in the CL package which is ;; applicable to arg lists containing only direct instances of ;; standardized classes. - ;; Thus, in order for the user to detect our sleaziness, he has to do - ;; something relatively obscure like + ;; Thus, in order for the user to detect our sleaziness in conforming + ;; code, he has to do something relatively obscure like ;; (1) actually use tools like FIND-METHOD to look for PRINT-OBJECT ;; methods, or ;; (2) define a PRINT-OBJECT method which is specialized on the stream ;; value (e.g. a Gray stream object). ;; As long as no one comes up with a non-obscure way of detecting this ;; sleaziness, fixing this nonconformity will probably have a low - ;; priority. -- WHN 20000121 + ;; priority. -- WHN 2001-11-25 (fixnum (output-integer object stream)) (list @@ -453,11 +461,17 @@ (output-symbol object stream) (output-list object stream))) (instance - (print-object object stream)) + (cond ((not (and (boundp '*print-object-is-disabled-p*) + *print-object-is-disabled-p*)) + (print-object object stream)) + ((typep object 'structure-object) + (default-structure-print object stream *current-level-in-print*)) + (t + (write-string "#" stream)))) (function (unless (and (funcallable-instance-p object) (printed-as-funcallable-standard-class object stream)) - (output-function object stream))) + (output-fun object stream))) (symbol (output-symbol object stream)) (number @@ -493,19 +507,19 @@ ;;;; symbols -;;; Values of *PRINT-CASE* and (READTABLE-CASE *READTABLE*) the last -;;; time the printer was called. +;;; values of *PRINT-CASE* and (READTABLE-CASE *READTABLE*) the last +;;; time the printer was called (defvar *previous-case* nil) (defvar *previous-readtable-case* nil) ;;; This variable contains the current definition of one of three ;;; symbol printers. SETUP-PRINTER-STATE sets this variable. -(defvar *internal-symbol-output-function* nil) +(defvar *internal-symbol-output-fun* nil) ;;; This function sets the internal global symbol -;;; *INTERNAL-SYMBOL-OUTPUT-FUNCTION* to the right function depending -;;; on the value of *PRINT-CASE*. See the manual for details. The -;;; print buffer stream is also reset. +;;; *INTERNAL-SYMBOL-OUTPUT-FUN* to the right function depending on +;;; the value of *PRINT-CASE*. See the manual for details. The print +;;; buffer stream is also reset. (defun setup-printer-state () (unless (and (eq *print-case* *previous-case*) (eq (readtable-case *readtable*) *previous-readtable-case*)) @@ -519,7 +533,7 @@ (setf (readtable-case *readtable*) :upcase) (error "invalid READTABLE-CASE value: ~S" *previous-readtable-case*)) - (setq *internal-symbol-output-function* + (setq *internal-symbol-output-fun* (case *previous-readtable-case* (:upcase (case *print-case* @@ -583,11 +597,12 @@ ;;; words, diddle its case according to *PRINT-CASE* and ;;; READTABLE-CASE. (defun output-symbol-name (name stream &optional (maybe-quote t)) - (declare (type simple-base-string name)) - (setup-printer-state) - (if (and maybe-quote (symbol-quotep name)) - (output-quoted-symbol-name name stream) - (funcall *internal-symbol-output-function* name stream))) + (declare (type simple-string name)) + (let ((*readtable* (if *print-readably* *standard-readtable* *readtable*))) + (setup-printer-state) + (if (and maybe-quote (symbol-quotep name)) + (output-quoted-symbol-name name stream) + (funcall *internal-symbol-output-fun* name stream)))) ;;;; escaping symbols @@ -669,7 +684,6 @@ :initial-element 36)) (declaim (type (simple-array (unsigned-byte 8) (#.char-code-limit)) *digit-bases*)) - (dotimes (i 36) (let ((char (digit-char i 36))) (setf (aref *digit-bases* (char-code char)) i))) @@ -817,13 +831,16 @@ (return t) MARKER ; number marker in a numeric number... + ;; ("What," you may ask, "is a 'number marker'?" It's something + ;; that a conforming implementation might use in number syntax. + ;; See ANSI 2.3.1.1 "Potential Numbers as Tokens".) (when (test letter) (advance OTHER nil)) (go DIGIT)))) -;;;; *INTERNAL-SYMBOL-OUTPUT-FUNCTION* +;;;; *INTERNAL-SYMBOL-OUTPUT-FUN* ;;;; -;;;; Case hackery. These functions are stored in -;;;; *INTERNAL-SYMBOL-OUTPUT-FUNCTION* according to the values of +;;;; case hackery: These functions are stored in +;;;; *INTERNAL-SYMBOL-OUTPUT-FUN* according to the values of ;;;; *PRINT-CASE* and READTABLE-CASE. ;;; called when: @@ -859,19 +876,19 @@ ;;; :DOWNCASE :CAPITALIZE (defun output-capitalize-symbol (pname stream) (declare (simple-string pname)) - (let ((prev-not-alpha t) + (let ((prev-not-alphanum t) (up (eq (readtable-case *readtable*) :upcase))) (dotimes (i (length pname)) (let ((char (char pname i))) (write-char (if up - (if (or prev-not-alpha (lower-case-p char)) + (if (or prev-not-alphanum (lower-case-p char)) char (char-downcase char)) - (if prev-not-alpha + (if prev-not-alphanum (char-upcase char) char)) stream) - (setq prev-not-alpha (not (alpha-char-p char))))))) + (setq prev-not-alphanum (not (alphanumericp char))))))) ;;; called when: ;;; READTABLE-CASE *PRINT-CASE* @@ -932,7 +949,8 @@ (output-object (pop list) stream) (unless list (return)) - (when (or (atom list) (check-for-circularity list)) + (when (or (atom list) + (check-for-circularity list)) (write-string " . " stream) (output-object list stream) (return)) @@ -943,7 +961,13 @@ (defun output-vector (vector stream) (declare (vector vector)) (cond ((stringp vector) - (cond ((or *print-escape* *print-readably*) + (cond ((and *print-readably* + (not (eq (array-element-type vector) + (load-time-value + (array-element-type + (make-array 0 :element-type 'character)))))) + (error 'print-not-readable :object vector)) + ((or *print-escape* *print-readably*) (write-char #\" stream) (quote-string vector stream) (write-char #\" stream)) @@ -959,7 +983,7 @@ (write-char (if (zerop bit) #\0 #\1) stream))) (t (when (and *print-readably* - (not (eq (array-element-type vector) t))) + (not (array-readably-printable-p vector))) (error 'print-not-readable :object vector)) (descend-into (stream) (write-string "#(" stream) @@ -986,6 +1010,14 @@ (when (needs-slash-p char) (write-char #\\ stream)) (write-char char stream)))))) +(defun array-readably-printable-p (array) + (and (eq (array-element-type array) t) + (let ((zero (position 0 (array-dimensions array))) + (number (position 0 (array-dimensions array) + :test (complement #'eql) + :from-end t))) + (or (null zero) (null number) (> zero number))))) + ;;; Output the printed representation of any array in either the #< or #A ;;; form. (defun output-array (array stream) @@ -1002,10 +1034,11 @@ ;;; Output the readable #A form of an array. (defun output-array-guts (array stream) (when (and *print-readably* - (not (eq (array-element-type array) t))) + (not (array-readably-printable-p array))) (error 'print-not-readable :object array)) (write-char #\# stream) - (let ((*print-base* 10)) + (let ((*print-base* 10) + (*print-radix* nil)) (output-integer (array-rank array) stream)) (write-char #\A stream) (with-array-data ((data array) (start) (end)) @@ -1034,7 +1067,7 @@ ;;; use until CLOS is set up (at which time it will be replaced with ;;; the real generic function implementation) (defun print-object (instance stream) - (default-structure-print instance stream *current-level*)) + (default-structure-print instance stream *current-level-in-print*)) ;;;; integer, ratio, and complex printing (i.e. everything but floats) @@ -1138,8 +1171,8 @@ (2 (write-char #\b stream)) (8 (write-char #\o stream)) (16 (write-char #\x stream)) - (t (write *print-base* :stream stream :radix nil :base 10))) - (write-char #\r stream)) + (t (write *print-base* :stream stream :radix nil :base 10) + (write-char #\r stream)))) (let ((*print-radix* nil)) (output-integer (numerator ratio) stream) (write-char #\/ stream) @@ -1218,6 +1251,7 @@ ;;; [CMUC]tradix.press. DO NOT EVEN THINK OF ATTEMPTING TO ;;; UNDERSTAND THIS CODE WITHOUT READING THE PAPER! +(declaim (type (simple-array character (10)) *digits*)) (defvar *digits* "0123456789") (defun flonum-to-string (x &optional width fdigits scale fmin) @@ -1355,6 +1389,96 @@ ;; all done (values digit-string (1+ digits) (= decpnt 0) (= decpnt digits) decpnt))) +;;; implementation of figure 1 from Burger and Dybvig, 1996. As the +;;; implementation of the Dragon from Classic CMUCL (and 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 +;;; an improved algorithm, but CSR ran out of energy +;;; +;;; FIXME: Burger and Dybvig also provide an algorithm for +;;; fixed-format floating point printing. If it were implemented, +;;; then we could delete the Dragon altogether (see FLONUM-TO-STRING). +;;; +;;; possible extension for the enthusiastic: printing floats in bases +;;; other than base 10. +(defconstant single-float-min-e + (nth-value 1 (decode-float least-positive-single-float))) +(defconstant double-float-min-e + (nth-value 1 (decode-float least-positive-double-float))) +#!+long-float +(defconstant long-float-min-e + (nth-value 1 (decode-float least-positive-long-float))) + +(defun flonum-to-digits (v) + (let ((print-base 10) ; B + (float-radix 2) ; b + (float-digits (float-digits v)) ; p + (min-e + (etypecase v + (single-float single-float-min-e) + (double-float double-float-min-e) + #!+long-float + (long-float long-float-min-e)))) + (multiple-value-bind (f e) + (integer-decode-float v) + (let (;; FIXME: these even tests assume normal IEEE rounding + ;; mode. I wonder if we should cater for non-normal? + (high-ok (evenp f)) + (low-ok (evenp f)) + (result (make-array 50 :element-type 'base-char + :fill-pointer 0 :adjustable t))) + (labels ((scale (r s m+ m-) + (do ((k 0 (1+ k)) + (s s (* s print-base))) + ((not (or (> (+ r m+) s) + (and high-ok (= (+ r m+) s)))) + (do ((k k (1- k)) + (r r (* r print-base)) + (m+ m+ (* m+ print-base)) + (m- m- (* m- print-base))) + ((not (or (< (* (+ r m+) print-base) s) + (and high-ok (= (* (+ r m+) print-base) s)))) + (values k (generate r s m+ m-))))))) + (generate (r s m+ m-) + (let (d tc1 tc2) + (tagbody + loop + (setf (values d r) (truncate (* r print-base) s)) + (setf m+ (* m+ print-base)) + (setf m- (* m- print-base)) + (setf tc1 (or (< r m-) (and low-ok (= r m-)))) + (setf tc2 (or (> (+ r m+) s) + (and high-ok (= (+ r m+) s)))) + (when (or tc1 tc2) + (go end)) + (vector-push-extend (char *digits* d) result) + (go loop) + end + (let ((d (cond + ((and (not tc1) tc2) (1+ d)) + ((and tc1 (not tc2)) d) + (t ; (and tc1 tc2) + (if (< (* r 2) s) d (1+ d)))))) + (vector-push-extend (char *digits* d) result) + (return-from generate result)))))) + (if (>= e 0) + (if (/= f (expt float-radix (1- float-digits))) + (let ((be (expt float-radix e))) + (scale (* f be 2) 2 be be)) + (let* ((be (expt float-radix e)) + (be1 (* be float-radix))) + (scale (* f be1 2) (* float-radix 2) be1 be))) + (if (or (= e min-e) (/= f (expt float-radix (1- float-digits)))) + (scale (* f 2) (* (expt float-radix (- e)) 2) 1 1) + (scale (* f float-radix 2) + (* (expt float-radix (- 1 e)) 2) float-radix 1)))))))) + ;;; Given a non-negative floating point number, SCALE-EXPONENT returns ;;; a new floating point number Z in the range (0.1, 1.0] and an ;;; exponent E such that Z * 10^E is (approximately) equal to the @@ -1367,30 +1491,40 @@ ;;; part of the computation to avoid over/under flow. When ;;; denormalized, we must pull out a large factor, since there is more ;;; negative exponent range than positive range. + +(eval-when (:compile-toplevel :execute) + (setf *read-default-float-format* + #!+long-float 'long-float #!-long-float 'double-float)) (defun scale-exponent (original-x) (let* ((x (coerce original-x 'long-float))) (multiple-value-bind (sig exponent) (decode-float x) (declare (ignore sig)) - (if (= x 0.0l0) - (values (float 0.0l0 original-x) 1) - (let* ((ex (round (* exponent (log 2l0 10)))) + (if (= x 0.0e0) + (values (float 0.0e0 original-x) 1) + (let* ((ex (locally (declare (optimize (safety 0))) + (the fixnum + (round (* exponent (log 2e0 10)))))) (x (if (minusp ex) (if (float-denormalized-p x) #!-long-float - (* x 1.0l16 (expt 10.0l0 (- (- ex) 16))) + (* x 1.0e16 (expt 10.0e0 (- (- ex) 16))) #!+long-float - (* x 1.0l18 (expt 10.0l0 (- (- ex) 18))) - (* x 10.0l0 (expt 10.0l0 (- (- ex) 1)))) - (/ x 10.0l0 (expt 10.0l0 (1- ex)))))) - (do ((d 10.0l0 (* d 10.0l0)) + (* x 1.0e18 (expt 10.0e0 (- (- ex) 18))) + (* x 10.0e0 (expt 10.0e0 (- (- ex) 1)))) + (/ x 10.0e0 (expt 10.0e0 (1- ex)))))) + (do ((d 10.0e0 (* d 10.0e0)) (y x (/ x d)) (ex ex (1+ ex))) - ((< y 1.0l0) - (do ((m 10.0l0 (* m 10.0l0)) + ((< y 1.0e0) + (do ((m 10.0e0 (* m 10.0e0)) (z y (* y m)) (ex ex (1- ex))) - ((>= z 0.1l0) - (values (float z original-x) ex)))))))))) + ((>= z 0.1e0) + (values (float z original-x) ex)) + (declare (long-float m) (integer ex)))) + (declare (long-float d)))))))) +(eval-when (:compile-toplevel :execute) + (setf *read-default-float-format* 'single-float)) ;;;; entry point for the float printer @@ -1409,6 +1543,12 @@ ;;; attractive to handle exponential notation with the same accuracy ;;; as non-exponential notation, using the method described in the ;;; Steele and White paper. +;;; +;;; NOTE II: this has been bypassed slightly by implementing Burger +;;; and Dybvig, 1996. When someone has time (KLUDGE) they can +;;; probably (a) implement the optimizations suggested by Burger and +;;; Dyvbig, and (b) remove all vestiges of Dragon4, including from +;;; fixed-format printing. ;;; Print the appropriate exponent marker for X and the specified exponent. (defun print-float-exponent (x exp stream) @@ -1466,26 +1606,34 @@ (write-string "0.0" stream) (print-float-exponent x 0 stream)) (t - (output-float-aux x stream (float 1/1000 x) (float 10000000 x)))))))) + (output-float-aux x stream -3 8))))))) (defun output-float-aux (x stream e-min e-max) - (if (and (>= x e-min) (< x e-max)) - ;; free format - (multiple-value-bind (str len lpoint tpoint) (flonum-to-string x) - (declare (ignore len)) - (when lpoint (write-char #\0 stream)) - (write-string str stream) - (when tpoint (write-char #\0 stream)) - (print-float-exponent x 0 stream)) - ;; exponential format - (multiple-value-bind (f ex) (scale-exponent x) - (multiple-value-bind (str len lpoint tpoint) - (flonum-to-string f nil nil 1) - (declare (ignore len)) - (when lpoint (write-char #\0 stream)) - (write-string str stream) - (when tpoint (write-char #\0 stream)) - ;; Subtract out scale factor of 1 passed to FLONUM-TO-STRING. - (print-float-exponent x (1- ex) stream))))) + (multiple-value-bind (e string) + (flonum-to-digits x) + (cond + ((< e-min e e-max) + (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 (<= (length string) e) + (write-char #\0 stream)) + (print-float-exponent x 0 stream)) + (progn + (write-string "0." stream) + (dotimes (i (- e)) + (write-char #\0 stream)) + (write-string string stream) + (print-float-exponent x 0 stream)))) + (t (write-string string stream :end 1) + (write-char #\. stream) + (write-string string stream :start 1) + (when (= (length string) 1) + (write-char #\0 stream)) + (print-float-exponent x (1- e) stream))))) ;;;; other leaf objects @@ -1493,9 +1641,10 @@ ;;; the character name or the character in the #\char format. (defun output-character (char stream) (if (or *print-escape* *print-readably*) - (let ((name (char-name char))) + (let ((graphicp (graphic-char-p char)) + (name (char-name char))) (write-string "#\\" stream) - (if name + (if (and name (not graphicp)) (quote-string name stream) (write-char char stream))) (write-char char stream))) @@ -1550,23 +1699,15 @@ (declare (ignore object stream)) nil) -(defun output-function (object stream) +(defun output-fun (object stream) (let* ((*print-length* 3) ; in case we have to.. (*print-level* 3) ; ..print an interpreted function definition - (name (cond ((find (function-subtype object) - #(#.sb!vm:closure-header-type - #.sb!vm:byte-code-closure-type)) - "CLOSURE") - (#!+sb-interpreter - (sb!eval::interpreted-function-p object) - (or (sb!eval::interpreted-function-%name object) - (sb!eval:interpreted-function-lambda-expression - object))) - ((find (function-subtype object) - #(#.sb!vm:function-header-type - #.sb!vm:closure-function-header-type)) - (%function-name object)) - (t 'no-name-available))) + ;; FIXME: This find-the-function-name idiom ought to be + ;; encapsulated in a function somewhere. + (name (case (fun-subtype object) + (#.sb!vm:closure-header-widetag "CLOSURE") + (#.sb!vm:simple-fun-header-widetag (%simple-fun-name object)) + (t 'no-name-available))) (identified-by-name-p (and (symbolp name) (fboundp name) (eq (fdefinition name) object)))) @@ -1581,30 +1722,32 @@ (defun output-random (object stream) (print-unreadable-object (object stream :identity t) - (let ((lowtag (get-lowtag object))) + (let ((lowtag (lowtag-of object))) (case lowtag - (#.sb!vm:other-pointer-type - (let ((type (get-type object))) - (case type - (#.sb!vm:value-cell-header-type + (#.sb!vm:other-pointer-lowtag + (let ((widetag (widetag-of object))) + (case widetag + (#.sb!vm:value-cell-header-widetag (write-string "value cell " stream) - (output-object (sb!c:value-cell-ref object) stream)) + (output-object (value-cell-ref object) stream)) (t - (write-string "unknown pointer object, type=" stream) + (write-string "unknown pointer object, widetag=" stream) (let ((*print-base* 16) (*print-radix* t)) - (output-integer type stream)))))) - ((#.sb!vm:function-pointer-type - #.sb!vm:instance-pointer-type - #.sb!vm:list-pointer-type) - (write-string "unknown pointer object, type=" stream)) + (output-integer widetag stream)))))) + ((#.sb!vm:fun-pointer-lowtag + #.sb!vm:instance-pointer-lowtag + #.sb!vm:list-pointer-lowtag) + (write-string "unknown pointer object, lowtag=" stream) + (let ((*print-base* 16) (*print-radix* t)) + (output-integer lowtag stream))) (t - (case (get-type object) - (#.sb!vm:unbound-marker-type + (case (widetag-of object) + (#.sb!vm:unbound-marker-widetag (write-string "unbound marker" stream)) (t (write-string "unknown immediate object, lowtag=" stream) (let ((*print-base* 2) (*print-radix* t)) (output-integer lowtag stream)) - (write-string ", type=" stream) + (write-string ", widetag=" stream) (let ((*print-base* 16) (*print-radix* t)) - (output-integer (get-type object) stream))))))))) + (output-integer (widetag-of object) stream)))))))))