0.8.16.16:
[sbcl.git] / src / code / late-format.lisp
index 5a509cd..4cb042a 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
                   :initform *default-format-error-control-string*)
    (offset :reader format-error-offset :initarg :offset
           :initform *default-format-error-offset*)
+   (second-relative :reader format-error-second-relative
+                    :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
-         "~:[~;error in format: ~]~
-                ~?~@[~%  ~A~%  ~V@T^~]"
+         "~:[~*~;error in ~S: ~]~?~@[~%  ~A~%  ~V@T^~@[~V@T^~]~]"
          (format-error-print-banner condition)
+          'format
          (format-error-complaint condition)
          (format-error-args condition)
          (format-error-control-string condition)
-         (format-error-offset condition)))
+         (format-error-offset condition)
+          (format-error-second-relative condition)))
 \f
 (def!struct format-directive
   (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))
   (declare (simple-string string))
   (let ((index 0)
        (end (length string))
-       (result nil))
+       (result nil)
+        ;; FIXME: consider rewriting this 22.3.5.2-related processing
+        ;; using specials to maintain state and doing the logic inside
+        ;; the directive expanders themselves.
+        (block)
+        (pprint)
+        (semicolon)
+        (justification-semicolon))
     (loop
       (let ((next-directive (or (position #\~ string :start index) end)))
        (when (> next-directive index)
          (push (subseq string index next-directive) result))
        (when (= next-directive end)
          (return))
-       (let ((directive (parse-directive string next-directive)))
+       (let* ((directive (parse-directive string next-directive))
+               (char (format-directive-character directive)))
+          ;; this processing is required by CLHS 22.3.5.2
+          (cond
+            ((char= char #\<) (push directive block))
+            ((and block (char= char #\;) (format-directive-colonp directive))
+             (setf semicolon directive))
+            ((char= char #\>)
+             (aver block)
+             (cond
+               ((format-directive-colonp directive)
+                (unless pprint
+                  (setf pprint (car block)))
+                (setf semicolon nil))
+               (semicolon
+                (unless justification-semicolon
+                  (setf justification-semicolon semicolon))))
+             (pop block))
+            ;; block cases are handled by the #\< expander/interpreter
+            ((not block)
+             (case char
+               ((#\W #\I #\_) (unless pprint (setf pprint directive)))
+               (#\T (when (and (format-directive-colonp directive)
+                               (not pprint))
+                      (setf pprint directive))))))
          (push directive result)
          (setf index (format-directive-end directive)))))
+    (when (and pprint justification-semicolon)
+      (let ((pprint-offset (1- (format-directive-end pprint)))
+            (justification-offset
+             (1- (format-directive-end justification-semicolon))))
+        (error 'format-error
+               :complaint "misuse of justification and pprint directives"
+               :control-string string
+               :offset (min pprint-offset justification-offset)
+               :second-relative (- (max pprint-offset justification-offset)
+                                   (min pprint-offset justification-offset)
+                                   1)
+               :references (list '(:ansi-cl :section (22 3 5 2))))))
     (nreverse result)))
 
 (defun parse-directive (string start)
     (flet ((get-char ()
             (if (= posn end)
                 (error 'format-error
-                       :complaint "String ended before directive was found."
+                       :complaint "string ended before directive was found"
                        :control-string string
                        :offset start)
                 (schar string posn)))
               (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))
                 (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
             ;; 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 #\> ()
           :offset (caar params)))
   (multiple-value-bind (prefix insides suffix)
       (multiple-value-bind (prefix-default suffix-default)
-         (if colonp (values "(" ")") (values nil ""))
+         (if colonp (values "(" ")") (values "" ""))
        (flet ((extract-string (list prefix-p)
                 (let ((directive (find-if #'format-directive-p list)))
                   (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))
            insides
            suffix)))
 
-(defun add-fill-style-newlines (list string offset)
-  (if list
-      (let ((directive (car list)))
-       (if (simple-string-p directive)
-           (nconc (add-fill-style-newlines-aux directive string offset)
-                  (add-fill-style-newlines (cdr list)
-                                           string
-                                           (+ offset (length directive))))
-           (cons directive
-                 (add-fill-style-newlines (cdr list)
-                                          string
-                                          (format-directive-end directive)))))
-      nil))
+(defun add-fill-style-newlines (list string offset &optional last-directive)
+  (cond
+    (list
+     (let ((directive (car list)))
+       (cond
+        ((simple-string-p directive)
+         (let* ((non-space (position #\Space directive :test #'char/=))
+                (newlinep (and last-directive
+                               (char=
+                                (format-directive-character last-directive)
+                                #\Newline))))
+           (cond
+             ((and newlinep non-space)
+              (nconc
+               (list (subseq directive 0 non-space))
+               (add-fill-style-newlines-aux
+                (subseq directive non-space) string (+ offset non-space))
+               (add-fill-style-newlines
+                (cdr list) string (+ offset (length directive)))))
+             (newlinep
+              (cons directive
+                    (add-fill-style-newlines
+                     (cdr list) string (+ offset (length directive)))))
+             (t
+              (nconc (add-fill-style-newlines-aux directive string offset)
+                     (add-fill-style-newlines
+                      (cdr list) string (+ offset (length directive))))))))
+        (t
+         (cons directive
+               (add-fill-style-newlines
+                (cdr list) string
+                (format-directive-end directive) directive))))))
+    (t nil)))
 
 (defun add-fill-style-newlines-aux (literal string offset)
   (let ((end (length literal))
                        ((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))