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 ;;;; TOKENIZE-CONTROL-STRING
14 ;;; The case for caching is to speed up out-of-line calls that use a fixed
15 ;;; control string in a loop, not to avoid re-tokenizing all strings that
16 ;;; happen to be STRING= to that string.
17 (defun-cached (tokenize-control-string
19 :hash-function
#+sb-xc-host
20 (lambda (x) (declare (ignore x
)) 1)
21 #-sb-xc-host
#'pointer-hash
)
22 ;; Due to string mutability, the comparator is STRING=
23 ;; even though the hash is address-based.
25 (declare (simple-string string
))
29 ;; FIXME: consider rewriting this 22.3.5.2-related processing
30 ;; using specials to maintain state and doing the logic inside
31 ;; the directive expanders themselves.
35 (justification-semicolon))
37 (let ((next-directive (or (position #\~ string
:start index
) end
)))
38 (when (> next-directive index
)
39 (push (subseq string index next-directive
) result
))
40 (when (= next-directive end
)
42 (let* ((directive (parse-directive string next-directive
))
43 (char (format-directive-character directive
)))
44 ;; this processing is required by CLHS 22.3.5.2
46 ((char= char
#\
<) (push directive block
))
47 ((and block
(char= char
#\
;) (format-directive-colonp directive))
48 (setf semicolon directive
))
51 (format-error-at string next-directive
52 "~~> without a matching ~~<"))
54 ((format-directive-colonp directive
)
56 (setf pprint
(car block
)))
59 (unless justification-semicolon
60 (setf justification-semicolon semicolon
))))
62 ;; block cases are handled by the #\< expander/interpreter
65 ((#\W
#\I
#\_
) (unless pprint
(setf pprint directive
)))
66 (#\T
(when (and (format-directive-colonp directive
)
68 (setf pprint directive
))))))
69 (push directive result
)
70 (setf index
(format-directive-end directive
)))))
71 (when (and pprint justification-semicolon
)
72 (let ((pprint-offset (1- (format-directive-end pprint
)))
74 (1- (format-directive-end justification-semicolon
))))
76 string
(min pprint-offset justification-offset
)
77 "Misuse of justification and pprint directives" '()
78 :second-relative
(- (max pprint-offset justification-offset
)
79 (min pprint-offset justification-offset
)
81 :references
'((:ansi-cl
:section
(22 3 5 2))))))
84 (defun parse-directive (string start
)
85 (let ((posn (1+ start
)) (params nil
) (colonp nil
) (atsignp nil
)
86 (end (length string
)))
89 (format-error-at string start
90 "String ended before directive was found")
93 (when (or colonp atsignp
)
96 "Parameters found after #\\: or #\\@ modifier" '()
97 :references
'((:ansi-cl
:section
(22 3)))))))
99 (let ((char (get-char)))
100 (cond ((or (char<= #\
0 char
#\
9) (char= char
#\
+) (char= char
#\-
))
102 (multiple-value-bind (param new-posn
)
103 (parse-integer string
:start posn
:junk-allowed t
)
104 (push (cons posn param
) params
)
112 ((or (char= char
#\v)
115 (push (cons posn
:arg
) params
)
125 (push (cons posn
:remaining
) params
)
136 (push (cons posn
(get-char)) params
)
138 (unless (char= (get-char) #\
,)
142 (push (cons posn nil
) params
))
146 string posn
"Too many colons supplied" '()
147 :references
'((:ansi-cl
:section
(22 3))))
152 string posn
"Too many #\\@ characters supplied" '()
153 :references
'((:ansi-cl
:section
(22 3))))
156 (when (and (char= (schar string
(1- posn
)) #\
,)
158 (char/= (schar string
(- posn
2)) #\')))
160 (push (cons (1- posn
) nil
) params
))
163 (let ((char (get-char)))
164 (when (char= char
#\
/)
165 (let ((closing-slash (position #\
/ string
:start
(1+ posn
))))
167 (setf posn closing-slash
)
168 (format-error-at string posn
"No matching closing slash"))))
169 (make-format-directive
170 :string string
:start start
:end
(1+ posn
)
171 :character
(char-upcase char
)
172 :colonp colonp
:atsignp atsignp
173 :params
(nreverse params
))))))
177 (sb!xc
:defmacro formatter
(control-string)
178 `#',(%formatter control-string
))
180 (defun %formatter
(control-string &optional
(arg-count 0) (need-retval t
))
181 ;; ARG-COUNT is supplied only when the use of this formatter is in a literal
182 ;; call to FORMAT, in which case we can possibly elide &optional parsing.
183 ;; But we can't in general, because FORMATTER may be called by users
184 ;; to obtain functions that may be invoked in random wrong ways.
185 ;; NEED-RETVAL signifies that the caller wants back the list of
186 ;; unconsumed arguments. This is the default assumption.
188 (catch 'need-orig-args
189 (let* ((*simple-args
* nil
)
190 (*only-simple-args
* t
)
191 (control-string (coerce control-string
'simple-string
))
192 (guts (expand-control-string control-string
)) ; can throw
195 (dolist (arg *simple-args
*)
196 (cond ((plusp arg-count
)
197 (push (car arg
) required
)
201 (args-exhausted ,control-string
,(cdr arg
)))
203 (return `(named-lambda ,control-string
205 ,@(if optional
'(&optional
)) ,@optional
207 (declare (ignorable stream args
))
209 ,(and need-retval
'args
)))))
210 (let ((*orig-args-available
* t
)
211 (*only-simple-args
* nil
))
212 `(lambda (stream &rest orig-args
)
213 (declare (ignorable stream
))
214 (let ((args orig-args
))
215 ,(expand-control-string control-string
)
216 ,(and need-retval
'args
))))))
218 (defun args-exhausted (control-string offset
)
219 (format-error-at control-string offset
"No more arguments"))
221 (defvar *format-gensym-counter
*)
222 (defun expand-control-string (string)
223 (let* ((string (etypecase string
227 (coerce string
'simple-string
))))
228 (*default-format-error-control-string
* string
)
229 (*format-gensym-counter
* 0)
230 (directives (tokenize-control-string string
)))
232 ,@(expand-directive-list directives
))))
234 (defun expand-directive-list (directives)
237 (remaining-directives directives
))
239 (unless remaining-directives
241 (multiple-value-bind (form new-directives
)
242 (expand-directive (car remaining-directives
)
243 (cdr remaining-directives
))
244 (flet ((merge-string (string)
246 (let ((concat (concatenate 'string
249 (setf previous concat
)
251 `(write-string ,concat stream
))))
253 (setf previous string
)
254 (push form results
)))))
256 ((typep form
'(cons (member write-string write-char
)
257 (cons (or string character
))))
258 (merge-string (second form
)))
259 ((typep form
'(cons (eql terpri
)))
260 (merge-string #\Newline
))
263 (setf previous nil
))))
264 (setf remaining-directives new-directives
)))
267 (defun expand-directive (directive more-directives
)
271 (let ((char (format-directive-character directive
)))
274 (aref *format-directive-expanders
* (sb!xc
:char-code char
))))))
275 (*default-format-error-offset
*
276 (1- (format-directive-end directive
))))
277 (declare (type (or null function
) expander
))
279 (funcall expander directive more-directives
)
280 (format-error "Unknown directive ~@[(character: ~A)~]"
281 (char-name (format-directive-character directive
))))))
283 (values `(write-char ,(schar directive
0) stream
)
286 (values nil more-directives
))
288 (values `(write-string ,directive stream
)
291 (sb!xc
:defmacro expander-next-arg
(string offset
)
294 (format-error-at ,string
,offset
"No more arguments")))
296 (defun expand-next-arg (&optional offset
)
297 (if (or *orig-args-available
* (not *only-simple-args
*))
298 `(,*expander-next-arg-macro
*
299 ,*default-format-error-control-string
*
300 ,(or offset
*default-format-error-offset
*))
302 (without-package-locks
304 (load-time-value (find-package "SB!FORMAT") t
)
306 (write-to-string (incf *format-gensym-counter
*)
307 :pretty nil
:base
10 :radix nil
)))))
308 (push (cons symbol
(or offset
*default-format-error-offset
*))
312 (defmacro expand-bind-defaults
(specs params
&body body
)
313 (once-only ((params params
))
315 (collect ((expander-bindings) (runtime-bindings))
317 (destructuring-bind (var default
) spec
318 (let ((symbol (sb!xc
:gensym
"FVAR")))
323 (let* ((param-and-offset (pop ,params
))
324 (offset (car param-and-offset
))
325 (param (cdr param-and-offset
)))
327 (:arg
`(or ,(expand-next-arg offset
) ,,default
))
329 (setf *only-simple-args
* nil
)
333 `(let ,(expander-bindings)
334 `(let ,(list ,@(runtime-bindings))
338 "Too many parameters, expected no more than ~W"
343 (format-error-at nil
(caar ,params
)
344 "Too many parameters, expected none"))
347 ;;;; format directive machinery
349 (eval-when (:compile-toplevel
:execute
)
350 (#+sb-xc-host defmacro
#-sb-xc-host sb
!xc
:defmacro def-complex-format-directive
(char lambda-list
&body body
)
351 (let ((defun-name (intern (format nil
352 "~:@(~:C~)-FORMAT-DIRECTIVE-EXPANDER"
354 (directive (sb!xc
:gensym
"DIRECTIVE"))
355 (directives (if lambda-list
(car (last lambda-list
)) (sb!xc
:gensym
"DIRECTIVES"))))
357 (defun ,defun-name
(,directive
,directives
)
359 `((let ,(mapcar (lambda (var)
361 (,(symbolicate "FORMAT-DIRECTIVE-" var
)
363 (butlast lambda-list
))
365 `((declare (ignore ,directive
,directives
))
367 (%set-format-directive-expander
,char
#',defun-name
))))
369 (#+sb-xc-host defmacro
#-sb-xc-host sb
!xc
:defmacro def-format-directive
(char lambda-list
&body body
)
370 (let ((directives (sb!xc
:gensym
"DIRECTIVES"))
372 (body-without-decls body
))
374 (let ((form (car body-without-decls
)))
375 (unless (and (consp form
) (eq (car form
) 'declare
))
377 (push (pop body-without-decls
) declarations
)))
378 (setf declarations
(reverse declarations
))
379 `(def-complex-format-directive ,char
(,@lambda-list
,directives
)
381 (values (progn ,@body-without-decls
)
385 (eval-when (#-sb-xc
:compile-toplevel
:load-toplevel
:execute
)
387 (defun %set-format-directive-expander
(char fn
)
388 (let ((code (sb!xc
:char-code
(char-upcase char
))))
389 (setf (aref *format-directive-expanders
* code
) fn
))
392 (defun %set-format-directive-interpreter
(char fn
)
393 (let ((code (sb!xc
:char-code
(char-upcase char
))))
394 (setf (aref *format-directive-interpreters
* code
) fn
))
397 (defun find-directive (directives kind stop-at-semi
)
399 (let ((next (car directives
)))
400 (if (format-directive-p next
)
401 (let ((char (format-directive-character next
)))
402 (if (or (char= kind char
)
403 (and stop-at-semi
(char= char
#\
;)))
406 (cdr (flet ((after (char)
407 (member (find-directive (cdr directives
)
418 (find-directive (cdr directives
) kind stop-at-semi
)))))
422 ;;;; format directives for simple output
424 (def-format-directive #\A
(colonp atsignp params
)
426 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
429 `(format-princ stream
,(expand-next-arg) ',colonp
',atsignp
430 ,mincol
,colinc
,minpad
,padchar
))
432 `(or ,(expand-next-arg) "()")
436 (def-format-directive #\S
(colonp atsignp params
)
438 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
441 `(format-prin1 stream
,(expand-next-arg) ,colonp
,atsignp
442 ,mincol
,colinc
,minpad
,padchar
)))
444 `(let ((arg ,(expand-next-arg)))
447 (princ "()" stream
))))
449 `(prin1 ,(expand-next-arg) stream
))))
451 (def-format-directive #\C
(colonp atsignp params string end
)
452 (expand-bind-defaults () params
453 (let ((n-arg (sb!xc
:gensym
"ARG")))
454 `(let ((,n-arg
,(expand-next-arg)))
455 (unless (typep ,n-arg
'character
)
456 (format-error-at ,string
,(1- end
)
457 "~S is not of type CHARACTER." ,n-arg
))
459 `(format-print-named-character ,n-arg stream
))
461 `(prin1 ,n-arg stream
))
463 `(write-char ,n-arg stream
)))))))
465 (def-format-directive #\W
(colonp atsignp params
)
466 (expand-bind-defaults () params
467 (if (or colonp atsignp
)
468 `(let (,@(when colonp
469 '((*print-pretty
* t
)))
471 '((*print-level
* nil
)
472 (*print-length
* nil
))))
473 (output-object ,(expand-next-arg) stream
))
474 `(output-object ,(expand-next-arg) stream
))))
476 ;;;; format directives for integer output
478 (defun expand-format-integer (base colonp atsignp params
)
479 (if (or colonp atsignp params
)
480 (expand-bind-defaults
481 ((mincol 0) (padchar #\space
) (commachar #\
,) (commainterval 3))
483 `(format-print-integer stream
,(expand-next-arg) ,colonp
,atsignp
484 ,base
,mincol
,padchar
,commachar
486 `(let ((*print-base
* ,base
)
488 (princ ,(expand-next-arg) stream
))))
490 (def-format-directive #\D
(colonp atsignp params
)
491 (expand-format-integer 10 colonp atsignp params
))
493 (def-format-directive #\B
(colonp atsignp params
)
494 (expand-format-integer 2 colonp atsignp params
))
496 (def-format-directive #\O
(colonp atsignp params
)
497 (expand-format-integer 8 colonp atsignp params
))
499 (def-format-directive #\X
(colonp atsignp params
)
500 (expand-format-integer 16 colonp atsignp params
))
502 (def-format-directive #\R
(colonp atsignp params string end
)
503 (expand-bind-defaults
504 ((base nil
) (mincol 0) (padchar #\space
) (commachar #\
,)
507 (let ((n-arg (sb!xc
:gensym
"ARG")))
508 `(let ((,n-arg
,(expand-next-arg)))
511 (format-error-at ,string
,(1- end
) "~S is not of type INTEGER." ,n-arg
))
513 (format-print-integer stream
,n-arg
,colonp
,atsignp
515 ,padchar
,commachar
,commainterval
)
518 `(format-print-old-roman stream
,n-arg
)
519 `(format-print-roman stream
,n-arg
))
521 `(format-print-ordinal stream
,n-arg
)
522 `(format-print-cardinal stream
,n-arg
))))))))
524 ;;;; format directive for pluralization
526 (def-format-directive #\P
(colonp atsignp params end
)
527 (expand-bind-defaults () params
531 (*orig-args-available
*
532 `(if (eq orig-args args
)
534 ,*default-format-error-control-string
* ,(1- end
)
535 "No previous argument")
536 (do ((arg-ptr orig-args
(cdr arg-ptr
)))
537 ((eq (cdr arg-ptr
) args
)
540 (unless *simple-args
*
541 (format-error "No previous argument"))
542 (caar *simple-args
*))
544 (/show0
"THROWing NEED-ORIG-ARGS from tilde-P")
545 (throw 'need-orig-args nil
)))))
547 `(write-string (if (eql ,arg
1) "y" "ies") stream
)
548 `(unless (eql ,arg
1) (write-char #\s stream
))))))
550 ;;;; format directives for floating point output
552 (def-format-directive #\F
(colonp atsignp params
)
553 (check-modifier "colon" colonp
)
554 (expand-bind-defaults ((w nil
) (d nil
) (k nil
) (ovf nil
) (pad #\space
)) params
555 `(format-fixed stream
,(expand-next-arg) ,w
,d
,k
,ovf
,pad
,atsignp
)))
557 (def-format-directive #\E
(colonp atsignp params
)
558 (check-modifier "colon" colonp
)
559 (expand-bind-defaults
560 ((w nil
) (d nil
) (e nil
) (k 1) (ovf nil
) (pad #\space
) (mark nil
))
562 `(format-exponential stream
,(expand-next-arg) ,w
,d
,e
,k
,ovf
,pad
,mark
565 (def-format-directive #\G
(colonp atsignp params
)
566 (check-modifier "colon" colonp
)
567 (expand-bind-defaults
568 ((w nil
) (d nil
) (e nil
) (k nil
) (ovf nil
) (pad #\space
) (mark nil
))
570 `(format-general stream
,(expand-next-arg) ,w
,d
,e
,k
,ovf
,pad
,mark
,atsignp
)))
572 (def-format-directive #\$
(colonp atsignp params
)
573 (expand-bind-defaults ((d 2) (n 1) (w 0) (pad #\space
)) params
574 `(format-dollars stream
,(expand-next-arg) ,d
,n
,w
,pad
,colonp
577 ;;;; format directives for line/page breaks etc.
579 (def-format-directive #\%
(colonp atsignp params
)
580 (check-modifier "colon" colonp
)
581 (check-modifier "at-sign" atsignp
)
584 ((typep params
'(cons (cons * (mod 65536)) null
))
585 `(write-string ,(make-string (cdar params
) :initial-element
#\Newline
) stream
))
587 (expand-bind-defaults ((count 1)) params
591 (def-format-directive #\
& (colonp atsignp params
)
592 (check-modifier "colon" colonp
)
593 (check-modifier "at-sign" atsignp
)
595 (expand-bind-defaults ((count 1)) params
599 (dotimes (i (1- ,count
))
601 '(fresh-line stream
)))
603 (def-format-directive #\|
(colonp atsignp params
)
604 (check-modifier "colon" colonp
)
605 (check-modifier "at-sign" atsignp
)
607 (expand-bind-defaults ((count 1)) params
609 (write-char (code-char form-feed-char-code
) stream
)))
610 '(write-char (code-char form-feed-char-code
) stream
)))
612 (def-format-directive #\~
(colonp atsignp params
)
613 (check-modifier "colon" colonp
)
614 (check-modifier "at-sign" atsignp
)
616 (expand-bind-defaults ((count 1)) params
618 (write-char #\~ stream
)))
619 '(write-char #\~ stream
)))
621 (def-complex-format-directive #\newline
(colonp atsignp params directives
)
622 ;; FIXME: this is not an error!
623 (check-modifier '("colon" "at-sign") (and colonp atsignp
))
624 (values (expand-bind-defaults () params
626 '(write-char #\newline stream
)
628 (if (and (not colonp
)
630 (simple-string-p (car directives
)))
631 (cons (string-left-trim *format-whitespace-chars
*
636 ;;;; format directives for tabs and simple pretty printing
638 (def-format-directive #\T
(colonp atsignp params
)
640 (expand-bind-defaults ((n 1) (m 1)) params
641 `(pprint-tab ,(if atsignp
:section-relative
:section
)
644 (expand-bind-defaults ((colrel 1) (colinc 1)) params
645 `(format-relative-tab stream
,colrel
,colinc
))
646 (expand-bind-defaults ((colnum 1) (colinc 1)) params
647 `(format-absolute-tab stream
,colnum
,colinc
)))))
649 (def-format-directive #\_
(colonp atsignp params
)
650 (expand-bind-defaults () params
651 `(pprint-newline ,(if colonp
660 (def-format-directive #\I
(colonp atsignp params
)
661 (check-modifier "at-sign" atsignp
)
662 (expand-bind-defaults ((n 0)) params
663 `(pprint-indent ,(if colonp
:current
:block
) ,n stream
)))
665 ;;;; format directive for ~*
667 (def-format-directive #\
* (colonp atsignp params end
)
668 (check-modifier '("colon" "at-sign") (and colonp atsignp
))
669 (flet ((make-lose (index)
671 ,*default-format-error-control-string
* ,(1- end
)
672 "Index ~W is out of bounds. It should have been between ~
674 ,index
(length orig-args
))))
676 (expand-bind-defaults ((posn 0)) params
677 (unless *orig-args-available
*
678 (/show0
"THROWing NEED-ORIG-ARGS from tilde-@*")
679 (throw 'need-orig-args nil
))
680 `(if (<= 0 ,posn
(length orig-args
))
681 (setf args
(nthcdr ,posn orig-args
))
684 (expand-bind-defaults ((n 1)) params
685 (unless *orig-args-available
*
686 (/show0
"THROWing NEED-ORIG-ARGS from tilde-:*")
687 (throw 'need-orig-args nil
))
688 `(do ((cur-posn 0 (1+ cur-posn
))
689 (arg-ptr orig-args
(cdr arg-ptr
)))
691 (let ((new-posn (- cur-posn
,n
)))
692 (if (<= 0 new-posn
(length orig-args
))
693 (setf args
(nthcdr new-posn orig-args
))
694 ,(make-lose 'new-posn
))))))
696 (expand-bind-defaults ((n 1)) params
697 (setf *only-simple-args
* nil
)
700 (expand-next-arg))))))
702 ;;;; format directive for indirection
704 (def-format-directive #\? (colonp atsignp params string end
)
705 (check-modifier "colon" colonp
)
706 (expand-bind-defaults () params
712 "~A~%while processing indirect format string:"
713 :args
(list condition
)
715 :control-string
,string
716 :offset
,(1- end
)))))
718 (if *orig-args-available
*
719 `(setf args
(%format stream
,(expand-next-arg) orig-args args
))
720 (throw 'need-orig-args nil
))
721 `(%format stream
,(expand-next-arg) ,(expand-next-arg))))))
723 ;;;; format directives for capitalization
725 (def-complex-format-directive #\
( (colonp atsignp params directives
)
726 (let* ((close (or (find-directive directives
#\
) nil
)
727 (format-error "No corresponding close parenthesis")))
728 (posn (position close directives
))
729 (before (subseq directives
0 posn
))
730 (after (nthcdr (1+ posn
) directives
)))
732 (expand-bind-defaults () params
733 `(let ((stream (make-case-frob-stream stream
741 ,@(expand-directive-list before
)))
744 (def-complex-format-directive #\
) ()
745 (format-error "No corresponding open parenthesis"))
747 ;;;; format directives and support functions for conditionalization
749 (def-complex-format-directive #\
[ (colonp atsignp params directives
)
750 (check-modifier '("colon" "at-sign") (and colonp atsignp
))
751 (multiple-value-bind (sublists last-semi-with-colon-p remaining
)
752 (parse-conditional-directive directives
)
757 (format-error "Can only specify one section"))
758 (expand-bind-defaults () params
759 (expand-maybe-conditional (car sublists
))))
761 (unless (= (length sublists
) 2)
762 (format-error "Must specify exactly two sections"))
763 (expand-bind-defaults () params
764 (apply #'expand-true-false-conditional sublists
)))
766 (expand-bind-defaults ((index nil
)) params
767 (setf *only-simple-args
* nil
)
770 (case `(or ,index
,(expand-next-arg))))
771 (when last-semi-with-colon-p
772 (push `(t ,@(expand-directive-list (pop sublists
)))
774 (let ((count (length sublists
)))
775 (dolist (sublist sublists
)
776 (push `(,(decf count
)
777 ,@(expand-directive-list sublist
))
779 `(let ((,case-sym
,case
))
780 (unless (integerp ,case-sym
)
782 ,*default-format-error-control-string
*
783 ,*default-format-error-offset
*
784 "The argument to ~~[ is not an integer: ~A" ,case-sym
))
785 (case ,case-sym
,@clauses
))))))
788 (defun parse-conditional-directive (directives)
790 (last-semi-with-colon-p nil
)
791 (remaining directives
))
793 (let* ((close-or-semi (or (find-directive remaining
#\
] t
)
794 (format-error "No corresponding close bracket")))
795 (posn (position close-or-semi remaining
)))
796 (push (subseq remaining
0 posn
) sublists
)
797 (setf remaining
(nthcdr (1+ posn
) remaining
))
798 (when (char= (format-directive-character close-or-semi
) #\
])
800 (setf last-semi-with-colon-p
801 (format-directive-colonp close-or-semi
))))
802 (values sublists last-semi-with-colon-p remaining
)))
804 (defun expand-maybe-conditional (sublist)
806 `(let ((prev-args args
)
807 (arg ,(expand-next-arg)))
809 (setf args prev-args
)
810 ,@(expand-directive-list sublist
)))))
811 (if *only-simple-args
*
812 (multiple-value-bind (guts new-args
)
813 (let ((*simple-args
* *simple-args
*))
814 (values (expand-directive-list sublist
)
816 (cond ((and new-args
(eq *simple-args
* (cdr new-args
)))
817 (setf *simple-args
* new-args
)
818 `(when ,(caar new-args
)
821 (setf *only-simple-args
* nil
)
825 (defun expand-true-false-conditional (true false
)
826 (let ((arg (expand-next-arg)))
830 ,@(expand-directive-list true
))
832 ,@(expand-directive-list false
)))))
833 (if *only-simple-args
*
834 (multiple-value-bind (true-guts true-args true-simple
)
835 (let ((*simple-args
* *simple-args
*)
836 (*only-simple-args
* t
))
837 (values (expand-directive-list true
)
840 (multiple-value-bind (false-guts false-args false-simple
)
841 (let ((*simple-args
* *simple-args
*)
842 (*only-simple-args
* t
))
843 (values (expand-directive-list false
)
846 (if (= (length true-args
) (length false-args
))
850 ,(do ((false false-args
(cdr false
))
851 (true true-args
(cdr true
))
852 (bindings nil
(cons `(,(caar false
) ,(caar true
))
854 ((eq true
*simple-args
*)
855 (setf *simple-args
* true-args
)
856 (setf *only-simple-args
*
857 (and true-simple false-simple
))
864 (setf *only-simple-args
* nil
)
868 (def-complex-format-directive #\
; ()
870 "~~; directive not contained within either ~~[...~~] or ~~<...~~>"))
872 (def-complex-format-directive #\
] ()
873 (format-error "No corresponding open bracket"))
875 ;;;; format directive for up-and-out
877 (def-format-directive #\^
(colonp atsignp params
)
878 (check-modifier "at-sign" atsignp
)
879 (when (and colonp
(not *up-up-and-out-allowed
*))
880 (format-error "Attempt to use ~~:^ outside a ~~:{...~~} construct"))
881 `(when ,(expand-bind-defaults ((arg1 nil
) (arg2 nil
) (arg3 nil
)) params
882 `(cond (,arg3
(<= ,arg1
,arg2
,arg3
))
883 (,arg2
(eql ,arg1
,arg2
))
884 (,arg1
(eql ,arg1
0))
888 (setf *only-simple-args
* nil
)
891 '(return-from outside-loop nil
)
894 ;;;; format directives for iteration
896 (def-complex-format-directive #\
{ (colonp atsignp params string end directives
)
897 (let* ((close (or (find-directive directives
#\
} nil
)
898 (format-error "No corresponding close brace")))
899 (closed-with-colon (format-directive-colonp close
))
900 (posn (position close directives
)))
904 (if *orig-args-available
*
910 "~A~%while processing indirect format string:"
912 :print-banner nil
))))
914 (%format stream inside-string orig-args args
))))
915 (throw 'need-orig-args nil
))
916 (let ((*up-up-and-out-allowed
* colonp
))
917 (expand-directive-list (subseq directives
0 posn
)))))
918 (compute-loop (count)
920 (setf *only-simple-args
* nil
))
922 ,@(unless closed-with-colon
926 `((when (and ,count
(minusp (decf ,count
)))
929 (let ((*expander-next-arg-macro
* 'expander-next-arg
)
930 (*only-simple-args
* nil
)
931 (*orig-args-available
* t
))
932 `((let* ((orig-args ,(expand-next-arg))
935 (declare (ignorable orig-args outside-args args
))
937 ,@(compute-insides)))))
939 ,@(when closed-with-colon
942 (compute-block (count)
945 ,(compute-loop count
))
946 (compute-loop count
)))
947 (compute-bindings (count)
949 (compute-block count
)
950 `(let* ((orig-args ,(expand-next-arg))
952 (declare (ignorable orig-args args
))
953 ,(let ((*expander-next-arg-macro
* 'expander-next-arg
)
954 (*only-simple-args
* nil
)
955 (*orig-args-available
* t
))
956 (compute-block count
))))))
958 (expand-bind-defaults ((count nil
)) params
960 `(let ((inside-string ,(expand-next-arg)))
961 ,(compute-bindings count
))
962 (compute-bindings count
)))
964 `(let ((inside-string ,(expand-next-arg)))
965 ,(compute-bindings nil
))
966 (compute-bindings nil
)))
967 (nthcdr (1+ posn
) directives
)))))
969 (def-complex-format-directive #\
} ()
970 (format-error "No corresponding open brace"))
972 ;;;; format directives and support functions for justification
974 (defparameter *illegal-inside-justification
*
975 (mapcar (lambda (x) (parse-directive x
0))
976 '("~W" "~:W" "~@W" "~:@W"
977 "~_" "~:_" "~@_" "~:@_"
979 "~I" "~:I" "~@I" "~:@I"
982 (defun illegal-inside-justification-p (directive)
983 (member directive
*illegal-inside-justification
*
985 (and (format-directive-p x
)
986 (format-directive-p y
)
987 (eql (format-directive-character x
) (format-directive-character y
))
988 (eql (format-directive-colonp x
) (format-directive-colonp y
))
989 (eql (format-directive-atsignp x
) (format-directive-atsignp y
))))))
991 (def-complex-format-directive #\
< (colonp atsignp params string end directives
)
992 (multiple-value-bind (segments first-semi close remaining
)
993 (parse-format-justification directives
)
995 (if (format-directive-colonp close
) ; logical block vs. justification
996 (multiple-value-bind (prefix per-line-p insides suffix
)
997 (parse-format-logical-block segments colonp first-semi
998 close params string end
)
999 (expand-format-logical-block prefix per-line-p insides
1001 (let ((count (reduce #'+ (mapcar (lambda (x) (count-if #'illegal-inside-justification-p x
)) segments
))))
1003 ;; ANSI specifies that "an error is signalled" in this
1006 "~D illegal directive~:P found inside justification block"
1008 :references
'((:ansi-cl
:section
(22 3 5 2)))))
1009 ;; ANSI does not explicitly say that an error should be
1010 ;; signalled, but the @ modifier is not explicitly allowed
1012 (when (format-directive-atsignp close
)
1014 nil
(1- (format-directive-end close
))
1015 "@ modifier not allowed in close directive of ~
1016 justification block (i.e. ~~<...~~@>."
1018 :references
'((:ansi-cl
:section
(22 3 6 2)))))
1019 (expand-format-justification segments colonp atsignp
1020 first-semi params
)))
1023 (def-complex-format-directive #\
> ()
1024 (format-error "No corresponding open bracket"))
1026 (defun parse-format-logical-block
1027 (segments colonp first-semi close params string end
)
1029 (format-error-at nil
(caar params
)
1030 "No parameters can be supplied with ~~<...~~:>."))
1031 (multiple-value-bind (prefix insides suffix
)
1032 (multiple-value-bind (prefix-default suffix-default
)
1033 (if colonp
(values "(" ")") (values "" ""))
1034 (flet ((extract-string (list prefix-p
)
1035 (let ((directive (find-if #'format-directive-p list
)))
1038 nil
(1- (format-directive-end directive
))
1039 "Cannot include format directives inside the ~
1040 ~:[suffix~;prefix~] segment of ~~<...~~:>"
1042 :references
'((:ansi-cl
:section
(22 3 5 2))))
1043 (apply #'concatenate
'string list
)))))
1044 (case (length segments
)
1045 (0 (values prefix-default nil suffix-default
))
1046 (1 (values prefix-default
(car segments
) suffix-default
))
1047 (2 (values (extract-string (car segments
) t
)
1048 (cadr segments
) suffix-default
))
1049 (3 (values (extract-string (car segments
) t
)
1051 (extract-string (caddr segments
) nil
)))
1053 (format-error "Too many segments for ~~<...~~:>")))))
1054 (when (format-directive-atsignp close
)
1056 (add-fill-style-newlines insides
1059 (format-directive-end first-semi
)
1062 (and first-semi
(format-directive-atsignp first-semi
))
1066 (defun add-fill-style-newlines (list string offset
&optional last-directive
)
1069 (let ((directive (car list
)))
1071 ((simple-string-p directive
)
1072 (let* ((non-space (position #\Space directive
:test
#'char
/=))
1073 (newlinep (and last-directive
1075 (format-directive-character last-directive
)
1078 ((and newlinep non-space
)
1080 (list (subseq directive
0 non-space
))
1081 (add-fill-style-newlines-aux
1082 (subseq directive non-space
) string
(+ offset non-space
))
1083 (add-fill-style-newlines
1084 (cdr list
) string
(+ offset
(length directive
)))))
1087 (add-fill-style-newlines
1088 (cdr list
) string
(+ offset
(length directive
)))))
1090 (nconc (add-fill-style-newlines-aux directive string offset
)
1091 (add-fill-style-newlines
1092 (cdr list
) string
(+ offset
(length directive
))))))))
1095 (add-fill-style-newlines
1097 (format-directive-end directive
) directive
))))))
1100 (defun add-fill-style-newlines-aux (literal string offset
)
1101 (let ((end (length literal
))
1103 (collect ((results))
1105 (let ((blank (position #\space literal
:start posn
)))
1107 (results (subseq literal posn
))
1109 (let ((non-blank (or (position #\space literal
:start blank
1112 (results (subseq literal posn non-blank
))
1113 (results (make-format-directive
1114 :string string
:character
#\_
1115 :start
(+ offset non-blank
) :end
(+ offset non-blank
)
1116 :colonp t
:atsignp nil
:params nil
))
1117 (setf posn non-blank
))
1122 (defun parse-format-justification (directives)
1123 (let ((first-semi nil
)
1125 (remaining directives
))
1126 (collect ((segments))
1128 (let ((close-or-semi (or (find-directive remaining
#\
> t
)
1129 (format-error "No corresponding close bracket"))))
1130 (let ((posn (position close-or-semi remaining
)))
1131 (segments (subseq remaining
0 posn
))
1132 (setf remaining
(nthcdr (1+ posn
) remaining
)))
1133 (when (char= (format-directive-character close-or-semi
)
1135 (setf close close-or-semi
)
1138 (setf first-semi close-or-semi
))))
1139 (values (segments) first-semi close remaining
))))
1141 (sb!xc
:defmacro expander-pprint-next-arg
(string offset
)
1144 (format-error-at ,string
,offset
"No more arguments"))
1148 (defun expand-format-logical-block (prefix per-line-p insides suffix atsignp
)
1149 `(let ((arg ,(if atsignp
'args
(expand-next-arg))))
1151 (setf *only-simple-args
* nil
)
1153 (pprint-logical-block
1155 ,(if per-line-p
:per-line-prefix
:prefix
) ,prefix
1159 `((orig-args arg
))))
1160 (declare (ignorable args
,@(unless atsignp
'(orig-args))))
1162 ,@(let ((*expander-next-arg-macro
* 'expander-pprint-next-arg
)
1163 (*only-simple-args
* nil
)
1164 (*orig-args-available
*
1165 (if atsignp
*orig-args-available
* t
)))
1166 (expand-directive-list insides
)))))))
1168 (defun expand-format-justification (segments colonp atsignp first-semi params
)
1169 (let ((newline-segment-p
1171 (format-directive-colonp first-semi
))))
1172 (expand-bind-defaults
1173 ((mincol 0) (colinc 1) (minpad 0) (padchar #\space
))
1175 `(let ((segments nil
)
1176 ,@(when newline-segment-p
1177 '((newline-segment nil
)
1181 ,@(when newline-segment-p
1182 `((setf newline-segment
1183 (with-simple-output-to-string (stream)
1184 ,@(expand-directive-list (pop segments
))))
1185 ,(expand-bind-defaults
1187 (line-len '(or (sb!impl
::line-length stream
) 72)))
1188 (format-directive-params first-semi
)
1189 `(setf extra-space
,extra line-len
,line-len
))))
1190 ,@(mapcar (lambda (segment)
1191 `(push (with-simple-output-to-string (stream)
1192 ,@(expand-directive-list segment
))
1195 (format-justification stream
1196 ,@(if newline-segment-p
1197 '(newline-segment extra-space line-len
)
1199 segments
,colonp
,atsignp
1200 ,mincol
,colinc
,minpad
,padchar
)))))
1202 ;;;; format directive and support function for user-defined method
1204 (def-format-directive #\
/ (string start end colonp atsignp params
)
1205 (let ((symbol (extract-user-fun-name string start end
)))
1206 (collect ((param-names) (bindings))
1207 (dolist (param-and-offset params
)
1208 (let ((param (cdr param-and-offset
)))
1209 (let ((param-name (sb!xc
:gensym
"PARAM")))
1210 (param-names param-name
)
1211 (bindings `(,param-name
1213 (:arg
(expand-next-arg))
1214 (:remaining
'(length args
))
1217 (,symbol stream
,(expand-next-arg) ,colonp
,atsignp
1218 ,@(param-names))))))
1220 (defun extract-user-fun-name (string start end
)
1221 (let* ((slash (or (position #\
/ string
:start start
:end
(1- end
)
1223 (format-error "Malformed ~~/ directive")))
1224 (name (string-upcase (let ((foo string
))
1225 ;; HACK: This is to keep the compiler
1226 ;; quiet about deleting code inside
1227 ;; the subseq expansion.
1228 (subseq foo
(1+ slash
) (1- end
)))))
1229 (first-colon (position #\
: name
))
1230 (second-colon (if first-colon
(position #\
: name
:start
(1+ first-colon
))))
1232 (if (not first-colon
)
1233 (load-time-value (find-package "COMMON-LISP-USER") t
)
1234 (let ((package-name (subseq name
0 first-colon
)))
1235 (or (find-package package-name
)
1236 ;; FIXME: should be PACKAGE-ERROR? Could we just
1237 ;; use FIND-UNDELETED-PACKAGE-OR-LOSE?
1238 (format-error "No package named ~S" package-name
))))))
1240 ((and second-colon
(= second-colon
(1+ first-colon
)))
1241 (subseq name
(1+ second-colon
)))
1243 (subseq name
(1+ first-colon
)))
1247 ;;; compile-time checking for argument mismatch. This code is
1248 ;;; inspired by that of Gerd Moellmann, and comes decorated with
1250 (defun %compiler-walk-format-string
(string args
)
1251 (let* ((string (coerce string
'simple-string
))
1252 (*default-format-error-control-string
* string
))
1253 (macrolet ((incf-both (&optional
(increment 1))
1255 (incf min
,increment
)
1256 (incf max
,increment
)))
1257 (walk-complex-directive (function)
1258 `(multiple-value-bind (min-inc max-inc remaining
)
1259 (,function directive directives args
)
1262 (setq directives remaining
))))
1263 ;; FIXME: these functions take a list of arguments as well as
1264 ;; the directive stream. This is to enable possibly some
1265 ;; limited type checking on FORMAT's arguments, as well as
1266 ;; simple argument count mismatch checking: when the minimum and
1267 ;; maximum argument counts are the same at a given point, we
1268 ;; know which argument is going to be used for a given
1269 ;; directive, and some (annotated below) require arguments of
1270 ;; particular types.
1272 ((walk-justification (justification directives args
)
1273 (declare (ignore args
))
1274 (let ((*default-format-error-offset
*
1275 (1- (format-directive-end justification
))))
1276 (multiple-value-bind (segments first-semi close remaining
)
1277 (parse-format-justification directives
)
1278 (declare (ignore segments first-semi
))
1280 ((not (format-directive-colonp close
))
1281 (values 0 0 directives
))
1282 ((format-directive-atsignp justification
)
1283 (values 0 sb
!xc
:call-arguments-limit directives
))
1284 ;; FIXME: here we could assert that the
1285 ;; corresponding argument was a list.
1286 (t (values 1 1 remaining
))))))
1287 (walk-conditional (conditional directives args
)
1288 (let ((*default-format-error-offset
*
1289 (1- (format-directive-end conditional
))))
1290 (multiple-value-bind (sublists last-semi-with-colon-p remaining
)
1291 (parse-conditional-directive directives
)
1292 (declare (ignore last-semi-with-colon-p
))
1294 (loop for s in sublists
1296 1 (walk-directive-list s args
)))))
1298 ((format-directive-atsignp conditional
)
1299 (values 1 (max 1 sub-max
) remaining
))
1300 ((loop for p in
(format-directive-params conditional
)
1301 thereis
(or (integerp (cdr p
))
1302 (memq (cdr p
) '(:remaining
:arg
))))
1303 (values 0 sub-max remaining
))
1304 ;; FIXME: if not COLONP, then the next argument
1305 ;; must be a number.
1306 (t (values 1 (1+ sub-max
) remaining
)))))))
1307 (walk-iteration (iteration directives args
)
1308 (declare (ignore args
))
1309 (let ((*default-format-error-offset
*
1310 (1- (format-directive-end iteration
))))
1311 (let* ((close (find-directive directives
#\
} nil
))
1312 (posn (or (position close directives
)
1313 (format-error "No corresponding close brace")))
1314 (remaining (nthcdr (1+ posn
) directives
)))
1315 ;; FIXME: if POSN is zero, the next argument must be
1316 ;; a format control (either a function or a string).
1317 (if (format-directive-atsignp iteration
)
1318 (values (if (zerop posn
) 1 0)
1319 sb
!xc
:call-arguments-limit
1321 ;; FIXME: the argument corresponding to this
1322 ;; directive must be a list.
1323 (let ((nreq (if (zerop posn
) 2 1)))
1324 (values nreq nreq remaining
))))))
1325 (walk-directive-list (directives args
)
1326 (let ((min 0) (max 0))
1328 (let ((directive (pop directives
)))
1329 (when (null directive
)
1330 (return (values min
(min max sb
!xc
:call-arguments-limit
))))
1331 (when (format-directive-p directive
)
1332 (incf-both (count :arg
(format-directive-params directive
)
1334 (let ((c (format-directive-character directive
)))
1336 ((find c
"ABCDEFGORSWX$/")
1339 (unless (format-directive-colonp directive
)
1341 ((or (find c
"IT%&|_();>~") (char= c
#\Newline
)))
1342 ;; FIXME: check correspondence of ~( and ~)
1344 (walk-complex-directive walk-justification
))
1346 (walk-complex-directive walk-conditional
))
1348 (walk-complex-directive walk-iteration
))
1350 ;; FIXME: the argument corresponding to this
1351 ;; directive must be a format control.
1353 ((format-directive-atsignp directive
)
1355 (setq max sb
!xc
:call-arguments-limit
))
1357 (t (throw 'give-up-format-string-walk nil
))))))))))
1358 (catch 'give-up-format-string-walk
1359 (let ((directives (tokenize-control-string string
)))
1360 (walk-directive-list directives args
)))))))
1362 ;;; Optimize common case of constant keyword arguments
1363 ;;; to WRITE and WRITE-TO-STRING
1365 ((expand (fn object keys
)
1366 (do (streamvar bind ignore
)
1367 ((or (atom keys
) (atom (cdr keys
)))
1371 (let* ((objvar (copy-symbol 'object
))
1372 (bind `((,objvar
,object
) ,@(nreverse bind
)))
1373 (ignore (when ignore
`((declare (ignore ,@ignore
))))))
1376 ;; When :STREAM was specified, this used to insert a call
1377 ;; to (OUT-SYNONYM-OF STREAMVAR) which added junk to the
1378 ;; expansion which was not likely to improve performance.
1379 ;; The benefit of this transform is that it avoids runtime
1380 ;; keyword parsing and binding of 16 specials vars, *not*
1381 ;; that it can inline testing for T or NIL as the stream.
1382 `(let ,bind
,@ignore
1384 `((%write
,objvar
,streamvar
))
1385 `((output-object ,objvar
*standard-output
*)
1389 `(let ,bind
,@ignore
(stringify-object ,objvar
))
1390 `(stringify-object ,object
)))))
1392 (let* ((key (pop keys
))
1395 (cond ((getf '(:array
*print-array
*
1398 :circle
*print-circle
*
1399 :escape
*print-escape
*
1400 :gensym
*print-gensym
*
1401 :length
*print-length
*
1402 :level
*print-level
*
1403 :lines
*print-lines
*
1404 :miser-width
*print-miser-width
*
1405 :pprint-dispatch
*print-pprint-dispatch
*
1406 :pretty
*print-pretty
*
1407 :radix
*print-radix
*
1408 :readably
*print-readably
*
1409 :right-margin
*print-right-margin
*
1410 :suppress-errors
*suppress-print-errors
*)
1412 ((and (eq key
:stream
) (eq fn
'write
))
1413 (or streamvar
(setq streamvar
(copy-symbol 'stream
))))
1415 (return (values nil t
))))))
1416 (when (assoc variable bind
)
1417 ;; First key has precedence, but we still need to execute the
1418 ;; argument, and in the right order.
1419 (setf variable
(gensym "IGNORE"))
1420 (push variable ignore
))
1421 (push (list variable value
) bind
)))))
1423 (sb!c
:define-source-transform write
(object &rest keys
)
1424 (expand 'write object keys
))
1426 (sb!c
:define-source-transform write-to-string
(object &rest keys
)
1427 (expand 'write-to-string object keys
)))
1429 ;;; A long as we're processing ERROR strings to remove "SB!" packages,
1430 ;;; we might as well squash out tilde-newline-whitespace too.
1431 ;;; This might even be robust enough to keep in the target image,
1432 ;;; but, FIXME: this punts on ~newline with {~@,~:,~@:} modifiers
1434 (defun sb!impl
::!xc-preprocess-format-control
(string)
1436 ;; Tokenizing is the correct way to deal with "~~/foo/"
1437 ;; without mistaking it for an occurrence of the "~/" directive.
1438 (dolist (piece (tokenize-control-string string
)
1439 (let ((new (apply 'concatenate
'string
(nreverse pieces
))))
1440 (if (string/= new string
) new string
)))
1444 (let ((p (position-if
1445 (lambda (x) (and (not (eql x
#\Space
)) (graphic-char-p x
)))
1448 (push (subseq piece p
) pieces
)))
1449 (push piece pieces
))
1453 (let ((text (subseq string
1454 (format-directive-start piece
)
1455 (format-directive-end piece
)))
1457 (cond ((and (eql (format-directive-character piece
) #\Newline
)
1458 (not (format-directive-colonp piece
))
1459 (not (format-directive-atsignp piece
)))
1460 (setq ltrim t processed t
))
1461 ((eql (format-directive-character piece
) #\
/)
1462 (when (string-equal text
"~/sb!" :end1
5)
1463 (setq text
(concatenate 'string
"~/sb-" (subseq text
5))))))
1465 (push text pieces
))))))))