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 (and (char= (schar string (1- posn)) #\,)
145 (char/= (schar string (- posn 2)) #\')))
147 (push (cons (1- posn) nil) params))
150 (let ((char (get-char)))
151 (when (char= char #\/)
152 (let ((closing-slash (position #\/ string :start (1+ posn))))
154 (setf posn closing-slash)
156 :complaint "no matching closing slash"
157 :control-string string
159 (make-format-directive
160 :string string :start start :end (1+ posn)
161 :character (char-upcase char)
162 :colonp colonp :atsignp atsignp
163 :params (nreverse params))))))
167 (sb!xc:defmacro formatter (control-string)
168 `#',(%formatter control-string))
170 (defun %formatter (control-string)
172 (catch 'need-orig-args
173 (let* ((*simple-args* nil)
174 (*only-simple-args* t)
175 (guts (expand-control-string control-string))
177 (dolist (arg *simple-args*)
181 :complaint "required argument missing"
182 :control-string ,control-string
185 (return `(lambda (stream &optional ,@args &rest args)
188 (let ((*orig-args-available* t)
189 (*only-simple-args* nil))
190 `(lambda (stream &rest orig-args)
191 (let ((args orig-args))
192 ,(expand-control-string control-string)
195 (defun expand-control-string (string)
196 (let* ((string (etypecase string
200 (coerce string 'simple-string))))
201 (*default-format-error-control-string* string)
202 (directives (tokenize-control-string string)))
204 ,@(expand-directive-list directives))))
206 (defun expand-directive-list (directives)
208 (remaining-directives directives))
210 (unless remaining-directives
212 (multiple-value-bind (form new-directives)
213 (expand-directive (car remaining-directives)
214 (cdr remaining-directives))
216 (setf remaining-directives new-directives)))
219 (defun expand-directive (directive more-directives)
223 (aref *format-directive-expanders*
224 (char-code (format-directive-character directive))))
225 (*default-format-error-offset*
226 (1- (format-directive-end directive))))
227 (declare (type (or null function) expander))
229 (funcall expander directive more-directives)
231 :complaint "unknown directive ~@[(character: ~A)~]"
232 :args (list (char-name (format-directive-character directive)))))))
234 (values `(write-string ,directive stream)
237 (defmacro-mundanely expander-next-arg (string offset)
241 :complaint "no more arguments"
242 :control-string ,string
245 (defun expand-next-arg (&optional offset)
246 (if (or *orig-args-available* (not *only-simple-args*))
247 `(,*expander-next-arg-macro*
248 ,*default-format-error-control-string*
249 ,(or offset *default-format-error-offset*))
250 (let ((symbol (gensym "FORMAT-ARG-")))
251 (push (cons symbol (or offset *default-format-error-offset*))
255 (defmacro expand-bind-defaults (specs params &body body)
256 (once-only ((params params))
258 (collect ((expander-bindings) (runtime-bindings))
260 (destructuring-bind (var default) spec
261 (let ((symbol (gensym)))
266 (let* ((param-and-offset (pop ,params))
267 (offset (car param-and-offset))
268 (param (cdr param-and-offset)))
270 (:arg `(or ,(expand-next-arg offset)
273 (setf *only-simple-args* nil)
277 `(let ,(expander-bindings)
278 `(let ,(list ,@(runtime-bindings))
283 "too many parameters, expected no more than ~W"
284 :args (list ,(length specs))
285 :offset (caar ,params)))
290 :complaint "too many parameters, expected none"
291 :offset (caar ,params)))
294 ;;;; format directive machinery
296 ;;; FIXME: only used in this file, could be SB!XC:DEFMACRO in EVAL-WHEN
297 (defmacro def-complex-format-directive (char lambda-list &body body)
298 (let ((defun-name (intern (format nil
299 "~:@(~:C~)-FORMAT-DIRECTIVE-EXPANDER"
302 (directives (if lambda-list (car (last lambda-list)) (gensym))))
304 (defun ,defun-name (,directive ,directives)
306 `((let ,(mapcar (lambda (var)
308 (,(symbolicate "FORMAT-DIRECTIVE-" var)
310 (butlast lambda-list))
312 `((declare (ignore ,directive ,directives))
314 (%set-format-directive-expander ,char #',defun-name))))
316 ;;; FIXME: only used in this file, could be SB!XC:DEFMACRO in EVAL-WHEN
317 (defmacro def-format-directive (char lambda-list &body body)
318 (let ((directives (gensym))
320 (body-without-decls body))
322 (let ((form (car body-without-decls)))
323 (unless (and (consp form) (eq (car form) 'declare))
325 (push (pop body-without-decls) declarations)))
326 (setf declarations (reverse declarations))
327 `(def-complex-format-directive ,char (,@lambda-list ,directives)
329 (values (progn ,@body-without-decls)
332 (eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute)
334 (defun %set-format-directive-expander (char fn)
335 (setf (aref *format-directive-expanders* (char-code (char-upcase char))) fn)
338 (defun %set-format-directive-interpreter (char fn)
339 (setf (aref *format-directive-interpreters*
340 (char-code (char-upcase char)))
344 (defun find-directive (directives kind stop-at-semi)
346 (let ((next (car directives)))
347 (if (format-directive-p next)
348 (let ((char (format-directive-character next)))
349 (if (or (char= kind char)
350 (and stop-at-semi (char= char #\;)))
353 (cdr (flet ((after (char)
354 (member (find-directive (cdr directives)
365 (find-directive (cdr directives) kind stop-at-semi)))))
369 ;;;; format directives for simple output
371 (def-format-directive #\A (colonp atsignp params)
373 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
376 `(format-princ stream ,(expand-next-arg) ',colonp ',atsignp
377 ,mincol ,colinc ,minpad ,padchar))
379 `(or ,(expand-next-arg) "()")
383 (def-format-directive #\S (colonp atsignp params)
385 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
388 `(format-prin1 stream ,(expand-next-arg) ,colonp ,atsignp
389 ,mincol ,colinc ,minpad ,padchar)))
391 `(let ((arg ,(expand-next-arg)))
394 (princ "()" stream))))
396 `(prin1 ,(expand-next-arg) stream))))
398 (def-format-directive #\C (colonp atsignp params)
399 (expand-bind-defaults () params
401 `(format-print-named-character ,(expand-next-arg) stream)
403 `(prin1 ,(expand-next-arg) stream)
404 `(write-char ,(expand-next-arg) stream)))))
406 (def-format-directive #\W (colonp atsignp params)
407 (expand-bind-defaults () params
408 (if (or colonp atsignp)
409 `(let (,@(when colonp
410 '((*print-pretty* t)))
412 '((*print-level* nil)
413 (*print-length* nil))))
414 (output-object ,(expand-next-arg) stream))
415 `(output-object ,(expand-next-arg) stream))))
417 ;;;; format directives for integer output
419 (defun expand-format-integer (base colonp atsignp params)
420 (if (or colonp atsignp params)
421 (expand-bind-defaults
422 ((mincol 0) (padchar #\space) (commachar #\,) (commainterval 3))
424 `(format-print-integer stream ,(expand-next-arg) ,colonp ,atsignp
425 ,base ,mincol ,padchar ,commachar
427 `(write ,(expand-next-arg) :stream stream :base ,base :radix nil
430 (def-format-directive #\D (colonp atsignp params)
431 (expand-format-integer 10 colonp atsignp params))
433 (def-format-directive #\B (colonp atsignp params)
434 (expand-format-integer 2 colonp atsignp params))
436 (def-format-directive #\O (colonp atsignp params)
437 (expand-format-integer 8 colonp atsignp params))
439 (def-format-directive #\X (colonp atsignp params)
440 (expand-format-integer 16 colonp atsignp params))
442 (def-format-directive #\R (colonp atsignp params)
444 (expand-bind-defaults
445 ((base 10) (mincol 0) (padchar #\space) (commachar #\,)
448 `(format-print-integer stream ,(expand-next-arg) ,colonp ,atsignp
450 ,padchar ,commachar ,commainterval))
453 `(format-print-old-roman stream ,(expand-next-arg))
454 `(format-print-roman stream ,(expand-next-arg)))
456 `(format-print-ordinal stream ,(expand-next-arg))
457 `(format-print-cardinal stream ,(expand-next-arg))))))
459 ;;;; format directive for pluralization
461 (def-format-directive #\P (colonp atsignp params end)
462 (expand-bind-defaults () params
466 (*orig-args-available*
467 `(if (eq orig-args args)
469 :complaint "no previous argument"
471 (do ((arg-ptr orig-args (cdr arg-ptr)))
472 ((eq (cdr arg-ptr) args)
475 (unless *simple-args*
477 :complaint "no previous argument"))
478 (caar *simple-args*))
480 (/show0 "THROWing NEED-ORIG-ARGS from tilde-P")
481 (throw 'need-orig-args nil)))))
483 `(write-string (if (eql ,arg 1) "y" "ies") stream)
484 `(unless (eql ,arg 1) (write-char #\s stream))))))
486 ;;;; format directives for floating point output
488 (def-format-directive #\F (colonp atsignp params)
492 "The colon modifier cannot be used with this directive."))
493 (expand-bind-defaults ((w nil) (d nil) (k nil) (ovf nil) (pad #\space)) params
494 `(format-fixed stream ,(expand-next-arg) ,w ,d ,k ,ovf ,pad ,atsignp)))
496 (def-format-directive #\E (colonp atsignp params)
500 "The colon modifier cannot be used with this directive."))
501 (expand-bind-defaults
502 ((w nil) (d nil) (e nil) (k 1) (ovf nil) (pad #\space) (mark nil))
504 `(format-exponential stream ,(expand-next-arg) ,w ,d ,e ,k ,ovf ,pad ,mark
507 (def-format-directive #\G (colonp atsignp params)
511 "The colon modifier cannot be used with this directive."))
512 (expand-bind-defaults
513 ((w nil) (d nil) (e nil) (k nil) (ovf nil) (pad #\space) (mark nil))
515 `(format-general stream ,(expand-next-arg) ,w ,d ,e ,k ,ovf ,pad ,mark ,atsignp)))
517 (def-format-directive #\$ (colonp atsignp params)
518 (expand-bind-defaults ((d 2) (n 1) (w 0) (pad #\space)) params
519 `(format-dollars stream ,(expand-next-arg) ,d ,n ,w ,pad ,colonp
522 ;;;; format directives for line/page breaks etc.
524 (def-format-directive #\% (colonp atsignp params)
525 (when (or colonp atsignp)
528 "The colon and atsign modifiers cannot be used with this directive."
531 (expand-bind-defaults ((count 1)) params
536 (def-format-directive #\& (colonp atsignp params)
537 (when (or colonp atsignp)
540 "The colon and atsign modifiers cannot be used with this directive."
543 (expand-bind-defaults ((count 1)) params
546 (dotimes (i (1- ,count))
548 '(fresh-line stream)))
550 (def-format-directive #\| (colonp atsignp params)
551 (when (or colonp atsignp)
554 "The colon and atsign modifiers cannot be used with this directive."
557 (expand-bind-defaults ((count 1)) params
559 (write-char (code-char form-feed-char-code) stream)))
560 '(write-char (code-char form-feed-char-code) stream)))
562 (def-format-directive #\~ (colonp atsignp params)
563 (when (or colonp atsignp)
566 "The colon and atsign modifiers cannot be used with this directive."
569 (expand-bind-defaults ((count 1)) params
571 (write-char #\~ stream)))
572 '(write-char #\~ stream)))
574 (def-complex-format-directive #\newline (colonp atsignp params directives)
575 (when (and colonp atsignp)
577 :complaint "both colon and atsign modifiers used simultaneously"))
578 (values (expand-bind-defaults () params
580 '(write-char #\newline stream)
582 (if (and (not colonp)
584 (simple-string-p (car directives)))
585 (cons (string-left-trim *format-whitespace-chars*
590 ;;;; format directives for tabs and simple pretty printing
592 (def-format-directive #\T (colonp atsignp params)
594 (expand-bind-defaults ((n 1) (m 1)) params
595 `(pprint-tab ,(if atsignp :section-relative :section)
598 (expand-bind-defaults ((colrel 1) (colinc 1)) params
599 `(format-relative-tab stream ,colrel ,colinc))
600 (expand-bind-defaults ((colnum 1) (colinc 1)) params
601 `(format-absolute-tab stream ,colnum ,colinc)))))
603 (def-format-directive #\_ (colonp atsignp params)
604 (expand-bind-defaults () params
605 `(pprint-newline ,(if colonp
614 (def-format-directive #\I (colonp atsignp params)
618 "cannot use the at-sign modifier with this directive"))
619 (expand-bind-defaults ((n 0)) params
620 `(pprint-indent ,(if colonp :current :block) ,n stream)))
622 ;;;; format directive for ~*
624 (def-format-directive #\* (colonp atsignp params end)
629 "both colon and atsign modifiers used simultaneously")
630 (expand-bind-defaults ((posn 0)) params
631 (unless *orig-args-available*
632 (/show0 "THROWing NEED-ORIG-ARGS from tilde-@*")
633 (throw 'need-orig-args nil))
634 `(if (<= 0 ,posn (length orig-args))
635 (setf args (nthcdr ,posn orig-args))
637 :complaint "Index ~W out of bounds. Should have been ~
639 :args (list ,posn (length orig-args))
640 :offset ,(1- end)))))
642 (expand-bind-defaults ((n 1)) params
643 (unless *orig-args-available*
644 (/show0 "THROWing NEED-ORIG-ARGS from tilde-:*")
645 (throw 'need-orig-args nil))
646 `(do ((cur-posn 0 (1+ cur-posn))
647 (arg-ptr orig-args (cdr arg-ptr)))
649 (let ((new-posn (- cur-posn ,n)))
650 (if (<= 0 new-posn (length orig-args))
651 (setf args (nthcdr new-posn orig-args))
654 "Index ~W is out of bounds; should have been ~
656 :args (list new-posn (length orig-args))
657 :offset ,(1- end)))))))
659 (expand-bind-defaults ((n 1)) params
660 (setf *only-simple-args* nil)
663 (expand-next-arg)))))
665 ;;;; format directive for indirection
667 (def-format-directive #\? (colonp atsignp params string end)
670 :complaint "cannot use the colon modifier with this directive"))
671 (expand-bind-defaults () params
677 "~A~%while processing indirect format string:"
678 :args (list condition)
680 :control-string ,string
681 :offset ,(1- end)))))
683 (if *orig-args-available*
684 `(setf args (%format stream ,(expand-next-arg) orig-args args))
685 (throw 'need-orig-args nil))
686 `(%format stream ,(expand-next-arg) ,(expand-next-arg))))))
688 ;;;; format directives for capitalization
690 (def-complex-format-directive #\( (colonp atsignp params directives)
691 (let ((close (find-directive directives #\) nil)))
694 :complaint "no corresponding close parenthesis"))
695 (let* ((posn (position close directives))
696 (before (subseq directives 0 posn))
697 (after (nthcdr (1+ posn) directives)))
699 (expand-bind-defaults () params
700 `(let ((stream (make-case-frob-stream stream
708 ,@(expand-directive-list before)))
711 (def-complex-format-directive #\) ()
713 :complaint "no corresponding open parenthesis"))
715 ;;;; format directives and support functions for conditionalization
717 (def-complex-format-directive #\[ (colonp atsignp params directives)
718 (multiple-value-bind (sublists last-semi-with-colon-p remaining)
719 (parse-conditional-directive directives)
725 "both colon and atsign modifiers used simultaneously")
729 "Can only specify one section")
730 (expand-bind-defaults () params
731 (expand-maybe-conditional (car sublists)))))
733 (if (= (length sublists) 2)
734 (expand-bind-defaults () params
735 (expand-true-false-conditional (car sublists)
739 "must specify exactly two sections"))
740 (expand-bind-defaults ((index (expand-next-arg))) params
741 (setf *only-simple-args* nil)
743 (when last-semi-with-colon-p
744 (push `(t ,@(expand-directive-list (pop sublists)))
746 (let ((count (length sublists)))
747 (dolist (sublist sublists)
748 (push `(,(decf count)
749 ,@(expand-directive-list sublist))
751 `(case ,index ,@clauses)))))
754 (defun parse-conditional-directive (directives)
756 (last-semi-with-colon-p nil)
757 (remaining directives))
759 (let ((close-or-semi (find-directive remaining #\] t)))
760 (unless close-or-semi
762 :complaint "no corresponding close bracket"))
763 (let ((posn (position close-or-semi remaining)))
764 (push (subseq remaining 0 posn) sublists)
765 (setf remaining (nthcdr (1+ posn) remaining))
766 (when (char= (format-directive-character close-or-semi) #\])
768 (setf last-semi-with-colon-p
769 (format-directive-colonp close-or-semi)))))
770 (values sublists last-semi-with-colon-p remaining)))
772 (defun expand-maybe-conditional (sublist)
774 `(let ((prev-args args)
775 (arg ,(expand-next-arg)))
777 (setf args prev-args)
778 ,@(expand-directive-list sublist)))))
779 (if *only-simple-args*
780 (multiple-value-bind (guts new-args)
781 (let ((*simple-args* *simple-args*))
782 (values (expand-directive-list sublist)
784 (cond ((eq *simple-args* (cdr new-args))
785 (setf *simple-args* new-args)
786 `(when ,(caar new-args)
789 (setf *only-simple-args* nil)
793 (defun expand-true-false-conditional (true false)
794 (let ((arg (expand-next-arg)))
798 ,@(expand-directive-list true))
800 ,@(expand-directive-list false)))))
801 (if *only-simple-args*
802 (multiple-value-bind (true-guts true-args true-simple)
803 (let ((*simple-args* *simple-args*)
804 (*only-simple-args* t))
805 (values (expand-directive-list true)
808 (multiple-value-bind (false-guts false-args false-simple)
809 (let ((*simple-args* *simple-args*)
810 (*only-simple-args* t))
811 (values (expand-directive-list false)
814 (if (= (length true-args) (length false-args))
818 ,(do ((false false-args (cdr false))
819 (true true-args (cdr true))
820 (bindings nil (cons `(,(caar false) ,(caar true))
822 ((eq true *simple-args*)
823 (setf *simple-args* true-args)
824 (setf *only-simple-args*
825 (and true-simple false-simple))
832 (setf *only-simple-args* nil)
836 (def-complex-format-directive #\; ()
839 "~~; directive not contained within either ~~[...~~] or ~~<...~~>"))
841 (def-complex-format-directive #\] ()
844 "no corresponding open bracket"))
846 ;;;; format directive for up-and-out
848 (def-format-directive #\^ (colonp atsignp params)
851 :complaint "cannot use the at-sign modifier with this directive"))
852 (when (and colonp (not *up-up-and-out-allowed*))
854 :complaint "attempt to use ~~:^ outside a ~~:{...~~} construct"))
855 `(when ,(case (length params)
859 (setf *only-simple-args* nil)
861 (1 (expand-bind-defaults ((count 0)) params
863 (2 (expand-bind-defaults ((arg1 0) (arg2 0)) params
865 (t (expand-bind-defaults ((arg1 0) (arg2 0) (arg3 0)) params
866 `(<= ,arg1 ,arg2 ,arg3))))
868 '(return-from outside-loop nil)
871 ;;;; format directives for iteration
873 (def-complex-format-directive #\{ (colonp atsignp params string end directives)
874 (let ((close (find-directive directives #\} nil)))
877 :complaint "no corresponding close brace"))
878 (let* ((closed-with-colon (format-directive-colonp close))
879 (posn (position close directives)))
883 (if *orig-args-available*
889 "~A~%while processing indirect format string:"
890 :args (list condition)
892 :control-string ,string
893 :offset ,(1- end)))))
895 (%format stream inside-string orig-args args))))
896 (throw 'need-orig-args nil))
897 (let ((*up-up-and-out-allowed* colonp))
898 (expand-directive-list (subseq directives 0 posn)))))
899 (compute-loop-aux (count)
901 (setf *only-simple-args* nil))
903 ,@(unless closed-with-colon
907 `((when (and ,count (minusp (decf ,count)))
910 (let ((*expander-next-arg-macro* 'expander-next-arg)
911 (*only-simple-args* nil)
912 (*orig-args-available* t))
913 `((let* ((orig-args ,(expand-next-arg))
916 (declare (ignorable orig-args outside-args args))
918 ,@(compute-insides)))))
920 ,@(when closed-with-colon
925 (expand-bind-defaults ((count nil)) params
926 (compute-loop-aux count))
927 (compute-loop-aux nil)))
936 `(let* ((orig-args ,(expand-next-arg))
938 (declare (ignorable orig-args args))
939 ,(let ((*expander-next-arg-macro* 'expander-next-arg)
940 (*only-simple-args* nil)
941 (*orig-args-available* t))
943 (values (if (zerop posn)
944 `(let ((inside-string ,(expand-next-arg)))
947 (nthcdr (1+ posn) directives))))))
949 (def-complex-format-directive #\} ()
951 :complaint "no corresponding open brace"))
953 ;;;; format directives and support functions for justification
955 (defparameter *illegal-inside-justification*
956 (mapcar (lambda (x) (parse-directive x 0))
957 '("~W" "~:W" "~@W" "~:@W"
958 "~_" "~:_" "~@_" "~:@_"
960 "~I" "~:I" "~@I" "~:@I"
963 (defun illegal-inside-justification-p (directive)
964 (member directive *illegal-inside-justification*
966 (and (format-directive-p x)
967 (format-directive-p y)
968 (eql (format-directive-character x) (format-directive-character y))
969 (eql (format-directive-colonp x) (format-directive-colonp y))
970 (eql (format-directive-atsignp x) (format-directive-atsignp y))))))
972 (def-complex-format-directive #\< (colonp atsignp params string end directives)
973 (multiple-value-bind (segments first-semi close remaining)
974 (parse-format-justification directives)
976 (if (format-directive-colonp close)
977 (multiple-value-bind (prefix per-line-p insides suffix)
978 (parse-format-logical-block segments colonp first-semi
979 close params string end)
980 (expand-format-logical-block prefix per-line-p insides
982 (let ((count (reduce #'+ (mapcar (lambda (x) (count-if #'illegal-inside-justification-p x)) segments))))
984 ;; ANSI specifies that "an error is signalled" in this
987 :complaint "~D illegal directive~:P found inside justification block"
989 (expand-format-justification segments colonp atsignp
993 (def-complex-format-directive #\> ()
995 :complaint "no corresponding open bracket"))
997 (defun parse-format-logical-block
998 (segments colonp first-semi close params string end)
1000 (error 'format-error
1001 :complaint "No parameters can be supplied with ~~<...~~:>."
1002 :offset (caar params)))
1003 (multiple-value-bind (prefix insides suffix)
1004 (multiple-value-bind (prefix-default suffix-default)
1005 (if colonp (values "(" ")") (values "" ""))
1006 (flet ((extract-string (list prefix-p)
1007 (let ((directive (find-if #'format-directive-p list)))
1009 (error 'format-error
1011 "cannot include format directives inside the ~
1012 ~:[suffix~;prefix~] segment of ~~<...~~:>"
1013 :args (list prefix-p)
1014 :offset (1- (format-directive-end directive)))
1015 (apply #'concatenate 'string list)))))
1016 (case (length segments)
1017 (0 (values prefix-default nil suffix-default))
1018 (1 (values prefix-default (car segments) suffix-default))
1019 (2 (values (extract-string (car segments) t)
1020 (cadr segments) suffix-default))
1021 (3 (values (extract-string (car segments) t)
1023 (extract-string (caddr segments) nil)))
1025 (error 'format-error
1026 :complaint "too many segments for ~~<...~~:>")))))
1027 (when (format-directive-atsignp close)
1029 (add-fill-style-newlines insides
1032 (format-directive-end first-semi)
1035 (and first-semi (format-directive-atsignp first-semi))
1039 (defun add-fill-style-newlines (list string offset &optional last-directive)
1042 (let ((directive (car list)))
1044 ((simple-string-p directive)
1045 (let* ((non-space (position #\Space directive :test #'char/=))
1046 (newlinep (and last-directive
1048 (format-directive-character last-directive)
1051 ((and newlinep non-space)
1053 (list (subseq directive 0 non-space))
1054 (add-fill-style-newlines-aux
1055 (subseq directive non-space) string (+ offset non-space))
1056 (add-fill-style-newlines
1057 (cdr list) string (+ offset (length directive)))))
1060 (add-fill-style-newlines
1061 (cdr list) string (+ offset (length directive)))))
1063 (nconc (add-fill-style-newlines-aux directive string offset)
1064 (add-fill-style-newlines
1065 (cdr list) string (+ offset (length directive))))))))
1068 (add-fill-style-newlines
1070 (format-directive-end directive) directive))))))
1073 (defun add-fill-style-newlines-aux (literal string offset)
1074 (let ((end (length literal))
1076 (collect ((results))
1078 (let ((blank (position #\space literal :start posn)))
1080 (results (subseq literal posn))
1082 (let ((non-blank (or (position #\space literal :start blank
1085 (results (subseq literal posn non-blank))
1086 (results (make-format-directive
1087 :string string :character #\_
1088 :start (+ offset non-blank) :end (+ offset non-blank)
1089 :colonp t :atsignp nil :params nil))
1090 (setf posn non-blank))
1095 (defun parse-format-justification (directives)
1096 (let ((first-semi nil)
1098 (remaining directives))
1099 (collect ((segments))
1101 (let ((close-or-semi (find-directive remaining #\> t)))
1102 (unless close-or-semi
1103 (error 'format-error
1104 :complaint "no corresponding close bracket"))
1105 (let ((posn (position close-or-semi remaining)))
1106 (segments (subseq remaining 0 posn))
1107 (setf remaining (nthcdr (1+ posn) remaining)))
1108 (when (char= (format-directive-character close-or-semi)
1110 (setf close close-or-semi)
1113 (setf first-semi close-or-semi))))
1114 (values (segments) first-semi close remaining))))
1116 (sb!xc:defmacro expander-pprint-next-arg (string offset)
1119 (error 'format-error
1120 :complaint "no more arguments"
1121 :control-string ,string
1126 (defun expand-format-logical-block (prefix per-line-p insides suffix atsignp)
1127 `(let ((arg ,(if atsignp 'args (expand-next-arg))))
1129 (setf *only-simple-args* nil)
1131 (pprint-logical-block
1133 ,(if per-line-p :per-line-prefix :prefix) ,prefix
1137 `((orig-args arg))))
1138 (declare (ignorable args ,@(unless atsignp '(orig-args))))
1140 ,@(let ((*expander-next-arg-macro* 'expander-pprint-next-arg)
1141 (*only-simple-args* nil)
1142 (*orig-args-available*
1143 (if atsignp *orig-args-available* t)))
1144 (expand-directive-list insides)))))))
1146 (defun expand-format-justification (segments colonp atsignp first-semi params)
1147 (let ((newline-segment-p
1149 (format-directive-colonp first-semi))))
1150 (expand-bind-defaults
1151 ((mincol 0) (colinc 1) (minpad 0) (padchar #\space))
1153 `(let ((segments nil)
1154 ,@(when newline-segment-p
1155 '((newline-segment nil)
1159 ,@(when newline-segment-p
1160 `((setf newline-segment
1161 (with-output-to-string (stream)
1162 ,@(expand-directive-list (pop segments))))
1163 ,(expand-bind-defaults
1165 (line-len '(or (sb!impl::line-length stream) 72)))
1166 (format-directive-params first-semi)
1167 `(setf extra-space ,extra line-len ,line-len))))
1168 ,@(mapcar (lambda (segment)
1169 `(push (with-output-to-string (stream)
1170 ,@(expand-directive-list segment))
1173 (format-justification stream
1174 ,@(if newline-segment-p
1175 '(newline-segment extra-space line-len)
1177 segments ,colonp ,atsignp
1178 ,mincol ,colinc ,minpad ,padchar)))))
1180 ;;;; format directive and support function for user-defined method
1182 (def-format-directive #\/ (string start end colonp atsignp params)
1183 (let ((symbol (extract-user-fun-name string start end)))
1184 (collect ((param-names) (bindings))
1185 (dolist (param-and-offset params)
1186 (let ((param (cdr param-and-offset)))
1187 (let ((param-name (gensym)))
1188 (param-names param-name)
1189 (bindings `(,param-name
1191 (:arg (expand-next-arg))
1192 (:remaining '(length args))
1195 (,symbol stream ,(expand-next-arg) ,colonp ,atsignp
1196 ,@(param-names))))))
1198 (defun extract-user-fun-name (string start end)
1199 (let ((slash (position #\/ string :start start :end (1- end)
1202 (error 'format-error
1203 :complaint "malformed ~~/ directive"))
1204 (let* ((name (string-upcase (let ((foo string))
1205 ;; Hack alert: This is to keep the compiler
1206 ;; quiet about deleting code inside the
1207 ;; subseq expansion.
1208 (subseq foo (1+ slash) (1- end)))))
1209 (first-colon (position #\: name))
1210 (second-colon (if first-colon (position #\: name :start (1+ first-colon))))
1211 (package-name (if first-colon
1212 (subseq name 0 first-colon)
1213 "COMMON-LISP-USER"))
1214 (package (find-package package-name)))
1216 ;; FIXME: should be PACKAGE-ERROR? Could we just use
1217 ;; FIND-UNDELETED-PACKAGE-OR-LOSE?
1218 (error 'format-error
1219 :complaint "no package named ~S"
1220 :args (list package-name)))
1222 ((and second-colon (= second-colon (1+ first-colon)))
1223 (subseq name (1+ second-colon)))
1225 (subseq name (1+ first-colon)))
1229 ;;; compile-time checking for argument mismatch. This code is
1230 ;;; inspired by that of Gerd Moellmann, and comes decorated with
1232 (defun %compiler-walk-format-string (string args)
1233 (declare (type simple-string string))
1234 (let ((*default-format-error-control-string* string))
1235 (macrolet ((incf-both (&optional (increment 1))
1237 (incf min ,increment)
1238 (incf max ,increment)))
1239 (walk-complex-directive (function)
1240 `(multiple-value-bind (min-inc max-inc remaining)
1241 (,function directive directives args)
1244 (setq directives remaining))))
1245 ;; FIXME: these functions take a list of arguments as well as
1246 ;; the directive stream. This is to enable possibly some
1247 ;; limited type checking on FORMAT's arguments, as well as
1248 ;; simple argument count mismatch checking: when the minimum and
1249 ;; maximum argument counts are the same at a given point, we
1250 ;; know which argument is going to be used for a given
1251 ;; directive, and some (annotated below) require arguments of
1252 ;; particular types.
1254 ((walk-justification (justification directives args)
1255 (declare (ignore args))
1256 (let ((*default-format-error-offset*
1257 (1- (format-directive-end justification))))
1258 (multiple-value-bind (segments first-semi close remaining)
1259 (parse-format-justification directives)
1260 (declare (ignore segments first-semi))
1262 ((not (format-directive-colonp close))
1263 (values 0 0 directives))
1264 ((format-directive-atsignp justification)
1265 (values 0 sb!xc:call-arguments-limit directives))
1266 ;; FIXME: here we could assert that the
1267 ;; corresponding argument was a list.
1268 (t (values 1 1 remaining))))))
1269 (walk-conditional (conditional directives args)
1270 (let ((*default-format-error-offset*
1271 (1- (format-directive-end conditional))))
1272 (multiple-value-bind (sublists last-semi-with-colon-p remaining)
1273 (parse-conditional-directive directives)
1274 (declare (ignore last-semi-with-colon-p))
1276 (loop for s in sublists
1278 1 (walk-directive-list s args)))))
1280 ((format-directive-atsignp conditional)
1281 (values 1 (max 1 sub-max) remaining))
1282 ((loop for p in (format-directive-params conditional)
1283 thereis (or (integerp (cdr p))
1284 (memq (cdr p) '(:remaining :arg))))
1285 (values 0 sub-max remaining))
1286 ;; FIXME: if not COLONP, then the next argument
1287 ;; must be a number.
1288 (t (values 1 (1+ sub-max) remaining)))))))
1289 (walk-iteration (iteration directives args)
1290 (declare (ignore args))
1291 (let ((*default-format-error-offset*
1292 (1- (format-directive-end iteration))))
1293 (let* ((close (find-directive directives #\} nil))
1294 (posn (or (position close directives)
1295 (error 'format-error
1296 :complaint "no corresponding close brace")))
1297 (remaining (nthcdr (1+ posn) directives)))
1298 ;; FIXME: if POSN is zero, the next argument must be
1299 ;; a format control (either a function or a string).
1300 (if (format-directive-atsignp iteration)
1301 (values (if (zerop posn) 1 0)
1302 sb!xc:call-arguments-limit
1304 ;; FIXME: the argument corresponding to this
1305 ;; directive must be a list.
1306 (let ((nreq (if (zerop posn) 2 1)))
1307 (values nreq nreq remaining))))))
1308 (walk-directive-list (directives args)
1309 (let ((min 0) (max 0))
1311 (let ((directive (pop directives)))
1312 (when (null directive)
1313 (return (values min (min max sb!xc:call-arguments-limit))))
1314 (when (format-directive-p directive)
1315 (incf-both (count :arg (format-directive-params directive)
1317 (let ((c (format-directive-character directive)))
1319 ((find c "ABCDEFGORSWX$/")
1322 (unless (format-directive-colonp directive)
1324 ((or (find c "IT%&|_();>") (char= c #\Newline)))
1325 ;; FIXME: check correspondence of ~( and ~)
1327 (walk-complex-directive walk-justification))
1329 (walk-complex-directive walk-conditional))
1331 (walk-complex-directive walk-iteration))
1333 ;; FIXME: the argument corresponding to this
1334 ;; directive must be a format control.
1336 ((format-directive-atsignp directive)
1338 (setq max sb!xc:call-arguments-limit))
1340 (t (throw 'give-up-format-string-walk nil))))))))))
1341 (catch 'give-up-format-string-walk
1342 (let ((directives (tokenize-control-string string)))
1343 (walk-directive-list directives args)))))))