(defvar *print-base* 10
"The radix in which the printer will print rationals.")
(defvar *print-radix* nil
"If true, print a radix specifier when printing a rational number.")
(defvar *print-case* :upcase
"One of the symbols :upcase, :downcase, or :capitalize.")
(defvar *print-gensym* t
"If true, print `#:' before apparently uninterned symbols.")
(defvar *print-array* t "If true, arrays are printed in readable #(...), #*, or #nA(...) syntax.")
(defvar *print-level* nil
"Control how many levels deep a nested object will print.")
(defvar *print-length* nil
"Control how many elements at a given level are printed.")
(defvar *print-circle* nil
"If true, detect circularity and sharing in an object being printed.")
(defvar *print-escape* t
"If false, escape characters and package prefixes are not output.")
(defvar *print-readably* nil
"If true, print objects readably.")
(defvar *print-pretty* t "If true, the pretty printer is used when printing.")
(defvar *print-lines* nil
"Limit on the number of output lines produced when pretty printing.")
(defvar *print-miser-width* nil "Switch to a compact style of output whenever the width available for printing a substructure is less than or equal to this many ems when pretty printing.")
(defvar *print-right-margin* nil
"Specify the right margin to use when the pretty printer is making layout decisions.")
(defmacro with-standard-io-syntax (&rest forms)
"Bind all reader/printer control vars to the standard values then eval FORMS."
`(let ((*package* (find-package "CL-USER"))
(*print-array* t)
(*print-base* 10)
(*print-case* :upcase)
(*print-circle* nil)
(*print-escape* t)
(*print-gensym* t)
(*print-length* nil)
(*print-level* nil)
(*print-lines* nil)
(*print-miser-width* nil)
(*print-pretty* nil)
(*print-radix* nil)
(*print-readably* t)
(*print-right-margin* nil)
(*read-base* 10)
(*read-default-float-format* 'single-float)
(*read-eval* t)
(*read-suppress* nil)
(*readtable* (copy-readtable nil)))
,@forms))
(defgeneric print-object (object stream))
(defun write (object &key
((:array *print-array*) *print-array*)
((:base *print-base*) *print-base*)
((:case *print-case*) *print-case*)
((:circle *print-circle*) *print-circle*)
((:escape *print-escape*) *print-escape*)
((:gensym *print-gensym*) *print-gensym*)
((:length *print-length*) *print-length*)
((:level *print-level*) *print-level*)
((:lines *print-lines*) *print-lines*)
((:miser-width *print-miser-width*) *print-miser-width*)
((:pprint-dispatch *print-pprint-dispatch*)
*print-pprint-dispatch*)
((:pretty *print-pretty*) *print-pretty*)
((:radix *print-radix*) *print-radix*)
((:readably *print-readably*) *print-readably*)
((:right-margin *print-right-margin*) *print-right-margin*)
(stream *standard-output*))
(if *print-pretty*
(print-object-prettily object stream)
(print-object object stream))
object)
(defun write-to-string (object &key
((:array *print-array*) *print-array*)
((:base *print-base*) *print-base*)
((:case *print-case*) *print-case*)
((:circle *print-circle*) *print-circle*)
((:escape *print-escape*) *print-escape*)
((:gensym *print-gensym*) *print-gensym*)
((:length *print-length*) *print-length*)
((:level *print-level*) *print-level*)
((:lines *print-lines*) *print-lines*)
((:miser-width *print-miser-width*) *print-miser-width*)
((:pprint-dispatch *print-pprint-dispatch*)
*print-pprint-dispatch*)
((:pretty *print-pretty*) *print-pretty*)
((:radix *print-radix*) *print-radix*)
((:readably *print-readably*) *print-readably*)
((:right-margin *print-right-margin*) *print-right-margin*))
(with-output-to-string (stream)
(if *print-pretty*
(print-object-prettily object stream)
(print-object object stream))))
(defun prin1 (object &optional output-stream)
(write object :stream output-stream :escape t))
(defun prin1-to-string (object) (write-to-string object :escape t))
(defun princ (object &optional output-stream)
(write object :stream output-stream :escape nil :readably nil))
(defun princ-to-string (object)
(write-to-string object :escape nil :readably nil))
(defun print (object &optional output-stream)
(terpri output-stream)
(prin1 object output-stream)
(write-char #\Space output-stream)
object)
(defun pprint (object &optional output-stream)
(terpri output-stream)
(write object :stream output-stream :pretty t :escape t)
(values))
(defun printer-escaping-enabled-p () (or *print-escape* *print-readably*))
(defmethod print-object ((object integer) stream) (print-integer object stream))
(defun print-integer (integer stream)
(let ((chars "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
digits)
(loop with n = (abs integer)
do (multiple-value-bind (q r) (floor n *print-base*)
(push (char chars r) digits)
(setq n q))
until (zerop n))
(when *print-radix*
(case *print-base*
(2 (write-string "#b" stream))
(8 (write-string "#o" stream))
(16 (write-string "#x" stream))
(10 nil)
(t (write-char #\# stream)
(let ((base *print-base*)
(*print-base* 10)
(*print-radix* nil))
(print-integer base stream))
(write-char #\r stream))))
(write-string (concatenate 'string
(when (minusp integer) '(#\-))
digits
(when (and *print-radix* (= *print-base* 10))
"."))
stream)
integer))
(defmethod print-object ((ratio ratio) stream)
(if (and *print-radix* (= *print-base* 10))
(progn
(write-string "#10r" stream)
(let ((*print-radix* nil))
(print-integer (numerator ratio) stream)))
(print-integer (numerator ratio) stream))
(write-char #\/ stream)
(let ((*print-radix* nil)) (print-integer (denominator ratio) stream))
ratio)
(defmethod print-object ((complex complex) stream)
(write-string "#C(" stream)
(print-object (realpart complex) stream)
(write-char #\Space stream)
(print-object (imagpart complex) stream)
(write-char #\))
complex)
(defmethod print-object ((character character) stream)
(cond
((printer-escaping-enabled-p)
(write-string "#\\" stream)
(if (and (graphic-char-p character) (not (char= character #\Space)))
(write-char character stream)
(write-string (char-name character) stream)))
(t (write-char character stream)))
character)
(defun string-invert (str)
(cond
((every #'(lambda (c) (or (not (alpha-char-p c)) (upper-case-p c))) str)
(map 'string #'char-downcase str))
((every #'(lambda (c) (or (not (alpha-char-p c)) (lower-case-p c))) str)
(map 'string #'char-upcase str))
(t str)))
(defun make-str (chars)
(make-array (length chars) :element-type 'character :initial-contents chars))
(defun print-symbol-as-is (symbol stream)
(let ((name (symbol-name symbol)))
(ecase (readtable-case *readtable*)
(:upcase
(write-string
(ecase *print-case*
(:upcase name)
(:downcase (map 'string #'char-downcase name))
(:capitalize
(make-str (loop for c across name and prev = nil then c
collecting
(if (and (upper-case-p c) prev (alpha-char-p prev))
(char-downcase c)
c)))))
stream))
(:downcase
(write-string
(ecase *print-case*
(:upcase (map 'string #'char-upcase name))
(:downcase name)
(:capitalize
(make-str (loop for c across name and prev = nil then c
collecting
(if (and (lower-case-p c)
(or (null prev) (not (alpha-char-p prev))))
(char-upcase c)
c)))))
stream))
(:preserve (write-string name stream))
(:invert (write-string (string-invert name) stream)))
symbol))
(defun print-name-escaping (name stream &key force-escaping)
(let ((readtable-case (readtable-case *readtable*)))
(if (or force-escaping
(loop with standard-table = (copy-readtable nil)
for c across name
thereis (not (and (eq (syntax-type c standard-table) :constituent)
(eq (syntax-type c) :constituent))))
(notevery #'graphic-char-p name)
(and (eq readtable-case :upcase) (some 'lower-case-p name))
(and (eq readtable-case :downcase) (some 'upper-case-p name)))
(let ((escaped (loop for c across name
if (find c '(#\\ #\|)) append (list #\\ c)
else collect c)))
(write-string (concatenate 'string "|" escaped "|") stream))
(write-string (case readtable-case
((:upcase :downcase)
(ecase *print-case*
(:upcase (string-upcase name))
(:downcase (string-downcase name))
(:capitalize (string-capitalize name))))
(:invert
(cond
((notany #'both-case-p name) name)
((notany #'upper-case-p name) (string-upcase name))
((notany #'lower-case-p name) (string-downcase name))
(t name)))
(t name))
stream))))
(defun print-symbol-escaping (symbol stream)
(let* ((name (symbol-name symbol))
(accessible-p (eq symbol (find-symbol name))))
(cond
(accessible-p nil)
((symbol-package symbol)
(let ((package-name (package-name (symbol-package symbol))))
(unless (string= package-name "KEYWORD")
(print-name-escaping package-name stream))
(multiple-value-bind (symbol status) (find-symbol name package-name)
(declare (ignore symbol))
(write-string (if (eq status :external) ":" "::") stream))))
((or *print-readably* *print-gensym*) (write-string "#:" stream))
(t nil))
(print-name-escaping
name stream
:force-escaping (and accessible-p
(every #'(lambda (c) (digit-char-p c *print-base*))
name)))
symbol))
(defmethod print-object ((symbol symbol) stream)
(funcall (if (printer-escaping-enabled-p)
#'print-symbol-escaping
#'print-symbol-as-is)
symbol
stream))
(defvar *shared-object-table* (make-hash-table))
(defvar *shared-object-label* (make-hash-table))
(defvar *shared-object-label-counter* 0)
(defvar *current-print-level* 0)
(defun print-max-level-p ()
(and (not *print-readably*)
*print-level*
(= *current-print-level* *print-level*)))
(defun print-max-length-p (n)
(and (not *print-readably*) *print-length* (= n *print-length*)))
(defun inc-shared-object-reference (object)
(if (and (symbolp object) (symbol-package object))
0
(multiple-value-bind (n present-p) (gethash object *shared-object-table*)
(if present-p
(progn (when (zerop n)
(setf (gethash object *shared-object-label*)
(incf *shared-object-label-counter*)))
(incf (gethash object *shared-object-table*)))
(setf (gethash object *shared-object-table*) 0)))))
(defmethod search-shared-object :around ((object t))
(if (zerop *current-print-level*)
(progn (setq *shared-object-label* (clrhash *shared-object-label*)
*shared-object-table* (clrhash *shared-object-table*)
*shared-object-label-counter* 0)
(inc-shared-object-reference object)
(call-next-method object)
(maphash #'(lambda (object n)
(if (zerop n)
(remhash object *shared-object-table*)
(setf (gethash object *shared-object-table*)
0)))
*shared-object-table*))
(when (zerop (inc-shared-object-reference object))
(call-next-method object))))
(defun search-shared-element (object)
(let ((*current-print-level* (1+ *current-print-level*)))
(unless (print-max-level-p) (search-shared-object object))))
(defmethod search-shared-object ((object t))) (defmethod search-shared-object ((list list))
(do ((x list)
(l 0 (1+ l)))
((or (print-max-level-p) (print-max-length-p l) (atom x)))
(search-shared-element (car x))
(setq x (cdr x))
(when (plusp (inc-shared-object-reference x))
(return))))
(defmethod search-shared-object ((vector vector))
(do ((i 0 (1+ i)))
((or (= i (length vector)) (print-max-level-p) (print-max-length-p i)))
(search-shared-element (aref vector i))))
(defmethod search-shared-object ((array array))
(do ((i 0 (1+ i)))
((or (= i (array-total-size array))
(print-max-level-p) (print-max-length-p i)))
(search-shared-element (row-major-aref array i))))
(defun print-element (object stream)
(let ((*current-print-level* (1+ *current-print-level*)))
(multiple-value-bind (n present-p) (gethash object *shared-object-table*)
(if (and present-p *print-circle*)
(if (zerop n)
(progn
(print-label object stream)
(print-object object stream))
(print-reference object stream))
(print-object object stream)))))
(defun print-label (object stream)
(multiple-value-bind (n present-p) (gethash object *shared-object-label*)
(assert present-p)
(write-string "#" stream)
(let ((*print-base* 10) (*print-radix* nil)) (print-integer n stream))
(write-string "=" stream)
(incf (gethash object *shared-object-table*))))
(defun print-reference (object stream)
(multiple-value-bind (n present-p) (gethash object *shared-object-label*)
(assert present-p)
(write-string "#" stream)
(let ((*print-base* 10) (*print-radix* nil)) (print-integer n stream))
(write-string "#" stream)))
(defmethod print-object ((list cons) stream)
(when (and *print-circle* (zerop *current-print-level*))
(search-shared-object list))
(if (print-max-level-p)
(write-string "#" stream)
(let ((x list)
(l 0))
(multiple-value-bind (n present-p) (gethash x *shared-object-table*)
(when (and (zerop *current-print-level*) present-p *print-circle*)
(print-label x stream))
(write-string "(" stream)
(loop (when (atom x)
(when x
(write-string " . " stream)
(print-element x stream))
(write-string ")" stream)
(return))
(when (print-max-length-p l)
(write-string "...)" stream)
(return))
(print-element (car x) stream)
(setq x (cdr x)
l (1+ l))
(when (consp x)
(write-string " " stream)
(multiple-value-setq (n present-p)
(gethash x *shared-object-table*))
(when (and present-p *print-circle*)
(write-string ". " stream)
(if (zerop n)
(print-element x stream)
(print-reference x stream))
(write-string ")" stream)
(return))))))))
(defmethod print-object :around ((array array) stream)
(cond
((and (not *print-readably*) (not *print-array*) (not (stringp array)))
(print-unreadable-object (array stream :type t :identity t)))
((and (print-max-level-p) (not (stringp array)) (not (bit-vector-p array)))
(write-string "#" stream))
(t (when (and *print-circle* (zerop *current-print-level*)
(not (stringp array)) (not (bit-vector-p array)))
(search-shared-object array)
(multiple-value-bind (n present-p)
(gethash array *shared-object-table*)
(declare (ignore n))
(when present-p (print-label array stream))))
(call-next-method array stream))))
(defmethod print-object ((vector vector) stream)
(let ((l 0)
(length (length vector)))
(write-string "#(" stream)
(loop (when (= l length)
(write-string ")" stream)
(return))
(when (print-max-length-p l)
(write-string "...)" stream)
(return))
(print-element (aref vector l) stream)
(setq l (1+ l))
(when (< l length) (write-string " " stream)))))
(defmethod print-object ((array array) stream)
(let* ((dimensions (array-dimensions array))
(indices (make-list (array-rank array) :initial-element 0)))
(labels
((p-array (i-list d-list)
(cond
((print-max-level-p) (write-string "#" stream))
((null i-list) (print-element (apply #'aref array indices) stream))
(t (write-string "(" stream)
(do ((i 0 (1+ i)))
((= i (car d-list)))
(when (plusp i) (write-string " " stream))
(when (print-max-length-p i)
(write-string "..." stream)
(return))
(setf (car i-list) i)
(if (null (cdr i-list))
(print-element (apply #'aref array indices) stream)
(let ((*current-print-level* (1+ *current-print-level*)))
(p-array (cdr i-list) (cdr d-list)))))
(write-string ")" stream)))))
(write-string "#" stream)
(let ((*print-base* 10) (*print-radix* nil))
(print-integer (array-rank array) stream))
(write-string "A" stream)
(p-array indices dimensions))))
(defmethod print-object ((string string) stream)
(let ((escape-p (printer-escaping-enabled-p)))
(when escape-p (write-char #\" stream))
(loop for c across string
if (and escape-p (member c '(#\" #\\))) do (write-char #\\ stream)
do (write-char c stream))
(when escape-p (write-char #\" stream))
string))
(defmethod print-object ((bit-vector bit-vector) stream)
(if (or *print-array* *print-readably*)
(progn
(write-string "#*" stream)
(loop for bit across bit-vector
do (write-char (if (zerop bit) #\0 #\1) stream)))
(print-unreadable-object (bit-vector stream :type t :identity t)))
bit-vector)
(defmethod print-object ((object t) stream)
(print-unreadable-object (object stream :type t :identity t)))
(defun print-object-prettily (object stream)
(print-object object stream))