1 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; This software is derived from the CMU CL system, which was
5 ;;;; written at Carnegie Mellon University and released into the
6 ;;;; public domain. The software is in the public domain and is
7 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
8 ;;;; files for more information.
10 (in-package "SB!FORMAT")
12 (define-condition format-error (error)
13 ((complaint :reader format-error-complaint :initarg :complaint)
14 (arguments :reader format-error-arguments :initarg :arguments :initform nil)
15 (control-string :reader format-error-control-string
16 :initarg :control-string
17 :initform *default-format-error-control-string*)
18 (offset :reader format-error-offset :initarg :offset
19 :initform *default-format-error-offset*)
20 (print-banner :reader format-error-print-banner :initarg :print-banner
22 (:report %print-format-error))
24 (defun %print-format-error (condition stream)
26 "~:[~;error in format: ~]~
28 (format-error-print-banner condition)
29 (format-error-complaint condition)
30 (format-error-arguments condition)
31 (format-error-control-string condition)
32 (format-error-offset condition)))
34 (def!struct format-directive
35 (string (required-argument) :type simple-string)
36 (start (required-argument) :type (and unsigned-byte fixnum))
37 (end (required-argument) :type (and unsigned-byte fixnum))
38 (character (required-argument) :type base-char)
39 (colonp nil :type (member t nil))
40 (atsignp nil :type (member t nil))
41 (params nil :type list))
42 (def!method print-object ((x format-directive) stream)
43 (print-unreadable-object (x stream)
44 (write-string (format-directive-string x)
46 :start (format-directive-start x)
47 :end (format-directive-end x))))
49 ;;;; TOKENIZE-CONTROL-STRING
51 (defun tokenize-control-string (string)
52 (declare (simple-string string))
57 (let ((next-directive (or (position #\~ string :start index) end)))
58 (when (> next-directive index)
59 (push (subseq string index next-directive) result))
60 (when (= next-directive end)
62 (let ((directive (parse-directive string next-directive)))
63 (push directive result)
64 (setf index (format-directive-end directive)))))
67 (defun parse-directive (string start)
68 (let ((posn (1+ start)) (params nil) (colonp nil) (atsignp nil)
69 (end (length string)))
73 :complaint "String ended before directive was found."
74 :control-string string
76 (schar string posn))))
78 (let ((char (get-char)))
79 (cond ((or (char<= #\0 char #\9) (char= char #\+) (char= char #\-))
80 (multiple-value-bind (param new-posn)
81 (parse-integer string :start posn :junk-allowed t)
82 (push (cons posn param) params)
90 ((or (char= char #\v) (char= char #\V))
91 (push (cons posn :arg) params)
100 (push (cons posn :remaining) params)
110 (push (cons posn (get-char)) params)
112 (unless (char= (get-char) #\,)
115 (push (cons posn nil) params))
119 :complaint "too many colons supplied"
120 :control-string string
126 :complaint "too many #\\@ characters supplied"
127 :control-string string
131 (when (char= (schar string (1- posn)) #\,)
132 (push (cons (1- posn) nil) params))
135 (let ((char (get-char)))
136 (when (char= char #\/)
137 (let ((closing-slash (position #\/ string :start (1+ posn))))
139 (setf posn closing-slash)
141 :complaint "no matching closing slash"
142 :control-string string
144 (make-format-directive
145 :string string :start start :end (1+ posn)
146 :character (char-upcase char)
147 :colonp colonp :atsignp atsignp
148 :params (nreverse params))))))
152 (sb!xc:defmacro formatter (control-string)
153 `#',(%formatter control-string))
155 (defun %formatter (control-string)
157 (catch 'need-orig-args
158 (let* ((*simple-args* nil)
159 (*only-simple-args* t)
160 (guts (expand-control-string control-string))
162 (dolist (arg *simple-args*)
166 :complaint "required argument missing"
167 :control-string ,control-string
170 (return `(lambda (stream &optional ,@args &rest args)
173 (let ((*orig-args-available* t)
174 (*only-simple-args* nil))
175 `(lambda (stream &rest orig-args)
176 (let ((args orig-args))
177 ,(expand-control-string control-string)
180 (defun expand-control-string (string)
181 (let* ((string (etypecase string
185 (coerce string 'simple-string))))
186 (*default-format-error-control-string* string)
187 (directives (tokenize-control-string string)))
189 ,@(expand-directive-list directives))))
191 (defun expand-directive-list (directives)
193 (remaining-directives directives))
195 (unless remaining-directives
197 (multiple-value-bind (form new-directives)
198 (expand-directive (car remaining-directives)
199 (cdr remaining-directives))
201 (setf remaining-directives new-directives)))
204 (defun expand-directive (directive more-directives)
208 (aref *format-directive-expanders*
209 (char-code (format-directive-character directive))))
210 (*default-format-error-offset*
211 (1- (format-directive-end directive))))
213 (funcall expander directive more-directives)
215 :complaint "unknown directive"))))
217 (values `(write-string ,directive stream)
220 (defmacro-mundanely expander-next-arg (string offset)
224 :complaint "no more arguments"
225 :control-string ,string
228 (defun expand-next-arg (&optional offset)
229 (if (or *orig-args-available* (not *only-simple-args*))
230 `(,*expander-next-arg-macro*
231 ,*default-format-error-control-string*
232 ,(or offset *default-format-error-offset*))
233 (let ((symbol (gensym "FORMAT-ARG-")))
234 (push (cons symbol (or offset *default-format-error-offset*))
238 (defmacro expand-bind-defaults (specs params &body body)
239 (once-only ((params params))
241 (collect ((expander-bindings) (runtime-bindings))
243 (destructuring-bind (var default) spec
244 (let ((symbol (gensym)))
249 (let* ((param-and-offset (pop ,params))
250 (offset (car param-and-offset))
251 (param (cdr param-and-offset)))
253 (:arg `(or ,(expand-next-arg offset)
256 (setf *only-simple-args* nil)
260 `(let ,(expander-bindings)
261 `(let ,(list ,@(runtime-bindings))
266 "too many parameters, expected no more than ~D"
267 :arguments (list ,(length specs))
268 :offset (caar ,params)))
273 :complaint "too many parameters, expected none"
274 :offset (caar ,params)))
277 ;;;; format directive machinery
279 ;;; FIXME: only used in this file, could be SB!XC:DEFMACRO in EVAL-WHEN
280 (defmacro def-complex-format-directive (char lambda-list &body body)
281 (let ((defun-name (intern (format nil
282 "~:@(~:C~)-FORMAT-DIRECTIVE-EXPANDER"
285 (directives (if lambda-list (car (last lambda-list)) (gensym))))
287 (defun ,defun-name (,directive ,directives)
289 `((let ,(mapcar (lambda (var)
291 (,(symbolicate "FORMAT-DIRECTIVE-" var)
293 (butlast lambda-list))
295 `((declare (ignore ,directive ,directives))
297 (%set-format-directive-expander ,char #',defun-name))))
299 ;;; FIXME: only used in this file, could be SB!XC:DEFMACRO in EVAL-WHEN
300 (defmacro def-format-directive (char lambda-list &body body)
301 (let ((directives (gensym))
303 (body-without-decls body))
305 (let ((form (car body-without-decls)))
306 (unless (and (consp form) (eq (car form) 'declare))
308 (push (pop body-without-decls) declarations)))
309 (setf declarations (reverse declarations))
310 `(def-complex-format-directive ,char (,@lambda-list ,directives)
312 (values (progn ,@body-without-decls)
315 (eval-when (:compile-toplevel :load-toplevel :execute)
317 (defun %set-format-directive-expander (char fn)
318 (setf (aref *format-directive-expanders* (char-code (char-upcase char))) fn)
321 (defun %set-format-directive-interpreter (char fn)
322 (setf (aref *format-directive-interpreters*
323 (char-code (char-upcase char)))
327 (defun find-directive (directives kind stop-at-semi)
329 (let ((next (car directives)))
330 (if (format-directive-p next)
331 (let ((char (format-directive-character next)))
332 (if (or (char= kind char)
333 (and stop-at-semi (char= char #\;)))
336 (cdr (flet ((after (char)
337 (member (find-directive (cdr directives)
348 (find-directive (cdr directives) kind stop-at-semi)))))
352 ;;;; format directives for simple output
354 (def-format-directive #\A (colonp atsignp params)
356 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
359 `(format-princ stream ,(expand-next-arg) ',colonp ',atsignp
360 ,mincol ,colinc ,minpad ,padchar))
362 `(or ,(expand-next-arg) "()")
366 (def-format-directive #\S (colonp atsignp params)
368 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
371 `(format-prin1 stream ,(expand-next-arg) ,colonp ,atsignp
372 ,mincol ,colinc ,minpad ,padchar)))
374 `(let ((arg ,(expand-next-arg)))
377 (princ "()" stream))))
379 `(prin1 ,(expand-next-arg) stream))))
381 (def-format-directive #\C (colonp atsignp params)
382 (expand-bind-defaults () params
384 `(format-print-named-character ,(expand-next-arg) stream)
386 `(prin1 ,(expand-next-arg) stream)
387 `(write-char ,(expand-next-arg) stream)))))
389 (def-format-directive #\W (colonp atsignp params)
390 (expand-bind-defaults () params
391 (if (or colonp atsignp)
392 `(let (,@(when colonp
393 '((*print-pretty* t)))
395 '((*print-level* nil)
396 (*print-length* nil))))
397 (output-object ,(expand-next-arg) stream))
398 `(output-object ,(expand-next-arg) stream))))
400 ;;;; format directives for integer output
402 (defun expand-format-integer (base colonp atsignp params)
403 (if (or colonp atsignp params)
404 (expand-bind-defaults
405 ((mincol 0) (padchar #\space) (commachar #\,) (commainterval 3))
407 `(format-print-integer stream ,(expand-next-arg) ,colonp ,atsignp
408 ,base ,mincol ,padchar ,commachar
410 `(write ,(expand-next-arg) :stream stream :base ,base :radix nil
413 (def-format-directive #\D (colonp atsignp params)
414 (expand-format-integer 10 colonp atsignp params))
416 (def-format-directive #\B (colonp atsignp params)
417 (expand-format-integer 2 colonp atsignp params))
419 (def-format-directive #\O (colonp atsignp params)
420 (expand-format-integer 8 colonp atsignp params))
422 (def-format-directive #\X (colonp atsignp params)
423 (expand-format-integer 16 colonp atsignp params))
425 (def-format-directive #\R (colonp atsignp params)
427 (expand-bind-defaults
428 ((base 10) (mincol 0) (padchar #\space) (commachar #\,)
431 `(format-print-integer stream ,(expand-next-arg) ,colonp ,atsignp
433 ,padchar ,commachar ,commainterval))
436 `(format-print-old-roman stream ,(expand-next-arg))
437 `(format-print-roman stream ,(expand-next-arg)))
439 `(format-print-ordinal stream ,(expand-next-arg))
440 `(format-print-cardinal stream ,(expand-next-arg))))))
442 ;;;; format directive for pluralization
444 (def-format-directive #\P (colonp atsignp params end)
445 (expand-bind-defaults () params
449 (*orig-args-available*
450 `(if (eq orig-args args)
452 :complaint "no previous argument"
454 (do ((arg-ptr orig-args (cdr arg-ptr)))
455 ((eq (cdr arg-ptr) args)
458 (unless *simple-args*
460 :complaint "no previous argument"))
461 (caar *simple-args*))
463 (throw 'need-orig-args nil)))))
465 `(write-string (if (eql ,arg 1) "y" "ies") stream)
466 `(unless (eql ,arg 1) (write-char #\s stream))))))
468 ;;;; format directives for floating point output
470 (def-format-directive #\F (colonp atsignp params)
474 "The colon modifier cannot be used with this directive."))
475 (expand-bind-defaults ((w nil) (d nil) (k nil) (ovf nil) (pad #\space)) params
476 `(format-fixed stream ,(expand-next-arg) ,w ,d ,k ,ovf ,pad ,atsignp)))
478 (def-format-directive #\E (colonp atsignp params)
482 "The colon modifier cannot be used with this directive."))
483 (expand-bind-defaults
484 ((w nil) (d nil) (e nil) (k 1) (ovf nil) (pad #\space) (mark nil))
486 `(format-exponential stream ,(expand-next-arg) ,w ,d ,e ,k ,ovf ,pad ,mark
489 (def-format-directive #\G (colonp atsignp params)
493 "The colon modifier cannot be used with this directive."))
494 (expand-bind-defaults
495 ((w nil) (d nil) (e nil) (k nil) (ovf nil) (pad #\space) (mark nil))
497 `(format-general stream ,(expand-next-arg) ,w ,d ,e ,k ,ovf ,pad ,mark ,atsignp)))
499 (def-format-directive #\$ (colonp atsignp params)
500 (expand-bind-defaults ((d 2) (n 1) (w 0) (pad #\space)) params
501 `(format-dollars stream ,(expand-next-arg) ,d ,n ,w ,pad ,colonp
504 ;;;; format directives for line/page breaks etc.
506 (def-format-directive #\% (colonp atsignp params)
507 (when (or colonp atsignp)
510 "The colon and atsign modifiers cannot be used with this directive."
513 (expand-bind-defaults ((count 1)) params
518 (def-format-directive #\& (colonp atsignp params)
519 (when (or colonp atsignp)
522 "The colon and atsign modifiers cannot be used with this directive."
525 (expand-bind-defaults ((count 1)) params
528 (dotimes (i (1- ,count))
530 '(fresh-line stream)))
532 (def-format-directive #\| (colonp atsignp params)
533 (when (or colonp atsignp)
536 "The colon and atsign modifiers cannot be used with this directive."
539 (expand-bind-defaults ((count 1)) params
541 (write-char (code-char form-feed-char-code) stream)))
542 '(write-char (code-char form-feed-char-code) stream)))
544 (def-format-directive #\~ (colonp atsignp params)
545 (when (or colonp atsignp)
548 "The colon and atsign modifiers cannot be used with this directive."
551 (expand-bind-defaults ((count 1)) params
553 (write-char #\~ stream)))
554 '(write-char #\~ stream)))
556 (def-complex-format-directive #\newline (colonp atsignp params directives)
557 (when (and colonp atsignp)
559 :complaint "both colon and atsign modifiers used simultaneously"))
560 (values (expand-bind-defaults () params
562 '(write-char #\newline stream)
564 (if (and (not colonp)
566 (simple-string-p (car directives)))
567 (cons (string-left-trim *format-whitespace-chars*
572 ;;;; format directives for tabs and simple pretty printing
574 (def-format-directive #\T (colonp atsignp params)
576 (expand-bind-defaults ((n 1) (m 1)) params
577 `(pprint-tab ,(if atsignp :section-relative :section)
580 (expand-bind-defaults ((colrel 1) (colinc 1)) params
581 `(format-relative-tab stream ,colrel ,colinc))
582 (expand-bind-defaults ((colnum 1) (colinc 1)) params
583 `(format-absolute-tab stream ,colnum ,colinc)))))
585 (def-format-directive #\_ (colonp atsignp params)
586 (expand-bind-defaults () params
587 `(pprint-newline ,(if colonp
596 (def-format-directive #\I (colonp atsignp params)
600 "cannot use the at-sign modifier with this directive"))
601 (expand-bind-defaults ((n 0)) params
602 `(pprint-indent ,(if colonp :current :block) ,n stream)))
604 ;;;; format directive for ~*
606 (def-format-directive #\* (colonp atsignp params end)
611 "both colon and atsign modifiers used simultaneously")
612 (expand-bind-defaults ((posn 0)) params
613 (unless *orig-args-available*
614 (throw 'need-orig-args nil))
615 `(if (<= 0 ,posn (length orig-args))
616 (setf args (nthcdr ,posn orig-args))
618 :complaint "Index ~D out of bounds. Should have been ~
620 :arguments (list ,posn (length orig-args))
621 :offset ,(1- end)))))
623 (expand-bind-defaults ((n 1)) params
624 (unless *orig-args-available*
625 (throw 'need-orig-args nil))
626 `(do ((cur-posn 0 (1+ cur-posn))
627 (arg-ptr orig-args (cdr arg-ptr)))
629 (let ((new-posn (- cur-posn ,n)))
630 (if (<= 0 new-posn (length orig-args))
631 (setf args (nthcdr new-posn orig-args))
634 "Index ~D is out of bounds; should have been ~
637 (list new-posn (length orig-args))
638 :offset ,(1- end)))))))
640 (expand-bind-defaults ((n 1)) params
641 (setf *only-simple-args* nil)
644 (expand-next-arg)))))
646 ;;;; format directive for indirection
648 (def-format-directive #\? (colonp atsignp params string end)
651 :complaint "cannot use the colon modifier with this directive"))
652 (expand-bind-defaults () params
655 #'(lambda (condition)
658 "~A~%while processing indirect format string:"
659 :arguments (list condition)
661 :control-string ,string
662 :offset ,(1- end)))))
664 (if *orig-args-available*
665 `(setf args (%format stream ,(expand-next-arg) orig-args args))
666 (throw 'need-orig-args nil))
667 `(%format stream ,(expand-next-arg) ,(expand-next-arg))))))
669 ;;;; format directives for capitalization
671 (def-complex-format-directive #\( (colonp atsignp params directives)
672 (let ((close (find-directive directives #\) nil)))
675 :complaint "no corresponding close parenthesis"))
676 (let* ((posn (position close directives))
677 (before (subseq directives 0 posn))
678 (after (nthcdr (1+ posn) directives)))
680 (expand-bind-defaults () params
681 `(let ((stream (make-case-frob-stream stream
689 ,@(expand-directive-list before)))
692 (def-complex-format-directive #\) ()
694 :complaint "no corresponding open parenthesis"))
696 ;;;; format directives and support functions for conditionalization
698 (def-complex-format-directive #\[ (colonp atsignp params directives)
699 (multiple-value-bind (sublists last-semi-with-colon-p remaining)
700 (parse-conditional-directive directives)
706 "both colon and atsign modifiers used simultaneously")
710 "Can only specify one section")
711 (expand-bind-defaults () params
712 (expand-maybe-conditional (car sublists)))))
714 (if (= (length sublists) 2)
715 (expand-bind-defaults () params
716 (expand-true-false-conditional (car sublists)
720 "must specify exactly two sections"))
721 (expand-bind-defaults ((index (expand-next-arg))) params
722 (setf *only-simple-args* nil)
724 (when last-semi-with-colon-p
725 (push `(t ,@(expand-directive-list (pop sublists)))
727 (let ((count (length sublists)))
728 (dolist (sublist sublists)
729 (push `(,(decf count)
730 ,@(expand-directive-list sublist))
732 `(case ,index ,@clauses)))))
735 (defun parse-conditional-directive (directives)
737 (last-semi-with-colon-p nil)
738 (remaining directives))
740 (let ((close-or-semi (find-directive remaining #\] t)))
741 (unless close-or-semi
743 :complaint "no corresponding close bracket"))
744 (let ((posn (position close-or-semi remaining)))
745 (push (subseq remaining 0 posn) sublists)
746 (setf remaining (nthcdr (1+ posn) remaining))
747 (when (char= (format-directive-character close-or-semi) #\])
749 (setf last-semi-with-colon-p
750 (format-directive-colonp close-or-semi)))))
751 (values sublists last-semi-with-colon-p remaining)))
753 (defun expand-maybe-conditional (sublist)
755 `(let ((prev-args args)
756 (arg ,(expand-next-arg)))
758 (setf args prev-args)
759 ,@(expand-directive-list sublist)))))
760 (if *only-simple-args*
761 (multiple-value-bind (guts new-args)
762 (let ((*simple-args* *simple-args*))
763 (values (expand-directive-list sublist)
765 (cond ((eq *simple-args* (cdr new-args))
766 (setf *simple-args* new-args)
767 `(when ,(caar new-args)
770 (setf *only-simple-args* nil)
774 (defun expand-true-false-conditional (true false)
775 (let ((arg (expand-next-arg)))
779 ,@(expand-directive-list true))
781 ,@(expand-directive-list false)))))
782 (if *only-simple-args*
783 (multiple-value-bind (true-guts true-args true-simple)
784 (let ((*simple-args* *simple-args*)
785 (*only-simple-args* t))
786 (values (expand-directive-list true)
789 (multiple-value-bind (false-guts false-args false-simple)
790 (let ((*simple-args* *simple-args*)
791 (*only-simple-args* t))
792 (values (expand-directive-list false)
795 (if (= (length true-args) (length false-args))
799 ,(do ((false false-args (cdr false))
800 (true true-args (cdr true))
801 (bindings nil (cons `(,(caar false) ,(caar true))
803 ((eq true *simple-args*)
804 (setf *simple-args* true-args)
805 (setf *only-simple-args*
806 (and true-simple false-simple))
813 (setf *only-simple-args* nil)
817 (def-complex-format-directive #\; ()
820 "~~; directive not contained within either ~~[...~~] or ~~<...~~>"))
822 (def-complex-format-directive #\] ()
825 "no corresponding open bracket"))
827 ;;;; format directive for up-and-out
829 (def-format-directive #\^ (colonp atsignp params)
832 :complaint "cannot use the at-sign modifier with this directive"))
833 (when (and colonp (not *up-up-and-out-allowed*))
835 :complaint "attempt to use ~~:^ outside a ~~:{...~~} construct"))
836 `(when ,(case (length params)
840 (setf *only-simple-args* nil)
842 (1 (expand-bind-defaults ((count 0)) params
844 (2 (expand-bind-defaults ((arg1 0) (arg2 0)) params
846 (t (expand-bind-defaults ((arg1 0) (arg2 0) (arg3 0)) params
847 `(<= ,arg1 ,arg2 ,arg3))))
849 '(return-from outside-loop nil)
852 ;;;; format directives for iteration
854 (def-complex-format-directive #\{ (colonp atsignp params string end directives)
855 (let ((close (find-directive directives #\} nil)))
858 :complaint "no corresponding close brace"))
859 (let* ((closed-with-colon (format-directive-colonp close))
860 (posn (position close directives)))
864 (if *orig-args-available*
867 #'(lambda (condition)
870 "~A~%while processing indirect format string:"
871 :arguments (list condition)
873 :control-string ,string
874 :offset ,(1- end)))))
876 (%format stream inside-string orig-args args))))
877 (throw 'need-orig-args nil))
878 (let ((*up-up-and-out-allowed* colonp))
879 (expand-directive-list (subseq directives 0 posn)))))
880 (compute-loop-aux (count)
882 (setf *only-simple-args* nil))
884 ,@(unless closed-with-colon
888 `((when (and ,count (minusp (decf ,count)))
891 (let ((*expander-next-arg-macro* 'expander-next-arg)
892 (*only-simple-args* nil)
893 (*orig-args-available* t))
894 `((let* ((orig-args ,(expand-next-arg))
897 (declare (ignorable orig-args outside-args args))
899 ,@(compute-insides)))))
901 ,@(when closed-with-colon
906 (expand-bind-defaults ((count nil)) params
907 (compute-loop-aux count))
908 (compute-loop-aux nil)))
917 `(let* ((orig-args ,(expand-next-arg))
919 (declare (ignorable orig-args args))
920 ,(let ((*expander-next-arg-macro* 'expander-next-arg)
921 (*only-simple-args* nil)
922 (*orig-args-available* t))
924 (values (if (zerop posn)
925 `(let ((inside-string ,(expand-next-arg)))
928 (nthcdr (1+ posn) directives))))))
930 (def-complex-format-directive #\} ()
932 :complaint "no corresponding open brace"))
934 ;;;; format directives and support functions for justification
936 (def-complex-format-directive #\< (colonp atsignp params string end directives)
937 (multiple-value-bind (segments first-semi close remaining)
938 (parse-format-justification directives)
940 (if (format-directive-colonp close)
941 (multiple-value-bind (prefix per-line-p insides suffix)
942 (parse-format-logical-block segments colonp first-semi
943 close params string end)
944 (expand-format-logical-block prefix per-line-p insides
946 (expand-format-justification segments colonp atsignp
950 (def-complex-format-directive #\> ()
952 :complaint "no corresponding open bracket"))
954 (defun parse-format-logical-block
955 (segments colonp first-semi close params string end)
958 :complaint "No parameters can be supplied with ~~<...~~:>."
959 :offset (caar params)))
960 (multiple-value-bind (prefix insides suffix)
961 (multiple-value-bind (prefix-default suffix-default)
962 (if colonp (values "(" ")") (values nil ""))
963 (flet ((extract-string (list prefix-p)
964 (let ((directive (find-if #'format-directive-p list)))
968 "cannot include format directives inside the ~
969 ~:[suffix~;prefix~] segment of ~~<...~~:>"
970 :arguments (list prefix-p)
971 :offset (1- (format-directive-end directive)))
972 (apply #'concatenate 'string list)))))
973 (case (length segments)
974 (0 (values prefix-default nil suffix-default))
975 (1 (values prefix-default (car segments) suffix-default))
976 (2 (values (extract-string (car segments) t)
977 (cadr segments) suffix-default))
978 (3 (values (extract-string (car segments) t)
980 (extract-string (caddr segments) nil)))
983 :complaint "too many segments for ~~<...~~:>")))))
984 (when (format-directive-atsignp close)
986 (add-fill-style-newlines insides
989 (format-directive-end first-semi)
992 (and first-semi (format-directive-atsignp first-semi))
996 (defun add-fill-style-newlines (list string offset)
998 (let ((directive (car list)))
999 (if (simple-string-p directive)
1000 (nconc (add-fill-style-newlines-aux directive string offset)
1001 (add-fill-style-newlines (cdr list)
1003 (+ offset (length directive))))
1005 (add-fill-style-newlines (cdr list)
1007 (format-directive-end directive)))))
1010 (defun add-fill-style-newlines-aux (literal string offset)
1011 (let ((end (length literal))
1013 (collect ((results))
1015 (let ((blank (position #\space literal :start posn)))
1017 (results (subseq literal posn))
1019 (let ((non-blank (or (position #\space literal :start blank
1022 (results (subseq literal posn non-blank))
1023 (results (make-format-directive
1024 :string string :character #\_
1025 :start (+ offset non-blank) :end (+ offset non-blank)
1026 :colonp t :atsignp nil :params nil))
1027 (setf posn non-blank))
1032 (defun parse-format-justification (directives)
1033 (let ((first-semi nil)
1035 (remaining directives))
1036 (collect ((segments))
1038 (let ((close-or-semi (find-directive remaining #\> t)))
1039 (unless close-or-semi
1040 (error 'format-error
1041 :complaint "no corresponding close bracket"))
1042 (let ((posn (position close-or-semi remaining)))
1043 (segments (subseq remaining 0 posn))
1044 (setf remaining (nthcdr (1+ posn) remaining)))
1045 (when (char= (format-directive-character close-or-semi)
1047 (setf close close-or-semi)
1050 (setf first-semi close-or-semi))))
1051 (values (segments) first-semi close remaining))))
1053 (sb!xc:defmacro expander-pprint-next-arg (string offset)
1056 (error 'format-error
1057 :complaint "no more arguments"
1058 :control-string ,string
1063 (defun expand-format-logical-block (prefix per-line-p insides suffix atsignp)
1064 `(let ((arg ,(if atsignp 'args (expand-next-arg))))
1066 (setf *only-simple-args* nil)
1068 (pprint-logical-block
1070 ,(if per-line-p :per-line-prefix :prefix) ,prefix
1074 `((orig-args arg))))
1075 (declare (ignorable args ,@(unless atsignp '(orig-args))))
1077 ,@(let ((*expander-next-arg-macro* 'expander-pprint-next-arg)
1078 (*only-simple-args* nil)
1079 (*orig-args-available* t))
1080 (expand-directive-list insides)))))))
1082 (defun expand-format-justification (segments colonp atsignp first-semi params)
1083 (let ((newline-segment-p
1085 (format-directive-colonp first-semi))))
1086 (expand-bind-defaults
1087 ((mincol 0) (colinc 1) (minpad 0) (padchar #\space))
1089 `(let ((segments nil)
1090 ,@(when newline-segment-p
1091 '((newline-segment nil)
1095 ,@(when newline-segment-p
1096 `((setf newline-segment
1097 (with-output-to-string (stream)
1098 ,@(expand-directive-list (pop segments))))
1099 ,(expand-bind-defaults
1101 (line-len '(or (sb!impl::line-length stream) 72)))
1102 (format-directive-params first-semi)
1103 `(setf extra-space ,extra line-len ,line-len))))
1104 ,@(mapcar #'(lambda (segment)
1105 `(push (with-output-to-string (stream)
1106 ,@(expand-directive-list segment))
1109 (format-justification stream
1110 ,@(if newline-segment-p
1111 '(newline-segment extra-space line-len)
1113 segments ,colonp ,atsignp
1114 ,mincol ,colinc ,minpad ,padchar)))))
1116 ;;;; format directive and support function for user-defined method
1118 (def-format-directive #\/ (string start end colonp atsignp params)
1119 (let ((symbol (extract-user-function-name string start end)))
1120 (collect ((param-names) (bindings))
1121 (dolist (param-and-offset params)
1122 (let ((param (cdr param-and-offset)))
1123 (let ((param-name (gensym)))
1124 (param-names param-name)
1125 (bindings `(,param-name
1127 (:arg (expand-next-arg))
1128 (:remaining '(length args))
1131 (,symbol stream ,(expand-next-arg) ,colonp ,atsignp
1132 ,@(param-names))))))
1134 (defun extract-user-function-name (string start end)
1135 (let ((slash (position #\/ string :start start :end (1- end)
1138 (error 'format-error
1139 :complaint "malformed ~~/ directive"))
1140 (let* ((name (string-upcase (let ((foo string))
1141 ;; Hack alert: This is to keep the compiler
1142 ;; quiet about deleting code inside the
1143 ;; subseq expansion.
1144 (subseq foo (1+ slash) (1- end)))))
1145 (first-colon (position #\: name))
1146 (last-colon (if first-colon (position #\: name :from-end t)))
1147 (package-name (if last-colon
1148 (subseq name 0 first-colon)
1149 "COMMON-LISP-USER"))
1150 (package (find-package package-name)))
1152 ;; FIXME: should be PACKAGE-ERROR? Could we just use
1153 ;; FIND-UNDELETED-PACKAGE-OR-LOSE?
1154 (error 'format-error
1155 :complaint "no package named ~S"
1156 :arguments (list package-name)))
1157 (intern (if first-colon
1158 (subseq name (1+ first-colon))