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 reference-condition
)
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 (second-relative :reader format-error-second-relative
21 :initarg
:second-relative
:initform nil
)
22 (print-banner :reader format-error-print-banner
:initarg
:print-banner
24 (:report %print-format-error
)
25 (:default-initargs
:references nil
))
27 (defun %print-format-error
(condition stream
)
29 "~:[~*~;error in ~S: ~]~?~@[~% ~A~% ~V@T^~@[~V@T^~]~]"
30 (format-error-print-banner condition
)
32 (format-error-complaint condition
)
33 (format-error-args condition
)
34 (format-error-control-string condition
)
35 (format-error-offset condition
)
36 (format-error-second-relative condition
)))
38 (def!struct format-directive
39 (string (missing-arg) :type simple-string
)
40 (start (missing-arg) :type
(and unsigned-byte fixnum
))
41 (end (missing-arg) :type
(and unsigned-byte fixnum
))
42 (character (missing-arg) :type character
)
43 (colonp nil
:type
(member t nil
))
44 (atsignp nil
:type
(member t nil
))
45 (params nil
:type list
))
46 (def!method print-object
((x format-directive
) stream
)
47 (print-unreadable-object (x stream
)
48 (write-string (format-directive-string x
)
50 :start
(format-directive-start x
)
51 :end
(format-directive-end x
))))
53 ;;;; TOKENIZE-CONTROL-STRING
55 (defun tokenize-control-string (string)
56 (declare (simple-string string
))
60 ;; FIXME: consider rewriting this 22.3.5.2-related processing
61 ;; using specials to maintain state and doing the logic inside
62 ;; the directive expanders themselves.
66 (justification-semicolon))
68 (let ((next-directive (or (position #\~ string
:start index
) end
)))
69 (when (> next-directive index
)
70 (push (subseq string index next-directive
) result
))
71 (when (= next-directive end
)
73 (let* ((directive (parse-directive string next-directive
))
74 (char (format-directive-character directive
)))
75 ;; this processing is required by CLHS 22.3.5.2
77 ((char= char
#\
<) (push directive block
))
78 ((and block
(char= char
#\
;) (format-directive-colonp directive))
79 (setf semicolon directive
))
83 :complaint
"~~> without a matching ~~<"
84 :control-string string
85 :offset next-directive
))
87 ((format-directive-colonp directive
)
89 (setf pprint
(car block
)))
92 (unless justification-semicolon
93 (setf justification-semicolon semicolon
))))
95 ;; block cases are handled by the #\< expander/interpreter
98 ((#\W
#\I
#\_
) (unless pprint
(setf pprint directive
)))
99 (#\T
(when (and (format-directive-colonp directive
)
101 (setf pprint directive
))))))
102 (push directive result
)
103 (setf index
(format-directive-end directive
)))))
104 (when (and pprint justification-semicolon
)
105 (let ((pprint-offset (1- (format-directive-end pprint
)))
106 (justification-offset
107 (1- (format-directive-end justification-semicolon
))))
109 :complaint
"misuse of justification and pprint directives"
110 :control-string string
111 :offset
(min pprint-offset justification-offset
)
112 :second-relative
(- (max pprint-offset justification-offset
)
113 (min pprint-offset justification-offset
)
115 :references
(list '(:ansi-cl
:section
(22 3 5 2))))))
118 (defun parse-directive (string start
)
119 (let ((posn (1+ start
)) (params nil
) (colonp nil
) (atsignp nil
)
120 (end (length string
)))
124 :complaint
"string ended before directive was found"
125 :control-string string
127 (schar string posn
)))
129 (when (or colonp atsignp
)
131 :complaint
"parameters found after #\\: or #\\@ modifier"
132 :control-string string
134 :references
(list '(:ansi-cl
:section
(22 3)))))))
136 (let ((char (get-char)))
137 (cond ((or (char<= #\
0 char
#\
9) (char= char
#\
+) (char= char
#\-
))
139 (multiple-value-bind (param new-posn
)
140 (parse-integer string
:start posn
:junk-allowed t
)
141 (push (cons posn param
) params
)
149 ((or (char= char
#\v)
152 (push (cons posn
:arg
) params
)
162 (push (cons posn
:remaining
) params
)
173 (push (cons posn
(get-char)) params
)
175 (unless (char= (get-char) #\
,)
179 (push (cons posn nil
) params
))
183 :complaint
"too many colons supplied"
184 :control-string string
186 :references
(list '(:ansi-cl
:section
(22 3))))
191 :complaint
"too many #\\@ characters supplied"
192 :control-string string
194 :references
(list '(:ansi-cl
:section
(22 3))))
197 (when (and (char= (schar string
(1- posn
)) #\
,)
199 (char/= (schar string
(- posn
2)) #\')))
201 (push (cons (1- posn
) nil
) params
))
204 (let ((char (get-char)))
205 (when (char= char
#\
/)
206 (let ((closing-slash (position #\
/ string
:start
(1+ posn
))))
208 (setf posn closing-slash
)
210 :complaint
"no matching closing slash"
211 :control-string string
213 (make-format-directive
214 :string string
:start start
:end
(1+ posn
)
215 :character
(char-upcase char
)
216 :colonp colonp
:atsignp atsignp
217 :params
(nreverse params
))))))
221 (sb!xc
:defmacro formatter
(control-string)
222 `#',(%formatter control-string
))
225 (defun %formatter
(control-string &optional
(arg-count 0) (need-retval t
))
226 ;; ARG-COUNT is supplied only when the use of this formatter is in a literal
227 ;; call to FORMAT, in which case we can possibly elide &optional parsing.
228 ;; But we can't in general, because FORMATTER may be called by users
229 ;; to obtain functions that may be invoked in random wrong ways.
230 ;; NEED-RETVAL signifies that the caller wants back the list of
231 ;; unconsumed arguments. This is the default assumption.
233 (catch 'need-orig-args
234 (let* ((*simple-args
* nil
)
235 (*only-simple-args
* t
)
236 (guts (expand-control-string control-string
)) ; can throw
239 (dolist (arg *simple-args
*)
240 (cond ((plusp arg-count
)
241 (push (car arg
) required
)
245 (args-exhausted ,control-string
,(cdr arg
)))
247 (return `(lambda (stream ,@required
248 ,@(if optional
'(&optional
)) ,@optional
250 (declare (ignorable stream args
))
252 ,(and need-retval
'args
)))))
253 (let ((*orig-args-available
* t
)
254 (*only-simple-args
* nil
))
255 `(lambda (stream &rest orig-args
)
256 (declare (ignorable stream
))
257 (let ((args orig-args
))
258 ,(expand-control-string control-string
)
259 ,(and need-retval
'args
))))))
261 (defun args-exhausted (control-string offset
)
263 :complaint
"required argument missing"
264 :control-string control-string
267 (defun expand-control-string (string)
268 (let* ((string (etypecase string
272 (coerce string
'simple-string
))))
273 (*default-format-error-control-string
* string
)
274 (directives (tokenize-control-string string
)))
276 ,@(expand-directive-list directives
))))
278 (defun expand-directive-list (directives)
280 (remaining-directives directives
))
282 (unless remaining-directives
284 (multiple-value-bind (form new-directives
)
285 (expand-directive (car remaining-directives
)
286 (cdr remaining-directives
))
288 (setf remaining-directives new-directives
)))
291 (defun expand-directive (directive more-directives
)
295 (let ((char (format-directive-character directive
)))
298 (aref *format-directive-expanders
* (sb!xc
:char-code char
))))))
299 (*default-format-error-offset
*
300 (1- (format-directive-end directive
))))
301 (declare (type (or null function
) expander
))
303 (funcall expander directive more-directives
)
305 :complaint
"unknown directive ~@[(character: ~A)~]"
306 :args
(list (char-name (format-directive-character directive
)))))))
308 (values `(write-string ,directive stream
)
311 (defmacro-mundanely expander-next-arg
(string offset
)
315 :complaint
"no more arguments"
316 :control-string
,string
319 (defun expand-next-arg (&optional offset
)
320 (if (or *orig-args-available
* (not *only-simple-args
*))
321 `(,*expander-next-arg-macro
*
322 ,*default-format-error-control-string
*
323 ,(or offset
*default-format-error-offset
*))
324 (let ((symbol (sb!xc
:gensym
"FORMAT-ARG")))
325 (push (cons symbol
(or offset
*default-format-error-offset
*))
329 (defmacro expand-bind-defaults
(specs params
&body body
)
330 (once-only ((params params
))
332 (collect ((expander-bindings) (runtime-bindings))
334 (destructuring-bind (var default
) spec
335 (let ((symbol (sb!xc
:gensym
"FVAR")))
340 (let* ((param-and-offset (pop ,params
))
341 (offset (car param-and-offset
))
342 (param (cdr param-and-offset
)))
344 (:arg
`(or ,(expand-next-arg offset
) ,,default
))
346 (setf *only-simple-args
* nil
)
350 `(let ,(expander-bindings)
351 `(let ,(list ,@(runtime-bindings))
355 :complaint
"too many parameters, expected no more than ~W"
356 :args
(list ,(length specs
))
357 :offset
(caar ,params
)))
362 :complaint
"too many parameters, expected none"
363 :offset
(caar ,params
)))
366 ;;;; format directive machinery
368 (eval-when (:compile-toplevel
:execute
)
369 (#+sb-xc-host defmacro
#-sb-xc-host sb
!xc
:defmacro def-complex-format-directive
(char lambda-list
&body body
)
370 (let ((defun-name (intern (format nil
371 "~:@(~:C~)-FORMAT-DIRECTIVE-EXPANDER"
373 (directive (sb!xc
:gensym
"DIRECTIVE"))
374 (directives (if lambda-list
(car (last lambda-list
)) (sb!xc
:gensym
"DIRECTIVES"))))
376 (defun ,defun-name
(,directive
,directives
)
378 `((let ,(mapcar (lambda (var)
380 (,(symbolicate "FORMAT-DIRECTIVE-" var
)
382 (butlast lambda-list
))
384 `((declare (ignore ,directive
,directives
))
386 (%set-format-directive-expander
,char
#',defun-name
))))
388 (#+sb-xc-host defmacro
#-sb-xc-host sb
!xc
:defmacro def-format-directive
(char lambda-list
&body body
)
389 (let ((directives (sb!xc
:gensym
"DIRECTIVES"))
391 (body-without-decls body
))
393 (let ((form (car body-without-decls
)))
394 (unless (and (consp form
) (eq (car form
) 'declare
))
396 (push (pop body-without-decls
) declarations
)))
397 (setf declarations
(reverse declarations
))
398 `(def-complex-format-directive ,char
(,@lambda-list
,directives
)
400 (values (progn ,@body-without-decls
)
404 (eval-when (#-sb-xc
:compile-toplevel
:load-toplevel
:execute
)
406 (defun %set-format-directive-expander
(char fn
)
407 (let ((code (sb!xc
:char-code
(char-upcase char
))))
408 (setf (aref *format-directive-expanders
* code
) fn
))
411 (defun %set-format-directive-interpreter
(char fn
)
412 (let ((code (sb!xc
:char-code
(char-upcase char
))))
413 (setf (aref *format-directive-interpreters
* code
) fn
))
416 (defun find-directive (directives kind stop-at-semi
)
418 (let ((next (car directives
)))
419 (if (format-directive-p next
)
420 (let ((char (format-directive-character next
)))
421 (if (or (char= kind char
)
422 (and stop-at-semi
(char= char
#\
;)))
425 (cdr (flet ((after (char)
426 (member (find-directive (cdr directives
)
437 (find-directive (cdr directives
) kind stop-at-semi
)))))
441 ;;;; format directives for simple output
443 (def-format-directive #\A
(colonp atsignp params
)
445 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
448 `(format-princ stream
,(expand-next-arg) ',colonp
',atsignp
449 ,mincol
,colinc
,minpad
,padchar
))
451 `(or ,(expand-next-arg) "()")
455 (def-format-directive #\S
(colonp atsignp params
)
457 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
460 `(format-prin1 stream
,(expand-next-arg) ,colonp
,atsignp
461 ,mincol
,colinc
,minpad
,padchar
)))
463 `(let ((arg ,(expand-next-arg)))
466 (princ "()" stream
))))
468 `(prin1 ,(expand-next-arg) stream
))))
470 (def-format-directive #\C
(colonp atsignp params string end
)
471 (expand-bind-defaults () params
472 (let ((n-arg (sb!xc
:gensym
"ARG")))
473 `(let ((,n-arg
,(expand-next-arg)))
474 (unless (typep ,n-arg
'character
)
476 :complaint
"~s is not of type CHARACTER."
478 :control-string
,string
481 `(format-print-named-character ,n-arg stream
))
483 `(prin1 ,n-arg stream
))
485 `(write-char ,n-arg stream
)))))))
487 (def-format-directive #\W
(colonp atsignp params
)
488 (expand-bind-defaults () params
489 (if (or colonp atsignp
)
490 `(let (,@(when colonp
491 '((*print-pretty
* t
)))
493 '((*print-level
* nil
)
494 (*print-length
* nil
))))
495 (output-object ,(expand-next-arg) stream
))
496 `(output-object ,(expand-next-arg) stream
))))
498 ;;;; format directives for integer output
500 (defun expand-format-integer (base colonp atsignp params
)
501 (if (or colonp atsignp params
)
502 (expand-bind-defaults
503 ((mincol 0) (padchar #\space
) (commachar #\
,) (commainterval 3))
505 `(format-print-integer stream
,(expand-next-arg) ,colonp
,atsignp
506 ,base
,mincol
,padchar
,commachar
508 `(let ((*print-base
* ,base
)
510 (princ ,(expand-next-arg) stream
))))
512 (def-format-directive #\D
(colonp atsignp params
)
513 (expand-format-integer 10 colonp atsignp params
))
515 (def-format-directive #\B
(colonp atsignp params
)
516 (expand-format-integer 2 colonp atsignp params
))
518 (def-format-directive #\O
(colonp atsignp params
)
519 (expand-format-integer 8 colonp atsignp params
))
521 (def-format-directive #\X
(colonp atsignp params
)
522 (expand-format-integer 16 colonp atsignp params
))
524 (def-format-directive #\R
(colonp atsignp params string end
)
525 (expand-bind-defaults
526 ((base nil
) (mincol 0) (padchar #\space
) (commachar #\
,)
529 (let ((n-arg (sb!xc
:gensym
"ARG")))
530 `(let ((,n-arg
,(expand-next-arg)))
534 :complaint
"~s is not of type INTEGER."
536 :control-string
,string
539 (format-print-integer stream
,n-arg
,colonp
,atsignp
541 ,padchar
,commachar
,commainterval
)
544 `(format-print-old-roman stream
,n-arg
)
545 `(format-print-roman stream
,n-arg
))
547 `(format-print-ordinal stream
,n-arg
)
548 `(format-print-cardinal stream
,n-arg
))))))))
550 ;;;; format directive for pluralization
552 (def-format-directive #\P
(colonp atsignp params end
)
553 (expand-bind-defaults () params
557 (*orig-args-available
*
558 `(if (eq orig-args args
)
560 :complaint
"no previous argument"
562 (do ((arg-ptr orig-args
(cdr arg-ptr
)))
563 ((eq (cdr arg-ptr
) args
)
566 (unless *simple-args
*
568 :complaint
"no previous argument"))
569 (caar *simple-args
*))
571 (/show0
"THROWing NEED-ORIG-ARGS from tilde-P")
572 (throw 'need-orig-args nil
)))))
574 `(write-string (if (eql ,arg
1) "y" "ies") stream
)
575 `(unless (eql ,arg
1) (write-char #\s stream
))))))
577 ;;;; format directives for floating point output
579 (def-format-directive #\F
(colonp atsignp params
)
583 "The colon modifier cannot be used with this directive."))
584 (expand-bind-defaults ((w nil
) (d nil
) (k nil
) (ovf nil
) (pad #\space
)) params
585 `(format-fixed stream
,(expand-next-arg) ,w
,d
,k
,ovf
,pad
,atsignp
)))
587 (def-format-directive #\E
(colonp atsignp params
)
591 "The colon modifier cannot be used with this directive."))
592 (expand-bind-defaults
593 ((w nil
) (d nil
) (e nil
) (k 1) (ovf nil
) (pad #\space
) (mark nil
))
595 `(format-exponential stream
,(expand-next-arg) ,w
,d
,e
,k
,ovf
,pad
,mark
598 (def-format-directive #\G
(colonp atsignp params
)
602 "The colon modifier cannot be used with this directive."))
603 (expand-bind-defaults
604 ((w nil
) (d nil
) (e nil
) (k nil
) (ovf nil
) (pad #\space
) (mark nil
))
606 `(format-general stream
,(expand-next-arg) ,w
,d
,e
,k
,ovf
,pad
,mark
,atsignp
)))
608 (def-format-directive #\$
(colonp atsignp params
)
609 (expand-bind-defaults ((d 2) (n 1) (w 0) (pad #\space
)) params
610 `(format-dollars stream
,(expand-next-arg) ,d
,n
,w
,pad
,colonp
613 ;;;; format directives for line/page breaks etc.
615 (def-format-directive #\%
(colonp atsignp params
)
616 (when (or colonp atsignp
)
619 "The colon and atsign modifiers cannot be used with this directive."
622 (expand-bind-defaults ((count 1)) params
627 (def-format-directive #\
& (colonp atsignp params
)
628 (when (or colonp atsignp
)
631 "The colon and atsign modifiers cannot be used with this directive."
634 (expand-bind-defaults ((count 1)) params
638 (dotimes (i (1- ,count
))
640 '(fresh-line stream
)))
642 (def-format-directive #\|
(colonp atsignp params
)
643 (when (or colonp atsignp
)
646 "The colon and atsign modifiers cannot be used with this directive."
649 (expand-bind-defaults ((count 1)) params
651 (write-char (code-char form-feed-char-code
) stream
)))
652 '(write-char (code-char form-feed-char-code
) stream
)))
654 (def-format-directive #\~
(colonp atsignp params
)
655 (when (or colonp atsignp
)
658 "The colon and atsign modifiers cannot be used with this directive."
661 (expand-bind-defaults ((count 1)) params
663 (write-char #\~ stream
)))
664 '(write-char #\~ stream
)))
666 (def-complex-format-directive #\newline
(colonp atsignp params directives
)
667 (when (and colonp atsignp
)
668 ;; FIXME: this is not an error!
670 :complaint
"both colon and atsign modifiers used simultaneously"))
671 (values (expand-bind-defaults () params
673 '(write-char #\newline stream
)
675 (if (and (not colonp
)
677 (simple-string-p (car directives
)))
678 (cons (string-left-trim *format-whitespace-chars
*
683 ;;;; format directives for tabs and simple pretty printing
685 (def-format-directive #\T
(colonp atsignp params
)
687 (expand-bind-defaults ((n 1) (m 1)) params
688 `(pprint-tab ,(if atsignp
:section-relative
:section
)
691 (expand-bind-defaults ((colrel 1) (colinc 1)) params
692 `(format-relative-tab stream
,colrel
,colinc
))
693 (expand-bind-defaults ((colnum 1) (colinc 1)) params
694 `(format-absolute-tab stream
,colnum
,colinc
)))))
696 (def-format-directive #\_
(colonp atsignp params
)
697 (expand-bind-defaults () params
698 `(pprint-newline ,(if colonp
707 (def-format-directive #\I
(colonp atsignp params
)
711 "cannot use the at-sign modifier with this directive"))
712 (expand-bind-defaults ((n 0)) params
713 `(pprint-indent ,(if colonp
:current
:block
) ,n stream
)))
715 ;;;; format directive for ~*
717 (def-format-directive #\
* (colonp atsignp params end
)
722 "both colon and atsign modifiers used simultaneously")
723 (expand-bind-defaults ((posn 0)) params
724 (unless *orig-args-available
*
725 (/show0
"THROWing NEED-ORIG-ARGS from tilde-@*")
726 (throw 'need-orig-args nil
))
727 `(if (<= 0 ,posn
(length orig-args
))
728 (setf args
(nthcdr ,posn orig-args
))
730 :complaint
"Index ~W out of bounds. Should have been ~
732 :args
(list ,posn
(length orig-args
))
733 :offset
,(1- end
)))))
735 (expand-bind-defaults ((n 1)) params
736 (unless *orig-args-available
*
737 (/show0
"THROWing NEED-ORIG-ARGS from tilde-:*")
738 (throw 'need-orig-args nil
))
739 `(do ((cur-posn 0 (1+ cur-posn
))
740 (arg-ptr orig-args
(cdr arg-ptr
)))
742 (let ((new-posn (- cur-posn
,n
)))
743 (if (<= 0 new-posn
(length orig-args
))
744 (setf args
(nthcdr new-posn orig-args
))
747 "Index ~W is out of bounds; should have been ~
749 :args
(list new-posn
(length orig-args
))
750 :offset
,(1- end
)))))))
752 (expand-bind-defaults ((n 1)) params
753 (setf *only-simple-args
* nil
)
756 (expand-next-arg)))))
758 ;;;; format directive for indirection
760 (def-format-directive #\? (colonp atsignp params string end
)
763 :complaint
"cannot use the colon modifier with this directive"))
764 (expand-bind-defaults () params
770 "~A~%while processing indirect format string:"
771 :args
(list condition
)
773 :control-string
,string
774 :offset
,(1- end
)))))
776 (if *orig-args-available
*
777 `(setf args
(%format stream
,(expand-next-arg) orig-args args
))
778 (throw 'need-orig-args nil
))
779 `(%format stream
,(expand-next-arg) ,(expand-next-arg))))))
781 ;;;; format directives for capitalization
783 (def-complex-format-directive #\
( (colonp atsignp params directives
)
784 (let ((close (find-directive directives
#\
) nil
)))
787 :complaint
"no corresponding close parenthesis"))
788 (let* ((posn (position close directives
))
789 (before (subseq directives
0 posn
))
790 (after (nthcdr (1+ posn
) directives
)))
792 (expand-bind-defaults () params
793 `(let ((stream (make-case-frob-stream stream
801 ,@(expand-directive-list before
)))
804 (def-complex-format-directive #\
) ()
806 :complaint
"no corresponding open parenthesis"))
808 ;;;; format directives and support functions for conditionalization
810 (def-complex-format-directive #\
[ (colonp atsignp params directives
)
811 (multiple-value-bind (sublists last-semi-with-colon-p remaining
)
812 (parse-conditional-directive directives
)
818 "both colon and atsign modifiers used simultaneously")
822 "Can only specify one section")
823 (expand-bind-defaults () params
824 (expand-maybe-conditional (car sublists
)))))
826 (if (= (length sublists
) 2)
827 (expand-bind-defaults () params
828 (expand-true-false-conditional (car sublists
)
832 "must specify exactly two sections"))
833 (expand-bind-defaults ((index nil
)) params
834 (setf *only-simple-args
* nil
)
836 (case `(or ,index
,(expand-next-arg))))
837 (when last-semi-with-colon-p
838 (push `(t ,@(expand-directive-list (pop sublists
)))
840 (let ((count (length sublists
)))
841 (dolist (sublist sublists
)
842 (push `(,(decf count
)
843 ,@(expand-directive-list sublist
))
845 `(case ,case
,@clauses
)))))
848 (defun parse-conditional-directive (directives)
850 (last-semi-with-colon-p nil
)
851 (remaining directives
))
853 (let ((close-or-semi (find-directive remaining
#\
] t
)))
854 (unless close-or-semi
856 :complaint
"no corresponding close bracket"))
857 (let ((posn (position close-or-semi remaining
)))
858 (push (subseq remaining
0 posn
) sublists
)
859 (setf remaining
(nthcdr (1+ posn
) remaining
))
860 (when (char= (format-directive-character close-or-semi
) #\
])
862 (setf last-semi-with-colon-p
863 (format-directive-colonp close-or-semi
)))))
864 (values sublists last-semi-with-colon-p remaining
)))
866 (defun expand-maybe-conditional (sublist)
868 `(let ((prev-args args
)
869 (arg ,(expand-next-arg)))
871 (setf args prev-args
)
872 ,@(expand-directive-list sublist
)))))
873 (if *only-simple-args
*
874 (multiple-value-bind (guts new-args
)
875 (let ((*simple-args
* *simple-args
*))
876 (values (expand-directive-list sublist
)
878 (cond ((and new-args
(eq *simple-args
* (cdr new-args
)))
879 (setf *simple-args
* new-args
)
880 `(when ,(caar new-args
)
883 (setf *only-simple-args
* nil
)
887 (defun expand-true-false-conditional (true false
)
888 (let ((arg (expand-next-arg)))
892 ,@(expand-directive-list true
))
894 ,@(expand-directive-list false
)))))
895 (if *only-simple-args
*
896 (multiple-value-bind (true-guts true-args true-simple
)
897 (let ((*simple-args
* *simple-args
*)
898 (*only-simple-args
* t
))
899 (values (expand-directive-list true
)
902 (multiple-value-bind (false-guts false-args false-simple
)
903 (let ((*simple-args
* *simple-args
*)
904 (*only-simple-args
* t
))
905 (values (expand-directive-list false
)
908 (if (= (length true-args
) (length false-args
))
912 ,(do ((false false-args
(cdr false
))
913 (true true-args
(cdr true
))
914 (bindings nil
(cons `(,(caar false
) ,(caar true
))
916 ((eq true
*simple-args
*)
917 (setf *simple-args
* true-args
)
918 (setf *only-simple-args
*
919 (and true-simple false-simple
))
926 (setf *only-simple-args
* nil
)
930 (def-complex-format-directive #\
; ()
933 "~~; directive not contained within either ~~[...~~] or ~~<...~~>"))
935 (def-complex-format-directive #\
] ()
938 "no corresponding open bracket"))
940 ;;;; format directive for up-and-out
942 (def-format-directive #\^
(colonp atsignp params
)
945 :complaint
"cannot use the at-sign modifier with this directive"))
946 (when (and colonp
(not *up-up-and-out-allowed
*))
948 :complaint
"attempt to use ~~:^ outside a ~~:{...~~} construct"))
949 `(when ,(expand-bind-defaults ((arg1 nil
) (arg2 nil
) (arg3 nil
)) params
950 `(cond (,arg3
(<= ,arg1
,arg2
,arg3
))
951 (,arg2
(eql ,arg1
,arg2
))
952 (,arg1
(eql ,arg1
0))
956 (setf *only-simple-args
* nil
)
959 '(return-from outside-loop nil
)
962 ;;;; format directives for iteration
964 (def-complex-format-directive #\
{ (colonp atsignp params string end directives
)
965 (let ((close (find-directive directives
#\
} nil
)))
968 :complaint
"no corresponding close brace"))
969 (let* ((closed-with-colon (format-directive-colonp close
))
970 (posn (position close directives
)))
974 (if *orig-args-available
*
980 "~A~%while processing indirect format string:"
981 :args
(list condition
)
983 :control-string
,string
984 :offset
,(1- end
)))))
986 (%format stream inside-string orig-args args
))))
987 (throw 'need-orig-args nil
))
988 (let ((*up-up-and-out-allowed
* colonp
))
989 (expand-directive-list (subseq directives
0 posn
)))))
990 (compute-loop (count)
992 (setf *only-simple-args
* nil
))
994 ,@(unless closed-with-colon
998 `((when (and ,count
(minusp (decf ,count
)))
1001 (let ((*expander-next-arg-macro
* 'expander-next-arg
)
1002 (*only-simple-args
* nil
)
1003 (*orig-args-available
* t
))
1004 `((let* ((orig-args ,(expand-next-arg))
1007 (declare (ignorable orig-args outside-args args
))
1009 ,@(compute-insides)))))
1011 ,@(when closed-with-colon
1014 (compute-block (count)
1016 `(block outside-loop
1017 ,(compute-loop count
))
1018 (compute-loop count
)))
1019 (compute-bindings (count)
1021 (compute-block count
)
1022 `(let* ((orig-args ,(expand-next-arg))
1024 (declare (ignorable orig-args args
))
1025 ,(let ((*expander-next-arg-macro
* 'expander-next-arg
)
1026 (*only-simple-args
* nil
)
1027 (*orig-args-available
* t
))
1028 (compute-block count
))))))
1030 (expand-bind-defaults ((count nil
)) params
1032 `(let ((inside-string ,(expand-next-arg)))
1033 ,(compute-bindings count
))
1034 (compute-bindings count
)))
1036 `(let ((inside-string ,(expand-next-arg)))
1037 ,(compute-bindings nil
))
1038 (compute-bindings nil
)))
1039 (nthcdr (1+ posn
) directives
))))))
1041 (def-complex-format-directive #\
} ()
1042 (error 'format-error
1043 :complaint
"no corresponding open brace"))
1045 ;;;; format directives and support functions for justification
1047 (defparameter *illegal-inside-justification
*
1048 (mapcar (lambda (x) (parse-directive x
0))
1049 '("~W" "~:W" "~@W" "~:@W"
1050 "~_" "~:_" "~@_" "~:@_"
1052 "~I" "~:I" "~@I" "~:@I"
1055 (defun illegal-inside-justification-p (directive)
1056 (member directive
*illegal-inside-justification
*
1058 (and (format-directive-p x
)
1059 (format-directive-p y
)
1060 (eql (format-directive-character x
) (format-directive-character y
))
1061 (eql (format-directive-colonp x
) (format-directive-colonp y
))
1062 (eql (format-directive-atsignp x
) (format-directive-atsignp y
))))))
1064 (def-complex-format-directive #\
< (colonp atsignp params string end directives
)
1065 (multiple-value-bind (segments first-semi close remaining
)
1066 (parse-format-justification directives
)
1068 (if (format-directive-colonp close
) ; logical block vs. justification
1069 (multiple-value-bind (prefix per-line-p insides suffix
)
1070 (parse-format-logical-block segments colonp first-semi
1071 close params string end
)
1072 (expand-format-logical-block prefix per-line-p insides
1074 (let ((count (reduce #'+ (mapcar (lambda (x) (count-if #'illegal-inside-justification-p x
)) segments
))))
1076 ;; ANSI specifies that "an error is signalled" in this
1078 (error 'format-error
1079 :complaint
"~D illegal directive~:P found inside justification block"
1081 :references
(list '(:ansi-cl
:section
(22 3 5 2)))))
1082 ;; ANSI does not explicitly say that an error should be
1083 ;; signalled, but the @ modifier is not explicitly allowed
1085 (when (format-directive-atsignp close
)
1086 (error 'format-error
1087 :complaint
"@ modifier not allowed in close ~
1088 directive of justification ~
1089 block (i.e. ~~<...~~@>."
1090 :offset
(1- (format-directive-end close
))
1091 :references
(list '(:ansi-cl
:section
(22 3 6 2)))))
1092 (expand-format-justification segments colonp atsignp
1093 first-semi params
)))
1096 (def-complex-format-directive #\
> ()
1097 (error 'format-error
1098 :complaint
"no corresponding open bracket"))
1100 (defun parse-format-logical-block
1101 (segments colonp first-semi close params string end
)
1103 (error 'format-error
1104 :complaint
"No parameters can be supplied with ~~<...~~:>."
1105 :offset
(caar params
)))
1106 (multiple-value-bind (prefix insides suffix
)
1107 (multiple-value-bind (prefix-default suffix-default
)
1108 (if colonp
(values "(" ")") (values "" ""))
1109 (flet ((extract-string (list prefix-p
)
1110 (let ((directive (find-if #'format-directive-p list
)))
1112 (error 'format-error
1114 "cannot include format directives inside the ~
1115 ~:[suffix~;prefix~] segment of ~~<...~~:>"
1116 :args
(list prefix-p
)
1117 :offset
(1- (format-directive-end directive
))
1119 (list '(:ansi-cl
:section
(22 3 5 2))))
1120 (apply #'concatenate
'string list
)))))
1121 (case (length segments
)
1122 (0 (values prefix-default nil suffix-default
))
1123 (1 (values prefix-default
(car segments
) suffix-default
))
1124 (2 (values (extract-string (car segments
) t
)
1125 (cadr segments
) suffix-default
))
1126 (3 (values (extract-string (car segments
) t
)
1128 (extract-string (caddr segments
) nil
)))
1130 (error 'format-error
1131 :complaint
"too many segments for ~~<...~~:>")))))
1132 (when (format-directive-atsignp close
)
1134 (add-fill-style-newlines insides
1137 (format-directive-end first-semi
)
1140 (and first-semi
(format-directive-atsignp first-semi
))
1144 (defun add-fill-style-newlines (list string offset
&optional last-directive
)
1147 (let ((directive (car list
)))
1149 ((simple-string-p directive
)
1150 (let* ((non-space (position #\Space directive
:test
#'char
/=))
1151 (newlinep (and last-directive
1153 (format-directive-character last-directive
)
1156 ((and newlinep non-space
)
1158 (list (subseq directive
0 non-space
))
1159 (add-fill-style-newlines-aux
1160 (subseq directive non-space
) string
(+ offset non-space
))
1161 (add-fill-style-newlines
1162 (cdr list
) string
(+ offset
(length directive
)))))
1165 (add-fill-style-newlines
1166 (cdr list
) string
(+ offset
(length directive
)))))
1168 (nconc (add-fill-style-newlines-aux directive string offset
)
1169 (add-fill-style-newlines
1170 (cdr list
) string
(+ offset
(length directive
))))))))
1173 (add-fill-style-newlines
1175 (format-directive-end directive
) directive
))))))
1178 (defun add-fill-style-newlines-aux (literal string offset
)
1179 (let ((end (length literal
))
1181 (collect ((results))
1183 (let ((blank (position #\space literal
:start posn
)))
1185 (results (subseq literal posn
))
1187 (let ((non-blank (or (position #\space literal
:start blank
1190 (results (subseq literal posn non-blank
))
1191 (results (make-format-directive
1192 :string string
:character
#\_
1193 :start
(+ offset non-blank
) :end
(+ offset non-blank
)
1194 :colonp t
:atsignp nil
:params nil
))
1195 (setf posn non-blank
))
1200 (defun parse-format-justification (directives)
1201 (let ((first-semi nil
)
1203 (remaining directives
))
1204 (collect ((segments))
1206 (let ((close-or-semi (find-directive remaining
#\
> t
)))
1207 (unless close-or-semi
1208 (error 'format-error
1209 :complaint
"no corresponding close bracket"))
1210 (let ((posn (position close-or-semi remaining
)))
1211 (segments (subseq remaining
0 posn
))
1212 (setf remaining
(nthcdr (1+ posn
) remaining
)))
1213 (when (char= (format-directive-character close-or-semi
)
1215 (setf close close-or-semi
)
1218 (setf first-semi close-or-semi
))))
1219 (values (segments) first-semi close remaining
))))
1221 (sb!xc
:defmacro expander-pprint-next-arg
(string offset
)
1224 (error 'format-error
1225 :complaint
"no more arguments"
1226 :control-string
,string
1231 (defun expand-format-logical-block (prefix per-line-p insides suffix atsignp
)
1232 `(let ((arg ,(if atsignp
'args
(expand-next-arg))))
1234 (setf *only-simple-args
* nil
)
1236 (pprint-logical-block
1238 ,(if per-line-p
:per-line-prefix
:prefix
) ,prefix
1242 `((orig-args arg
))))
1243 (declare (ignorable args
,@(unless atsignp
'(orig-args))))
1245 ,@(let ((*expander-next-arg-macro
* 'expander-pprint-next-arg
)
1246 (*only-simple-args
* nil
)
1247 (*orig-args-available
*
1248 (if atsignp
*orig-args-available
* t
)))
1249 (expand-directive-list insides
)))))))
1251 (defun expand-format-justification (segments colonp atsignp first-semi params
)
1252 (let ((newline-segment-p
1254 (format-directive-colonp first-semi
))))
1255 (expand-bind-defaults
1256 ((mincol 0) (colinc 1) (minpad 0) (padchar #\space
))
1258 `(let ((segments nil
)
1259 ,@(when newline-segment-p
1260 '((newline-segment nil
)
1264 ,@(when newline-segment-p
1265 `((setf newline-segment
1266 (with-simple-output-to-string (stream)
1267 ,@(expand-directive-list (pop segments
))))
1268 ,(expand-bind-defaults
1270 (line-len '(or (sb!impl
::line-length stream
) 72)))
1271 (format-directive-params first-semi
)
1272 `(setf extra-space
,extra line-len
,line-len
))))
1273 ,@(mapcar (lambda (segment)
1274 `(push (with-simple-output-to-string (stream)
1275 ,@(expand-directive-list segment
))
1278 (format-justification stream
1279 ,@(if newline-segment-p
1280 '(newline-segment extra-space line-len
)
1282 segments
,colonp
,atsignp
1283 ,mincol
,colinc
,minpad
,padchar
)))))
1285 ;;;; format directive and support function for user-defined method
1287 (def-format-directive #\
/ (string start end colonp atsignp params
)
1288 (let ((symbol (extract-user-fun-name string start end
)))
1289 (collect ((param-names) (bindings))
1290 (dolist (param-and-offset params
)
1291 (let ((param (cdr param-and-offset
)))
1292 (let ((param-name (sb!xc
:gensym
"PARAM")))
1293 (param-names param-name
)
1294 (bindings `(,param-name
1296 (:arg
(expand-next-arg))
1297 (:remaining
'(length args
))
1300 (,symbol stream
,(expand-next-arg) ,colonp
,atsignp
1301 ,@(param-names))))))
1303 (defun extract-user-fun-name (string start end
)
1304 (let ((slash (position #\
/ string
:start start
:end
(1- end
)
1307 (error 'format-error
1308 :complaint
"malformed ~~/ directive"))
1309 (let* ((name (string-upcase (let ((foo string
))
1310 ;; Hack alert: This is to keep the compiler
1311 ;; quiet about deleting code inside the
1312 ;; subseq expansion.
1313 (subseq foo
(1+ slash
) (1- end
)))))
1314 (first-colon (position #\
: name
))
1315 (second-colon (if first-colon
(position #\
: name
:start
(1+ first-colon
))))
1316 (package-name (if first-colon
1317 (subseq name
0 first-colon
)
1318 "COMMON-LISP-USER"))
1319 (package (find-package package-name
)))
1321 ;; FIXME: should be PACKAGE-ERROR? Could we just use
1322 ;; FIND-UNDELETED-PACKAGE-OR-LOSE?
1323 (error 'format-error
1324 :complaint
"no package named ~S"
1325 :args
(list package-name
)))
1327 ((and second-colon
(= second-colon
(1+ first-colon
)))
1328 (subseq name
(1+ second-colon
)))
1330 (subseq name
(1+ first-colon
)))
1334 ;;; compile-time checking for argument mismatch. This code is
1335 ;;; inspired by that of Gerd Moellmann, and comes decorated with
1337 (defun %compiler-walk-format-string
(string args
)
1338 (declare (type simple-string string
))
1339 (let ((*default-format-error-control-string
* string
))
1340 (macrolet ((incf-both (&optional
(increment 1))
1342 (incf min
,increment
)
1343 (incf max
,increment
)))
1344 (walk-complex-directive (function)
1345 `(multiple-value-bind (min-inc max-inc remaining
)
1346 (,function directive directives args
)
1349 (setq directives remaining
))))
1350 ;; FIXME: these functions take a list of arguments as well as
1351 ;; the directive stream. This is to enable possibly some
1352 ;; limited type checking on FORMAT's arguments, as well as
1353 ;; simple argument count mismatch checking: when the minimum and
1354 ;; maximum argument counts are the same at a given point, we
1355 ;; know which argument is going to be used for a given
1356 ;; directive, and some (annotated below) require arguments of
1357 ;; particular types.
1359 ((walk-justification (justification directives args
)
1360 (declare (ignore args
))
1361 (let ((*default-format-error-offset
*
1362 (1- (format-directive-end justification
))))
1363 (multiple-value-bind (segments first-semi close remaining
)
1364 (parse-format-justification directives
)
1365 (declare (ignore segments first-semi
))
1367 ((not (format-directive-colonp close
))
1368 (values 0 0 directives
))
1369 ((format-directive-atsignp justification
)
1370 (values 0 sb
!xc
:call-arguments-limit directives
))
1371 ;; FIXME: here we could assert that the
1372 ;; corresponding argument was a list.
1373 (t (values 1 1 remaining
))))))
1374 (walk-conditional (conditional directives args
)
1375 (let ((*default-format-error-offset
*
1376 (1- (format-directive-end conditional
))))
1377 (multiple-value-bind (sublists last-semi-with-colon-p remaining
)
1378 (parse-conditional-directive directives
)
1379 (declare (ignore last-semi-with-colon-p
))
1381 (loop for s in sublists
1383 1 (walk-directive-list s args
)))))
1385 ((format-directive-atsignp conditional
)
1386 (values 1 (max 1 sub-max
) remaining
))
1387 ((loop for p in
(format-directive-params conditional
)
1388 thereis
(or (integerp (cdr p
))
1389 (memq (cdr p
) '(:remaining
:arg
))))
1390 (values 0 sub-max remaining
))
1391 ;; FIXME: if not COLONP, then the next argument
1392 ;; must be a number.
1393 (t (values 1 (1+ sub-max
) remaining
)))))))
1394 (walk-iteration (iteration directives args
)
1395 (declare (ignore args
))
1396 (let ((*default-format-error-offset
*
1397 (1- (format-directive-end iteration
))))
1398 (let* ((close (find-directive directives
#\
} nil
))
1399 (posn (or (position close directives
)
1400 (error 'format-error
1401 :complaint
"no corresponding close brace")))
1402 (remaining (nthcdr (1+ posn
) directives
)))
1403 ;; FIXME: if POSN is zero, the next argument must be
1404 ;; a format control (either a function or a string).
1405 (if (format-directive-atsignp iteration
)
1406 (values (if (zerop posn
) 1 0)
1407 sb
!xc
:call-arguments-limit
1409 ;; FIXME: the argument corresponding to this
1410 ;; directive must be a list.
1411 (let ((nreq (if (zerop posn
) 2 1)))
1412 (values nreq nreq remaining
))))))
1413 (walk-directive-list (directives args
)
1414 (let ((min 0) (max 0))
1416 (let ((directive (pop directives
)))
1417 (when (null directive
)
1418 (return (values min
(min max sb
!xc
:call-arguments-limit
))))
1419 (when (format-directive-p directive
)
1420 (incf-both (count :arg
(format-directive-params directive
)
1422 (let ((c (format-directive-character directive
)))
1424 ((find c
"ABCDEFGORSWX$/")
1427 (unless (format-directive-colonp directive
)
1429 ((or (find c
"IT%&|_();>~") (char= c
#\Newline
)))
1430 ;; FIXME: check correspondence of ~( and ~)
1432 (walk-complex-directive walk-justification
))
1434 (walk-complex-directive walk-conditional
))
1436 (walk-complex-directive walk-iteration
))
1438 ;; FIXME: the argument corresponding to this
1439 ;; directive must be a format control.
1441 ((format-directive-atsignp directive
)
1443 (setq max sb
!xc
:call-arguments-limit
))
1445 (t (throw 'give-up-format-string-walk nil
))))))))))
1446 (catch 'give-up-format-string-walk
1447 (let ((directives (tokenize-control-string string
)))
1448 (walk-directive-list directives args
)))))))