Make AND and OR not be recursively expanded in syntax
[sbcl.git] / src / code / late-format.lisp
bloba988d61c4209358c094e7faf7df5840443a246ef
1 ;;;; This software is part of the SBCL system. See the README file for
2 ;;;; more information.
3 ;;;;
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
18 :hash-bits 7
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.
24 ((string string=))
25 (declare (simple-string string))
26 (let ((index 0)
27 (end (length string))
28 (result nil)
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.
32 (block)
33 (pprint)
34 (semicolon)
35 (justification-semicolon))
36 (loop
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)
41 (return))
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
45 (cond
46 ((char= char #\<) (push directive block))
47 ((and block (char= char #\;) (format-directive-colonp directive))
48 (setf semicolon directive))
49 ((char= char #\>)
50 (unless block
51 (format-error-at string next-directive
52 "~~> without a matching ~~<"))
53 (cond
54 ((format-directive-colonp directive)
55 (unless pprint
56 (setf pprint (car block)))
57 (setf semicolon nil))
58 (semicolon
59 (unless justification-semicolon
60 (setf justification-semicolon semicolon))))
61 (pop block))
62 ;; block cases are handled by the #\< expander/interpreter
63 ((not block)
64 (case char
65 ((#\W #\I #\_) (unless pprint (setf pprint directive)))
66 (#\T (when (and (format-directive-colonp directive)
67 (not pprint))
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)))
73 (justification-offset
74 (1- (format-directive-end justification-semicolon))))
75 (format-error-at*
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 (list '(:ansi-cl :section (22 3 5 2))))))
82 (nreverse result)))
84 (defun parse-directive (string start)
85 (let ((posn (1+ start)) (params nil) (colonp nil) (atsignp nil)
86 (end (length string)))
87 (flet ((get-char ()
88 (if (= posn end)
89 (format-error-at string start
90 "String ended before directive was found")
91 (schar string posn)))
92 (check-ordering ()
93 (when (or colonp atsignp)
94 (format-error-at*
95 string posn
96 "Parameters found after #\\: or #\\@ modifier" '()
97 :references (list '(:ansi-cl :section (22 3)))))))
98 (loop
99 (let ((char (get-char)))
100 (cond ((or (char<= #\0 char #\9) (char= char #\+) (char= char #\-))
101 (check-ordering)
102 (multiple-value-bind (param new-posn)
103 (parse-integer string :start posn :junk-allowed t)
104 (push (cons posn param) params)
105 (setf posn new-posn)
106 (case (get-char)
107 (#\,)
108 ((#\: #\@)
109 (decf posn))
111 (return)))))
112 ((or (char= char #\v)
113 (char= char #\V))
114 (check-ordering)
115 (push (cons posn :arg) params)
116 (incf posn)
117 (case (get-char)
118 (#\,)
119 ((#\: #\@)
120 (decf posn))
122 (return))))
123 ((char= char #\#)
124 (check-ordering)
125 (push (cons posn :remaining) params)
126 (incf posn)
127 (case (get-char)
128 (#\,)
129 ((#\: #\@)
130 (decf posn))
132 (return))))
133 ((char= char #\')
134 (check-ordering)
135 (incf posn)
136 (push (cons posn (get-char)) params)
137 (incf posn)
138 (unless (char= (get-char) #\,)
139 (decf posn)))
140 ((char= char #\,)
141 (check-ordering)
142 (push (cons posn nil) params))
143 ((char= char #\:)
144 (if colonp
145 (format-error-at*
146 string posn "Too many colons supplied" '()
147 :references (list '(:ansi-cl :section (22 3))))
148 (setf colonp t)))
149 ((char= char #\@)
150 (if atsignp
151 (format-error-at*
152 string posn "Too many #\\@ characters supplied" '()
153 :references (list '(:ansi-cl :section (22 3))))
154 (setf atsignp t)))
156 (when (and (char= (schar string (1- posn)) #\,)
157 (or (< posn 2)
158 (char/= (schar string (- posn 2)) #\')))
159 (check-ordering)
160 (push (cons (1- posn) nil) params))
161 (return))))
162 (incf posn))
163 (let ((char (get-char)))
164 (when (char= char #\/)
165 (let ((closing-slash (position #\/ string :start (1+ posn))))
166 (if closing-slash
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))))))
175 ;;;; FORMATTER stuff
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.
187 (block nil
188 (catch 'need-orig-args
189 (let* ((*simple-args* nil)
190 (*only-simple-args* t)
191 (guts (expand-control-string control-string)) ; can throw
192 (required nil)
193 (optional nil))
194 (dolist (arg *simple-args*)
195 (cond ((plusp arg-count)
196 (push (car arg) required)
197 (decf arg-count))
199 (push `(,(car arg)
200 (args-exhausted ,control-string ,(cdr arg)))
201 optional))))
202 (return `(lambda (stream ,@required
203 ,@(if optional '(&optional)) ,@optional
204 &rest args)
205 (declare (ignorable stream args))
206 ,guts
207 ,(and need-retval 'args)))))
208 (let ((*orig-args-available* t)
209 (*only-simple-args* nil))
210 `(lambda (stream &rest orig-args)
211 (declare (ignorable stream))
212 (let ((args orig-args))
213 ,(expand-control-string control-string)
214 ,(and need-retval 'args))))))
216 (defun args-exhausted (control-string offset)
217 (format-error-at control-string offset "No more arguments"))
219 (defun expand-control-string (string)
220 (let* ((string (etypecase string
221 (simple-string
222 string)
223 (string
224 (coerce string 'simple-string))))
225 (*default-format-error-control-string* string)
226 (directives (tokenize-control-string string)))
227 `(block nil
228 ,@(expand-directive-list directives))))
230 (defun expand-directive-list (directives)
231 (let ((results nil)
232 (remaining-directives directives))
233 (loop
234 (unless remaining-directives
235 (return))
236 (multiple-value-bind (form new-directives)
237 (expand-directive (car remaining-directives)
238 (cdr remaining-directives))
239 (push form results)
240 (setf remaining-directives new-directives)))
241 (reverse results)))
243 (defun expand-directive (directive more-directives)
244 (etypecase directive
245 (format-directive
246 (let ((expander
247 (let ((char (format-directive-character directive)))
248 (typecase char
249 (base-char
250 (aref *format-directive-expanders* (sb!xc:char-code char))))))
251 (*default-format-error-offset*
252 (1- (format-directive-end directive))))
253 (declare (type (or null function) expander))
254 (if expander
255 (funcall expander directive more-directives)
256 (format-error "Unknown directive ~@[(character: ~A)~]"
257 (char-name (format-directive-character directive))))))
258 (simple-string
259 (values `(write-string ,directive stream)
260 more-directives))))
262 (sb!xc:defmacro expander-next-arg (string offset)
263 `(if args
264 (pop args)
265 (format-error-at ,string ,offset "No more arguments")))
267 (defun expand-next-arg (&optional offset)
268 (if (or *orig-args-available* (not *only-simple-args*))
269 `(,*expander-next-arg-macro*
270 ,*default-format-error-control-string*
271 ,(or offset *default-format-error-offset*))
272 (let ((symbol (sb!xc:gensym "FORMAT-ARG")))
273 (push (cons symbol (or offset *default-format-error-offset*))
274 *simple-args*)
275 symbol)))
277 (defmacro expand-bind-defaults (specs params &body body)
278 (once-only ((params params))
279 (if specs
280 (collect ((expander-bindings) (runtime-bindings))
281 (dolist (spec specs)
282 (destructuring-bind (var default) spec
283 (let ((symbol (sb!xc:gensym "FVAR")))
284 (expander-bindings
285 `(,var ',symbol))
286 (runtime-bindings
287 `(list ',symbol
288 (let* ((param-and-offset (pop ,params))
289 (offset (car param-and-offset))
290 (param (cdr param-and-offset)))
291 (case param
292 (:arg `(or ,(expand-next-arg offset) ,,default))
293 (:remaining
294 (setf *only-simple-args* nil)
295 '(length args))
296 ((nil) ,default)
297 (t param))))))))
298 `(let ,(expander-bindings)
299 `(let ,(list ,@(runtime-bindings))
300 ,@(if ,params
301 (format-error-at
302 nil (caar ,params)
303 "Too many parameters, expected no more than ~W"
304 ,(length specs)))
305 ,,@body)))
306 `(progn
307 (when ,params
308 (format-error-at nil (caar ,params)
309 "Too many parameters, expected none"))
310 ,@body))))
312 ;;;; format directive machinery
314 (eval-when (:compile-toplevel :execute)
315 (#+sb-xc-host defmacro #-sb-xc-host sb!xc:defmacro def-complex-format-directive (char lambda-list &body body)
316 (let ((defun-name (intern (format nil
317 "~:@(~:C~)-FORMAT-DIRECTIVE-EXPANDER"
318 char)))
319 (directive (sb!xc:gensym "DIRECTIVE"))
320 (directives (if lambda-list (car (last lambda-list)) (sb!xc:gensym "DIRECTIVES"))))
321 `(progn
322 (defun ,defun-name (,directive ,directives)
323 ,@(if lambda-list
324 `((let ,(mapcar (lambda (var)
325 `(,var
326 (,(symbolicate "FORMAT-DIRECTIVE-" var)
327 ,directive)))
328 (butlast lambda-list))
329 ,@body))
330 `((declare (ignore ,directive ,directives))
331 ,@body)))
332 (%set-format-directive-expander ,char #',defun-name))))
334 (#+sb-xc-host defmacro #-sb-xc-host sb!xc:defmacro def-format-directive (char lambda-list &body body)
335 (let ((directives (sb!xc:gensym "DIRECTIVES"))
336 (declarations nil)
337 (body-without-decls body))
338 (loop
339 (let ((form (car body-without-decls)))
340 (unless (and (consp form) (eq (car form) 'declare))
341 (return))
342 (push (pop body-without-decls) declarations)))
343 (setf declarations (reverse declarations))
344 `(def-complex-format-directive ,char (,@lambda-list ,directives)
345 ,@declarations
346 (values (progn ,@body-without-decls)
347 ,directives))))
348 ) ; EVAL-WHEN
350 (eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute)
352 (defun %set-format-directive-expander (char fn)
353 (let ((code (sb!xc:char-code (char-upcase char))))
354 (setf (aref *format-directive-expanders* code) fn))
355 char)
357 (defun %set-format-directive-interpreter (char fn)
358 (let ((code (sb!xc:char-code (char-upcase char))))
359 (setf (aref *format-directive-interpreters* code) fn))
360 char)
362 (defun find-directive (directives kind stop-at-semi)
363 (if directives
364 (let ((next (car directives)))
365 (if (format-directive-p next)
366 (let ((char (format-directive-character next)))
367 (if (or (char= kind char)
368 (and stop-at-semi (char= char #\;)))
369 (car directives)
370 (find-directive
371 (cdr (flet ((after (char)
372 (member (find-directive (cdr directives)
373 char
374 nil)
375 directives)))
376 (case char
377 (#\( (after #\)))
378 (#\< (after #\>))
379 (#\[ (after #\]))
380 (#\{ (after #\}))
381 (t directives))))
382 kind stop-at-semi)))
383 (find-directive (cdr directives) kind stop-at-semi)))))
385 ) ; EVAL-WHEN
387 ;;;; format directives for simple output
389 (def-format-directive #\A (colonp atsignp params)
390 (if params
391 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
392 (padchar #\space))
393 params
394 `(format-princ stream ,(expand-next-arg) ',colonp ',atsignp
395 ,mincol ,colinc ,minpad ,padchar))
396 `(princ ,(if colonp
397 `(or ,(expand-next-arg) "()")
398 (expand-next-arg))
399 stream)))
401 (def-format-directive #\S (colonp atsignp params)
402 (cond (params
403 (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0)
404 (padchar #\space))
405 params
406 `(format-prin1 stream ,(expand-next-arg) ,colonp ,atsignp
407 ,mincol ,colinc ,minpad ,padchar)))
408 (colonp
409 `(let ((arg ,(expand-next-arg)))
410 (if arg
411 (prin1 arg stream)
412 (princ "()" stream))))
414 `(prin1 ,(expand-next-arg) stream))))
416 (def-format-directive #\C (colonp atsignp params string end)
417 (expand-bind-defaults () params
418 (let ((n-arg (sb!xc:gensym "ARG")))
419 `(let ((,n-arg ,(expand-next-arg)))
420 (unless (typep ,n-arg 'character)
421 (format-error-at ,string ,(1- end)
422 "~S is not of type CHARACTER." ,n-arg))
423 ,(cond (colonp
424 `(format-print-named-character ,n-arg stream))
425 (atsignp
426 `(prin1 ,n-arg stream))
428 `(write-char ,n-arg stream)))))))
430 (def-format-directive #\W (colonp atsignp params)
431 (expand-bind-defaults () params
432 (if (or colonp atsignp)
433 `(let (,@(when colonp
434 '((*print-pretty* t)))
435 ,@(when atsignp
436 '((*print-level* nil)
437 (*print-length* nil))))
438 (output-object ,(expand-next-arg) stream))
439 `(output-object ,(expand-next-arg) stream))))
441 ;;;; format directives for integer output
443 (defun expand-format-integer (base colonp atsignp params)
444 (if (or colonp atsignp params)
445 (expand-bind-defaults
446 ((mincol 0) (padchar #\space) (commachar #\,) (commainterval 3))
447 params
448 `(format-print-integer stream ,(expand-next-arg) ,colonp ,atsignp
449 ,base ,mincol ,padchar ,commachar
450 ,commainterval))
451 `(let ((*print-base* ,base)
452 (*print-radix* nil))
453 (princ ,(expand-next-arg) stream))))
455 (def-format-directive #\D (colonp atsignp params)
456 (expand-format-integer 10 colonp atsignp params))
458 (def-format-directive #\B (colonp atsignp params)
459 (expand-format-integer 2 colonp atsignp params))
461 (def-format-directive #\O (colonp atsignp params)
462 (expand-format-integer 8 colonp atsignp params))
464 (def-format-directive #\X (colonp atsignp params)
465 (expand-format-integer 16 colonp atsignp params))
467 (def-format-directive #\R (colonp atsignp params string end)
468 (expand-bind-defaults
469 ((base nil) (mincol 0) (padchar #\space) (commachar #\,)
470 (commainterval 3))
471 params
472 (let ((n-arg (sb!xc:gensym "ARG")))
473 `(let ((,n-arg ,(expand-next-arg)))
474 (unless (or ,base
475 (integerp ,n-arg))
476 (format-error-at ,string ,(1- end) "~S is not of type INTEGER." ,n-arg))
477 (if ,base
478 (format-print-integer stream ,n-arg ,colonp ,atsignp
479 ,base ,mincol
480 ,padchar ,commachar ,commainterval)
481 ,(if atsignp
482 (if colonp
483 `(format-print-old-roman stream ,n-arg)
484 `(format-print-roman stream ,n-arg))
485 (if colonp
486 `(format-print-ordinal stream ,n-arg)
487 `(format-print-cardinal stream ,n-arg))))))))
489 ;;;; format directive for pluralization
491 (def-format-directive #\P (colonp atsignp params end)
492 (expand-bind-defaults () params
493 (let ((arg (cond
494 ((not colonp)
495 (expand-next-arg))
496 (*orig-args-available*
497 `(if (eq orig-args args)
498 (format-error-at
499 ,*default-format-error-control-string* ,(1- end)
500 "No previous argument")
501 (do ((arg-ptr orig-args (cdr arg-ptr)))
502 ((eq (cdr arg-ptr) args)
503 (car arg-ptr)))))
504 (*only-simple-args*
505 (unless *simple-args*
506 (format-error "No previous argument"))
507 (caar *simple-args*))
509 (/show0 "THROWing NEED-ORIG-ARGS from tilde-P")
510 (throw 'need-orig-args nil)))))
511 (if atsignp
512 `(write-string (if (eql ,arg 1) "y" "ies") stream)
513 `(unless (eql ,arg 1) (write-char #\s stream))))))
515 ;;;; format directives for floating point output
517 (def-format-directive #\F (colonp atsignp params)
518 (check-modifier "colon" colonp)
519 (expand-bind-defaults ((w nil) (d nil) (k nil) (ovf nil) (pad #\space)) params
520 `(format-fixed stream ,(expand-next-arg) ,w ,d ,k ,ovf ,pad ,atsignp)))
522 (def-format-directive #\E (colonp atsignp params)
523 (check-modifier "colon" colonp)
524 (expand-bind-defaults
525 ((w nil) (d nil) (e nil) (k 1) (ovf nil) (pad #\space) (mark nil))
526 params
527 `(format-exponential stream ,(expand-next-arg) ,w ,d ,e ,k ,ovf ,pad ,mark
528 ,atsignp)))
530 (def-format-directive #\G (colonp atsignp params)
531 (check-modifier "colon" colonp)
532 (expand-bind-defaults
533 ((w nil) (d nil) (e nil) (k nil) (ovf nil) (pad #\space) (mark nil))
534 params
535 `(format-general stream ,(expand-next-arg) ,w ,d ,e ,k ,ovf ,pad ,mark ,atsignp)))
537 (def-format-directive #\$ (colonp atsignp params)
538 (expand-bind-defaults ((d 2) (n 1) (w 0) (pad #\space)) params
539 `(format-dollars stream ,(expand-next-arg) ,d ,n ,w ,pad ,colonp
540 ,atsignp)))
542 ;;;; format directives for line/page breaks etc.
544 (def-format-directive #\% (colonp atsignp params)
545 (check-modifier "colon" colonp)
546 (check-modifier "at-sign" atsignp)
547 (if params
548 (expand-bind-defaults ((count 1)) params
549 `(dotimes (i ,count)
550 (terpri stream)))
551 '(terpri stream)))
553 (def-format-directive #\& (colonp atsignp params)
554 (check-modifier "colon" colonp)
555 (check-modifier "at-sign" atsignp)
556 (if params
557 (expand-bind-defaults ((count 1)) params
558 `(progn
559 (when (plusp ,count)
560 (fresh-line stream)
561 (dotimes (i (1- ,count))
562 (terpri stream)))))
563 '(fresh-line stream)))
565 (def-format-directive #\| (colonp atsignp params)
566 (check-modifier "colon" colonp)
567 (check-modifier "at-sign" atsignp)
568 (if params
569 (expand-bind-defaults ((count 1)) params
570 `(dotimes (i ,count)
571 (write-char (code-char form-feed-char-code) stream)))
572 '(write-char (code-char form-feed-char-code) stream)))
574 (def-format-directive #\~ (colonp atsignp params)
575 (check-modifier "colon" colonp)
576 (check-modifier "at-sign" atsignp)
577 (if params
578 (expand-bind-defaults ((count 1)) params
579 `(dotimes (i ,count)
580 (write-char #\~ stream)))
581 '(write-char #\~ stream)))
583 (def-complex-format-directive #\newline (colonp atsignp params directives)
584 ;; FIXME: this is not an error!
585 (check-modifier '("colon" "at-sign") (and colonp atsignp))
586 (values (expand-bind-defaults () params
587 (if atsignp
588 '(write-char #\newline stream)
589 nil))
590 (if (and (not colonp)
591 directives
592 (simple-string-p (car directives)))
593 (cons (string-left-trim *format-whitespace-chars*
594 (car directives))
595 (cdr directives))
596 directives)))
598 ;;;; format directives for tabs and simple pretty printing
600 (def-format-directive #\T (colonp atsignp params)
601 (if colonp
602 (expand-bind-defaults ((n 1) (m 1)) params
603 `(pprint-tab ,(if atsignp :section-relative :section)
604 ,n ,m stream))
605 (if atsignp
606 (expand-bind-defaults ((colrel 1) (colinc 1)) params
607 `(format-relative-tab stream ,colrel ,colinc))
608 (expand-bind-defaults ((colnum 1) (colinc 1)) params
609 `(format-absolute-tab stream ,colnum ,colinc)))))
611 (def-format-directive #\_ (colonp atsignp params)
612 (expand-bind-defaults () params
613 `(pprint-newline ,(if colonp
614 (if atsignp
615 :mandatory
616 :fill)
617 (if atsignp
618 :miser
619 :linear))
620 stream)))
622 (def-format-directive #\I (colonp atsignp params)
623 (check-modifier "at-sign" atsignp)
624 (expand-bind-defaults ((n 0)) params
625 `(pprint-indent ,(if colonp :current :block) ,n stream)))
627 ;;;; format directive for ~*
629 (def-format-directive #\* (colonp atsignp params end)
630 (check-modifier '("colon" "at-sign") (and colonp atsignp))
631 (flet ((make-lose (index)
632 `(format-error-at
633 ,*default-format-error-control-string* ,(1- end)
634 "Index ~W is out of bounds. It should have been between ~
635 0 and ~W."
636 ,index (length orig-args))))
637 (if atsignp
638 (expand-bind-defaults ((posn 0)) params
639 (unless *orig-args-available*
640 (/show0 "THROWing NEED-ORIG-ARGS from tilde-@*")
641 (throw 'need-orig-args nil))
642 `(if (<= 0 ,posn (length orig-args))
643 (setf args (nthcdr ,posn orig-args))
644 ,(make-lose posn)))
645 (if colonp
646 (expand-bind-defaults ((n 1)) params
647 (unless *orig-args-available*
648 (/show0 "THROWing NEED-ORIG-ARGS from tilde-:*")
649 (throw 'need-orig-args nil))
650 `(do ((cur-posn 0 (1+ cur-posn))
651 (arg-ptr orig-args (cdr arg-ptr)))
652 ((eq arg-ptr args)
653 (let ((new-posn (- cur-posn ,n)))
654 (if (<= 0 new-posn (length orig-args))
655 (setf args (nthcdr new-posn orig-args))
656 ,(make-lose 'new-posn))))))
657 (if params
658 (expand-bind-defaults ((n 1)) params
659 (setf *only-simple-args* nil)
660 `(dotimes (i ,n)
661 ,(expand-next-arg)))
662 (expand-next-arg))))))
664 ;;;; format directive for indirection
666 (def-format-directive #\? (colonp atsignp params string end)
667 (check-modifier "colon" colonp)
668 (expand-bind-defaults () params
669 `(handler-bind
670 ((format-error
671 (lambda (condition)
672 (error 'format-error
673 :complaint
674 "~A~%while processing indirect format string:"
675 :args (list condition)
676 :print-banner nil
677 :control-string ,string
678 :offset ,(1- end)))))
679 ,(if atsignp
680 (if *orig-args-available*
681 `(setf args (%format stream ,(expand-next-arg) orig-args args))
682 (throw 'need-orig-args nil))
683 `(%format stream ,(expand-next-arg) ,(expand-next-arg))))))
685 ;;;; format directives for capitalization
687 (def-complex-format-directive #\( (colonp atsignp params directives)
688 (let* ((close (or (find-directive directives #\) nil)
689 (format-error "No corresponding close parenthesis")))
690 (posn (position close directives))
691 (before (subseq directives 0 posn))
692 (after (nthcdr (1+ posn) directives)))
693 (values
694 (expand-bind-defaults () params
695 `(let ((stream (make-case-frob-stream stream
696 ,(if colonp
697 (if atsignp
698 :upcase
699 :capitalize)
700 (if atsignp
701 :capitalize-first
702 :downcase)))))
703 ,@(expand-directive-list before)))
704 after)))
706 (def-complex-format-directive #\) ()
707 (format-error "No corresponding open parenthesis"))
709 ;;;; format directives and support functions for conditionalization
711 (def-complex-format-directive #\[ (colonp atsignp params directives)
712 (check-modifier '("colon" "at-sign") (and colonp atsignp))
713 (multiple-value-bind (sublists last-semi-with-colon-p remaining)
714 (parse-conditional-directive directives)
715 (values
716 (cond
717 (atsignp
718 (when (cdr sublists)
719 (format-error "Can only specify one section"))
720 (expand-bind-defaults () params
721 (expand-maybe-conditional (car sublists))))
722 (colonp
723 (unless (= (length sublists) 2)
724 (format-error "Must specify exactly two sections"))
725 (expand-bind-defaults () params
726 (apply #'expand-true-false-conditional sublists)))
728 (expand-bind-defaults ((index nil)) params
729 (setf *only-simple-args* nil)
730 (let ((clauses nil)
731 (case-sym (gensym))
732 (case `(or ,index ,(expand-next-arg))))
733 (when last-semi-with-colon-p
734 (push `(t ,@(expand-directive-list (pop sublists)))
735 clauses))
736 (let ((count (length sublists)))
737 (dolist (sublist sublists)
738 (push `(,(decf count)
739 ,@(expand-directive-list sublist))
740 clauses)))
741 `(let ((,case-sym ,case))
742 (unless (integerp ,case-sym)
743 (format-error-at
744 ,*default-format-error-control-string*
745 ,*default-format-error-offset*
746 "The argument to ~~[ is not an integer: ~A" ,case-sym))
747 (case ,case-sym ,@clauses))))))
748 remaining)))
750 (defun parse-conditional-directive (directives)
751 (let ((sublists nil)
752 (last-semi-with-colon-p nil)
753 (remaining directives))
754 (loop
755 (let* ((close-or-semi (or (find-directive remaining #\] t)
756 (format-error "No corresponding close bracket")))
757 (posn (position close-or-semi remaining)))
758 (push (subseq remaining 0 posn) sublists)
759 (setf remaining (nthcdr (1+ posn) remaining))
760 (when (char= (format-directive-character close-or-semi) #\])
761 (return))
762 (setf last-semi-with-colon-p
763 (format-directive-colonp close-or-semi))))
764 (values sublists last-semi-with-colon-p remaining)))
766 (defun expand-maybe-conditional (sublist)
767 (flet ((hairy ()
768 `(let ((prev-args args)
769 (arg ,(expand-next-arg)))
770 (when arg
771 (setf args prev-args)
772 ,@(expand-directive-list sublist)))))
773 (if *only-simple-args*
774 (multiple-value-bind (guts new-args)
775 (let ((*simple-args* *simple-args*))
776 (values (expand-directive-list sublist)
777 *simple-args*))
778 (cond ((and new-args (eq *simple-args* (cdr new-args)))
779 (setf *simple-args* new-args)
780 `(when ,(caar new-args)
781 ,@guts))
783 (setf *only-simple-args* nil)
784 (hairy))))
785 (hairy))))
787 (defun expand-true-false-conditional (true false)
788 (let ((arg (expand-next-arg)))
789 (flet ((hairy ()
790 `(if ,arg
791 (progn
792 ,@(expand-directive-list true))
793 (progn
794 ,@(expand-directive-list false)))))
795 (if *only-simple-args*
796 (multiple-value-bind (true-guts true-args true-simple)
797 (let ((*simple-args* *simple-args*)
798 (*only-simple-args* t))
799 (values (expand-directive-list true)
800 *simple-args*
801 *only-simple-args*))
802 (multiple-value-bind (false-guts false-args false-simple)
803 (let ((*simple-args* *simple-args*)
804 (*only-simple-args* t))
805 (values (expand-directive-list false)
806 *simple-args*
807 *only-simple-args*))
808 (if (= (length true-args) (length false-args))
809 `(if ,arg
810 (progn
811 ,@true-guts)
812 ,(do ((false false-args (cdr false))
813 (true true-args (cdr true))
814 (bindings nil (cons `(,(caar false) ,(caar true))
815 bindings)))
816 ((eq true *simple-args*)
817 (setf *simple-args* true-args)
818 (setf *only-simple-args*
819 (and true-simple false-simple))
820 (if bindings
821 `(let ,bindings
822 ,@false-guts)
823 `(progn
824 ,@false-guts)))))
825 (progn
826 (setf *only-simple-args* nil)
827 (hairy)))))
828 (hairy)))))
830 (def-complex-format-directive #\; ()
831 (format-error
832 "~~; directive not contained within either ~~[...~~] or ~~<...~~>"))
834 (def-complex-format-directive #\] ()
835 (format-error "No corresponding open bracket"))
837 ;;;; format directive for up-and-out
839 (def-format-directive #\^ (colonp atsignp params)
840 (check-modifier "at-sign" atsignp)
841 (when (and colonp (not *up-up-and-out-allowed*))
842 (format-error "Attempt to use ~~:^ outside a ~~:{...~~} construct"))
843 `(when ,(expand-bind-defaults ((arg1 nil) (arg2 nil) (arg3 nil)) params
844 `(cond (,arg3 (<= ,arg1 ,arg2 ,arg3))
845 (,arg2 (eql ,arg1 ,arg2))
846 (,arg1 (eql ,arg1 0))
847 (t ,(if colonp
848 '(null outside-args)
849 (progn
850 (setf *only-simple-args* nil)
851 '(null args))))))
852 ,(if colonp
853 '(return-from outside-loop nil)
854 '(return))))
856 ;;;; format directives for iteration
858 (def-complex-format-directive #\{ (colonp atsignp params string end directives)
859 (let* ((close (or (find-directive directives #\} nil)
860 (format-error "No corresponding close brace")))
861 (closed-with-colon (format-directive-colonp close))
862 (posn (position close directives)))
863 (labels
864 ((compute-insides ()
865 (if (zerop posn)
866 (if *orig-args-available*
867 `((handler-bind
868 ((format-error
869 (lambda (condition)
870 (format-error-at*
871 ,string ,(1- end)
872 "~A~%while processing indirect format string:"
873 (list condition)
874 :print-banner nil))))
875 (setf args
876 (%format stream inside-string orig-args args))))
877 (throw 'need-orig-args nil))
878 (let ((*up-up-and-out-allowed* colonp))
879 (expand-directive-list (subseq directives 0 posn)))))
880 (compute-loop (count)
881 (when atsignp
882 (setf *only-simple-args* nil))
883 `(loop
884 ,@(unless closed-with-colon
885 '((when (null args)
886 (return))))
887 ,@(when count
888 `((when (and ,count (minusp (decf ,count)))
889 (return))))
890 ,@(if colonp
891 (let ((*expander-next-arg-macro* 'expander-next-arg)
892 (*only-simple-args* nil)
893 (*orig-args-available* t))
894 `((let* ((orig-args ,(expand-next-arg))
895 (outside-args args)
896 (args orig-args))
897 (declare (ignorable orig-args outside-args args))
898 (block nil
899 ,@(compute-insides)))))
900 (compute-insides))
901 ,@(when closed-with-colon
902 '((when (null args)
903 (return))))))
904 (compute-block (count)
905 (if colonp
906 `(block outside-loop
907 ,(compute-loop count))
908 (compute-loop count)))
909 (compute-bindings (count)
910 (if atsignp
911 (compute-block count)
912 `(let* ((orig-args ,(expand-next-arg))
913 (args orig-args))
914 (declare (ignorable orig-args args))
915 ,(let ((*expander-next-arg-macro* 'expander-next-arg)
916 (*only-simple-args* nil)
917 (*orig-args-available* t))
918 (compute-block count))))))
919 (values (if params
920 (expand-bind-defaults ((count nil)) params
921 (if (zerop posn)
922 `(let ((inside-string ,(expand-next-arg)))
923 ,(compute-bindings count))
924 (compute-bindings count)))
925 (if (zerop posn)
926 `(let ((inside-string ,(expand-next-arg)))
927 ,(compute-bindings nil))
928 (compute-bindings nil)))
929 (nthcdr (1+ posn) directives)))))
931 (def-complex-format-directive #\} ()
932 (format-error "no corresponding open brace"))
934 ;;;; format directives and support functions for justification
936 (defparameter *illegal-inside-justification*
937 (mapcar (lambda (x) (parse-directive x 0))
938 '("~W" "~:W" "~@W" "~:@W"
939 "~_" "~:_" "~@_" "~:@_"
940 "~:>" "~:@>"
941 "~I" "~:I" "~@I" "~:@I"
942 "~:T" "~:@T")))
944 (defun illegal-inside-justification-p (directive)
945 (member directive *illegal-inside-justification*
946 :test (lambda (x y)
947 (and (format-directive-p x)
948 (format-directive-p y)
949 (eql (format-directive-character x) (format-directive-character y))
950 (eql (format-directive-colonp x) (format-directive-colonp y))
951 (eql (format-directive-atsignp x) (format-directive-atsignp y))))))
953 (def-complex-format-directive #\< (colonp atsignp params string end directives)
954 (multiple-value-bind (segments first-semi close remaining)
955 (parse-format-justification directives)
956 (values
957 (if (format-directive-colonp close) ; logical block vs. justification
958 (multiple-value-bind (prefix per-line-p insides suffix)
959 (parse-format-logical-block segments colonp first-semi
960 close params string end)
961 (expand-format-logical-block prefix per-line-p insides
962 suffix atsignp))
963 (let ((count (reduce #'+ (mapcar (lambda (x) (count-if #'illegal-inside-justification-p x)) segments))))
964 (when (> count 0)
965 ;; ANSI specifies that "an error is signalled" in this
966 ;; situation.
967 (format-error*
968 "~D illegal directive~:P found inside justification block"
969 (list count)
970 :references (list '(:ansi-cl :section (22 3 5 2)))))
971 ;; ANSI does not explicitly say that an error should be
972 ;; signalled, but the @ modifier is not explicitly allowed
973 ;; for ~> either.
974 (when (format-directive-atsignp close)
975 (format-error-at*
976 nil (1- (format-directive-end close))
977 "@ modifier not allowed in close directive of ~
978 justification block (i.e. ~~<...~~@>."
980 :references (list '(:ansi-cl :section (22 3 6 2)))))
981 (expand-format-justification segments colonp atsignp
982 first-semi params)))
983 remaining)))
985 (def-complex-format-directive #\> ()
986 (format-error "no corresponding open bracket"))
988 (defun parse-format-logical-block
989 (segments colonp first-semi close params string end)
990 (when params
991 (format-error-at nil (caar params)
992 "No parameters can be supplied with ~~<...~~:>."))
993 (multiple-value-bind (prefix insides suffix)
994 (multiple-value-bind (prefix-default suffix-default)
995 (if colonp (values "(" ")") (values "" ""))
996 (flet ((extract-string (list prefix-p)
997 (let ((directive (find-if #'format-directive-p list)))
998 (if directive
999 (format-error-at*
1000 nil (1- (format-directive-end directive))
1001 "Cannot include format directives inside the ~
1002 ~:[suffix~;prefix~] segment of ~~<...~~:>"
1003 (list prefix-p)
1004 :references (list '(:ansi-cl :section (22 3 5 2))))
1005 (apply #'concatenate 'string list)))))
1006 (case (length segments)
1007 (0 (values prefix-default nil suffix-default))
1008 (1 (values prefix-default (car segments) suffix-default))
1009 (2 (values (extract-string (car segments) t)
1010 (cadr segments) suffix-default))
1011 (3 (values (extract-string (car segments) t)
1012 (cadr segments)
1013 (extract-string (caddr segments) nil)))
1015 (format-error "Too many segments for ~~<...~~:>")))))
1016 (when (format-directive-atsignp close)
1017 (setf insides
1018 (add-fill-style-newlines insides
1019 string
1020 (if first-semi
1021 (format-directive-end first-semi)
1022 end))))
1023 (values prefix
1024 (and first-semi (format-directive-atsignp first-semi))
1025 insides
1026 suffix)))
1028 (defun add-fill-style-newlines (list string offset &optional last-directive)
1029 (cond
1030 (list
1031 (let ((directive (car list)))
1032 (cond
1033 ((simple-string-p directive)
1034 (let* ((non-space (position #\Space directive :test #'char/=))
1035 (newlinep (and last-directive
1036 (char=
1037 (format-directive-character last-directive)
1038 #\Newline))))
1039 (cond
1040 ((and newlinep non-space)
1041 (nconc
1042 (list (subseq directive 0 non-space))
1043 (add-fill-style-newlines-aux
1044 (subseq directive non-space) string (+ offset non-space))
1045 (add-fill-style-newlines
1046 (cdr list) string (+ offset (length directive)))))
1047 (newlinep
1048 (cons directive
1049 (add-fill-style-newlines
1050 (cdr list) string (+ offset (length directive)))))
1052 (nconc (add-fill-style-newlines-aux directive string offset)
1053 (add-fill-style-newlines
1054 (cdr list) string (+ offset (length directive))))))))
1056 (cons directive
1057 (add-fill-style-newlines
1058 (cdr list) string
1059 (format-directive-end directive) directive))))))
1060 (t nil)))
1062 (defun add-fill-style-newlines-aux (literal string offset)
1063 (let ((end (length literal))
1064 (posn 0))
1065 (collect ((results))
1066 (loop
1067 (let ((blank (position #\space literal :start posn)))
1068 (when (null blank)
1069 (results (subseq literal posn))
1070 (return))
1071 (let ((non-blank (or (position #\space literal :start blank
1072 :test #'char/=)
1073 end)))
1074 (results (subseq literal posn non-blank))
1075 (results (make-format-directive
1076 :string string :character #\_
1077 :start (+ offset non-blank) :end (+ offset non-blank)
1078 :colonp t :atsignp nil :params nil))
1079 (setf posn non-blank))
1080 (when (= posn end)
1081 (return))))
1082 (results))))
1084 (defun parse-format-justification (directives)
1085 (let ((first-semi nil)
1086 (close nil)
1087 (remaining directives))
1088 (collect ((segments))
1089 (loop
1090 (let ((close-or-semi (or (find-directive remaining #\> t)
1091 (format-error "No corresponding close bracket"))))
1092 (let ((posn (position close-or-semi remaining)))
1093 (segments (subseq remaining 0 posn))
1094 (setf remaining (nthcdr (1+ posn) remaining)))
1095 (when (char= (format-directive-character close-or-semi)
1096 #\>)
1097 (setf close close-or-semi)
1098 (return))
1099 (unless first-semi
1100 (setf first-semi close-or-semi))))
1101 (values (segments) first-semi close remaining))))
1103 (sb!xc:defmacro expander-pprint-next-arg (string offset)
1104 `(progn
1105 (when (null args)
1106 (format-error-at ,string ,offset "No more arguments"))
1107 (pprint-pop)
1108 (pop args)))
1110 (defun expand-format-logical-block (prefix per-line-p insides suffix atsignp)
1111 `(let ((arg ,(if atsignp 'args (expand-next-arg))))
1112 ,@(when atsignp
1113 (setf *only-simple-args* nil)
1114 '((setf args nil)))
1115 (pprint-logical-block
1116 (stream arg
1117 ,(if per-line-p :per-line-prefix :prefix) ,prefix
1118 :suffix ,suffix)
1119 (let ((args arg)
1120 ,@(unless atsignp
1121 `((orig-args arg))))
1122 (declare (ignorable args ,@(unless atsignp '(orig-args))))
1123 (block nil
1124 ,@(let ((*expander-next-arg-macro* 'expander-pprint-next-arg)
1125 (*only-simple-args* nil)
1126 (*orig-args-available*
1127 (if atsignp *orig-args-available* t)))
1128 (expand-directive-list insides)))))))
1130 (defun expand-format-justification (segments colonp atsignp first-semi params)
1131 (let ((newline-segment-p
1132 (and first-semi
1133 (format-directive-colonp first-semi))))
1134 (expand-bind-defaults
1135 ((mincol 0) (colinc 1) (minpad 0) (padchar #\space))
1136 params
1137 `(let ((segments nil)
1138 ,@(when newline-segment-p
1139 '((newline-segment nil)
1140 (extra-space 0)
1141 (line-len 72))))
1142 (block nil
1143 ,@(when newline-segment-p
1144 `((setf newline-segment
1145 (with-simple-output-to-string (stream)
1146 ,@(expand-directive-list (pop segments))))
1147 ,(expand-bind-defaults
1148 ((extra 0)
1149 (line-len '(or (sb!impl::line-length stream) 72)))
1150 (format-directive-params first-semi)
1151 `(setf extra-space ,extra line-len ,line-len))))
1152 ,@(mapcar (lambda (segment)
1153 `(push (with-simple-output-to-string (stream)
1154 ,@(expand-directive-list segment))
1155 segments))
1156 segments))
1157 (format-justification stream
1158 ,@(if newline-segment-p
1159 '(newline-segment extra-space line-len)
1160 '(nil 0 0))
1161 segments ,colonp ,atsignp
1162 ,mincol ,colinc ,minpad ,padchar)))))
1164 ;;;; format directive and support function for user-defined method
1166 (def-format-directive #\/ (string start end colonp atsignp params)
1167 (let ((symbol (extract-user-fun-name string start end)))
1168 (collect ((param-names) (bindings))
1169 (dolist (param-and-offset params)
1170 (let ((param (cdr param-and-offset)))
1171 (let ((param-name (sb!xc:gensym "PARAM")))
1172 (param-names param-name)
1173 (bindings `(,param-name
1174 ,(case param
1175 (:arg (expand-next-arg))
1176 (:remaining '(length args))
1177 (t param)))))))
1178 `(let ,(bindings)
1179 (,symbol stream ,(expand-next-arg) ,colonp ,atsignp
1180 ,@(param-names))))))
1182 (defun extract-user-fun-name (string start end)
1183 (let* ((slash (or (position #\/ string :start start :end (1- end)
1184 :from-end t)
1185 (format-error "Malformed ~~/ directive")))
1186 (name (string-upcase (let ((foo string))
1187 ;; HACK: This is to keep the compiler
1188 ;; quiet about deleting code inside
1189 ;; the subseq expansion.
1190 (subseq foo (1+ slash) (1- end)))))
1191 (first-colon (position #\: name))
1192 (second-colon (if first-colon (position #\: name :start (1+ first-colon))))
1193 (package-name (if first-colon
1194 (subseq name 0 first-colon)
1195 "COMMON-LISP-USER"))
1196 (package (or (find-package package-name)
1197 ;; FIXME: should be PACKAGE-ERROR? Could we just
1198 ;; use FIND-UNDELETED-PACKAGE-OR-LOSE?
1199 (format-error "No package named ~S" package-name))))
1200 (intern (cond
1201 ((and second-colon (= second-colon (1+ first-colon)))
1202 (subseq name (1+ second-colon)))
1203 (first-colon
1204 (subseq name (1+ first-colon)))
1205 (t name))
1206 package)))
1208 ;;; compile-time checking for argument mismatch. This code is
1209 ;;; inspired by that of Gerd Moellmann, and comes decorated with
1210 ;;; FIXMEs:
1211 (defun %compiler-walk-format-string (string args)
1212 (declare (type simple-string string))
1213 (let ((*default-format-error-control-string* string))
1214 (macrolet ((incf-both (&optional (increment 1))
1215 `(progn
1216 (incf min ,increment)
1217 (incf max ,increment)))
1218 (walk-complex-directive (function)
1219 `(multiple-value-bind (min-inc max-inc remaining)
1220 (,function directive directives args)
1221 (incf min min-inc)
1222 (incf max max-inc)
1223 (setq directives remaining))))
1224 ;; FIXME: these functions take a list of arguments as well as
1225 ;; the directive stream. This is to enable possibly some
1226 ;; limited type checking on FORMAT's arguments, as well as
1227 ;; simple argument count mismatch checking: when the minimum and
1228 ;; maximum argument counts are the same at a given point, we
1229 ;; know which argument is going to be used for a given
1230 ;; directive, and some (annotated below) require arguments of
1231 ;; particular types.
1232 (labels
1233 ((walk-justification (justification directives args)
1234 (declare (ignore args))
1235 (let ((*default-format-error-offset*
1236 (1- (format-directive-end justification))))
1237 (multiple-value-bind (segments first-semi close remaining)
1238 (parse-format-justification directives)
1239 (declare (ignore segments first-semi))
1240 (cond
1241 ((not (format-directive-colonp close))
1242 (values 0 0 directives))
1243 ((format-directive-atsignp justification)
1244 (values 0 sb!xc:call-arguments-limit directives))
1245 ;; FIXME: here we could assert that the
1246 ;; corresponding argument was a list.
1247 (t (values 1 1 remaining))))))
1248 (walk-conditional (conditional directives args)
1249 (let ((*default-format-error-offset*
1250 (1- (format-directive-end conditional))))
1251 (multiple-value-bind (sublists last-semi-with-colon-p remaining)
1252 (parse-conditional-directive directives)
1253 (declare (ignore last-semi-with-colon-p))
1254 (let ((sub-max
1255 (loop for s in sublists
1256 maximize (nth-value
1257 1 (walk-directive-list s args)))))
1258 (cond
1259 ((format-directive-atsignp conditional)
1260 (values 1 (max 1 sub-max) remaining))
1261 ((loop for p in (format-directive-params conditional)
1262 thereis (or (integerp (cdr p))
1263 (memq (cdr p) '(:remaining :arg))))
1264 (values 0 sub-max remaining))
1265 ;; FIXME: if not COLONP, then the next argument
1266 ;; must be a number.
1267 (t (values 1 (1+ sub-max) remaining)))))))
1268 (walk-iteration (iteration directives args)
1269 (declare (ignore args))
1270 (let ((*default-format-error-offset*
1271 (1- (format-directive-end iteration))))
1272 (let* ((close (find-directive directives #\} nil))
1273 (posn (or (position close directives)
1274 (format-error "No corresponding close brace")))
1275 (remaining (nthcdr (1+ posn) directives)))
1276 ;; FIXME: if POSN is zero, the next argument must be
1277 ;; a format control (either a function or a string).
1278 (if (format-directive-atsignp iteration)
1279 (values (if (zerop posn) 1 0)
1280 sb!xc:call-arguments-limit
1281 remaining)
1282 ;; FIXME: the argument corresponding to this
1283 ;; directive must be a list.
1284 (let ((nreq (if (zerop posn) 2 1)))
1285 (values nreq nreq remaining))))))
1286 (walk-directive-list (directives args)
1287 (let ((min 0) (max 0))
1288 (loop
1289 (let ((directive (pop directives)))
1290 (when (null directive)
1291 (return (values min (min max sb!xc:call-arguments-limit))))
1292 (when (format-directive-p directive)
1293 (incf-both (count :arg (format-directive-params directive)
1294 :key #'cdr))
1295 (let ((c (format-directive-character directive)))
1296 (cond
1297 ((find c "ABCDEFGORSWX$/")
1298 (incf-both))
1299 ((char= c #\P)
1300 (unless (format-directive-colonp directive)
1301 (incf-both)))
1302 ((or (find c "IT%&|_();>~") (char= c #\Newline)))
1303 ;; FIXME: check correspondence of ~( and ~)
1304 ((char= c #\<)
1305 (walk-complex-directive walk-justification))
1306 ((char= c #\[)
1307 (walk-complex-directive walk-conditional))
1308 ((char= c #\{)
1309 (walk-complex-directive walk-iteration))
1310 ((char= c #\?)
1311 ;; FIXME: the argument corresponding to this
1312 ;; directive must be a format control.
1313 (cond
1314 ((format-directive-atsignp directive)
1315 (incf min)
1316 (setq max sb!xc:call-arguments-limit))
1317 (t (incf-both 2))))
1318 (t (throw 'give-up-format-string-walk nil))))))))))
1319 (catch 'give-up-format-string-walk
1320 (let ((directives (tokenize-control-string string)))
1321 (walk-directive-list directives args)))))))
1323 ;;; Optimize common case of constant keyword arguments
1324 ;;; to WRITE and WRITE-TO-STRING
1325 (flet
1326 ((expand (fn object keys)
1327 (do (streamvar bind ignore)
1328 ((or (atom keys) (atom (cdr keys)))
1329 (if keys ; fail
1330 (values nil t)
1331 (values
1332 (let* ((objvar (copy-symbol 'object))
1333 (bind `((,objvar ,object) ,@(nreverse bind)))
1334 (ignore (when ignore `((declare (ignore ,@ignore))))))
1335 (case fn
1336 (write
1337 ;; When :STREAM was specified, this used to insert a call
1338 ;; to (OUT-SYNONYM-OF STREAMVAR) which added junk to the
1339 ;; expansion which was not likely to improve performance.
1340 ;; The benefit of this transform is that it avoids runtime
1341 ;; keyword parsing and binding of 16 specials vars, *not*
1342 ;; that it can inline testing for T or NIL as the stream.
1343 `(let ,bind ,@ignore
1344 ,@(if streamvar
1345 `((%write ,objvar ,streamvar))
1346 `((output-object ,objvar *standard-output*)
1347 ,objvar))))
1348 (write-to-string
1349 (if (cdr bind)
1350 `(let ,bind ,@ignore (stringify-object ,objvar))
1351 `(stringify-object ,object)))))
1352 nil)))
1353 (let* ((key (pop keys))
1354 (value (pop keys))
1355 (variable
1356 (cond ((getf '(:array *print-array*
1357 :base *print-base*
1358 :case *print-case*
1359 :circle *print-circle*
1360 :escape *print-escape*
1361 :gensym *print-gensym*
1362 :length *print-length*
1363 :level *print-level*
1364 :lines *print-lines*
1365 :miser-width *print-miser-width*
1366 :pprint-dispatch *print-pprint-dispatch*
1367 :pretty *print-pretty*
1368 :radix *print-radix*
1369 :readably *print-readably*
1370 :right-margin *print-right-margin*
1371 :suppress-errors *suppress-print-errors*)
1372 key))
1373 ((and (eq key :stream) (eq fn 'write))
1374 (or streamvar (setq streamvar (copy-symbol 'stream))))
1376 (return (values nil t))))))
1377 (when (assoc variable bind)
1378 ;; First key has precedence, but we still need to execute the
1379 ;; argument, and in the right order.
1380 (setf variable (gensym "IGNORE"))
1381 (push variable ignore))
1382 (push (list variable value) bind)))))
1384 (sb!c:define-source-transform write (object &rest keys)
1385 (expand 'write object keys))
1387 (sb!c:define-source-transform write-to-string (object &rest keys)
1388 (expand 'write-to-string object keys)))
1390 ;;; A long as we're processing ERROR strings to remove "SB!" packages,
1391 ;;; we might as well squash out tilde-newline-whitespace too.
1392 ;;; This might even be robust enough to keep in the target image,
1393 ;;; but, FIXME: this punts on ~newline with {~@,~:,~@:} modifiers
1394 #+sb-xc-host
1395 (defun sb!impl::!xc-preprocess-format-control (string)
1396 (let (pieces ltrim)
1397 ;; Tokenizing is the correct way to deal with "~~/foo/"
1398 ;; without mistaking it for an occurrence of the "~/" directive.
1399 (dolist (piece (tokenize-control-string string)
1400 (let ((new (apply 'concatenate 'string (nreverse pieces))))
1401 (if (string/= new string) new string)))
1402 (etypecase piece
1403 (string
1404 (if ltrim
1405 (let ((p (position-if
1406 (lambda (x) (and (not (eql x #\Space)) (graphic-char-p x)))
1407 piece)))
1408 (when p
1409 (push (subseq piece p) pieces)))
1410 (push piece pieces))
1411 (setq ltrim nil))
1412 (format-directive
1413 (setq ltrim nil)
1414 (let ((text (subseq string
1415 (format-directive-start piece)
1416 (format-directive-end piece)))
1417 (processed nil))
1418 (cond ((and (eql (format-directive-character piece) #\Newline)
1419 (not (format-directive-colonp piece))
1420 (not (format-directive-atsignp piece)))
1421 (setq ltrim t processed t))
1422 ((eql (format-directive-character piece) #\/)
1423 (when (string-equal text "~/sb!" :end1 5)
1424 (setq text (concatenate 'string "~/sb-" (subseq text 5))))))
1425 (unless processed
1426 (push text pieces))))))))