;;;; pretty-printing of backquote expansions ;;;; This software is part of the SBCL system. See the README file for ;;;; more information. ;;;; ;;;; This software is derived from the CMU CL system, which was ;;;; written at Carnegie Mellon University and released into the ;;;; public domain. The software is in the public domain and is ;;;; provided with absolutely no warranty. See the COPYING and CREDITS ;;;; files for more information. (in-package "SB!IMPL") (defun backq-unparse-expr (form splicing) (ecase splicing ((nil) `(backq-comma ,form)) ((t) `((backq-comma-at ,form))) (:nconc `((backq-comma-dot ,form))) )) (defun backq-unparse (form &optional splicing) #!+sb-doc "Given a lisp form containing the magic functions BACKQ-LIST, BACKQ-LIST*, BACKQ-APPEND, etc. produced by the backquote reader macro, will return a corresponding backquote input form. In this form, `,' `,@' and `,.' are represented by lists whose cars are BACKQ-COMMA, BACKQ-COMMA-AT, and BACKQ-COMMA-DOT respectively, and whose cadrs are the form after the comma. SPLICING indicates whether a comma-escape return should be modified for splicing with other forms: a value of T or :NCONC meaning that an extra level of parentheses should be added." (cond ((atom form) (backq-unparse-expr form splicing)) ((not (null (cdr (last form)))) ;; FIXME: this probably throws a recursive error (bug "found illegal dotted backquote form: ~S" form)) (t (case (car form) (backq-list (mapcar #'backq-unparse (cdr form))) (backq-list* (do ((tail (cdr form) (cdr tail)) (accum nil)) ((null (cdr tail)) (nconc (nreverse accum) (backq-unparse (car tail) t))) (push (backq-unparse (car tail)) accum))) (backq-append (apply #'append (mapcar (lambda (el) (backq-unparse el t)) (cdr form)))) (backq-nconc (apply #'append (mapcar (lambda (el) (backq-unparse el :nconc)) (cdr form)))) (backq-cons (cons (backq-unparse (cadr form) nil) (backq-unparse (caddr form) t))) (backq-vector (coerce (backq-unparse (cadr form)) 'vector)) (quote (cond ((atom (cadr form)) (cadr form)) ((and (consp (cadr form)) (member (caadr form) *backq-tokens*)) (backq-unparse-expr form splicing)) (t (cons (backq-unparse `(quote ,(caadr form))) (backq-unparse `(quote ,(cdadr form))))))) (t (backq-unparse-expr form splicing)))))) (defun pprint-backquote (stream form &rest noise) (declare (ignore noise)) (write-char #\` stream) (write (backq-unparse form) :stream stream)) (defun pprint-backq-comma (stream form &rest noise) (declare (ignore noise)) (ecase (car form) (backq-comma (write-char #\, stream)) (backq-comma-at (write-string ",@" stream)) (backq-comma-dot (write-string ",." stream))) ;; Ha! an example of where the per-process specials for stream ;; attributes rather than per-stream actually makes life easier. ;; Since all of the attributes are shared in the dynamic state, we ;; can do... -- CSR, 2003-09-30 ;; ;; [...] above referred to the trick of printing to a string stream, ;; and then simply printing the resulting sequence to the pretty ;; stream, possibly with a space prepended. However, this doesn't ;; work for pretty streams which need to do margin calculations. Oh ;; well. It was good while it lasted. -- CSR, 2003-12-15 ;; ;; This is an evil hack. If we print to a string and then print again, ;; the circularity detection logic behaves as though it's already ;; printed that data... and it has, to a string stream that we send ;; to the bitbucket in the sky. -- PK, 2013-03-30 (when (eql (car form) 'backq-comma) (let ((output (with-output-to-string (s) ;; Patching evil with more evil. The next step is ;; likely to stop the madness and unconditionally ;; insert a space. (let (*circularity-hash-table* *circularity-counter*) (write (cadr form) :stream s))))) (when (and (plusp (length output)) (or (char= (char output 0) #\.) (char= (char output 0) #\@))) (write-char #\Space stream)))) (write (cadr form) :stream stream)) ;;; This is called by !PPRINT-COLD-INIT, fairly late, because ;;; SET-PPRINT-DISPATCH doesn't work until the compiler works. ;;; ;;; FIXME: It might be cleaner to just make these be toplevel forms and ;;; enforce the delay by putting this file late in the build sequence. (defun !backq-pp-cold-init () (set-pprint-dispatch '(cons (eql backq-list)) #'pprint-backquote) (set-pprint-dispatch '(cons (eql backq-list*)) #'pprint-backquote) (set-pprint-dispatch '(cons (eql backq-append)) #'pprint-backquote) (set-pprint-dispatch '(cons (eql backq-nconc)) #'pprint-backquote) (set-pprint-dispatch '(cons (eql backq-cons)) #'pprint-backquote) (set-pprint-dispatch '(cons (eql backq-vector)) #'pprint-backquote) (set-pprint-dispatch '(cons (eql backq-comma)) #'pprint-backq-comma) (set-pprint-dispatch '(cons (eql backq-comma-at)) #'pprint-backq-comma) (set-pprint-dispatch '(cons (eql backq-comma-dot)) #'pprint-backq-comma))