Fix bug 242 for fd-streams
[sbcl.git] / src / code / late-format.lisp
index fef1acc..88ff777 100644 (file)
@@ -9,7 +9,7 @@
 
 (in-package "SB!FORMAT")
 \f
-(define-condition format-error (error)
+(define-condition format-error (error reference-condition)
   ((complaint :reader format-error-complaint :initarg :complaint)
    (args :reader format-error-args :initarg :args :initform nil)
    (control-string :reader format-error-control-string
@@ -21,7 +21,8 @@
                     :initarg :second-relative :initform nil)
    (print-banner :reader format-error-print-banner :initarg :print-banner
                 :initform t))
-  (:report %print-format-error))
+  (:report %print-format-error)
+  (:default-initargs :references nil))
 
 (defun %print-format-error (condition stream)
   (format stream
@@ -38,7 +39,7 @@
   (string (missing-arg) :type simple-string)
   (start (missing-arg) :type (and unsigned-byte fixnum))
   (end (missing-arg) :type (and unsigned-byte fixnum))
-  (character (missing-arg) :type base-char)
+  (character (missing-arg) :type character)
   (colonp nil :type (member t nil))
   (atsignp nil :type (member t nil))
   (params nil :type list))
                :offset (min pprint-offset justification-offset)
                :second-relative (- (max pprint-offset justification-offset)
                                    (min pprint-offset justification-offset)
-                                   1))))
+                                   1)
+               :references (list '(:ansi-cl :section (22 3 5 2))))))
     (nreverse result)))
 
 (defun parse-directive (string start)
               (error 'format-error
                      :complaint "parameters found after #\\: or #\\@ modifier"
                      :control-string string
-                     :offset posn))))
+                     :offset posn
+                      :references (list '(:ansi-cl :section (22 3)))))))
       (loop
        (let ((char (get-char)))
          (cond ((or (char<= #\0 char #\9) (char= char #\+) (char= char #\-))
                     (error 'format-error
                            :complaint "too many colons supplied"
                            :control-string string
-                           :offset posn)
+                           :offset posn
+                            :references (list '(:ansi-cl :section (22 3))))
                     (setf colonp t)))
                ((char= char #\@)
                 (if atsignp
                     (error 'format-error
                            :complaint "too many #\\@ characters supplied"
                            :control-string string
-                           :offset posn)
+                           :offset posn
+                            :references (list '(:ansi-cl :section (22 3))))
                     (setf atsignp t)))
                (t
                 (when (and (char= (schar string (1- posn)) #\,)
   (etypecase directive
     (format-directive
      (let ((expander
-           (aref *format-directive-expanders*
-                 (char-code (format-directive-character directive))))
+            (let ((char (format-directive-character directive)))
+              (typecase char
+                (base-char
+                 (aref *format-directive-expanders* (char-code char)))
+                (character nil))))
           (*default-format-error-offset*
            (1- (format-directive-end directive))))
        (declare (type (or null function) expander))
   (expand-format-integer 16 colonp atsignp params))
 
 (def-format-directive #\R (colonp atsignp params)
-  (if params
-      (expand-bind-defaults
-         ((base 10) (mincol 0) (padchar #\space) (commachar #\,)
-          (commainterval 3))
-         params
-       `(format-print-integer stream ,(expand-next-arg) ,colonp ,atsignp
-                              ,base ,mincol
-                              ,padchar ,commachar ,commainterval))
-      (if atsignp
-         (if colonp
-             `(format-print-old-roman stream ,(expand-next-arg))
-             `(format-print-roman stream ,(expand-next-arg)))
-         (if colonp
-             `(format-print-ordinal stream ,(expand-next-arg))
-             `(format-print-cardinal stream ,(expand-next-arg))))))
+  (expand-bind-defaults
+      ((base nil) (mincol 0) (padchar #\space) (commachar #\,)
+       (commainterval 3))
+      params
+    (let ((n-arg (gensym))) 
+      `(let ((,n-arg ,(expand-next-arg)))
+         (if ,base
+             (format-print-integer stream ,n-arg ,colonp ,atsignp
+                                  ,base ,mincol
+                                  ,padchar ,commachar ,commainterval)
+             ,(if atsignp
+                  (if colonp
+                      `(format-print-old-roman stream ,n-arg)
+                      `(format-print-roman stream ,n-arg))
+                  (if colonp
+                      `(format-print-ordinal stream ,n-arg)
+                     `(format-print-cardinal stream ,n-arg))))))))
 \f
 ;;;; format directive for pluralization
 
                 (setf args (nthcdr ,posn orig-args))
                 (error 'format-error
                        :complaint "Index ~W out of bounds. Should have been ~
-                                   between 0 and ~W."
+                                    between 0 and ~W."
                        :args (list ,posn (length orig-args))
                        :offset ,(1- end)))))
       (if colonp
                        (error 'format-error
                               :complaint
                               "Index ~W is out of bounds; should have been ~
-                               between 0 and ~W."
+                                between 0 and ~W."
                               :args (list new-posn (length orig-args))
                               :offset ,(1- end)))))))
          (if params
                 (error 'format-error
                        :complaint
                        "must specify exactly two sections"))
-            (expand-bind-defaults ((index (expand-next-arg))) params
+            (expand-bind-defaults ((index nil)) params
               (setf *only-simple-args* nil)
-              (let ((clauses nil))
+              (let ((clauses nil)
+                     (case `(or ,index ,(expand-next-arg))))
                 (when last-semi-with-colon-p
                   (push `(t ,@(expand-directive-list (pop sublists)))
                         clauses))
                     (push `(,(decf count)
                             ,@(expand-directive-list sublist))
                           clauses)))
-                `(case ,index ,@clauses)))))
+                `(case ,case ,@clauses)))))
      remaining)))
 
 (defun parse-conditional-directive (directives)
            (let ((*simple-args* *simple-args*))
              (values (expand-directive-list sublist)
                      *simple-args*))
-         (cond ((eq *simple-args* (cdr new-args))
+         (cond ((and new-args (eq *simple-args* (cdr new-args)))
                 (setf *simple-args* new-args)
                 `(when ,(caar new-args)
                    ,@guts))
   (when (and colonp (not *up-up-and-out-allowed*))
     (error 'format-error
           :complaint "attempt to use ~~:^ outside a ~~:{...~~} construct"))
-  `(when ,(case (length params)
-           (0 (if colonp
-                  '(null outside-args)
-                  (progn
-                    (setf *only-simple-args* nil)
-                    '(null args))))
-           (1 (expand-bind-defaults ((count 0)) params
-                `(zerop ,count)))
-           (2 (expand-bind-defaults ((arg1 0) (arg2 0)) params
-                `(= ,arg1 ,arg2)))
-           (t (expand-bind-defaults ((arg1 0) (arg2 0) (arg3 0)) params
-                `(<= ,arg1 ,arg2 ,arg3))))
+  `(when ,(expand-bind-defaults ((arg1 nil) (arg2 nil) (arg3 nil)) params
+            `(cond (,arg3 (<= ,arg1 ,arg2 ,arg3))
+                   (,arg2 (eql ,arg1 ,arg2))
+                   (,arg1 (eql ,arg1 0))
+                   (t ,(if colonp
+                           '(null outside-args)
+                           (progn
+                             (setf *only-simple-args* nil)
+                             '(null args))))))
      ,(if colonp
          '(return-from outside-loop nil)
          '(return))))
                     (throw 'need-orig-args nil))
                 (let ((*up-up-and-out-allowed* colonp))
                   (expand-directive-list (subseq directives 0 posn)))))
-          (compute-loop-aux (count)
+          (compute-loop (count)
             (when atsignp
               (setf *only-simple-args* nil))
             `(loop
                ,@(when closed-with-colon
                    '((when (null args)
                        (return))))))
-          (compute-loop ()
-            (if params
-                (expand-bind-defaults ((count nil)) params
-                  (compute-loop-aux count))
-                (compute-loop-aux nil)))
-          (compute-block ()
+          (compute-block (count)
             (if colonp
                 `(block outside-loop
-                   ,(compute-loop))
-                (compute-loop)))
-          (compute-bindings ()
+                   ,(compute-loop count))
+                (compute-loop count)))
+          (compute-bindings (count)
             (if atsignp
-                (compute-block)
-                `(let* ((orig-args ,(expand-next-arg))
-                        (args orig-args))
-                   (declare (ignorable orig-args args))
-                   ,(let ((*expander-next-arg-macro* 'expander-next-arg)
-                          (*only-simple-args* nil)
-                          (*orig-args-available* t))
-                      (compute-block))))))
-       (values (if (zerop posn)
-                   `(let ((inside-string ,(expand-next-arg)))
-                      ,(compute-bindings))
-                   (compute-bindings))
+                 (compute-block count)
+                 `(let* ((orig-args ,(expand-next-arg))
+                         (args orig-args))
+                   (declare (ignorable orig-args args))
+                   ,(let ((*expander-next-arg-macro* 'expander-next-arg)
+                          (*only-simple-args* nil)
+                          (*orig-args-available* t))
+                      (compute-block count))))))
+       (values (if params
+                    (expand-bind-defaults ((count nil)) params
+                      (if (zerop posn)
+                          `(let ((inside-string ,(expand-next-arg)))
+                            ,(compute-bindings count))
+                          (compute-bindings count)))
+                    (if (zerop posn)
+                        `(let ((inside-string ,(expand-next-arg)))
+                          ,(compute-bindings nil))
+                        (compute-bindings nil)))
                (nthcdr (1+ posn) directives))))))
 
 (def-complex-format-directive #\} ()
             ;; situation.
             (error 'format-error
                    :complaint "~D illegal directive~:P found inside justification block"
-                   :args (list count)))
+                   :args (list count)
+                    :references (list '(:ansi-cl :section (22 3 5 2)))))
           (expand-format-justification segments colonp atsignp
-                                     first-semi params)))
+                                        first-semi params)))
      remaining)))
 
 (def-complex-format-directive #\> ()
                   (if directive
                       (error 'format-error
                              :complaint
-                             "cannot include format directives inside the ~
-                              ~:[suffix~;prefix~] segment of ~~<...~~:>"
+                              "cannot include format directives inside the ~
+                               ~:[suffix~;prefix~] segment of ~~<...~~:>"
                              :args (list prefix-p)
-                             :offset (1- (format-directive-end directive)))
+                             :offset (1- (format-directive-end directive))
+                              :references
+                              (list '(:ansi-cl :section (22 3 5 2))))
                       (apply #'concatenate 'string list)))))
        (case (length segments)
          (0 (values prefix-default nil suffix-default))
                        ((char= c #\P)
                         (unless (format-directive-colonp directive)
                           (incf-both)))
-                       ((or (find c "IT%&|_();>") (char= c #\Newline)))
+                       ((or (find c "IT%&|_();>~") (char= c #\Newline)))
                         ;; FIXME: check correspondence of ~( and ~)
                        ((char= c #\<)
                         (walk-complex-directive walk-justification))