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 (args :reader format-error-args :initarg :args :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-args condition)
31 (format-error-control-string condition)
32 (format-error-offset condition)))
34 (def!struct format-directive
35 (string (missing-arg) :type simple-string)
36 (start (missing-arg) :type (and unsigned-byte fixnum))
37 (end (missing-arg) :type (and unsigned-byte fixnum))
38 (character (missing-arg) :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
78 (when (or colonp atsignp)
80 :complaint "parameters found after #\\: or #\\@ modifier"
81 :control-string string
84 (let ((char (get-char)))
85 (cond ((or (char<= #\0 char #\9) (char= char #\+) (char= char #\-))
87 (multiple-value-bind (param new-posn)
88 (parse-integer string :start posn :junk-allowed t)
89 (push (cons posn param) params)
100 (push (cons posn :arg) params)
110 (push (cons posn :remaining) params)
121 (push (cons posn (get-char)) params)
123 (unless (char= (get-char) #\,)
127 (push (cons posn nil) params))
131 :complaint "too many colons supplied"
132 :control-string string
138 :complaint "too many #\\@ characters supplied"
139 :control-string string
143 (when (char= (schar string (1- posn)) #\,)
145 (push (cons (1- posn) nil) params))
148 (let ((char (get-char)))
149 (when (char= char #\/)
150 (let ((closing-slash (position #\/ string :start (1+ posn))))
152 (setf posn closing-slash)
154 :complaint "no matching closing slash"
155 :control-string string
157 (make-format-directive
158 :string string :start start :end (1+ posn)
159 :character (char-upcase char)
160 :colonp colonp :atsignp atsignp
161 :params (nreverse params))))))
165 (sb!xc:defmacro formatter (control-string)
166 `#',(%formatter control-string))
168 (defun %formatter (control-string)
170 (catch 'need-orig-args
171 (let* ((*simple-args* nil)
172 (*only-simple-args* t)
173 (guts (expand-control-string control-string))
175 (dolist (arg *simple-args*)
179 :complaint "required argument missing"
180 :control-string ,control-string
183 (return `(lambda (stream &optional ,@args &rest args)
186 (let ((*orig-args-available* t)
187 (*only-simple-args* nil))
188 `(lambda (stream &rest orig-args)
189 (let ((args orig-args))
190 ,(expand-control-string control-string)
193 (defun expand-control-string (string)
194 (let* ((string (etypecase string
198 (coerce string 'simple-string))))
199 (*default-format-error-control-string* string)
200 (directives (tokenize-control-string string)))
202 ,@(expand-directive-list directives))))
204 (defun expand-directive-list (directives)
206 (remaining-directives directives))
208 (unless remaining-directives
210 (multiple-value-bind (form new-directives)
211 (expand-directive (car remaining-directives)
212 (cdr remaining-directives))
214 (setf remaining-directives new-directives)))
217 (defun expand-directive (directive more-directives)
221 (aref *format-directive-expanders*
222 (char-code (format-directive-character directive))))
223 (*default-format-error-offset*
224 (1- (format-directive-end directive))))
226 (funcall expander directive more-directives)
228 :complaint "unknown directive ~@[(character: ~A)~]"
229 :args (list (char-name (format-directive-character directive)))))))
231 (values `(write-string ,directive stream)
234 (defmacro-mundanely expander-next-arg (string offset)
238 :complaint "no more arguments"
239 :control-string ,string
242 (defun expand-next-arg (&optional offset)
243 (if (or *orig-args-available* (not *only-simple-args*))
244 `(,*expander-next-arg-macro*
245 ,*default-format-error-control-string*
246 ,(or offset *default-format-error-offset*))
247 (let ((symbol (gensym "FORMAT-ARG-")))
248 (push (cons symbol (or offset *default-format-error-offset*))
252 (defmacro expand-bind-defaults (specs params &body body)
253 (once-only ((params params))
255 (collect ((expander-bindings) (runtime-bindings))
257 (destructuring-bind (var default) spec
258 (let ((symbol (gensym)))
263 (let* ((param-and-offset (pop ,params))
264 (offset (car param-and-offset))
265 (param (cdr param-and-offset)))
267 (:arg `(or ,(expand-next-arg offset)
270 (setf *only-simple-args* nil)
274 `(let ,(expander-bindings)
275 `(let ,(list ,@(runtime-bindings))
280 "too many parameters, expected no more than ~W"
281 :args (list ,(length specs))
282 :offset (caar ,params)))
287 :complaint "too many parameters, expected none"
288 :offset (caar ,params)))
291 ;;;; format directive machinery
293 ;;; FIXME: only used in this file, could be SB!XC:DEFMACRO in EVAL-WHEN
294 (defmacro def-complex-format-directive (char lambda-list &body body)
295 (let ((defun-name (intern (format nil
296 "~:@(~:C~)-FORMAT-DIRECTIVE-EXPANDER"
299 (directives (if lambda-list (car (last lambda-list)) (gensym))))
301 (defun ,defun-name (,directive ,directives)
303 `((let ,(mapcar (lambda (var)
305 (,(symbolicate "FORMAT-DIRECTIVE-" var)
307 (butlast lambda-list))
309 `((declare (ignore ,directive ,directives))
311 (%set-format-directive-expander ,char #',defun-name))))
313 ;;; FIXME: only used in this file, could be SB!XC:DEFMACRO in EVAL-WHEN
314 (defmacro def-format-directive (char lambda-list &body body)
315 (let ((directives (gensym))
317 (body-without-decls body))
319 (let ((form (car body-without-decls)))
320 (unless (and (consp form) (eq (car form) 'declare))
322 (push (pop body-without-decls) declarations)))
323 (setf declarations (reverse declarations))
324 `(def-complex-format-directive ,char (,@lambda-list ,directives)
326 (values (progn ,@body-without-decls)
329 (eval-when (:compile-toplevel :load-toplevel :execute)
331 (defun %set-format-directive-expander (char fn)
332 (setf (aref *format-directive-expanders* (char-code (char-upcase char))) fn)
335 (defun %set-format-directive-interpreter (char fn)
336 (setf (aref *format-directive-interpreters*
337 (char-code (char-upcase char)))
341 (defun find-directive (directives kind stop-at-semi)
343 (let ((next (car directives)))
344 (if (format-directive-p next)
345 (let ((char (format-directive-character next)))
346 (if (or (char= kind char)
347 (and stop-at-semi (char= char #\;)))
350 (cdr (flet ((after (char)
351 (member (find-directive (cdr directives)
362 (find-directive (cdr directives) kind stop-at-semi)))))
366 ;;;; format directives for simple output
368 (def-format-directive #\A (colonp atsignp params)
370 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
373 `(format-princ stream ,(expand-next-arg) ',colonp ',atsignp
374 ,mincol ,colinc ,minpad ,padchar))
376 `(or ,(expand-next-arg) "()")
380 (def-format-directive #\S (colonp atsignp params)
382 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
385 `(format-prin1 stream ,(expand-next-arg) ,colonp ,atsignp
386 ,mincol ,colinc ,minpad ,padchar)))
388 `(let ((arg ,(expand-next-arg)))
391 (princ "()" stream))))
393 `(prin1 ,(expand-next-arg) stream))))
395 (def-format-directive #\C (colonp atsignp params)
396 (expand-bind-defaults () params
398 `(format-print-named-character ,(expand-next-arg) stream)
400 `(prin1 ,(expand-next-arg) stream)
401 `(write-char ,(expand-next-arg) stream)))))
403 (def-format-directive #\W (colonp atsignp params)
404 (expand-bind-defaults () params
405 (if (or colonp atsignp)
406 `(let (,@(when colonp
407 '((*print-pretty* t)))
409 '((*print-level* nil)
410 (*print-length* nil))))
411 (output-object ,(expand-next-arg) stream))
412 `(output-object ,(expand-next-arg) stream))))
414 ;;;; format directives for integer output
416 (defun expand-format-integer (base colonp atsignp params)
417 (if (or colonp atsignp params)
418 (expand-bind-defaults
419 ((mincol 0) (padchar #\space) (commachar #\,) (commainterval 3))
421 `(format-print-integer stream ,(expand-next-arg) ,colonp ,atsignp
422 ,base ,mincol ,padchar ,commachar
424 `(write ,(expand-next-arg) :stream stream :base ,base :radix nil
427 (def-format-directive #\D (colonp atsignp params)
428 (expand-format-integer 10 colonp atsignp params))
430 (def-format-directive #\B (colonp atsignp params)
431 (expand-format-integer 2 colonp atsignp params))
433 (def-format-directive #\O (colonp atsignp params)
434 (expand-format-integer 8 colonp atsignp params))
436 (def-format-directive #\X (colonp atsignp params)
437 (expand-format-integer 16 colonp atsignp params))
439 (def-format-directive #\R (colonp atsignp params)
441 (expand-bind-defaults
442 ((base 10) (mincol 0) (padchar #\space) (commachar #\,)
445 `(format-print-integer stream ,(expand-next-arg) ,colonp ,atsignp
447 ,padchar ,commachar ,commainterval))
450 `(format-print-old-roman stream ,(expand-next-arg))
451 `(format-print-roman stream ,(expand-next-arg)))
453 `(format-print-ordinal stream ,(expand-next-arg))
454 `(format-print-cardinal stream ,(expand-next-arg))))))
456 ;;;; format directive for pluralization
458 (def-format-directive #\P (colonp atsignp params end)
459 (expand-bind-defaults () params
463 (*orig-args-available*
464 `(if (eq orig-args args)
466 :complaint "no previous argument"
468 (do ((arg-ptr orig-args (cdr arg-ptr)))
469 ((eq (cdr arg-ptr) args)
472 (unless *simple-args*
474 :complaint "no previous argument"))
475 (caar *simple-args*))
477 (/show0 "THROWing NEED-ORIG-ARGS from tilde-P")
478 (throw 'need-orig-args nil)))))
480 `(write-string (if (eql ,arg 1) "y" "ies") stream)
481 `(unless (eql ,arg 1) (write-char #\s stream))))))
483 ;;;; format directives for floating point output
485 (def-format-directive #\F (colonp atsignp params)
489 "The colon modifier cannot be used with this directive."))
490 (expand-bind-defaults ((w nil) (d nil) (k nil) (ovf nil) (pad #\space)) params
491 `(format-fixed stream ,(expand-next-arg) ,w ,d ,k ,ovf ,pad ,atsignp)))
493 (def-format-directive #\E (colonp atsignp params)
497 "The colon modifier cannot be used with this directive."))
498 (expand-bind-defaults
499 ((w nil) (d nil) (e nil) (k 1) (ovf nil) (pad #\space) (mark nil))
501 `(format-exponential stream ,(expand-next-arg) ,w ,d ,e ,k ,ovf ,pad ,mark
504 (def-format-directive #\G (colonp atsignp params)
508 "The colon modifier cannot be used with this directive."))
509 (expand-bind-defaults
510 ((w nil) (d nil) (e nil) (k nil) (ovf nil) (pad #\space) (mark nil))
512 `(format-general stream ,(expand-next-arg) ,w ,d ,e ,k ,ovf ,pad ,mark ,atsignp)))
514 (def-format-directive #\$ (colonp atsignp params)
515 (expand-bind-defaults ((d 2) (n 1) (w 0) (pad #\space)) params
516 `(format-dollars stream ,(expand-next-arg) ,d ,n ,w ,pad ,colonp
519 ;;;; format directives for line/page breaks etc.
521 (def-format-directive #\% (colonp atsignp params)
522 (when (or colonp atsignp)
525 "The colon and atsign modifiers cannot be used with this directive."
528 (expand-bind-defaults ((count 1)) params
533 (def-format-directive #\& (colonp atsignp params)
534 (when (or colonp atsignp)
537 "The colon and atsign modifiers cannot be used with this directive."
540 (expand-bind-defaults ((count 1)) params
543 (dotimes (i (1- ,count))
545 '(fresh-line stream)))
547 (def-format-directive #\| (colonp atsignp params)
548 (when (or colonp atsignp)
551 "The colon and atsign modifiers cannot be used with this directive."
554 (expand-bind-defaults ((count 1)) params
556 (write-char (code-char form-feed-char-code) stream)))
557 '(write-char (code-char form-feed-char-code) stream)))
559 (def-format-directive #\~ (colonp atsignp params)
560 (when (or colonp atsignp)
563 "The colon and atsign modifiers cannot be used with this directive."
566 (expand-bind-defaults ((count 1)) params
568 (write-char #\~ stream)))
569 '(write-char #\~ stream)))
571 (def-complex-format-directive #\newline (colonp atsignp params directives)
572 (when (and colonp atsignp)
574 :complaint "both colon and atsign modifiers used simultaneously"))
575 (values (expand-bind-defaults () params
577 '(write-char #\newline stream)
579 (if (and (not colonp)
581 (simple-string-p (car directives)))
582 (cons (string-left-trim *format-whitespace-chars*
587 ;;;; format directives for tabs and simple pretty printing
589 (def-format-directive #\T (colonp atsignp params)
591 (expand-bind-defaults ((n 1) (m 1)) params
592 `(pprint-tab ,(if atsignp :section-relative :section)
595 (expand-bind-defaults ((colrel 1) (colinc 1)) params
596 `(format-relative-tab stream ,colrel ,colinc))
597 (expand-bind-defaults ((colnum 1) (colinc 1)) params
598 `(format-absolute-tab stream ,colnum ,colinc)))))
600 (def-format-directive #\_ (colonp atsignp params)
601 (expand-bind-defaults () params
602 `(pprint-newline ,(if colonp
611 (def-format-directive #\I (colonp atsignp params)
615 "cannot use the at-sign modifier with this directive"))
616 (expand-bind-defaults ((n 0)) params
617 `(pprint-indent ,(if colonp :current :block) ,n stream)))
619 ;;;; format directive for ~*
621 (def-format-directive #\* (colonp atsignp params end)
626 "both colon and atsign modifiers used simultaneously")
627 (expand-bind-defaults ((posn 0)) params
628 (unless *orig-args-available*
629 (/show0 "THROWing NEED-ORIG-ARGS from tilde-@*")
630 (throw 'need-orig-args nil))
631 `(if (<= 0 ,posn (length orig-args))
632 (setf args (nthcdr ,posn orig-args))
634 :complaint "Index ~W out of bounds. Should have been ~
636 :args (list ,posn (length orig-args))
637 :offset ,(1- end)))))
639 (expand-bind-defaults ((n 1)) params
640 (unless *orig-args-available*
641 (/show0 "THROWing NEED-ORIG-ARGS from tilde-:*")
642 (throw 'need-orig-args nil))
643 `(do ((cur-posn 0 (1+ cur-posn))
644 (arg-ptr orig-args (cdr arg-ptr)))
646 (let ((new-posn (- cur-posn ,n)))
647 (if (<= 0 new-posn (length orig-args))
648 (setf args (nthcdr new-posn orig-args))
651 "Index ~W is out of bounds; should have been ~
653 :args (list new-posn (length orig-args))
654 :offset ,(1- end)))))))
656 (expand-bind-defaults ((n 1)) params
657 (setf *only-simple-args* nil)
660 (expand-next-arg)))))
662 ;;;; format directive for indirection
664 (def-format-directive #\? (colonp atsignp params string end)
667 :complaint "cannot use the colon modifier with this directive"))
668 (expand-bind-defaults () params
674 "~A~%while processing indirect format string:"
675 :args (list condition)
677 :control-string ,string
678 :offset ,(1- end)))))
680 (if *orig-args-available*
681 `(setf args (%format stream ,(expand-next-arg) orig-args args))
682 (throw 'need-orig-args nil))
683 `(%format stream ,(expand-next-arg) ,(expand-next-arg))))))
685 ;;;; format directives for capitalization
687 (def-complex-format-directive #\( (colonp atsignp params directives)
688 (let ((close (find-directive directives #\) nil)))
691 :complaint "no corresponding close parenthesis"))
692 (let* ((posn (position close directives))
693 (before (subseq directives 0 posn))
694 (after (nthcdr (1+ posn) directives)))
696 (expand-bind-defaults () params
697 `(let ((stream (make-case-frob-stream stream
705 ,@(expand-directive-list before)))
708 (def-complex-format-directive #\) ()
710 :complaint "no corresponding open parenthesis"))
712 ;;;; format directives and support functions for conditionalization
714 (def-complex-format-directive #\[ (colonp atsignp params directives)
715 (multiple-value-bind (sublists last-semi-with-colon-p remaining)
716 (parse-conditional-directive directives)
722 "both colon and atsign modifiers used simultaneously")
726 "Can only specify one section")
727 (expand-bind-defaults () params
728 (expand-maybe-conditional (car sublists)))))
730 (if (= (length sublists) 2)
731 (expand-bind-defaults () params
732 (expand-true-false-conditional (car sublists)
736 "must specify exactly two sections"))
737 (expand-bind-defaults ((index (expand-next-arg))) params
738 (setf *only-simple-args* nil)
740 (when last-semi-with-colon-p
741 (push `(t ,@(expand-directive-list (pop sublists)))
743 (let ((count (length sublists)))
744 (dolist (sublist sublists)
745 (push `(,(decf count)
746 ,@(expand-directive-list sublist))
748 `(case ,index ,@clauses)))))
751 (defun parse-conditional-directive (directives)
753 (last-semi-with-colon-p nil)
754 (remaining directives))
756 (let ((close-or-semi (find-directive remaining #\] t)))
757 (unless close-or-semi
759 :complaint "no corresponding close bracket"))
760 (let ((posn (position close-or-semi remaining)))
761 (push (subseq remaining 0 posn) sublists)
762 (setf remaining (nthcdr (1+ posn) remaining))
763 (when (char= (format-directive-character close-or-semi) #\])
765 (setf last-semi-with-colon-p
766 (format-directive-colonp close-or-semi)))))
767 (values sublists last-semi-with-colon-p remaining)))
769 (defun expand-maybe-conditional (sublist)
771 `(let ((prev-args args)
772 (arg ,(expand-next-arg)))
774 (setf args prev-args)
775 ,@(expand-directive-list sublist)))))
776 (if *only-simple-args*
777 (multiple-value-bind (guts new-args)
778 (let ((*simple-args* *simple-args*))
779 (values (expand-directive-list sublist)
781 (cond ((eq *simple-args* (cdr new-args))
782 (setf *simple-args* new-args)
783 `(when ,(caar new-args)
786 (setf *only-simple-args* nil)
790 (defun expand-true-false-conditional (true false)
791 (let ((arg (expand-next-arg)))
795 ,@(expand-directive-list true))
797 ,@(expand-directive-list false)))))
798 (if *only-simple-args*
799 (multiple-value-bind (true-guts true-args true-simple)
800 (let ((*simple-args* *simple-args*)
801 (*only-simple-args* t))
802 (values (expand-directive-list true)
805 (multiple-value-bind (false-guts false-args false-simple)
806 (let ((*simple-args* *simple-args*)
807 (*only-simple-args* t))
808 (values (expand-directive-list false)
811 (if (= (length true-args) (length false-args))
815 ,(do ((false false-args (cdr false))
816 (true true-args (cdr true))
817 (bindings nil (cons `(,(caar false) ,(caar true))
819 ((eq true *simple-args*)
820 (setf *simple-args* true-args)
821 (setf *only-simple-args*
822 (and true-simple false-simple))
829 (setf *only-simple-args* nil)
833 (def-complex-format-directive #\; ()
836 "~~; directive not contained within either ~~[...~~] or ~~<...~~>"))
838 (def-complex-format-directive #\] ()
841 "no corresponding open bracket"))
843 ;;;; format directive for up-and-out
845 (def-format-directive #\^ (colonp atsignp params)
848 :complaint "cannot use the at-sign modifier with this directive"))
849 (when (and colonp (not *up-up-and-out-allowed*))
851 :complaint "attempt to use ~~:^ outside a ~~:{...~~} construct"))
852 `(when ,(case (length params)
856 (setf *only-simple-args* nil)
858 (1 (expand-bind-defaults ((count 0)) params
860 (2 (expand-bind-defaults ((arg1 0) (arg2 0)) params
862 (t (expand-bind-defaults ((arg1 0) (arg2 0) (arg3 0)) params
863 `(<= ,arg1 ,arg2 ,arg3))))
865 '(return-from outside-loop nil)
868 ;;;; format directives for iteration
870 (def-complex-format-directive #\{ (colonp atsignp params string end directives)
871 (let ((close (find-directive directives #\} nil)))
874 :complaint "no corresponding close brace"))
875 (let* ((closed-with-colon (format-directive-colonp close))
876 (posn (position close directives)))
880 (if *orig-args-available*
886 "~A~%while processing indirect format string:"
887 :args (list condition)
889 :control-string ,string
890 :offset ,(1- end)))))
892 (%format stream inside-string orig-args args))))
893 (throw 'need-orig-args nil))
894 (let ((*up-up-and-out-allowed* colonp))
895 (expand-directive-list (subseq directives 0 posn)))))
896 (compute-loop-aux (count)
898 (setf *only-simple-args* nil))
900 ,@(unless closed-with-colon
904 `((when (and ,count (minusp (decf ,count)))
907 (let ((*expander-next-arg-macro* 'expander-next-arg)
908 (*only-simple-args* nil)
909 (*orig-args-available* t))
910 `((let* ((orig-args ,(expand-next-arg))
913 (declare (ignorable orig-args outside-args args))
915 ,@(compute-insides)))))
917 ,@(when closed-with-colon
922 (expand-bind-defaults ((count nil)) params
923 (compute-loop-aux count))
924 (compute-loop-aux nil)))
933 `(let* ((orig-args ,(expand-next-arg))
935 (declare (ignorable orig-args args))
936 ,(let ((*expander-next-arg-macro* 'expander-next-arg)
937 (*only-simple-args* nil)
938 (*orig-args-available* t))
940 (values (if (zerop posn)
941 `(let ((inside-string ,(expand-next-arg)))
944 (nthcdr (1+ posn) directives))))))
946 (def-complex-format-directive #\} ()
948 :complaint "no corresponding open brace"))
950 ;;;; format directives and support functions for justification
952 (defparameter *illegal-inside-justification*
953 (mapcar (lambda (x) (parse-directive x 0))
954 '("~W" "~:W" "~@W" "~:@W"
955 "~_" "~:_" "~@_" "~:@_"
957 "~I" "~:I" "~@I" "~:@I"
960 (defun illegal-inside-justification-p (directive)
961 (member directive *illegal-inside-justification*
963 (and (format-directive-p x)
964 (format-directive-p y)
965 (eql (format-directive-character x) (format-directive-character y))
966 (eql (format-directive-colonp x) (format-directive-colonp y))
967 (eql (format-directive-atsignp x) (format-directive-atsignp y))))))
969 (def-complex-format-directive #\< (colonp atsignp params string end directives)
970 (multiple-value-bind (segments first-semi close remaining)
971 (parse-format-justification directives)
973 (if (format-directive-colonp close)
974 (multiple-value-bind (prefix per-line-p insides suffix)
975 (parse-format-logical-block segments colonp first-semi
976 close params string end)
977 (expand-format-logical-block prefix per-line-p insides
979 (let ((count (apply #'+ (mapcar (lambda (x) (count-if #'illegal-inside-justification-p x)) segments))))
981 ;; ANSI specifies that "an error is signalled" in this
984 :complaint "~D illegal directive~:P found inside justification block"
986 (expand-format-justification segments colonp atsignp
990 (def-complex-format-directive #\> ()
992 :complaint "no corresponding open bracket"))
994 (defun parse-format-logical-block
995 (segments colonp first-semi close params string end)
998 :complaint "No parameters can be supplied with ~~<...~~:>."
999 :offset (caar params)))
1000 (multiple-value-bind (prefix insides suffix)
1001 (multiple-value-bind (prefix-default suffix-default)
1002 (if colonp (values "(" ")") (values nil ""))
1003 (flet ((extract-string (list prefix-p)
1004 (let ((directive (find-if #'format-directive-p list)))
1006 (error 'format-error
1008 "cannot include format directives inside the ~
1009 ~:[suffix~;prefix~] segment of ~~<...~~:>"
1010 :args (list prefix-p)
1011 :offset (1- (format-directive-end directive)))
1012 (apply #'concatenate 'string list)))))
1013 (case (length segments)
1014 (0 (values prefix-default nil suffix-default))
1015 (1 (values prefix-default (car segments) suffix-default))
1016 (2 (values (extract-string (car segments) t)
1017 (cadr segments) suffix-default))
1018 (3 (values (extract-string (car segments) t)
1020 (extract-string (caddr segments) nil)))
1022 (error 'format-error
1023 :complaint "too many segments for ~~<...~~:>")))))
1024 (when (format-directive-atsignp close)
1026 (add-fill-style-newlines insides
1029 (format-directive-end first-semi)
1032 (and first-semi (format-directive-atsignp first-semi))
1036 (defun add-fill-style-newlines (list string offset)
1038 (let ((directive (car list)))
1039 (if (simple-string-p directive)
1040 (nconc (add-fill-style-newlines-aux directive string offset)
1041 (add-fill-style-newlines (cdr list)
1043 (+ offset (length directive))))
1045 (add-fill-style-newlines (cdr list)
1047 (format-directive-end directive)))))
1050 (defun add-fill-style-newlines-aux (literal string offset)
1051 (let ((end (length literal))
1053 (collect ((results))
1055 (let ((blank (position #\space literal :start posn)))
1057 (results (subseq literal posn))
1059 (let ((non-blank (or (position #\space literal :start blank
1062 (results (subseq literal posn non-blank))
1063 (results (make-format-directive
1064 :string string :character #\_
1065 :start (+ offset non-blank) :end (+ offset non-blank)
1066 :colonp t :atsignp nil :params nil))
1067 (setf posn non-blank))
1072 (defun parse-format-justification (directives)
1073 (let ((first-semi nil)
1075 (remaining directives))
1076 (collect ((segments))
1078 (let ((close-or-semi (find-directive remaining #\> t)))
1079 (unless close-or-semi
1080 (error 'format-error
1081 :complaint "no corresponding close bracket"))
1082 (let ((posn (position close-or-semi remaining)))
1083 (segments (subseq remaining 0 posn))
1084 (setf remaining (nthcdr (1+ posn) remaining)))
1085 (when (char= (format-directive-character close-or-semi)
1087 (setf close close-or-semi)
1090 (setf first-semi close-or-semi))))
1091 (values (segments) first-semi close remaining))))
1093 (sb!xc:defmacro expander-pprint-next-arg (string offset)
1096 (error 'format-error
1097 :complaint "no more arguments"
1098 :control-string ,string
1103 (defun expand-format-logical-block (prefix per-line-p insides suffix atsignp)
1104 `(let ((arg ,(if atsignp 'args (expand-next-arg))))
1106 (setf *only-simple-args* nil)
1108 (pprint-logical-block
1110 ,(if per-line-p :per-line-prefix :prefix) ,prefix
1114 `((orig-args arg))))
1115 (declare (ignorable args ,@(unless atsignp '(orig-args))))
1117 ,@(let ((*expander-next-arg-macro* 'expander-pprint-next-arg)
1118 (*only-simple-args* nil)
1119 (*orig-args-available* t))
1120 (expand-directive-list insides)))))))
1122 (defun expand-format-justification (segments colonp atsignp first-semi params)
1123 (let ((newline-segment-p
1125 (format-directive-colonp first-semi))))
1126 (expand-bind-defaults
1127 ((mincol 0) (colinc 1) (minpad 0) (padchar #\space))
1129 `(let ((segments nil)
1130 ,@(when newline-segment-p
1131 '((newline-segment nil)
1135 ,@(when newline-segment-p
1136 `((setf newline-segment
1137 (with-output-to-string (stream)
1138 ,@(expand-directive-list (pop segments))))
1139 ,(expand-bind-defaults
1141 (line-len '(or (sb!impl::line-length stream) 72)))
1142 (format-directive-params first-semi)
1143 `(setf extra-space ,extra line-len ,line-len))))
1144 ,@(mapcar (lambda (segment)
1145 `(push (with-output-to-string (stream)
1146 ,@(expand-directive-list segment))
1149 (format-justification stream
1150 ,@(if newline-segment-p
1151 '(newline-segment extra-space line-len)
1153 segments ,colonp ,atsignp
1154 ,mincol ,colinc ,minpad ,padchar)))))
1156 ;;;; format directive and support function for user-defined method
1158 (def-format-directive #\/ (string start end colonp atsignp params)
1159 (let ((symbol (extract-user-fun-name string start end)))
1160 (collect ((param-names) (bindings))
1161 (dolist (param-and-offset params)
1162 (let ((param (cdr param-and-offset)))
1163 (let ((param-name (gensym)))
1164 (param-names param-name)
1165 (bindings `(,param-name
1167 (:arg (expand-next-arg))
1168 (:remaining '(length args))
1171 (,symbol stream ,(expand-next-arg) ,colonp ,atsignp
1172 ,@(param-names))))))
1174 (defun extract-user-fun-name (string start end)
1175 (let ((slash (position #\/ string :start start :end (1- end)
1178 (error 'format-error
1179 :complaint "malformed ~~/ directive"))
1180 (let* ((name (string-upcase (let ((foo string))
1181 ;; Hack alert: This is to keep the compiler
1182 ;; quiet about deleting code inside the
1183 ;; subseq expansion.
1184 (subseq foo (1+ slash) (1- end)))))
1185 (first-colon (position #\: name))
1186 (second-colon (if first-colon (position #\: name :start (1+ first-colon))))
1187 (package-name (if first-colon
1188 (subseq name 0 first-colon)
1189 "COMMON-LISP-USER"))
1190 (package (find-package package-name)))
1192 ;; FIXME: should be PACKAGE-ERROR? Could we just use
1193 ;; FIND-UNDELETED-PACKAGE-OR-LOSE?
1194 (error 'format-error
1195 :complaint "no package named ~S"
1196 :args (list package-name)))
1198 ((and second-colon (= second-colon (1+ first-colon)))
1199 (subseq name (1+ second-colon)))
1201 (subseq name (1+ first-colon)))