| 1 | ;;; format.lisp |
|---|
| 2 | ;;; |
|---|
| 3 | ;;; Copyright (C) 2004-2007 Peter Graves |
|---|
| 4 | ;;; $Id: format.lisp 14125 2012-08-18 13:49:37Z ehuelsmann $ |
|---|
| 5 | ;;; |
|---|
| 6 | ;;; This program is free software; you can redistribute it and/or |
|---|
| 7 | ;;; modify it under the terms of the GNU General Public License |
|---|
| 8 | ;;; as published by the Free Software Foundation; either version 2 |
|---|
| 9 | ;;; of the License, or (at your option) any later version. |
|---|
| 10 | ;;; |
|---|
| 11 | ;;; This program is distributed in the hope that it will be useful, |
|---|
| 12 | ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | ;;; GNU General Public License for more details. |
|---|
| 15 | ;;; |
|---|
| 16 | ;;; You should have received a copy of the GNU General Public License |
|---|
| 17 | ;;; along with this program; if not, write to the Free Software |
|---|
| 18 | ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|---|
| 19 | ;;; |
|---|
| 20 | ;;; As a special exception, the copyright holders of this library give you |
|---|
| 21 | ;;; permission to link this library with independent modules to produce an |
|---|
| 22 | ;;; executable, regardless of the license terms of these independent |
|---|
| 23 | ;;; modules, and to copy and distribute the resulting executable under |
|---|
| 24 | ;;; terms of your choice, provided that you also meet, for each linked |
|---|
| 25 | ;;; independent module, the terms and conditions of the license of that |
|---|
| 26 | ;;; module. An independent module is a module which is not derived from |
|---|
| 27 | ;;; or based on this library. If you modify this library, you may extend |
|---|
| 28 | ;;; this exception to your version of the library, but you are not |
|---|
| 29 | ;;; obligated to do so. If you do not wish to do so, delete this |
|---|
| 30 | ;;; exception statement from your version. |
|---|
| 31 | |
|---|
| 32 | ;;; Adapted from CMUCL/SBCL. |
|---|
| 33 | |
|---|
| 34 | (in-package "SYSTEM") |
|---|
| 35 | |
|---|
| 36 | ;; If we're here due to an autoloader, |
|---|
| 37 | ;; we should prevent a circular dependency: |
|---|
| 38 | ;; when the debugger tries to print an error, |
|---|
| 39 | ;; it autoloads us, but if that autoloading causes |
|---|
| 40 | ;; another error, it circularly starts autoloading us. |
|---|
| 41 | ;; |
|---|
| 42 | ;; So, we replace whatever is in the function slot until |
|---|
| 43 | ;; we can reliably call FORMAT |
|---|
| 44 | (setf (symbol-function 'format) #'sys::%format) |
|---|
| 45 | |
|---|
| 46 | (require "PRINT-OBJECT") |
|---|
| 47 | |
|---|
| 48 | ;;; From primordial-extensions.lisp. |
|---|
| 49 | |
|---|
| 50 | ;;; Concatenate together the names of some strings and symbols, |
|---|
| 51 | ;;; producing a symbol in the current package. |
|---|
| 52 | (eval-when (:compile-toplevel :load-toplevel :execute) |
|---|
| 53 | (defun symbolicate (&rest things) |
|---|
| 54 | (let ((name (apply #'concatenate 'string (mapcar #'string things)))) |
|---|
| 55 | (values (intern name))))) |
|---|
| 56 | |
|---|
| 57 | ;;; a helper function for various macros which expect clauses of a |
|---|
| 58 | ;;; given length, etc. |
|---|
| 59 | ;;; |
|---|
| 60 | ;;; Return true if X is a proper list whose length is between MIN and |
|---|
| 61 | ;;; MAX (inclusive). |
|---|
| 62 | (eval-when (:compile-toplevel :load-toplevel :execute) |
|---|
| 63 | (defun proper-list-of-length-p (x min &optional (max min)) |
|---|
| 64 | ;; FIXME: This implementation will hang on circular list |
|---|
| 65 | ;; structure. Since this is an error-checking utility, i.e. its |
|---|
| 66 | ;; job is to deal with screwed-up input, it'd be good style to fix |
|---|
| 67 | ;; it so that it can deal with circular list structure. |
|---|
| 68 | (cond ((minusp max) nil) |
|---|
| 69 | ((null x) (zerop min)) |
|---|
| 70 | ((consp x) |
|---|
| 71 | (and (plusp max) |
|---|
| 72 | (proper-list-of-length-p (cdr x) |
|---|
| 73 | (if (plusp (1- min)) |
|---|
| 74 | (1- min) |
|---|
| 75 | 0) |
|---|
| 76 | (1- max)))) |
|---|
| 77 | (t nil)))) |
|---|
| 78 | |
|---|
| 79 | ;;; From early-extensions.lisp. |
|---|
| 80 | |
|---|
| 81 | (defconstant form-feed-char-code 12) |
|---|
| 82 | |
|---|
| 83 | (defmacro named-let (name binds &body body) |
|---|
| 84 | (dolist (x binds) |
|---|
| 85 | (unless (proper-list-of-length-p x 2) |
|---|
| 86 | (error "malformed NAMED-LET variable spec: ~S" x))) |
|---|
| 87 | `(labels ((,name ,(mapcar #'first binds) ,@body)) |
|---|
| 88 | (,name ,@(mapcar #'second binds)))) |
|---|
| 89 | |
|---|
| 90 | ;;;; ONCE-ONLY |
|---|
| 91 | ;;;; |
|---|
| 92 | ;;;; "The macro ONCE-ONLY has been around for a long time on various |
|---|
| 93 | ;;;; systems [..] if you can understand how to write and when to use |
|---|
| 94 | ;;;; ONCE-ONLY, then you truly understand macro." -- Peter Norvig, |
|---|
| 95 | ;;;; _Paradigms of Artificial Intelligence Programming: Case Studies |
|---|
| 96 | ;;;; in Common Lisp_, p. 853 |
|---|
| 97 | |
|---|
| 98 | ;;; ONCE-ONLY is a utility useful in writing source transforms and |
|---|
| 99 | ;;; macros. It provides a concise way to wrap a LET around some code |
|---|
| 100 | ;;; to ensure that some forms are only evaluated once. |
|---|
| 101 | ;;; |
|---|
| 102 | ;;; Create a LET* which evaluates each value expression, binding a |
|---|
| 103 | ;;; temporary variable to the result, and wrapping the LET* around the |
|---|
| 104 | ;;; result of the evaluation of BODY. Within the body, each VAR is |
|---|
| 105 | ;;; bound to the corresponding temporary variable. |
|---|
| 106 | (defmacro once-only (specs &body body) |
|---|
| 107 | (named-let frob ((specs specs) |
|---|
| 108 | (body body)) |
|---|
| 109 | (if (null specs) |
|---|
| 110 | `(progn ,@body) |
|---|
| 111 | (let ((spec (first specs))) |
|---|
| 112 | ;; FIXME: should just be DESTRUCTURING-BIND of SPEC |
|---|
| 113 | (unless (proper-list-of-length-p spec 2) |
|---|
| 114 | (error "malformed ONCE-ONLY binding spec: ~S" spec)) |
|---|
| 115 | (let* ((name (first spec)) |
|---|
| 116 | (exp-temp (gensym (symbol-name name)))) |
|---|
| 117 | `(let ((,exp-temp ,(second spec)) |
|---|
| 118 | (,name (gensym "ONCE-ONLY-"))) |
|---|
| 119 | `(let ((,,name ,,exp-temp)) |
|---|
| 120 | ,,(frob (rest specs) body)))))))) |
|---|
| 121 | |
|---|
| 122 | ;;; From print.lisp. |
|---|
| 123 | |
|---|
| 124 | ;;; FLONUM-TO-STRING (and its subsidiary function FLOAT-STRING) does |
|---|
| 125 | ;;; most of the work for all printing of floating point numbers in the |
|---|
| 126 | ;;; printer and in FORMAT. It converts a floating point number to a |
|---|
| 127 | ;;; string in a free or fixed format with no exponent. The |
|---|
| 128 | ;;; interpretation of the arguments is as follows: |
|---|
| 129 | ;;; |
|---|
| 130 | ;;; X - The floating point number to convert, which must not be |
|---|
| 131 | ;;; negative. |
|---|
| 132 | ;;; WIDTH - The preferred field width, used to determine the number |
|---|
| 133 | ;;; of fraction digits to produce if the FDIGITS parameter |
|---|
| 134 | ;;; is unspecified or NIL. If the non-fraction digits and the |
|---|
| 135 | ;;; decimal point alone exceed this width, no fraction digits |
|---|
| 136 | ;;; will be produced unless a non-NIL value of FDIGITS has been |
|---|
| 137 | ;;; specified. Field overflow is not considerd an error at this |
|---|
| 138 | ;;; level. |
|---|
| 139 | ;;; FDIGITS - The number of fractional digits to produce. Insignificant |
|---|
| 140 | ;;; trailing zeroes may be introduced as needed. May be |
|---|
| 141 | ;;; unspecified or NIL, in which case as many digits as possible |
|---|
| 142 | ;;; are generated, subject to the constraint that there are no |
|---|
| 143 | ;;; trailing zeroes. |
|---|
| 144 | ;;; SCALE - If this parameter is specified or non-NIL, then the number |
|---|
| 145 | ;;; printed is (* x (expt 10 scale)). This scaling is exact, |
|---|
| 146 | ;;; and cannot lose precision. |
|---|
| 147 | ;;; FMIN - This parameter, if specified or non-NIL, is the minimum |
|---|
| 148 | ;;; number of fraction digits which will be produced, regardless |
|---|
| 149 | ;;; of the value of WIDTH or FDIGITS. This feature is used by |
|---|
| 150 | ;;; the ~E format directive to prevent complete loss of |
|---|
| 151 | ;;; significance in the printed value due to a bogus choice of |
|---|
| 152 | ;;; scale factor. |
|---|
| 153 | ;;; |
|---|
| 154 | ;;; Most of the optional arguments are for the benefit for FORMAT and are not |
|---|
| 155 | ;;; used by the printer. |
|---|
| 156 | ;;; |
|---|
| 157 | ;;; Returns: |
|---|
| 158 | ;;; (VALUES DIGIT-STRING DIGIT-LENGTH LEADING-POINT TRAILING-POINT DECPNT) |
|---|
| 159 | ;;; where the results have the following interpretation: |
|---|
| 160 | ;;; |
|---|
| 161 | ;;; DIGIT-STRING - The decimal representation of X, with decimal point. |
|---|
| 162 | ;;; DIGIT-LENGTH - The length of the string DIGIT-STRING. |
|---|
| 163 | ;;; LEADING-POINT - True if the first character of DIGIT-STRING is the |
|---|
| 164 | ;;; decimal point. |
|---|
| 165 | ;;; TRAILING-POINT - True if the last character of DIGIT-STRING is the |
|---|
| 166 | ;;; decimal point. |
|---|
| 167 | ;;; POINT-POS - The position of the digit preceding the decimal |
|---|
| 168 | ;;; point. Zero indicates point before first digit. |
|---|
| 169 | ;;; |
|---|
| 170 | ;;; NOTE: FLONUM-TO-STRING goes to a lot of trouble to guarantee |
|---|
| 171 | ;;; accuracy. Specifically, the decimal number printed is the closest |
|---|
| 172 | ;;; possible approximation to the true value of the binary number to |
|---|
| 173 | ;;; be printed from among all decimal representations with the same |
|---|
| 174 | ;;; number of digits. In free-format output, i.e. with the number of |
|---|
| 175 | ;;; digits unconstrained, it is guaranteed that all the information is |
|---|
| 176 | ;;; preserved, so that a properly- rounding reader can reconstruct the |
|---|
| 177 | ;;; original binary number, bit-for-bit, from its printed decimal |
|---|
| 178 | ;;; representation. Furthermore, only as many digits as necessary to |
|---|
| 179 | ;;; satisfy this condition will be printed. |
|---|
| 180 | ;;; |
|---|
| 181 | ;;; FLOAT-STRING actually generates the digits for positive numbers. |
|---|
| 182 | ;;; The algorithm is essentially that of algorithm Dragon4 in "How to |
|---|
| 183 | ;;; Print Floating-Point Numbers Accurately" by Steele and White. The |
|---|
| 184 | ;;; current (draft) version of this paper may be found in |
|---|
| 185 | ;;; [CMUC]<steele>tradix.press. DO NOT EVEN THINK OF ATTEMPTING TO |
|---|
| 186 | ;;; UNDERSTAND THIS CODE WITHOUT READING THE PAPER! |
|---|
| 187 | |
|---|
| 188 | (defun flonum-to-string (x &optional width fdigits scale fmin) |
|---|
| 189 | (declare (ignore fmin)) ; FIXME |
|---|
| 190 | (cond ((zerop x) |
|---|
| 191 | ;; Zero is a special case which FLOAT-STRING cannot handle. |
|---|
| 192 | (if fdigits |
|---|
| 193 | (let ((s (make-string (1+ fdigits) :initial-element #\0))) |
|---|
| 194 | (setf (schar s 0) #\.) |
|---|
| 195 | (values s (length s) t (zerop fdigits) 0)) |
|---|
| 196 | (values "." 1 t t 0))) |
|---|
| 197 | (t |
|---|
| 198 | (when scale |
|---|
| 199 | (setf x (* x (expt 10 scale)))) |
|---|
| 200 | (let* ((s (float-string x)) |
|---|
| 201 | (length (length s)) |
|---|
| 202 | (index (position #\. s))) |
|---|
| 203 | (when (and (< x 1) |
|---|
| 204 | (> length 0) |
|---|
| 205 | (eql (schar s 0) #\0)) |
|---|
| 206 | (setf s (subseq s 1) |
|---|
| 207 | length (length s) |
|---|
| 208 | index (position #\. s))) |
|---|
| 209 | (when fdigits |
|---|
| 210 | ;; "Leading zeros are not permitted, except that a single zero |
|---|
| 211 | ;; digit is output before the decimal point if the printed value |
|---|
| 212 | ;; is less than one, and this single zero digit is not output at |
|---|
| 213 | ;; all if w=d+1." |
|---|
| 214 | (let ((actual-fdigits (- length index 1))) |
|---|
| 215 | (cond ((< actual-fdigits fdigits) |
|---|
| 216 | ;; Add the required number of trailing zeroes. |
|---|
| 217 | (setf s (concatenate 'string s |
|---|
| 218 | (make-string (- fdigits actual-fdigits) |
|---|
| 219 | :initial-element #\0)) |
|---|
| 220 | length (length s))) |
|---|
| 221 | ((> actual-fdigits fdigits) |
|---|
| 222 | (let* ((desired-length (+ index 1 fdigits)) |
|---|
| 223 | (c (schar s desired-length))) |
|---|
| 224 | (setf s (subseq s 0 (+ index 1 fdigits)) |
|---|
| 225 | length (length s) |
|---|
| 226 | index (position #\. s)) |
|---|
| 227 | (when (char>= c #\5) |
|---|
| 228 | (setf s (round-up s) |
|---|
| 229 | length (length s) |
|---|
| 230 | index (position #\. s)))))))) |
|---|
| 231 | (when (and width (> length width)) |
|---|
| 232 | ;; The string is too long. Shorten it by removing insignificant |
|---|
| 233 | ;; trailing zeroes if possible. |
|---|
| 234 | (let ((minimum-width (+ (1+ index) (or fdigits 0)))) |
|---|
| 235 | (when (< minimum-width width) |
|---|
| 236 | (setf minimum-width width)) |
|---|
| 237 | (when (> length minimum-width) |
|---|
| 238 | ;; But we don't want to shorten e.g. "1.7d100"... |
|---|
| 239 | (when (every #'digit-char-p (subseq s (1+ index))) |
|---|
| 240 | (let ((c (schar s minimum-width))) |
|---|
| 241 | (setf s (subseq s 0 minimum-width) |
|---|
| 242 | length minimum-width) |
|---|
| 243 | (when (char>= c #\5) |
|---|
| 244 | (setf s (round-up s) |
|---|
| 245 | length (length s) |
|---|
| 246 | index (position #\. s)))))))) |
|---|
| 247 | (values s length (eql index 0) (eql index (1- length)) index))))) |
|---|
| 248 | |
|---|
| 249 | (defun round-up (string) |
|---|
| 250 | (let* ((index (position #\. string)) |
|---|
| 251 | (n (read-from-string (setf string (remove #\. string)))) |
|---|
| 252 | (s (princ-to-string (incf n)))) |
|---|
| 253 | (loop for char across string |
|---|
| 254 | while (equal char #\0) |
|---|
| 255 | do (setf s (concatenate 'string "0" s))) |
|---|
| 256 | (cond ((null index) |
|---|
| 257 | s) |
|---|
| 258 | (t |
|---|
| 259 | (when (> (length s) (length string)) |
|---|
| 260 | ;; Rounding up made the string longer, which means we went from (say) 99 |
|---|
| 261 | ;; to 100. Drop the trailing #\0 and move the #\. one character to the |
|---|
| 262 | ;; right. |
|---|
| 263 | (setf s (subseq s 0 (1- (length s)))) |
|---|
| 264 | (incf index)) |
|---|
| 265 | (concatenate 'string (subseq s 0 index) "." (subseq s index)))))) |
|---|
| 266 | |
|---|
| 267 | |
|---|
| 268 | (defun scale-exponent (original-x) |
|---|
| 269 | (let* ((x (coerce original-x 'long-float))) |
|---|
| 270 | (multiple-value-bind (sig exponent) (decode-float x) |
|---|
| 271 | (declare (ignore sig)) |
|---|
| 272 | (if (= x 0.0l0) |
|---|
| 273 | (values (float 0.0l0 original-x) 1) |
|---|
| 274 | (let* ((ex (locally (declare (optimize (safety 0))) |
|---|
| 275 | (the fixnum |
|---|
| 276 | (round (* exponent (log 2l0 10)))))) |
|---|
| 277 | (x (if (minusp ex) |
|---|
| 278 | (if (float-denormalized-p x) |
|---|
| 279 | (* x 1.0l16 (expt 10.0l0 (- (- ex) 16))) |
|---|
| 280 | (* x 10.0l0 (expt 10.0l0 (- (- ex) 1)))) |
|---|
| 281 | (/ x 10.0l0 (expt 10.0l0 (1- ex)))))) |
|---|
| 282 | (do ((d 10.0l0 (* d 10.0l0)) |
|---|
| 283 | (y x (/ x d)) |
|---|
| 284 | (ex ex (1+ ex))) |
|---|
| 285 | ((< y 1.0l0) |
|---|
| 286 | (do ((m 10.0l0 (* m 10.0l0)) |
|---|
| 287 | (z y (* y m)) |
|---|
| 288 | (ex ex (1- ex))) |
|---|
| 289 | ((>= z 0.1l0) |
|---|
| 290 | (values (float z original-x) ex)) |
|---|
| 291 | (declare (long-float m) (integer ex)))) |
|---|
| 292 | (declare (long-float d)))))))) |
|---|
| 293 | |
|---|
| 294 | (defconstant double-float-exponent-byte |
|---|
| 295 | (byte 11 20)) |
|---|
| 296 | |
|---|
| 297 | (defun float-denormalized-p (x) |
|---|
| 298 | "Return true if the double-float X is denormalized." |
|---|
| 299 | (and (zerop (ldb double-float-exponent-byte (double-float-high-bits x))) |
|---|
| 300 | (not (zerop x)))) |
|---|
| 301 | |
|---|
| 302 | ;;; From early-format.lisp. |
|---|
| 303 | |
|---|
| 304 | (in-package #:format) |
|---|
| 305 | |
|---|
| 306 | (defparameter *format-whitespace-chars* |
|---|
| 307 | (vector #\space |
|---|
| 308 | #\newline |
|---|
| 309 | #\tab)) |
|---|
| 310 | |
|---|
| 311 | (defvar *format-directive-expanders* |
|---|
| 312 | (make-hash-table :test #'eq)) |
|---|
| 313 | (defvar *format-directive-interpreters* |
|---|
| 314 | (make-hash-table :test #'eq)) |
|---|
| 315 | |
|---|
| 316 | (defvar *default-format-error-control-string* nil) |
|---|
| 317 | (defvar *default-format-error-offset* nil) |
|---|
| 318 | |
|---|
| 319 | ;;;; specials used to communicate information |
|---|
| 320 | |
|---|
| 321 | ;;; Used both by the expansion stuff and the interpreter stuff. When it is |
|---|
| 322 | ;;; non-NIL, up-up-and-out (~:^) is allowed. Otherwise, ~:^ isn't allowed. |
|---|
| 323 | (defvar *up-up-and-out-allowed* nil) |
|---|
| 324 | |
|---|
| 325 | ;;; Used by the interpreter stuff. When it's non-NIL, it's a function |
|---|
| 326 | ;;; that will invoke PPRINT-POP in the right lexical environemnt. |
|---|
| 327 | (declaim (type (or null function) *logical-block-popper*)) |
|---|
| 328 | (defvar *logical-block-popper* nil) |
|---|
| 329 | |
|---|
| 330 | ;;; Used by the expander stuff. This is bindable so that ~<...~:> |
|---|
| 331 | ;;; can change it. |
|---|
| 332 | (defvar *expander-next-arg-macro* 'expander-next-arg) |
|---|
| 333 | |
|---|
| 334 | ;;; Used by the expander stuff. Initially starts as T, and gets set to NIL |
|---|
| 335 | ;;; if someone needs to do something strange with the arg list (like use |
|---|
| 336 | ;;; the rest, or something). |
|---|
| 337 | (defvar *only-simple-args*) |
|---|
| 338 | |
|---|
| 339 | ;;; Used by the expander stuff. We do an initial pass with this as NIL. |
|---|
| 340 | ;;; If someone doesn't like this, they (THROW 'NEED-ORIG-ARGS NIL) and we try |
|---|
| 341 | ;;; again with it bound to T. If this is T, we don't try to do anything |
|---|
| 342 | ;;; fancy with args. |
|---|
| 343 | (defvar *orig-args-available* nil) |
|---|
| 344 | |
|---|
| 345 | ;;; Used by the expander stuff. List of (symbol . offset) for simple args. |
|---|
| 346 | (defvar *simple-args*) |
|---|
| 347 | |
|---|
| 348 | ;;; From late-format.lisp. |
|---|
| 349 | |
|---|
| 350 | (in-package #:format) |
|---|
| 351 | |
|---|
| 352 | (define-condition format-error (error) |
|---|
| 353 | ((complaint :reader format-error-complaint :initarg :complaint) |
|---|
| 354 | (args :reader format-error-args :initarg :args :initform nil) |
|---|
| 355 | (control-string :reader format-error-control-string |
|---|
| 356 | :initarg :control-string |
|---|
| 357 | :initform *default-format-error-control-string*) |
|---|
| 358 | (offset :reader format-error-offset :initarg :offset |
|---|
| 359 | :initform *default-format-error-offset*) |
|---|
| 360 | (print-banner :reader format-error-print-banner :initarg :print-banner |
|---|
| 361 | :initform t)) |
|---|
| 362 | (:report %print-format-error)) |
|---|
| 363 | |
|---|
| 364 | (defun %print-format-error (condition stream) |
|---|
| 365 | (format stream |
|---|
| 366 | "~:[~;error in format: ~]~ |
|---|
| 367 | ~?~@[~% ~A~% ~V@T^~]" |
|---|
| 368 | (format-error-print-banner condition) |
|---|
| 369 | (format-error-complaint condition) |
|---|
| 370 | (format-error-args condition) |
|---|
| 371 | (format-error-control-string condition) |
|---|
| 372 | (format-error-offset condition))) |
|---|
| 373 | |
|---|
| 374 | (defun missing-arg () |
|---|
| 375 | (error "Missing argument in format directive")) |
|---|
| 376 | |
|---|
| 377 | (defstruct format-directive |
|---|
| 378 | (string (missing-arg) :type simple-string) |
|---|
| 379 | (start (missing-arg) :type (and unsigned-byte fixnum)) |
|---|
| 380 | (end (missing-arg) :type (and unsigned-byte fixnum)) |
|---|
| 381 | (character (missing-arg) :type base-char) |
|---|
| 382 | (colonp nil :type (member t nil)) |
|---|
| 383 | (atsignp nil :type (member t nil)) |
|---|
| 384 | (params nil :type list)) |
|---|
| 385 | (defmethod print-object ((x format-directive) stream) |
|---|
| 386 | (print-unreadable-object (x stream) |
|---|
| 387 | (write-string (format-directive-string x) |
|---|
| 388 | stream |
|---|
| 389 | :start (format-directive-start x) |
|---|
| 390 | :end (format-directive-end x)))) |
|---|
| 391 | |
|---|
| 392 | ;;;; TOKENIZE-CONTROL-STRING |
|---|
| 393 | |
|---|
| 394 | (defun tokenize-control-string (string) |
|---|
| 395 | (declare (simple-string string)) |
|---|
| 396 | (let ((index 0) |
|---|
| 397 | (end (length string)) |
|---|
| 398 | (result nil) |
|---|
| 399 | (in-block nil) |
|---|
| 400 | (pprint nil) |
|---|
| 401 | (semi nil) |
|---|
| 402 | (justification-semi 0)) |
|---|
| 403 | (declare (type index fixnum)) |
|---|
| 404 | (loop |
|---|
| 405 | (let ((next-directive (or (position #\~ string :start index) end))) |
|---|
| 406 | (declare (type index next-directive)) |
|---|
| 407 | (when (> next-directive index) |
|---|
| 408 | (push (subseq string index next-directive) result)) |
|---|
| 409 | (when (= next-directive end) |
|---|
| 410 | (return)) |
|---|
| 411 | (let* ((directive (parse-directive string next-directive)) |
|---|
| 412 | (directive-char (format-directive-character directive))) |
|---|
| 413 | (declare (type character directive-char)) |
|---|
| 414 | ;; We are looking for illegal combinations of format |
|---|
| 415 | ;; directives in the control string. See the last paragraph |
|---|
| 416 | ;; of CLHS 22.3.5.2: "an error is also signaled if the |
|---|
| 417 | ;; ~<...~:;...~> form of ~<...~> is used in the same format |
|---|
| 418 | ;; string with ~W, ~_, ~<...~:>, ~I, or ~:T." |
|---|
| 419 | (cond ((char= #\< directive-char) |
|---|
| 420 | ;; Found a justification or logical block |
|---|
| 421 | (setf in-block t)) |
|---|
| 422 | ((and in-block (char= #\; directive-char)) |
|---|
| 423 | ;; Found a semi colon in a justification or logical block |
|---|
| 424 | (setf semi t)) |
|---|
| 425 | ((char= #\> directive-char) |
|---|
| 426 | ;; End of justification or logical block. Figure out which. |
|---|
| 427 | (setf in-block nil) |
|---|
| 428 | (cond ((format-directive-colonp directive) |
|---|
| 429 | ;; A logical-block directive. Note that fact, and also |
|---|
| 430 | ;; note that we don't care if we found any ~; |
|---|
| 431 | ;; directives in the block. |
|---|
| 432 | (setf pprint t) |
|---|
| 433 | (setf semi nil)) |
|---|
| 434 | (semi |
|---|
| 435 | ;; A justification block with a ~; directive in it. |
|---|
| 436 | (incf justification-semi)))) |
|---|
| 437 | ((and (not in-block) |
|---|
| 438 | (or (and (char= #\T directive-char) (format-directive-colonp directive)) |
|---|
| 439 | (char= #\W directive-char) |
|---|
| 440 | (char= #\_ directive-char) |
|---|
| 441 | (char= #\I directive-char))) |
|---|
| 442 | (setf pprint t))) |
|---|
| 443 | (push directive result) |
|---|
| 444 | (setf index (format-directive-end directive))))) |
|---|
| 445 | (when (and pprint (plusp justification-semi)) |
|---|
| 446 | (error 'format-error |
|---|
| 447 | :complaint "A justification directive cannot be in the same format string~%~ |
|---|
| 448 | as ~~W, ~~I, ~~:T, or a logical-block directive." |
|---|
| 449 | :control-string string |
|---|
| 450 | :offset 0)) |
|---|
| 451 | (nreverse result))) |
|---|
| 452 | |
|---|
| 453 | (defun parse-directive (string start) |
|---|
| 454 | (let ((posn (1+ start)) (params nil) (colonp nil) (atsignp nil) |
|---|
| 455 | (end (length string))) |
|---|
| 456 | (flet ((get-char () |
|---|
| 457 | (if (= posn end) |
|---|
| 458 | (error 'format-error |
|---|
| 459 | :complaint "String ended before directive was found." |
|---|
| 460 | :control-string string |
|---|
| 461 | :offset start) |
|---|
| 462 | (schar string posn))) |
|---|
| 463 | (check-ordering () |
|---|
| 464 | (when (or colonp atsignp) |
|---|
| 465 | (error 'format-error |
|---|
| 466 | :complaint "parameters found after #\\: or #\\@ modifier" |
|---|
| 467 | :control-string string |
|---|
| 468 | :offset posn)))) |
|---|
| 469 | (loop |
|---|
| 470 | (let ((char (get-char))) |
|---|
| 471 | (cond ((or (char<= #\0 char #\9) (char= char #\+) (char= char #\-)) |
|---|
| 472 | (check-ordering) |
|---|
| 473 | (multiple-value-bind (param new-posn) |
|---|
| 474 | (parse-integer string :start posn :junk-allowed t) |
|---|
| 475 | (push (cons posn param) params) |
|---|
| 476 | (setf posn new-posn) |
|---|
| 477 | (case (get-char) |
|---|
| 478 | (#\,) |
|---|
| 479 | ((#\: #\@) |
|---|
| 480 | (decf posn)) |
|---|
| 481 | (t |
|---|
| 482 | (return))))) |
|---|
| 483 | ((or (char= char #\v) |
|---|
| 484 | (char= char #\V)) |
|---|
| 485 | (check-ordering) |
|---|
| 486 | (push (cons posn :arg) params) |
|---|
| 487 | (incf posn) |
|---|
| 488 | (case (get-char) |
|---|
| 489 | (#\,) |
|---|
| 490 | ((#\: #\@) |
|---|
| 491 | (decf posn)) |
|---|
| 492 | (t |
|---|
| 493 | (return)))) |
|---|
| 494 | ((char= char #\#) |
|---|
| 495 | (check-ordering) |
|---|
| 496 | (push (cons posn :remaining) params) |
|---|
| 497 | (incf posn) |
|---|
| 498 | (case (get-char) |
|---|
| 499 | (#\,) |
|---|
| 500 | ((#\: #\@) |
|---|
| 501 | (decf posn)) |
|---|
| 502 | (t |
|---|
| 503 | (return)))) |
|---|
| 504 | ((char= char #\') |
|---|
| 505 | (check-ordering) |
|---|
| 506 | (incf posn) |
|---|
| 507 | (push (cons posn (get-char)) params) |
|---|
| 508 | (incf posn) |
|---|
| 509 | (unless (char= (get-char) #\,) |
|---|
| 510 | (decf posn))) |
|---|
| 511 | ((char= char #\,) |
|---|
| 512 | (check-ordering) |
|---|
| 513 | (push (cons posn nil) params)) |
|---|
| 514 | ((char= char #\:) |
|---|
| 515 | (if colonp |
|---|
| 516 | (error 'format-error |
|---|
| 517 | :complaint "too many colons supplied" |
|---|
| 518 | :control-string string |
|---|
| 519 | :offset posn) |
|---|
| 520 | (setf colonp t))) |
|---|
| 521 | ((char= char #\@) |
|---|
| 522 | (if atsignp |
|---|
| 523 | (error 'format-error |
|---|
| 524 | :complaint "too many #\\@ characters supplied" |
|---|
| 525 | :control-string string |
|---|
| 526 | :offset posn) |
|---|
| 527 | (setf atsignp t))) |
|---|
| 528 | (t |
|---|
| 529 | (when (and (char= (schar string (1- posn)) #\,) |
|---|
| 530 | (or (< posn 2) |
|---|
| 531 | (char/= (schar string (- posn 2)) #\'))) |
|---|
| 532 | (check-ordering) |
|---|
| 533 | (push (cons (1- posn) nil) params)) |
|---|
| 534 | (return)))) |
|---|
| 535 | (incf posn)) |
|---|
| 536 | (let ((char (get-char))) |
|---|
| 537 | (when (char= char #\/) |
|---|
| 538 | (let ((closing-slash (position #\/ string :start (1+ posn)))) |
|---|
| 539 | (if closing-slash |
|---|
| 540 | (setf posn closing-slash) |
|---|
| 541 | (error 'format-error |
|---|
| 542 | :complaint "no matching closing slash" |
|---|
| 543 | :control-string string |
|---|
| 544 | :offset posn)))) |
|---|
| 545 | (make-format-directive |
|---|
| 546 | :string string :start start :end (1+ posn) |
|---|
| 547 | :character (char-upcase char) |
|---|
| 548 | :colonp colonp :atsignp atsignp |
|---|
| 549 | :params (nreverse params)))))) |
|---|
| 550 | |
|---|
| 551 | ;;;; FORMATTER stuff |
|---|
| 552 | |
|---|
| 553 | (defmacro formatter (control-string) |
|---|
| 554 | `#',(%formatter control-string)) |
|---|
| 555 | |
|---|
| 556 | (defun %formatter (control-string) |
|---|
| 557 | (block nil |
|---|
| 558 | (catch 'need-orig-args |
|---|
| 559 | (let* ((*simple-args* nil) |
|---|
| 560 | (*only-simple-args* t) |
|---|
| 561 | (guts (expand-control-string control-string)) |
|---|
| 562 | (args nil)) |
|---|
| 563 | (dolist (arg *simple-args*) |
|---|
| 564 | (push `(,(car arg) |
|---|
| 565 | (error |
|---|
| 566 | 'format-error |
|---|
| 567 | :complaint "required argument missing" |
|---|
| 568 | :control-string ,control-string |
|---|
| 569 | :offset ,(cdr arg))) |
|---|
| 570 | args)) |
|---|
| 571 | (return `(lambda (stream &optional ,@args &rest args) |
|---|
| 572 | ,guts |
|---|
| 573 | args)))) |
|---|
| 574 | (let ((*orig-args-available* t) |
|---|
| 575 | (*only-simple-args* nil)) |
|---|
| 576 | `(lambda (stream &rest orig-args) |
|---|
| 577 | (let ((args orig-args)) |
|---|
| 578 | ,(expand-control-string control-string) |
|---|
| 579 | args))))) |
|---|
| 580 | |
|---|
| 581 | (defun expand-control-string (string) |
|---|
| 582 | (let* ((string (etypecase string |
|---|
| 583 | (simple-string |
|---|
| 584 | string) |
|---|
| 585 | (string |
|---|
| 586 | (coerce string 'simple-string)))) |
|---|
| 587 | (*default-format-error-control-string* string) |
|---|
| 588 | (directives (tokenize-control-string string))) |
|---|
| 589 | `(block nil |
|---|
| 590 | ,@(expand-directive-list directives)))) |
|---|
| 591 | |
|---|
| 592 | (defun expand-directive-list (directives) |
|---|
| 593 | (let ((results nil) |
|---|
| 594 | (remaining-directives directives)) |
|---|
| 595 | (loop |
|---|
| 596 | (unless remaining-directives |
|---|
| 597 | (return)) |
|---|
| 598 | (multiple-value-bind (form new-directives) |
|---|
| 599 | (expand-directive (car remaining-directives) |
|---|
| 600 | (cdr remaining-directives)) |
|---|
| 601 | (push form results) |
|---|
| 602 | (setf remaining-directives new-directives))) |
|---|
| 603 | (reverse results))) |
|---|
| 604 | |
|---|
| 605 | (defun expand-directive (directive more-directives) |
|---|
| 606 | (etypecase directive |
|---|
| 607 | (format-directive |
|---|
| 608 | (let ((expander |
|---|
| 609 | (gethash (format-directive-character directive) |
|---|
| 610 | *format-directive-expanders*)) |
|---|
| 611 | (*default-format-error-offset* |
|---|
| 612 | (1- (format-directive-end directive)))) |
|---|
| 613 | (declare (type (or null function) expander)) |
|---|
| 614 | (if expander |
|---|
| 615 | (funcall expander directive more-directives) |
|---|
| 616 | (error 'format-error |
|---|
| 617 | :complaint "unknown directive ~@[(character: ~A)~]" |
|---|
| 618 | :args (list (char-name (format-directive-character directive))))))) |
|---|
| 619 | (simple-string |
|---|
| 620 | (values `(write-string ,directive stream) |
|---|
| 621 | more-directives)))) |
|---|
| 622 | |
|---|
| 623 | (defmacro expander-next-arg (string offset) |
|---|
| 624 | `(if args |
|---|
| 625 | (pop args) |
|---|
| 626 | (error 'format-error |
|---|
| 627 | :complaint "no more arguments" |
|---|
| 628 | :control-string ,string |
|---|
| 629 | :offset ,offset))) |
|---|
| 630 | |
|---|
| 631 | (defun expand-next-arg (&optional offset) |
|---|
| 632 | (if (or *orig-args-available* (not *only-simple-args*)) |
|---|
| 633 | `(,*expander-next-arg-macro* |
|---|
| 634 | ,*default-format-error-control-string* |
|---|
| 635 | ,(or offset *default-format-error-offset*)) |
|---|
| 636 | (let ((symbol (gensym "FORMAT-ARG-"))) |
|---|
| 637 | (push (cons symbol (or offset *default-format-error-offset*)) |
|---|
| 638 | *simple-args*) |
|---|
| 639 | symbol))) |
|---|
| 640 | |
|---|
| 641 | (defmacro expand-bind-defaults (specs params &body body) |
|---|
| 642 | (sys::once-only ((params params)) |
|---|
| 643 | (if specs |
|---|
| 644 | (collect ((expander-bindings) (runtime-bindings)) |
|---|
| 645 | (dolist (spec specs) |
|---|
| 646 | (destructuring-bind (var default) spec |
|---|
| 647 | (let ((symbol (gensym))) |
|---|
| 648 | (expander-bindings |
|---|
| 649 | `(,var ',symbol)) |
|---|
| 650 | (runtime-bindings |
|---|
| 651 | `(list ',symbol |
|---|
| 652 | (let* ((param-and-offset (pop ,params)) |
|---|
| 653 | (offset (car param-and-offset)) |
|---|
| 654 | (param (cdr param-and-offset))) |
|---|
| 655 | (case param |
|---|
| 656 | (:arg `(or ,(expand-next-arg offset) |
|---|
| 657 | ,,default)) |
|---|
| 658 | (:remaining |
|---|
| 659 | (setf *only-simple-args* nil) |
|---|
| 660 | '(length args)) |
|---|
| 661 | ((nil) ,default) |
|---|
| 662 | (t param)))))))) |
|---|
| 663 | `(let ,(expander-bindings) |
|---|
| 664 | `(let ,(list ,@(runtime-bindings)) |
|---|
| 665 | ,@(if ,params |
|---|
| 666 | (error |
|---|
| 667 | 'format-error |
|---|
| 668 | :complaint |
|---|
| 669 | "too many parameters, expected no more than ~W" |
|---|
| 670 | :args (list ,(length specs)) |
|---|
| 671 | :offset (caar ,params))) |
|---|
| 672 | ,,@body))) |
|---|
| 673 | `(progn |
|---|
| 674 | (when ,params |
|---|
| 675 | (error 'format-error |
|---|
| 676 | :complaint "too many parameters, expected none" |
|---|
| 677 | :offset (caar ,params))) |
|---|
| 678 | ,@body)))) |
|---|
| 679 | |
|---|
| 680 | ;;;; format directive machinery |
|---|
| 681 | |
|---|
| 682 | ;;; FIXME: only used in this file, could be SB!XC:DEFMACRO in EVAL-WHEN |
|---|
| 683 | (defmacro def-complex-format-directive (char lambda-list &body body) |
|---|
| 684 | (let ((defun-name |
|---|
| 685 | (intern (concatenate 'string |
|---|
| 686 | (let ((name (char-name char))) |
|---|
| 687 | (cond (name |
|---|
| 688 | (string-capitalize name)) |
|---|
| 689 | (t |
|---|
| 690 | (string char)))) |
|---|
| 691 | "-FORMAT-DIRECTIVE-EXPANDER"))) |
|---|
| 692 | (directive (gensym)) |
|---|
| 693 | (directives (if lambda-list (car (last lambda-list)) (gensym)))) |
|---|
| 694 | `(progn |
|---|
| 695 | (defun ,defun-name (,directive ,directives) |
|---|
| 696 | ,@(if lambda-list |
|---|
| 697 | `((let ,(mapcar (lambda (var) |
|---|
| 698 | `(,var |
|---|
| 699 | (,(sys::symbolicate "FORMAT-DIRECTIVE-" var) |
|---|
| 700 | ,directive))) |
|---|
| 701 | (butlast lambda-list)) |
|---|
| 702 | ,@body)) |
|---|
| 703 | `((declare (ignore ,directive ,directives)) |
|---|
| 704 | ,@body))) |
|---|
| 705 | (%set-format-directive-expander ,char #',defun-name)))) |
|---|
| 706 | |
|---|
| 707 | ;;; FIXME: only used in this file, could be SB!XC:DEFMACRO in EVAL-WHEN |
|---|
| 708 | (defmacro def-format-directive (char lambda-list &body body) |
|---|
| 709 | (let ((directives (gensym)) |
|---|
| 710 | (declarations nil) |
|---|
| 711 | (body-without-decls body)) |
|---|
| 712 | (loop |
|---|
| 713 | (let ((form (car body-without-decls))) |
|---|
| 714 | (unless (and (consp form) (eq (car form) 'declare)) |
|---|
| 715 | (return)) |
|---|
| 716 | (push (pop body-without-decls) declarations))) |
|---|
| 717 | (setf declarations (reverse declarations)) |
|---|
| 718 | `(def-complex-format-directive ,char (,@lambda-list ,directives) |
|---|
| 719 | ,@declarations |
|---|
| 720 | (values (progn ,@body-without-decls) |
|---|
| 721 | ,directives)))) |
|---|
| 722 | |
|---|
| 723 | (eval-when (:compile-toplevel :load-toplevel :execute) |
|---|
| 724 | |
|---|
| 725 | (defun %set-format-directive-expander (char fn) |
|---|
| 726 | (setf (gethash (char-upcase char) *format-directive-expanders*) fn) |
|---|
| 727 | char) |
|---|
| 728 | |
|---|
| 729 | (defun %set-format-directive-interpreter (char fn) |
|---|
| 730 | (setf (gethash (char-upcase char) *format-directive-interpreters*) fn) |
|---|
| 731 | char) |
|---|
| 732 | |
|---|
| 733 | (defun find-directive (directives kind stop-at-semi) |
|---|
| 734 | (if directives |
|---|
| 735 | (let ((next (car directives))) |
|---|
| 736 | (if (format-directive-p next) |
|---|
| 737 | (let ((char (format-directive-character next))) |
|---|
| 738 | (if (or (char= kind char) |
|---|
| 739 | (and stop-at-semi (char= char #\;))) |
|---|
| 740 | (car directives) |
|---|
| 741 | (find-directive |
|---|
| 742 | (cdr (flet ((after (char) |
|---|
| 743 | (member (find-directive (cdr directives) |
|---|
| 744 | char |
|---|
| 745 | nil) |
|---|
| 746 | directives))) |
|---|
| 747 | (case char |
|---|
| 748 | (#\( (after #\))) |
|---|
| 749 | (#\< (after #\>)) |
|---|
| 750 | (#\[ (after #\])) |
|---|
| 751 | (#\{ (after #\})) |
|---|
| 752 | (t directives)))) |
|---|
| 753 | kind stop-at-semi))) |
|---|
| 754 | (find-directive (cdr directives) kind stop-at-semi))))) |
|---|
| 755 | |
|---|
| 756 | ) ; EVAL-WHEN |
|---|
| 757 | |
|---|
| 758 | ;;;; format directives for simple output |
|---|
| 759 | |
|---|
| 760 | (def-format-directive #\A (colonp atsignp params) |
|---|
| 761 | (if params |
|---|
| 762 | (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0) |
|---|
| 763 | (padchar #\space)) |
|---|
| 764 | params |
|---|
| 765 | `(format-princ stream ,(expand-next-arg) ',colonp ',atsignp |
|---|
| 766 | ,mincol ,colinc ,minpad ,padchar)) |
|---|
| 767 | `(princ ,(if colonp |
|---|
| 768 | `(or ,(expand-next-arg) "()") |
|---|
| 769 | (expand-next-arg)) |
|---|
| 770 | stream))) |
|---|
| 771 | |
|---|
| 772 | (def-format-directive #\S (colonp atsignp params) |
|---|
| 773 | (cond (params |
|---|
| 774 | (expand-bind-defaults ((mincol 0) (colinc 1) (minpad 0) |
|---|
| 775 | (padchar #\space)) |
|---|
| 776 | params |
|---|
| 777 | `(format-prin1 stream ,(expand-next-arg) ,colonp ,atsignp |
|---|
| 778 | ,mincol ,colinc ,minpad ,padchar))) |
|---|
| 779 | (colonp |
|---|
| 780 | `(let ((arg ,(expand-next-arg))) |
|---|
| 781 | (if arg |
|---|
| 782 | (prin1 arg stream) |
|---|
| 783 | (princ "()" stream)))) |
|---|
| 784 | (t |
|---|
| 785 | `(prin1 ,(expand-next-arg) stream)))) |
|---|
| 786 | |
|---|
| 787 | (def-format-directive #\C (colonp atsignp params) |
|---|
| 788 | (expand-bind-defaults () params |
|---|
| 789 | (if colonp |
|---|
| 790 | `(format-print-named-character ,(expand-next-arg) stream) |
|---|
| 791 | (if atsignp |
|---|
| 792 | `(prin1 ,(expand-next-arg) stream) |
|---|
| 793 | `(write-char ,(expand-next-arg) stream))))) |
|---|
| 794 | |
|---|
| 795 | (def-format-directive #\W (colonp atsignp params) |
|---|
| 796 | (expand-bind-defaults () params |
|---|
| 797 | (if (or colonp atsignp) |
|---|
| 798 | `(let (,@(when colonp |
|---|
| 799 | '((*print-pretty* t))) |
|---|
| 800 | ,@(when atsignp |
|---|
| 801 | '((*print-level* nil) |
|---|
| 802 | (*print-length* nil)))) |
|---|
| 803 | (sys::output-object ,(expand-next-arg) stream)) |
|---|
| 804 | `(sys::output-object ,(expand-next-arg) stream)))) |
|---|
| 805 | |
|---|
| 806 | ;;;; format directives for integer output |
|---|
| 807 | |
|---|
| 808 | (defun expand-format-integer (base colonp atsignp params) |
|---|
| 809 | (if (or colonp atsignp params) |
|---|
| 810 | (expand-bind-defaults |
|---|
| 811 | ((mincol 0) (padchar #\space) (commachar #\,) (commainterval 3)) |
|---|
| 812 | params |
|---|
| 813 | `(format-print-integer stream ,(expand-next-arg) ,colonp ,atsignp |
|---|
| 814 | ,base ,mincol ,padchar ,commachar |
|---|
| 815 | ,commainterval)) |
|---|
| 816 | `(write ,(expand-next-arg) :stream stream :base ,base :radix nil |
|---|
| 817 | :escape nil))) |
|---|
| 818 | |
|---|
| 819 | (def-format-directive #\D (colonp atsignp params) |
|---|
| 820 | (expand-format-integer 10 colonp atsignp params)) |
|---|
| 821 | |
|---|
| 822 | (def-format-directive #\B (colonp atsignp params) |
|---|
| 823 | (expand-format-integer 2 colonp atsignp params)) |
|---|
| 824 | |
|---|
| 825 | (def-format-directive #\O (colonp atsignp params) |
|---|
| 826 | (expand-format-integer 8 colonp atsignp params)) |
|---|
| 827 | |
|---|
| 828 | (def-format-directive #\X (colonp atsignp params) |
|---|
| 829 | (expand-format-integer 16 colonp atsignp params)) |
|---|
| 830 | |
|---|
| 831 | (def-format-directive #\R (colonp atsignp params) |
|---|
| 832 | (expand-bind-defaults |
|---|
| 833 | ((base nil) (mincol 0) (padchar #\space) (commachar #\,) |
|---|
| 834 | (commainterval 3)) |
|---|
| 835 | params |
|---|
| 836 | (let ((n-arg (gensym))) |
|---|
| 837 | `(let ((,n-arg ,(expand-next-arg))) |
|---|
| 838 | (if ,base |
|---|
| 839 | (format-print-integer stream ,n-arg ,colonp ,atsignp |
|---|
| 840 | ,base ,mincol |
|---|
| 841 | ,padchar ,commachar ,commainterval) |
|---|
| 842 | ,(if atsignp |
|---|
| 843 | (if colonp |
|---|
| 844 | `(format-print-old-roman stream ,n-arg) |
|---|
| 845 | `(format-print-roman stream ,n-arg)) |
|---|
| 846 | (if colonp |
|---|
| 847 | `(format-print-ordinal stream ,n-arg) |
|---|
| 848 | `(format-print-cardinal stream ,n-arg)))))))) |
|---|
| 849 | |
|---|
| 850 | ;;;; format directive for pluralization |
|---|
| 851 | |
|---|
| 852 | (def-format-directive #\P (colonp atsignp params end) |
|---|
| 853 | (expand-bind-defaults () params |
|---|
| 854 | (let ((arg (cond |
|---|
| 855 | ((not colonp) |
|---|
| 856 | (expand-next-arg)) |
|---|
| 857 | (*orig-args-available* |
|---|
| 858 | `(if (eq orig-args args) |
|---|
| 859 | (error 'format-error |
|---|
| 860 | :complaint "no previous argument" |
|---|
| 861 | :offset ,(1- end)) |
|---|
| 862 | (do ((arg-ptr orig-args (cdr arg-ptr))) |
|---|
| 863 | ((eq (cdr arg-ptr) args) |
|---|
| 864 | (car arg-ptr))))) |
|---|
| 865 | (*only-simple-args* |
|---|
| 866 | (unless *simple-args* |
|---|
| 867 | (error 'format-error |
|---|
| 868 | :complaint "no previous argument")) |
|---|
| 869 | (caar *simple-args*)) |
|---|
| 870 | (t |
|---|
| 871 | (throw 'need-orig-args nil))))) |
|---|
| 872 | (if atsignp |
|---|
| 873 | `(write-string (if (eql ,arg 1) "y" "ies") stream) |
|---|
| 874 | `(unless (eql ,arg 1) (write-char #\s stream)))))) |
|---|
| 875 | |
|---|
| 876 | ;;;; format directives for floating point output |
|---|
| 877 | |
|---|
| 878 | (def-format-directive #\F (colonp atsignp params) |
|---|
| 879 | (when colonp |
|---|
| 880 | (error 'format-error |
|---|
| 881 | :complaint |
|---|
| 882 | "The colon modifier cannot be used with this directive.")) |
|---|
| 883 | (expand-bind-defaults ((w nil) (d nil) (k nil) (ovf nil) (pad #\space)) params |
|---|
| 884 | `(format-fixed stream ,(expand-next-arg) ,w ,d ,k ,ovf ,pad ,atsignp))) |
|---|
| 885 | |
|---|
| 886 | (def-format-directive #\E (colonp atsignp params) |
|---|
| 887 | (when colonp |
|---|
| 888 | (error 'format-error |
|---|
| 889 | :complaint |
|---|
| 890 | "The colon modifier cannot be used with this directive.")) |
|---|
| 891 | (expand-bind-defaults |
|---|
| 892 | ((w nil) (d nil) (e nil) (k 1) (ovf nil) (pad #\space) (mark nil)) |
|---|
| 893 | params |
|---|
| 894 | `(format-exponential stream ,(expand-next-arg) ,w ,d ,e ,k ,ovf ,pad ,mark |
|---|
| 895 | ,atsignp))) |
|---|
| 896 | |
|---|
| 897 | (def-format-directive #\G (colonp atsignp params) |
|---|
| 898 | (when colonp |
|---|
| 899 | (error 'format-error |
|---|
| 900 | :complaint |
|---|
| 901 | "The colon modifier cannot be used with this directive.")) |
|---|
| 902 | (expand-bind-defaults |
|---|
| 903 | ((w nil) (d nil) (e nil) (k nil) (ovf nil) (pad #\space) (mark nil)) |
|---|
| 904 | params |
|---|
| 905 | `(format-general stream ,(expand-next-arg) ,w ,d ,e ,k ,ovf ,pad ,mark ,atsignp))) |
|---|
| 906 | |
|---|
| 907 | (def-format-directive #\$ (colonp atsignp params) |
|---|
| 908 | (expand-bind-defaults ((d 2) (n 1) (w 0) (pad #\space)) params |
|---|
| 909 | `(format-dollars stream ,(expand-next-arg) ,d ,n ,w ,pad ,colonp |
|---|
| 910 | ,atsignp))) |
|---|
| 911 | |
|---|
| 912 | ;;;; format directives for line/page breaks etc. |
|---|
| 913 | |
|---|
| 914 | (def-format-directive #\% (colonp atsignp params) |
|---|
| 915 | (when (or colonp atsignp) |
|---|
| 916 | (error 'format-error |
|---|
| 917 | :complaint |
|---|
| 918 | "The colon and atsign modifiers cannot be used with this directive." |
|---|
| 919 | )) |
|---|
| 920 | (if params |
|---|
| 921 | (expand-bind-defaults ((count 1)) params |
|---|
| 922 | `(dotimes (i ,count) |
|---|
| 923 | (terpri stream))) |
|---|
| 924 | '(terpri stream))) |
|---|
| 925 | |
|---|
| 926 | (def-format-directive #\& (colonp atsignp params) |
|---|
| 927 | (when (or colonp atsignp) |
|---|
| 928 | (error 'format-error |
|---|
| 929 | :complaint |
|---|
| 930 | "The colon and atsign modifiers cannot be used with this directive." |
|---|
| 931 | )) |
|---|
| 932 | (if params |
|---|
| 933 | (expand-bind-defaults ((count 1)) params |
|---|
| 934 | `(progn |
|---|
| 935 | (fresh-line stream) |
|---|
| 936 | (dotimes (i (1- ,count)) |
|---|
| 937 | (terpri stream)))) |
|---|
| 938 | '(fresh-line stream))) |
|---|
| 939 | |
|---|
| 940 | (def-format-directive #\| (colonp atsignp params) |
|---|
| 941 | (when (or colonp atsignp) |
|---|
| 942 | (error 'format-error |
|---|
| 943 | :complaint |
|---|
| 944 | "The colon and atsign modifiers cannot be used with this directive." |
|---|
| 945 | )) |
|---|
| 946 | (if params |
|---|
| 947 | (expand-bind-defaults ((count 1)) params |
|---|
| 948 | `(dotimes (i ,count) |
|---|
| 949 | (write-char (code-char sys::form-feed-char-code) stream))) |
|---|
| 950 | '(write-char (code-char sys::form-feed-char-code) stream))) |
|---|
| 951 | |
|---|
| 952 | (def-format-directive #\~ (colonp atsignp params) |
|---|
| 953 | (when (or colonp atsignp) |
|---|
| 954 | (error 'format-error |
|---|
| 955 | :complaint |
|---|
| 956 | "The colon and atsign modifiers cannot be used with this directive." |
|---|
| 957 | )) |
|---|
| 958 | (if params |
|---|
| 959 | (expand-bind-defaults ((count 1)) params |
|---|
| 960 | `(dotimes (i ,count) |
|---|
| 961 | (write-char #\~ stream))) |
|---|
| 962 | '(write-char #\~ stream))) |
|---|
| 963 | |
|---|
| 964 | (def-complex-format-directive #\newline (colonp atsignp params directives) |
|---|
| 965 | (when (and colonp atsignp) |
|---|
| 966 | (error 'format-error |
|---|
| 967 | :complaint "both colon and atsign modifiers used simultaneously")) |
|---|
| 968 | (values (expand-bind-defaults () params |
|---|
| 969 | (if atsignp |
|---|
| 970 | '(write-char #\newline stream) |
|---|
| 971 | nil)) |
|---|
| 972 | (if (and (not colonp) |
|---|
| 973 | directives |
|---|
| 974 | (simple-string-p (car directives))) |
|---|
| 975 | (cons (string-left-trim *format-whitespace-chars* |
|---|
| 976 | (car directives)) |
|---|
| 977 | (cdr directives)) |
|---|
| 978 | directives))) |
|---|
| 979 | |
|---|
| 980 | ;;;; format directives for tabs and simple pretty printing |
|---|
| 981 | |
|---|
| 982 | (def-format-directive #\T (colonp atsignp params) |
|---|
| 983 | (if colonp |
|---|
| 984 | (expand-bind-defaults ((n 1) (m 1)) params |
|---|
| 985 | `(pprint-tab ,(if atsignp :section-relative :section) |
|---|
| 986 | ,n ,m stream)) |
|---|
| 987 | (if atsignp |
|---|
| 988 | (expand-bind-defaults ((colrel 1) (colinc 1)) params |
|---|
| 989 | `(format-relative-tab stream ,colrel ,colinc)) |
|---|
| 990 | (expand-bind-defaults ((colnum 1) (colinc 1)) params |
|---|
| 991 | `(format-absolute-tab stream ,colnum ,colinc))))) |
|---|
| 992 | |
|---|
| 993 | (def-format-directive #\_ (colonp atsignp params) |
|---|
| 994 | (expand-bind-defaults () params |
|---|
| 995 | `(pprint-newline ,(if colonp |
|---|
| 996 | (if atsignp |
|---|
| 997 | :mandatory |
|---|
| 998 | :fill) |
|---|
| 999 | (if atsignp |
|---|
| 1000 | :miser |
|---|
| 1001 | :linear)) |
|---|
| 1002 | stream))) |
|---|
| 1003 | |
|---|
| 1004 | (def-format-directive #\I (colonp atsignp params) |
|---|
| 1005 | (when atsignp |
|---|
| 1006 | (error 'format-error |
|---|
| 1007 | :complaint |
|---|
| 1008 | "cannot use the at-sign modifier with this directive")) |
|---|
| 1009 | (expand-bind-defaults ((n 0)) params |
|---|
| 1010 | `(pprint-indent ,(if colonp :current :block) ,n stream))) |
|---|
| 1011 | |
|---|
| 1012 | ;;;; format directive for ~* |
|---|
| 1013 | |
|---|
| 1014 | (def-format-directive #\* (colonp atsignp params end) |
|---|
| 1015 | (if atsignp |
|---|
| 1016 | (if colonp |
|---|
| 1017 | (error 'format-error |
|---|
| 1018 | :complaint |
|---|
| 1019 | "both colon and atsign modifiers used simultaneously") |
|---|
| 1020 | (expand-bind-defaults ((posn 0)) params |
|---|
| 1021 | (unless *orig-args-available* |
|---|
| 1022 | (throw 'need-orig-args nil)) |
|---|
| 1023 | `(if (<= 0 ,posn (length orig-args)) |
|---|
| 1024 | (setf args (nthcdr ,posn orig-args)) |
|---|
| 1025 | (error 'format-error |
|---|
| 1026 | :complaint "Index ~W out of bounds. Should have been ~ |
|---|
| 1027 | between 0 and ~W." |
|---|
| 1028 | :args (list ,posn (length orig-args)) |
|---|
| 1029 | :offset ,(1- end))))) |
|---|
| 1030 | (if colonp |
|---|
| 1031 | (expand-bind-defaults ((n 1)) params |
|---|
| 1032 | (unless *orig-args-available* |
|---|
| 1033 | (throw 'need-orig-args nil)) |
|---|
| 1034 | `(do ((cur-posn 0 (1+ cur-posn)) |
|---|
| 1035 | (arg-ptr orig-args (cdr arg-ptr))) |
|---|
| 1036 | ((eq arg-ptr args) |
|---|
| 1037 | (let ((new-posn (- cur-posn ,n))) |
|---|
| 1038 | (if (<= 0 new-posn (length orig-args)) |
|---|
| 1039 | (setf args (nthcdr new-posn orig-args)) |
|---|
| 1040 | (error 'format-error |
|---|
| 1041 | :complaint |
|---|
| 1042 | "Index ~W is out of bounds; should have been ~ |
|---|
| 1043 | between 0 and ~W." |
|---|
| 1044 | :args (list new-posn (length orig-args)) |
|---|
| 1045 | :offset ,(1- end))))))) |
|---|
| 1046 | (if params |
|---|
| 1047 | (expand-bind-defaults ((n 1)) params |
|---|
| 1048 | (setf *only-simple-args* nil) |
|---|
| 1049 | `(dotimes (i ,n) |
|---|
| 1050 | ,(expand-next-arg))) |
|---|
| 1051 | (expand-next-arg))))) |
|---|
| 1052 | |
|---|
| 1053 | ;;;; format directive for indirection |
|---|
| 1054 | |
|---|
| 1055 | (def-format-directive #\? (colonp atsignp params string end) |
|---|
| 1056 | (when colonp |
|---|
| 1057 | (error 'format-error |
|---|
| 1058 | :complaint "cannot use the colon modifier with this directive")) |
|---|
| 1059 | (expand-bind-defaults () params |
|---|
| 1060 | `(handler-bind |
|---|
| 1061 | ((format-error |
|---|
| 1062 | (lambda (condition) |
|---|
| 1063 | (error 'format-error |
|---|
| 1064 | :complaint |
|---|
| 1065 | "~A~%while processing indirect format string:" |
|---|
| 1066 | :args (list condition) |
|---|
| 1067 | :print-banner nil |
|---|
| 1068 | :control-string ,string |
|---|
| 1069 | :offset ,(1- end))))) |
|---|
| 1070 | ,(if atsignp |
|---|
| 1071 | (if *orig-args-available* |
|---|
| 1072 | `(setf args (%format stream ,(expand-next-arg) orig-args args)) |
|---|
| 1073 | (throw 'need-orig-args nil)) |
|---|
| 1074 | `(%format stream ,(expand-next-arg) ,(expand-next-arg)))))) |
|---|
| 1075 | |
|---|
| 1076 | ;;;; format directives for capitalization |
|---|
| 1077 | |
|---|
| 1078 | (def-complex-format-directive #\( (colonp atsignp params directives) |
|---|
| 1079 | (let ((close (find-directive directives #\) nil))) |
|---|
| 1080 | (unless close |
|---|
| 1081 | (error 'format-error |
|---|
| 1082 | :complaint "no corresponding close parenthesis")) |
|---|
| 1083 | (let* ((posn (position close directives)) |
|---|
| 1084 | (before (subseq directives 0 posn)) |
|---|
| 1085 | (after (nthcdr (1+ posn) directives))) |
|---|
| 1086 | (values |
|---|
| 1087 | (expand-bind-defaults () params |
|---|
| 1088 | `(let ((stream (sys::make-case-frob-stream (if (typep stream 'xp::xp-structure) |
|---|
| 1089 | (xp::base-stream stream) |
|---|
| 1090 | stream) |
|---|
| 1091 | ,(if colonp |
|---|
| 1092 | (if atsignp |
|---|
| 1093 | :upcase |
|---|
| 1094 | :capitalize) |
|---|
| 1095 | (if atsignp |
|---|
| 1096 | :capitalize-first |
|---|
| 1097 | :downcase))))) |
|---|
| 1098 | ,@(expand-directive-list before))) |
|---|
| 1099 | after)))) |
|---|
| 1100 | |
|---|
| 1101 | (def-complex-format-directive #\) () |
|---|
| 1102 | (error 'format-error |
|---|
| 1103 | :complaint "no corresponding open parenthesis")) |
|---|
| 1104 | |
|---|
| 1105 | ;;;; format directives and support functions for conditionalization |
|---|
| 1106 | |
|---|
| 1107 | (def-complex-format-directive #\[ (colonp atsignp params directives) |
|---|
| 1108 | (multiple-value-bind (sublists last-semi-with-colon-p remaining) |
|---|
| 1109 | (parse-conditional-directive directives) |
|---|
| 1110 | (values |
|---|
| 1111 | (if atsignp |
|---|
| 1112 | (if colonp |
|---|
| 1113 | (error 'format-error |
|---|
| 1114 | :complaint |
|---|
| 1115 | "both colon and atsign modifiers used simultaneously") |
|---|
| 1116 | (if (cdr sublists) |
|---|
| 1117 | (error 'format-error |
|---|
| 1118 | :complaint |
|---|
| 1119 | "Can only specify one section") |
|---|
| 1120 | (expand-bind-defaults () params |
|---|
| 1121 | (expand-maybe-conditional (car sublists))))) |
|---|
| 1122 | (if colonp |
|---|
| 1123 | (if (= (length sublists) 2) |
|---|
| 1124 | (expand-bind-defaults () params |
|---|
| 1125 | (expand-true-false-conditional (car sublists) |
|---|
| 1126 | (cadr sublists))) |
|---|
| 1127 | (error 'format-error |
|---|
| 1128 | :complaint |
|---|
| 1129 | "must specify exactly two sections")) |
|---|
| 1130 | (expand-bind-defaults ((index nil)) params |
|---|
| 1131 | (setf *only-simple-args* nil) |
|---|
| 1132 | (let ((clauses nil) |
|---|
| 1133 | (case `(or ,index ,(expand-next-arg)))) |
|---|
| 1134 | (when last-semi-with-colon-p |
|---|
| 1135 | (push `(t ,@(expand-directive-list (pop sublists))) |
|---|
| 1136 | clauses)) |
|---|
| 1137 | (let ((count (length sublists))) |
|---|
| 1138 | (dolist (sublist sublists) |
|---|
| 1139 | (push `(,(decf count) |
|---|
| 1140 | ,@(expand-directive-list sublist)) |
|---|
| 1141 | clauses))) |
|---|
| 1142 | `(case ,case ,@clauses))))) |
|---|
| 1143 | remaining))) |
|---|
| 1144 | |
|---|
| 1145 | (defun parse-conditional-directive (directives) |
|---|
| 1146 | (let ((sublists nil) |
|---|
| 1147 | (last-semi-with-colon-p nil) |
|---|
| 1148 | (remaining directives)) |
|---|
| 1149 | (loop |
|---|
| 1150 | (let ((close-or-semi (find-directive remaining #\] t))) |
|---|
| 1151 | (unless close-or-semi |
|---|
| 1152 | (error 'format-error |
|---|
| 1153 | :complaint "no corresponding close bracket")) |
|---|
| 1154 | (let ((posn (position close-or-semi remaining))) |
|---|
| 1155 | (push (subseq remaining 0 posn) sublists) |
|---|
| 1156 | (setf remaining (nthcdr (1+ posn) remaining)) |
|---|
| 1157 | (when (char= (format-directive-character close-or-semi) #\]) |
|---|
| 1158 | (return)) |
|---|
| 1159 | (setf last-semi-with-colon-p |
|---|
| 1160 | (format-directive-colonp close-or-semi))))) |
|---|
| 1161 | (values sublists last-semi-with-colon-p remaining))) |
|---|
| 1162 | |
|---|
| 1163 | (defun expand-maybe-conditional (sublist) |
|---|
| 1164 | (flet ((hairy () |
|---|
| 1165 | `(let ((prev-args args) |
|---|
| 1166 | (arg ,(expand-next-arg))) |
|---|
| 1167 | (when arg |
|---|
| 1168 | (setf args prev-args) |
|---|
| 1169 | ,@(expand-directive-list sublist))))) |
|---|
| 1170 | (if *only-simple-args* |
|---|
| 1171 | (multiple-value-bind (guts new-args) |
|---|
| 1172 | (let ((*simple-args* *simple-args*)) |
|---|
| 1173 | (values (expand-directive-list sublist) |
|---|
| 1174 | *simple-args*)) |
|---|
| 1175 | (cond ((and new-args (eq *simple-args* (cdr new-args))) |
|---|
| 1176 | (setf *simple-args* new-args) |
|---|
| 1177 | `(when ,(caar new-args) |
|---|
| 1178 | ,@guts)) |
|---|
| 1179 | (t |
|---|
| 1180 | (setf *only-simple-args* nil) |
|---|
| 1181 | (hairy)))) |
|---|
| 1182 | (hairy)))) |
|---|
| 1183 | |
|---|
| 1184 | (defun expand-true-false-conditional (true false) |
|---|
| 1185 | (let ((arg (expand-next-arg))) |
|---|
| 1186 | (flet ((hairy () |
|---|
| 1187 | `(if ,arg |
|---|
| 1188 | (progn |
|---|
| 1189 | ,@(expand-directive-list true)) |
|---|
| 1190 | (progn |
|---|
| 1191 | ,@(expand-directive-list false))))) |
|---|
| 1192 | (if *only-simple-args* |
|---|
| 1193 | (multiple-value-bind (true-guts true-args true-simple) |
|---|
| 1194 | (let ((*simple-args* *simple-args*) |
|---|
| 1195 | (*only-simple-args* t)) |
|---|
| 1196 | (values (expand-directive-list true) |
|---|
| 1197 | *simple-args* |
|---|
| 1198 | *only-simple-args*)) |
|---|
| 1199 | (multiple-value-bind (false-guts false-args false-simple) |
|---|
| 1200 | (let ((*simple-args* *simple-args*) |
|---|
| 1201 | (*only-simple-args* t)) |
|---|
| 1202 | (values (expand-directive-list false) |
|---|
| 1203 | *simple-args* |
|---|
| 1204 | *only-simple-args*)) |
|---|
| 1205 | (if (= (length true-args) (length false-args)) |
|---|
| 1206 | `(if ,arg |
|---|
| 1207 | (progn |
|---|
| 1208 | ,@true-guts) |
|---|
| 1209 | ,(do ((false false-args (cdr false)) |
|---|
| 1210 | (true true-args (cdr true)) |
|---|
| 1211 | (bindings nil (cons `(,(caar false) ,(caar true)) |
|---|
| 1212 | bindings))) |
|---|
| 1213 | ((eq true *simple-args*) |
|---|
| 1214 | (setf *simple-args* true-args) |
|---|
| 1215 | (setf *only-simple-args* |
|---|
| 1216 | (and true-simple false-simple)) |
|---|
| 1217 | (if bindings |
|---|
| 1218 | `(let ,bindings |
|---|
| 1219 | ,@false-guts) |
|---|
| 1220 | `(progn |
|---|
| 1221 | ,@false-guts))))) |
|---|
| 1222 | (progn |
|---|
| 1223 | (setf *only-simple-args* nil) |
|---|
| 1224 | (hairy))))) |
|---|
| 1225 | (hairy))))) |
|---|
| 1226 | |
|---|
| 1227 | (def-complex-format-directive #\; () |
|---|
| 1228 | (error 'format-error |
|---|
| 1229 | :complaint |
|---|
| 1230 | "~~; directive not contained within either ~~[...~~] or ~~<...~~>")) |
|---|
| 1231 | |
|---|
| 1232 | (def-complex-format-directive #\] () |
|---|
| 1233 | (error 'format-error |
|---|
| 1234 | :complaint |
|---|
| 1235 | "no corresponding open bracket")) |
|---|
| 1236 | |
|---|
| 1237 | ;;;; format directive for up-and-out |
|---|
| 1238 | |
|---|
| 1239 | (def-format-directive #\^ (colonp atsignp params) |
|---|
| 1240 | (when atsignp |
|---|
| 1241 | (error 'format-error |
|---|
| 1242 | :complaint "cannot use the at-sign modifier with this directive")) |
|---|
| 1243 | (when (and colonp (not *up-up-and-out-allowed*)) |
|---|
| 1244 | (error 'format-error |
|---|
| 1245 | :complaint "attempt to use ~~:^ outside a ~~:{...~~} construct")) |
|---|
| 1246 | `(when ,(expand-bind-defaults ((arg1 nil) (arg2 nil) (arg3 nil)) params |
|---|
| 1247 | `(cond (,arg3 (<= ,arg1 ,arg2 ,arg3)) |
|---|
| 1248 | (,arg2 (eql ,arg1 ,arg2)) |
|---|
| 1249 | (,arg1 (eql ,arg1 0)) |
|---|
| 1250 | (t ,(if colonp |
|---|
| 1251 | '(null outside-args) |
|---|
| 1252 | (progn |
|---|
| 1253 | (setf *only-simple-args* nil) |
|---|
| 1254 | '(null args)))))) |
|---|
| 1255 | ,(if colonp |
|---|
| 1256 | '(return-from outside-loop nil) |
|---|
| 1257 | '(return)))) |
|---|
| 1258 | |
|---|
| 1259 | ;;;; format directives for iteration |
|---|
| 1260 | |
|---|
| 1261 | (def-complex-format-directive #\{ (colonp atsignp params string end directives) |
|---|
| 1262 | (let ((close (find-directive directives #\} nil))) |
|---|
| 1263 | (unless close |
|---|
| 1264 | (error 'format-error |
|---|
| 1265 | :complaint "no corresponding close brace")) |
|---|
| 1266 | (let* ((closed-with-colon (format-directive-colonp close)) |
|---|
| 1267 | (posn (position close directives))) |
|---|
| 1268 | (labels |
|---|
| 1269 | ((compute-insides () |
|---|
| 1270 | (if (zerop posn) |
|---|
| 1271 | (if *orig-args-available* |
|---|
| 1272 | `((handler-bind |
|---|
| 1273 | ((format-error |
|---|
| 1274 | (lambda (condition) |
|---|
| 1275 | (error 'format-error |
|---|
| 1276 | :complaint |
|---|
| 1277 | "~A~%while processing indirect format string:" |
|---|
| 1278 | :args (list condition) |
|---|
| 1279 | :print-banner nil |
|---|
| 1280 | :control-string ,string |
|---|
| 1281 | :offset ,(1- end))))) |
|---|
| 1282 | (setf args |
|---|
| 1283 | (%format stream inside-string orig-args args)))) |
|---|
| 1284 | (throw 'need-orig-args nil)) |
|---|
| 1285 | (let ((*up-up-and-out-allowed* colonp)) |
|---|
| 1286 | (expand-directive-list (subseq directives 0 posn))))) |
|---|
| 1287 | (compute-loop (count) |
|---|
| 1288 | (when atsignp |
|---|
| 1289 | (setf *only-simple-args* nil)) |
|---|
| 1290 | `(loop |
|---|
| 1291 | ,@(unless closed-with-colon |
|---|
| 1292 | '((when (null args) |
|---|
| 1293 | (return)))) |
|---|
| 1294 | ,@(when count |
|---|
| 1295 | `((when (and ,count (minusp (decf ,count))) |
|---|
| 1296 | (return)))) |
|---|
| 1297 | ,@(if colonp |
|---|
| 1298 | (let ((*expander-next-arg-macro* 'expander-next-arg) |
|---|
| 1299 | (*only-simple-args* nil) |
|---|
| 1300 | (*orig-args-available* t)) |
|---|
| 1301 | `((let* ((orig-args ,(expand-next-arg)) |
|---|
| 1302 | (outside-args args) |
|---|
| 1303 | (args orig-args)) |
|---|
| 1304 | (declare (ignorable orig-args outside-args args)) |
|---|
| 1305 | (block nil |
|---|
| 1306 | ,@(compute-insides))))) |
|---|
| 1307 | (compute-insides)) |
|---|
| 1308 | ,@(when closed-with-colon |
|---|
| 1309 | '((when (null args) |
|---|
| 1310 | (return)))))) |
|---|
| 1311 | (compute-block (count) |
|---|
| 1312 | (if colonp |
|---|
| 1313 | `(block outside-loop |
|---|
| 1314 | ,(compute-loop count)) |
|---|
| 1315 | (compute-loop count))) |
|---|
| 1316 | (compute-bindings (count) |
|---|
| 1317 | (if atsignp |
|---|
| 1318 | (compute-block count) |
|---|
| 1319 | `(let* ((orig-args ,(expand-next-arg)) |
|---|
| 1320 | (args orig-args)) |
|---|
| 1321 | (declare (ignorable orig-args args)) |
|---|
| 1322 | ,(let ((*expander-next-arg-macro* 'expander-next-arg) |
|---|
| 1323 | (*only-simple-args* nil) |
|---|
| 1324 | (*orig-args-available* t)) |
|---|
| 1325 | (compute-block count)))))) |
|---|
| 1326 | (values (if params |
|---|
| 1327 | (expand-bind-defaults ((count nil)) params |
|---|
| 1328 | (if (zerop posn) |
|---|
| 1329 | `(let ((inside-string ,(expand-next-arg))) |
|---|
| 1330 | ,(compute-bindings count)) |
|---|
| 1331 | (compute-bindings count))) |
|---|
| 1332 | (if (zerop posn) |
|---|
| 1333 | `(let ((inside-string ,(expand-next-arg))) |
|---|
| 1334 | ,(compute-bindings nil)) |
|---|
| 1335 | (compute-bindings nil))) |
|---|
| 1336 | (nthcdr (1+ posn) directives)))))) |
|---|
| 1337 | |
|---|
| 1338 | (def-complex-format-directive #\} () |
|---|
| 1339 | (error 'format-error |
|---|
| 1340 | :complaint "no corresponding open brace")) |
|---|
| 1341 | |
|---|
| 1342 | ;;;; format directives and support functions for justification |
|---|
| 1343 | |
|---|
| 1344 | (defparameter *illegal-inside-justification* |
|---|
| 1345 | (mapcar (lambda (x) (parse-directive x 0)) |
|---|
| 1346 | '("~W" "~:W" "~@W" "~:@W" |
|---|
| 1347 | "~_" "~:_" "~@_" "~:@_" |
|---|
| 1348 | "~:>" "~:@>" |
|---|
| 1349 | "~I" "~:I" "~@I" "~:@I" |
|---|
| 1350 | "~:T" "~:@T"))) |
|---|
| 1351 | |
|---|
| 1352 | (defun illegal-inside-justification-p (directive) |
|---|
| 1353 | (member directive *illegal-inside-justification* |
|---|
| 1354 | :test (lambda (x y) |
|---|
| 1355 | (and (format-directive-p x) |
|---|
| 1356 | (format-directive-p y) |
|---|
| 1357 | (eql (format-directive-character x) (format-directive-character y)) |
|---|
| 1358 | (eql (format-directive-colonp x) (format-directive-colonp y)) |
|---|
| 1359 | (eql (format-directive-atsignp x) (format-directive-atsignp y)))))) |
|---|
| 1360 | |
|---|
| 1361 | (def-complex-format-directive #\< (colonp atsignp params string end directives) |
|---|
| 1362 | (multiple-value-bind (segments first-semi close remaining) |
|---|
| 1363 | (parse-format-justification directives) |
|---|
| 1364 | (values |
|---|
| 1365 | (if (format-directive-colonp close) |
|---|
| 1366 | (multiple-value-bind (prefix per-line-p insides suffix) |
|---|
| 1367 | (parse-format-logical-block segments colonp first-semi |
|---|
| 1368 | close params string end) |
|---|
| 1369 | (expand-format-logical-block prefix per-line-p insides |
|---|
| 1370 | suffix atsignp)) |
|---|
| 1371 | (let ((count (reduce #'+ (mapcar (lambda (x) (count-if #'illegal-inside-justification-p x)) segments)))) |
|---|
| 1372 | (when (> count 0) |
|---|
| 1373 | ;; ANSI specifies that "an error is signalled" in this |
|---|
| 1374 | ;; situation. |
|---|
| 1375 | (error 'format-error |
|---|
| 1376 | :complaint "~D illegal directive~:P found inside justification block" |
|---|
| 1377 | :args (list count))) |
|---|
| 1378 | (expand-format-justification segments colonp atsignp |
|---|
| 1379 | first-semi params))) |
|---|
| 1380 | remaining))) |
|---|
| 1381 | |
|---|
| 1382 | (def-complex-format-directive #\> () |
|---|
| 1383 | (error 'format-error |
|---|
| 1384 | :complaint "no corresponding open bracket")) |
|---|
| 1385 | |
|---|
| 1386 | (defun parse-format-logical-block |
|---|
| 1387 | (segments colonp first-semi close params string end) |
|---|
| 1388 | (when params |
|---|
| 1389 | (error 'format-error |
|---|
| 1390 | :complaint "No parameters can be supplied with ~~<...~~:>." |
|---|
| 1391 | :offset (caar params))) |
|---|
| 1392 | (multiple-value-bind (prefix insides suffix) |
|---|
| 1393 | (multiple-value-bind (prefix-default suffix-default) |
|---|
| 1394 | (if colonp (values "(" ")") (values "" "")) |
|---|
| 1395 | (flet ((extract-string (list prefix-p) |
|---|
| 1396 | (let ((directive (find-if #'format-directive-p list))) |
|---|
| 1397 | (if directive |
|---|
| 1398 | (error 'format-error |
|---|
| 1399 | :complaint |
|---|
| 1400 | "cannot include format directives inside the ~ |
|---|
| 1401 | ~:[suffix~;prefix~] segment of ~~<...~~:>" |
|---|
| 1402 | :args (list prefix-p) |
|---|
| 1403 | :offset (1- (format-directive-end directive))) |
|---|
| 1404 | (apply #'concatenate 'string list))))) |
|---|
| 1405 | (case (length segments) |
|---|
| 1406 | (0 (values prefix-default nil suffix-default)) |
|---|
| 1407 | (1 (values prefix-default (car segments) suffix-default)) |
|---|
| 1408 | (2 (values (extract-string (car segments) t) |
|---|
| 1409 | (cadr segments) suffix-default)) |
|---|
| 1410 | (3 (values (extract-string (car segments) t) |
|---|
| 1411 | (cadr segments) |
|---|
| 1412 | (extract-string (caddr segments) nil))) |
|---|
| 1413 | (t |
|---|
| 1414 | (error 'format-error |
|---|
| 1415 | :complaint "too many segments for ~~<...~~:>"))))) |
|---|
| 1416 | (when (format-directive-atsignp close) |
|---|
| 1417 | (setf insides |
|---|
| 1418 | (add-fill-style-newlines insides |
|---|
| 1419 | string |
|---|
| 1420 | (if first-semi |
|---|
| 1421 | (format-directive-end first-semi) |
|---|
| 1422 | end)))) |
|---|
| 1423 | (values prefix |
|---|
| 1424 | (and first-semi (format-directive-atsignp first-semi)) |
|---|
| 1425 | insides |
|---|
| 1426 | suffix))) |
|---|
| 1427 | |
|---|
| 1428 | (defun add-fill-style-newlines (list string offset &optional last-directive) |
|---|
| 1429 | (cond |
|---|
| 1430 | (list |
|---|
| 1431 | (let ((directive (car list))) |
|---|
| 1432 | (cond |
|---|
| 1433 | ((simple-string-p directive) |
|---|
| 1434 | (let* ((non-space (position #\Space directive :test #'char/=)) |
|---|
| 1435 | (newlinep (and last-directive |
|---|
| 1436 | (char= |
|---|
| 1437 | (format-directive-character last-directive) |
|---|
| 1438 | #\Newline)))) |
|---|
| 1439 | (cond |
|---|
| 1440 | ((and newlinep non-space) |
|---|
| 1441 | (nconc |
|---|
| 1442 | (list (subseq directive 0 non-space)) |
|---|
| 1443 | (add-fill-style-newlines-aux |
|---|
| 1444 | (subseq directive non-space) string (+ offset non-space)) |
|---|
| 1445 | (add-fill-style-newlines |
|---|
| 1446 | (cdr list) string (+ offset (length directive))))) |
|---|
| 1447 | (newlinep |
|---|
| 1448 | (cons directive |
|---|
| 1449 | (add-fill-style-newlines |
|---|
| 1450 | (cdr list) string (+ offset (length directive))))) |
|---|
| 1451 | (t |
|---|
| 1452 | (nconc (add-fill-style-newlines-aux directive string offset) |
|---|
| 1453 | (add-fill-style-newlines |
|---|
| 1454 | (cdr list) string (+ offset (length directive)))))))) |
|---|
| 1455 | (t |
|---|
| 1456 | (cons directive |
|---|
| 1457 | (add-fill-style-newlines |
|---|
| 1458 | (cdr list) string |
|---|
| 1459 | (format-directive-end directive) directive)))))) |
|---|
| 1460 | (t nil))) |
|---|
| 1461 | |
|---|
| 1462 | (defun add-fill-style-newlines-aux (literal string offset) |
|---|
| 1463 | (let ((end (length literal)) |
|---|
| 1464 | (posn 0)) |
|---|
| 1465 | (collect ((results)) |
|---|
| 1466 | (loop |
|---|
| 1467 | (let ((blank (position #\space literal :start posn))) |
|---|
| 1468 | (when (null blank) |
|---|
| 1469 | (results (subseq literal posn)) |
|---|
| 1470 | (return)) |
|---|
| 1471 | (let ((non-blank (or (position #\space literal :start blank |
|---|
| 1472 | :test #'char/=) |
|---|
| 1473 | end))) |
|---|
| 1474 | (results (subseq literal posn non-blank)) |
|---|
| 1475 | (results (make-format-directive |
|---|
| 1476 | :string string :character #\_ |
|---|
| 1477 | :start (+ offset non-blank) :end (+ offset non-blank) |
|---|
| 1478 | :colonp t :atsignp nil :params nil)) |
|---|
| 1479 | (setf posn non-blank)) |
|---|
| 1480 | (when (= posn end) |
|---|
| 1481 | (return)))) |
|---|
| 1482 | (results)))) |
|---|
| 1483 | |
|---|
| 1484 | (defun parse-format-justification (directives) |
|---|
| 1485 | (let ((first-semi nil) |
|---|
| 1486 | (close nil) |
|---|
| 1487 | (remaining directives)) |
|---|
| 1488 | (collect ((segments)) |
|---|
| 1489 | (loop |
|---|
| 1490 | (let ((close-or-semi (find-directive remaining #\> t))) |
|---|
| 1491 | (unless close-or-semi |
|---|
| 1492 | (error 'format-error |
|---|
| 1493 | :complaint "no corresponding close bracket")) |
|---|
| 1494 | (let ((posn (position close-or-semi remaining))) |
|---|
| 1495 | (segments (subseq remaining 0 posn)) |
|---|
| 1496 | (setf remaining (nthcdr (1+ posn) remaining))) |
|---|
| 1497 | (when (char= (format-directive-character close-or-semi) |
|---|
| 1498 | #\>) |
|---|
| 1499 | (setf close close-or-semi) |
|---|
| 1500 | (return)) |
|---|
| 1501 | (unless first-semi |
|---|
| 1502 | (setf first-semi close-or-semi)))) |
|---|
| 1503 | (values (segments) first-semi close remaining)))) |
|---|
| 1504 | |
|---|
| 1505 | (defmacro expander-pprint-next-arg (string offset) |
|---|
| 1506 | `(progn |
|---|
| 1507 | (when (null args) |
|---|
| 1508 | (error 'format-error |
|---|
| 1509 | :complaint "no more arguments" |
|---|
| 1510 | :control-string ,string |
|---|
| 1511 | :offset ,offset)) |
|---|
| 1512 | (pprint-pop) |
|---|
| 1513 | (pop args))) |
|---|
| 1514 | |
|---|
| 1515 | (defun expand-format-logical-block (prefix per-line-p insides suffix atsignp) |
|---|
| 1516 | `(let ((arg ,(if atsignp 'args (expand-next-arg)))) |
|---|
| 1517 | ,@(when atsignp |
|---|
| 1518 | (setf *only-simple-args* nil) |
|---|
| 1519 | '((setf args nil))) |
|---|
| 1520 | (pprint-logical-block |
|---|
| 1521 | (stream arg |
|---|
| 1522 | ,(if per-line-p :per-line-prefix :prefix) ,prefix |
|---|
| 1523 | :suffix ,suffix) |
|---|
| 1524 | (let ((args arg) |
|---|
| 1525 | ,@(unless atsignp |
|---|
| 1526 | `((orig-args arg)))) |
|---|
| 1527 | (declare (ignorable args ,@(unless atsignp '(orig-args)))) |
|---|
| 1528 | (block nil |
|---|
| 1529 | ,@(let ((*expander-next-arg-macro* 'expander-pprint-next-arg) |
|---|
| 1530 | (*only-simple-args* nil) |
|---|
| 1531 | (*orig-args-available* |
|---|
| 1532 | (if atsignp *orig-args-available* t))) |
|---|
| 1533 | (expand-directive-list insides))))))) |
|---|
| 1534 | |
|---|
| 1535 | (defun expand-format-justification (segments colonp atsignp first-semi params) |
|---|
| 1536 | (let ((newline-segment-p |
|---|
| 1537 | (and first-semi |
|---|
| 1538 | (format-directive-colonp first-semi)))) |
|---|
| 1539 | (expand-bind-defaults |
|---|
| 1540 | ((mincol 0) (colinc 1) (minpad 0) (padchar #\space)) |
|---|
| 1541 | params |
|---|
| 1542 | `(let ((segments nil) |
|---|
| 1543 | ,@(when newline-segment-p |
|---|
| 1544 | '((newline-segment nil) |
|---|
| 1545 | (extra-space 0) |
|---|
| 1546 | (line-len 72)))) |
|---|
| 1547 | (block nil |
|---|
| 1548 | ,@(when newline-segment-p |
|---|
| 1549 | `((setf newline-segment |
|---|
| 1550 | (with-output-to-string (stream) |
|---|
| 1551 | ,@(expand-directive-list (pop segments)))) |
|---|
| 1552 | ,(expand-bind-defaults |
|---|
| 1553 | ((extra 0) |
|---|
| 1554 | (line-len '(or #-abcl(sb!impl::line-length stream) 72))) |
|---|
| 1555 | (format-directive-params first-semi) |
|---|
| 1556 | `(setf extra-space ,extra line-len ,line-len)))) |
|---|
| 1557 | ,@(mapcar (lambda (segment) |
|---|
| 1558 | `(push (with-output-to-string (stream) |
|---|
| 1559 | ,@(expand-directive-list segment)) |
|---|
| 1560 | segments)) |
|---|
| 1561 | segments)) |
|---|
| 1562 | (format-justification stream |
|---|
| 1563 | ,@(if newline-segment-p |
|---|
| 1564 | '(newline-segment extra-space line-len) |
|---|
| 1565 | '(nil 0 0)) |
|---|
| 1566 | segments ,colonp ,atsignp |
|---|
| 1567 | ,mincol ,colinc ,minpad ,padchar))))) |
|---|
| 1568 | |
|---|
| 1569 | ;;;; format directive and support function for user-defined method |
|---|
| 1570 | |
|---|
| 1571 | (def-format-directive #\/ (string start end colonp atsignp params) |
|---|
| 1572 | (let ((symbol (extract-user-fun-name string start end))) |
|---|
| 1573 | (collect ((param-names) (bindings)) |
|---|
| 1574 | (dolist (param-and-offset params) |
|---|
| 1575 | (let ((param (cdr param-and-offset))) |
|---|
| 1576 | (let ((param-name (gensym))) |
|---|
| 1577 | (param-names param-name) |
|---|
| 1578 | (bindings `(,param-name |
|---|
| 1579 | ,(case param |
|---|
| 1580 | (:arg (expand-next-arg)) |
|---|
| 1581 | (:remaining '(length args)) |
|---|
| 1582 | (t param))))))) |
|---|
| 1583 | `(let ,(bindings) |
|---|
| 1584 | (,symbol stream ,(expand-next-arg) ,colonp ,atsignp |
|---|
| 1585 | ,@(param-names)))))) |
|---|
| 1586 | |
|---|
| 1587 | (defun extract-user-fun-name (string start end) |
|---|
| 1588 | (let ((slash (position #\/ string :start start :end (1- end) |
|---|
| 1589 | :from-end t))) |
|---|
| 1590 | (unless slash |
|---|
| 1591 | (error 'format-error |
|---|
| 1592 | :complaint "malformed ~~/ directive")) |
|---|
| 1593 | (let* ((name (string-upcase (let ((foo string)) |
|---|
| 1594 | ;; Hack alert: This is to keep the compiler |
|---|
| 1595 | ;; quiet about deleting code inside the |
|---|
| 1596 | ;; subseq expansion. |
|---|
| 1597 | (subseq foo (1+ slash) (1- end))))) |
|---|
| 1598 | (first-colon (position #\: name)) |
|---|
| 1599 | (second-colon (if first-colon (position #\: name :start (1+ first-colon)))) |
|---|
| 1600 | (package-name (if first-colon |
|---|
| 1601 | (subseq name 0 first-colon) |
|---|
| 1602 | "COMMON-LISP-USER")) |
|---|
| 1603 | (package (find-package package-name))) |
|---|
| 1604 | (unless package |
|---|
| 1605 | ;; FIXME: should be PACKAGE-ERROR? Could we just use |
|---|
| 1606 | ;; FIND-UNDELETED-PACKAGE-OR-LOSE? |
|---|
| 1607 | (error 'format-error |
|---|
| 1608 | :complaint "no package named ~S" |
|---|
| 1609 | :args (list package-name))) |
|---|
| 1610 | (intern (cond |
|---|
| 1611 | ((and second-colon (= second-colon (1+ first-colon))) |
|---|
| 1612 | (subseq name (1+ second-colon))) |
|---|
| 1613 | (first-colon |
|---|
| 1614 | (subseq name (1+ first-colon))) |
|---|
| 1615 | (t name)) |
|---|
| 1616 | package)))) |
|---|
| 1617 | |
|---|
| 1618 | ;;; compile-time checking for argument mismatch. This code is |
|---|
| 1619 | ;;; inspired by that of Gerd Moellmann, and comes decorated with |
|---|
| 1620 | ;;; FIXMEs: |
|---|
| 1621 | (defun %compiler-walk-format-string (string args) |
|---|
| 1622 | (declare (type simple-string string)) |
|---|
| 1623 | (let ((*default-format-error-control-string* string)) |
|---|
| 1624 | (macrolet ((incf-both (&optional (increment 1)) |
|---|
| 1625 | `(progn |
|---|
| 1626 | (incf min ,increment) |
|---|
| 1627 | (incf max ,increment))) |
|---|
| 1628 | (walk-complex-directive (function) |
|---|
| 1629 | `(multiple-value-bind (min-inc max-inc remaining) |
|---|
| 1630 | (,function directive directives args) |
|---|
| 1631 | (incf min min-inc) |
|---|
| 1632 | (incf max max-inc) |
|---|
| 1633 | (setq directives remaining)))) |
|---|
| 1634 | ;; FIXME: these functions take a list of arguments as well as |
|---|
| 1635 | ;; the directive stream. This is to enable possibly some |
|---|
| 1636 | ;; limited type checking on FORMAT's arguments, as well as |
|---|
| 1637 | ;; simple argument count mismatch checking: when the minimum and |
|---|
| 1638 | ;; maximum argument counts are the same at a given point, we |
|---|
| 1639 | ;; know which argument is going to be used for a given |
|---|
| 1640 | ;; directive, and some (annotated below) require arguments of |
|---|
| 1641 | ;; particular types. |
|---|
| 1642 | (labels |
|---|
| 1643 | ((walk-justification (justification directives args) |
|---|
| 1644 | (declare (ignore args)) |
|---|
| 1645 | (let ((*default-format-error-offset* |
|---|
| 1646 | (1- (format-directive-end justification)))) |
|---|
| 1647 | (multiple-value-bind (segments first-semi close remaining) |
|---|
| 1648 | (parse-format-justification directives) |
|---|
| 1649 | (declare (ignore segments first-semi)) |
|---|
| 1650 | (cond |
|---|
| 1651 | ((not (format-directive-colonp close)) |
|---|
| 1652 | (values 0 0 directives)) |
|---|
| 1653 | ((format-directive-atsignp justification) |
|---|
| 1654 | (values 0 call-arguments-limit directives)) |
|---|
| 1655 | ;; FIXME: here we could assert that the |
|---|
| 1656 | ;; corresponding argument was a list. |
|---|
| 1657 | (t (values 1 1 remaining)))))) |
|---|
| 1658 | (walk-conditional (conditional directives args) |
|---|
| 1659 | (let ((*default-format-error-offset* |
|---|
| 1660 | (1- (format-directive-end conditional)))) |
|---|
| 1661 | (multiple-value-bind (sublists last-semi-with-colon-p remaining) |
|---|
| 1662 | (parse-conditional-directive directives) |
|---|
| 1663 | (declare (ignore last-semi-with-colon-p)) |
|---|
| 1664 | (let ((sub-max |
|---|
| 1665 | (loop for s in sublists |
|---|
| 1666 | maximize (nth-value |
|---|
| 1667 | 1 (walk-directive-list s args))))) |
|---|
| 1668 | (cond |
|---|
| 1669 | ((format-directive-atsignp conditional) |
|---|
| 1670 | (values 1 (max 1 sub-max) remaining)) |
|---|
| 1671 | ((loop for p in (format-directive-params conditional) |
|---|
| 1672 | thereis (or (integerp (cdr p)) |
|---|
| 1673 | (memq (cdr p) '(:remaining :arg)))) |
|---|
| 1674 | (values 0 sub-max remaining)) |
|---|
| 1675 | ;; FIXME: if not COLONP, then the next argument |
|---|
| 1676 | ;; must be a number. |
|---|
| 1677 | (t (values 1 (1+ sub-max) remaining))))))) |
|---|
| 1678 | (walk-iteration (iteration directives args) |
|---|
| 1679 | (declare (ignore args)) |
|---|
| 1680 | (let ((*default-format-error-offset* |
|---|
| 1681 | (1- (format-directive-end iteration)))) |
|---|
| 1682 | (let* ((close (find-directive directives #\} nil)) |
|---|
| 1683 | (posn (or (position close directives) |
|---|
| 1684 | (error 'format-error |
|---|
| 1685 | :complaint "no corresponding close brace"))) |
|---|
| 1686 | (remaining (nthcdr (1+ posn) directives))) |
|---|
| 1687 | ;; FIXME: if POSN is zero, the next argument must be |
|---|
| 1688 | ;; a format control (either a function or a string). |
|---|
| 1689 | (if (format-directive-atsignp iteration) |
|---|
| 1690 | (values (if (zerop posn) 1 0) |
|---|
| 1691 | call-arguments-limit |
|---|
| 1692 | remaining) |
|---|
| 1693 | ;; FIXME: the argument corresponding to this |
|---|
| 1694 | ;; directive must be a list. |
|---|
| 1695 | (let ((nreq (if (zerop posn) 2 1))) |
|---|
| 1696 | (values nreq nreq remaining)))))) |
|---|
| 1697 | (walk-directive-list (directives args) |
|---|
| 1698 | (let ((min 0) (max 0)) |
|---|
| 1699 | (loop |
|---|
| 1700 | (let ((directive (pop directives))) |
|---|
| 1701 | (when (null directive) |
|---|
| 1702 | (return (values min (min max call-arguments-limit)))) |
|---|
| 1703 | (when (format-directive-p directive) |
|---|
| 1704 | (incf-both (count :arg (format-directive-params directive) |
|---|
| 1705 | :key #'cdr)) |
|---|
| 1706 | (let ((c (format-directive-character directive))) |
|---|
| 1707 | (cond |
|---|
| 1708 | ((find c "ABCDEFGORSWX$/") |
|---|
| 1709 | (incf-both)) |
|---|
| 1710 | ((char= c #\P) |
|---|
| 1711 | (unless (format-directive-colonp directive) |
|---|
| 1712 | (incf-both))) |
|---|
| 1713 | ((or (find c "IT%&|_();>") (char= c #\Newline))) |
|---|
| 1714 | ;; FIXME: check correspondence of ~( and ~) |
|---|
| 1715 | ((char= c #\<) |
|---|
| 1716 | (walk-complex-directive walk-justification)) |
|---|
| 1717 | ((char= c #\[) |
|---|
| 1718 | (walk-complex-directive walk-conditional)) |
|---|
| 1719 | ((char= c #\{) |
|---|
| 1720 | (walk-complex-directive walk-iteration)) |
|---|
| 1721 | ((char= c #\?) |
|---|
| 1722 | ;; FIXME: the argument corresponding to this |
|---|
| 1723 | ;; directive must be a format control. |
|---|
| 1724 | (cond |
|---|
| 1725 | ((format-directive-atsignp directive) |
|---|
| 1726 | (incf min) |
|---|
| 1727 | (setq max call-arguments-limit)) |
|---|
| 1728 | (t (incf-both 2)))) |
|---|
| 1729 | (t (throw 'give-up-format-string-walk nil)))))))))) |
|---|
| 1730 | (catch 'give-up-format-string-walk |
|---|
| 1731 | (let ((directives (tokenize-control-string string))) |
|---|
| 1732 | (walk-directive-list directives args))))))) |
|---|
| 1733 | |
|---|
| 1734 | ;;; From target-format.lisp. |
|---|
| 1735 | |
|---|
| 1736 | (in-package #:format) |
|---|
| 1737 | |
|---|
| 1738 | (defun format (destination control-string &rest format-arguments) |
|---|
| 1739 | (etypecase destination |
|---|
| 1740 | (null |
|---|
| 1741 | (with-output-to-string (stream) |
|---|
| 1742 | (%format stream control-string format-arguments))) |
|---|
| 1743 | (string |
|---|
| 1744 | (with-output-to-string (stream destination) |
|---|
| 1745 | (%format stream control-string format-arguments))) |
|---|
| 1746 | ((member t) |
|---|
| 1747 | (%format *standard-output* control-string format-arguments) |
|---|
| 1748 | nil) |
|---|
| 1749 | ((or stream xp::xp-structure) |
|---|
| 1750 | (%format destination control-string format-arguments) |
|---|
| 1751 | nil))) |
|---|
| 1752 | |
|---|
| 1753 | (defun %format (stream string-or-fun orig-args &optional (args orig-args)) |
|---|
| 1754 | (if (functionp string-or-fun) |
|---|
| 1755 | (apply string-or-fun stream args) |
|---|
| 1756 | (catch 'up-and-out |
|---|
| 1757 | (let* ((string (etypecase string-or-fun |
|---|
| 1758 | (simple-string |
|---|
| 1759 | string-or-fun) |
|---|
| 1760 | (string |
|---|
| 1761 | (coerce string-or-fun 'simple-string)))) |
|---|
| 1762 | (*default-format-error-control-string* string) |
|---|
| 1763 | (*logical-block-popper* nil)) |
|---|
| 1764 | (interpret-directive-list stream (tokenize-control-string string) |
|---|
| 1765 | orig-args args))))) |
|---|
| 1766 | |
|---|
| 1767 | (defun interpret-directive-list (stream directives orig-args args) |
|---|
| 1768 | (if directives |
|---|
| 1769 | (let ((directive (car directives))) |
|---|
| 1770 | (etypecase directive |
|---|
| 1771 | (simple-string |
|---|
| 1772 | (write-string directive stream) |
|---|
| 1773 | (interpret-directive-list stream (cdr directives) orig-args args)) |
|---|
| 1774 | (format-directive |
|---|
| 1775 | (multiple-value-bind (new-directives new-args) |
|---|
| 1776 | (let* ((character (format-directive-character directive)) |
|---|
| 1777 | (function |
|---|
| 1778 | (gethash character *format-directive-interpreters*)) |
|---|
| 1779 | (*default-format-error-offset* |
|---|
| 1780 | (1- (format-directive-end directive)))) |
|---|
| 1781 | (unless function |
|---|
| 1782 | (error 'format-error |
|---|
| 1783 | :complaint "unknown format directive ~@[(character: ~A)~]" |
|---|
| 1784 | :args (list (char-name character)))) |
|---|
| 1785 | (multiple-value-bind (new-directives new-args) |
|---|
| 1786 | (funcall function stream directive |
|---|
| 1787 | (cdr directives) orig-args args) |
|---|
| 1788 | (values new-directives new-args))) |
|---|
| 1789 | (interpret-directive-list stream new-directives |
|---|
| 1790 | orig-args new-args))))) |
|---|
| 1791 | args)) |
|---|
| 1792 | |
|---|
| 1793 | ;;;; FORMAT directive definition macros and runtime support |
|---|
| 1794 | |
|---|
| 1795 | (eval-when (:compile-toplevel :execute) |
|---|
| 1796 | |
|---|
| 1797 | ;;; This macro is used to extract the next argument from the current arg list. |
|---|
| 1798 | ;;; This is the version used by format directive interpreters. |
|---|
| 1799 | (defmacro next-arg (&optional offset) |
|---|
| 1800 | `(progn |
|---|
| 1801 | (when (null args) |
|---|
| 1802 | (error 'format-error |
|---|
| 1803 | :complaint "no more arguments" |
|---|
| 1804 | ,@(when offset |
|---|
| 1805 | `(:offset ,offset)))) |
|---|
| 1806 | (when *logical-block-popper* |
|---|
| 1807 | (funcall *logical-block-popper*)) |
|---|
| 1808 | (pop args))) |
|---|
| 1809 | |
|---|
| 1810 | (defmacro def-complex-format-interpreter (char lambda-list &body body) |
|---|
| 1811 | (let ((defun-name |
|---|
| 1812 | (intern (concatenate 'string |
|---|
| 1813 | (let ((name (char-name char))) |
|---|
| 1814 | (cond (name |
|---|
| 1815 | (string-capitalize name)) |
|---|
| 1816 | (t |
|---|
| 1817 | (string char)))) |
|---|
| 1818 | "-FORMAT-DIRECTIVE-INTERPRETER"))) |
|---|
| 1819 | (directive (gensym)) |
|---|
| 1820 | (directives (if lambda-list (car (last lambda-list)) (gensym)))) |
|---|
| 1821 | `(progn |
|---|
| 1822 | (defun ,defun-name (stream ,directive ,directives orig-args args) |
|---|
| 1823 | (declare (ignorable stream orig-args args)) |
|---|
| 1824 | ,@(if lambda-list |
|---|
| 1825 | `((let ,(mapcar (lambda (var) |
|---|
| 1826 | `(,var |
|---|
| 1827 | (,(sys::symbolicate "FORMAT-DIRECTIVE-" var) |
|---|
| 1828 | ,directive))) |
|---|
| 1829 | (butlast lambda-list)) |
|---|
| 1830 | (values (progn ,@body) args))) |
|---|
| 1831 | `((declare (ignore ,directive ,directives)) |
|---|
| 1832 | ,@body))) |
|---|
| 1833 | (%set-format-directive-interpreter ,char #',defun-name)))) |
|---|
| 1834 | |
|---|
| 1835 | (defmacro def-format-interpreter (char lambda-list &body body) |
|---|
| 1836 | (let ((directives (gensym))) |
|---|
| 1837 | `(def-complex-format-interpreter ,char (,@lambda-list ,directives) |
|---|
| 1838 | ,@body |
|---|
| 1839 | ,directives))) |
|---|
| 1840 | |
|---|
| 1841 | (defmacro interpret-bind-defaults (specs params &body body) |
|---|
| 1842 | (sys::once-only ((params params)) |
|---|
| 1843 | (collect ((bindings)) |
|---|
| 1844 | (dolist (spec specs) |
|---|
| 1845 | (destructuring-bind (var default) spec |
|---|
| 1846 | (bindings `(,var (let* ((param-and-offset (pop ,params)) |
|---|
| 1847 | (offset (car param-and-offset)) |
|---|
| 1848 | (param (cdr param-and-offset))) |
|---|
| 1849 | (case param |
|---|
| 1850 | (:arg (or (next-arg offset) ,default)) |
|---|
| 1851 | (:remaining (length args)) |
|---|
| 1852 | ((nil) ,default) |
|---|
| 1853 | (t param))))))) |
|---|
| 1854 | `(let* ,(bindings) |
|---|
| 1855 | (when ,params |
|---|
| 1856 | (error 'format-error |
|---|
| 1857 | :complaint |
|---|
| 1858 | "too many parameters, expected no more than ~W" |
|---|
| 1859 | :args (list ,(length specs)) |
|---|
| 1860 | :offset (caar ,params))) |
|---|
| 1861 | ,@body)))) |
|---|
| 1862 | |
|---|
| 1863 | ) ; EVAL-WHEN |
|---|
| 1864 | |
|---|
| 1865 | ;;;; format interpreters and support functions for simple output |
|---|
| 1866 | |
|---|
| 1867 | (defun format-write-field (stream string mincol colinc minpad padchar padleft) |
|---|
| 1868 | (unless padleft |
|---|
| 1869 | (write-string string stream)) |
|---|
| 1870 | (dotimes (i minpad) |
|---|
| 1871 | (write-char padchar stream)) |
|---|
| 1872 | ;; As of sbcl-0.6.12.34, we could end up here when someone tries to |
|---|
| 1873 | ;; print e.g. (FORMAT T "~F" "NOTFLOAT"), in which case ANSI says |
|---|
| 1874 | ;; we're supposed to soldier on bravely, and so we have to deal with |
|---|
| 1875 | ;; the unsupplied-MINCOL-and-COLINC case without blowing up. |
|---|
| 1876 | (when (and mincol colinc) |
|---|
| 1877 | (do ((chars (+ (length string) (max minpad 0)) (+ chars colinc))) |
|---|
| 1878 | ((>= chars mincol)) |
|---|
| 1879 | (dotimes (i colinc) |
|---|
| 1880 | (write-char padchar stream)))) |
|---|
| 1881 | (when padleft |
|---|
| 1882 | (write-string string stream))) |
|---|
| 1883 | |
|---|
| 1884 | (defun format-princ (stream arg colonp atsignp mincol colinc minpad padchar) |
|---|
| 1885 | (format-write-field stream |
|---|
| 1886 | (if (or arg (not colonp)) |
|---|
| 1887 | (princ-to-string arg) |
|---|
| 1888 | "()") |
|---|
| 1889 | mincol colinc minpad padchar atsignp)) |
|---|
| 1890 | |
|---|
| 1891 | (def-format-interpreter #\A (colonp atsignp params) |
|---|
| 1892 | (if params |
|---|
| 1893 | (interpret-bind-defaults ((mincol 0) (colinc 1) (minpad 0) |
|---|
| 1894 | (padchar #\space)) |
|---|
| 1895 | params |
|---|
| 1896 | (format-princ stream (next-arg) colonp atsignp |
|---|
| 1897 | mincol colinc minpad padchar)) |
|---|
| 1898 | (princ (if colonp (or (next-arg) "()") (next-arg)) stream))) |
|---|
| 1899 | |
|---|
| 1900 | (defun format-prin1 (stream arg colonp atsignp mincol colinc minpad padchar) |
|---|
| 1901 | (format-write-field stream |
|---|
| 1902 | (if (or arg (not colonp)) |
|---|
| 1903 | (prin1-to-string arg) |
|---|
| 1904 | "()") |
|---|
| 1905 | mincol colinc minpad padchar atsignp)) |
|---|
| 1906 | |
|---|
| 1907 | (def-format-interpreter #\S (colonp atsignp params) |
|---|
| 1908 | (cond (params |
|---|
| 1909 | (interpret-bind-defaults ((mincol 0) (colinc 1) (minpad 0) |
|---|
| 1910 | (padchar #\space)) |
|---|
| 1911 | params |
|---|
| 1912 | (format-prin1 stream (next-arg) colonp atsignp |
|---|
| 1913 | mincol colinc minpad padchar))) |
|---|
| 1914 | (colonp |
|---|
| 1915 | (let ((arg (next-arg))) |
|---|
| 1916 | (if arg |
|---|
| 1917 | (prin1 arg stream) |
|---|
| 1918 | (princ "()" stream)))) |
|---|
| 1919 | (t |
|---|
| 1920 | (prin1 (next-arg) stream)))) |
|---|
| 1921 | |
|---|
| 1922 | (def-format-interpreter #\C (colonp atsignp params) |
|---|
| 1923 | (interpret-bind-defaults () params |
|---|
| 1924 | (if colonp |
|---|
| 1925 | (format-print-named-character (next-arg) stream) |
|---|
| 1926 | (if atsignp |
|---|
| 1927 | (prin1 (next-arg) stream) |
|---|
| 1928 | (write-char (next-arg) stream))))) |
|---|
| 1929 | |
|---|
| 1930 | (defun format-print-named-character (char stream) |
|---|
| 1931 | (let* ((name (char-name char))) |
|---|
| 1932 | (cond (name |
|---|
| 1933 | (write-string (string-capitalize name) stream)) |
|---|
| 1934 | (t |
|---|
| 1935 | (write-char char stream))))) |
|---|
| 1936 | |
|---|
| 1937 | (def-format-interpreter #\W (colonp atsignp params) |
|---|
| 1938 | (interpret-bind-defaults () params |
|---|
| 1939 | (let ((*print-pretty* (or colonp *print-pretty*)) |
|---|
| 1940 | (*print-level* (unless atsignp *print-level*)) |
|---|
| 1941 | (*print-length* (unless atsignp *print-length*))) |
|---|
| 1942 | (sys::output-object (next-arg) stream)))) |
|---|
| 1943 | |
|---|
| 1944 | ;;;; format interpreters and support functions for integer output |
|---|
| 1945 | |
|---|
| 1946 | ;;; FORMAT-PRINT-NUMBER does most of the work for the numeric printing |
|---|
| 1947 | ;;; directives. The parameters are interpreted as defined for ~D. |
|---|
| 1948 | (defun format-print-integer (stream number print-commas-p print-sign-p |
|---|
| 1949 | radix mincol padchar commachar commainterval) |
|---|
| 1950 | (let ((*print-base* radix) |
|---|
| 1951 | (*print-radix* nil)) |
|---|
| 1952 | (if (integerp number) |
|---|
| 1953 | (let* ((text (princ-to-string (abs number))) |
|---|
| 1954 | (commaed (if print-commas-p |
|---|
| 1955 | (format-add-commas text commachar commainterval) |
|---|
| 1956 | text)) |
|---|
| 1957 | (signed (cond ((minusp number) |
|---|
| 1958 | (concatenate 'string "-" commaed)) |
|---|
| 1959 | (print-sign-p |
|---|
| 1960 | (concatenate 'string "+" commaed)) |
|---|
| 1961 | (t commaed)))) |
|---|
| 1962 | ;; colinc = 1, minpad = 0, padleft = t |
|---|
| 1963 | (format-write-field stream signed mincol 1 0 padchar t)) |
|---|
| 1964 | (princ number stream)))) |
|---|
| 1965 | |
|---|
| 1966 | (defun format-add-commas (string commachar commainterval) |
|---|
| 1967 | (let ((length (length string))) |
|---|
| 1968 | (multiple-value-bind (commas extra) (truncate (1- length) commainterval) |
|---|
| 1969 | (let ((new-string (make-string (+ length commas))) |
|---|
| 1970 | (first-comma (1+ extra))) |
|---|
| 1971 | (replace new-string string :end1 first-comma :end2 first-comma) |
|---|
| 1972 | (do ((src first-comma (+ src commainterval)) |
|---|
| 1973 | (dst first-comma (+ dst commainterval 1))) |
|---|
| 1974 | ((= src length)) |
|---|
| 1975 | (setf (schar new-string dst) commachar) |
|---|
| 1976 | (replace new-string string :start1 (1+ dst) |
|---|
| 1977 | :start2 src :end2 (+ src commainterval))) |
|---|
| 1978 | new-string)))) |
|---|
| 1979 | |
|---|
| 1980 | ;;; FIXME: This is only needed in this file, could be defined with |
|---|
| 1981 | ;;; SB!XC:DEFMACRO inside EVAL-WHEN |
|---|
| 1982 | (defmacro interpret-format-integer (base) |
|---|
| 1983 | `(if (or colonp atsignp params) |
|---|
| 1984 | (interpret-bind-defaults |
|---|
| 1985 | ((mincol 0) (padchar #\space) (commachar #\,) (commainterval 3)) |
|---|
| 1986 | params |
|---|
| 1987 | (format-print-integer stream (next-arg) colonp atsignp ,base mincol |
|---|
| 1988 | padchar commachar commainterval)) |
|---|
| 1989 | (write (next-arg) :stream stream :base ,base :radix nil :escape nil))) |
|---|
| 1990 | |
|---|
| 1991 | (def-format-interpreter #\D (colonp atsignp params) |
|---|
| 1992 | (interpret-format-integer 10)) |
|---|
| 1993 | |
|---|
| 1994 | (def-format-interpreter #\B (colonp atsignp params) |
|---|
| 1995 | (interpret-format-integer 2)) |
|---|
| 1996 | |
|---|
| 1997 | (def-format-interpreter #\O (colonp atsignp params) |
|---|
| 1998 | (interpret-format-integer 8)) |
|---|
| 1999 | |
|---|
| 2000 | (def-format-interpreter #\X (colonp atsignp params) |
|---|
| 2001 | (interpret-format-integer 16)) |
|---|
| 2002 | |
|---|
| 2003 | (def-format-interpreter #\R (colonp atsignp params) |
|---|
| 2004 | (interpret-bind-defaults |
|---|
| 2005 | ((base nil) (mincol 0) (padchar #\space) (commachar #\,) |
|---|
| 2006 | (commainterval 3)) |
|---|
| 2007 | params |
|---|
| 2008 | (let ((arg (next-arg))) |
|---|
| 2009 | (if base |
|---|
| 2010 | (format-print-integer stream arg colonp atsignp base mincol |
|---|
| 2011 | padchar commachar commainterval) |
|---|
| 2012 | (if atsignp |
|---|
| 2013 | (if colonp |
|---|
| 2014 | (format-print-old-roman stream arg) |
|---|
| 2015 | (format-print-roman stream arg)) |
|---|
| 2016 | (if colonp |
|---|
| 2017 | (format-print-ordinal stream arg) |
|---|
| 2018 | (format-print-cardinal stream arg))))))) |
|---|
| 2019 | |
|---|
| 2020 | (defparameter *cardinal-ones* |
|---|
| 2021 | #(nil "one" "two" "three" "four" "five" "six" "seven" "eight" "nine")) |
|---|
| 2022 | |
|---|
| 2023 | (defparameter *cardinal-tens* |
|---|
| 2024 | #(nil nil "twenty" "thirty" "forty" |
|---|
| 2025 | "fifty" "sixty" "seventy" "eighty" "ninety")) |
|---|
| 2026 | |
|---|
| 2027 | (defparameter *cardinal-teens* |
|---|
| 2028 | #("ten" "eleven" "twelve" "thirteen" "fourteen" ;;; RAD |
|---|
| 2029 | "fifteen" "sixteen" "seventeen" "eighteen" "nineteen")) |
|---|
| 2030 | |
|---|
| 2031 | (defparameter *cardinal-periods* |
|---|
| 2032 | #("" " thousand" " million" " billion" " trillion" " quadrillion" |
|---|
| 2033 | " quintillion" " sextillion" " septillion" " octillion" " nonillion" |
|---|
| 2034 | " decillion" " undecillion" " duodecillion" " tredecillion" |
|---|
| 2035 | " quattuordecillion" " quindecillion" " sexdecillion" " septendecillion" |
|---|
| 2036 | " octodecillion" " novemdecillion" " vigintillion")) |
|---|
| 2037 | |
|---|
| 2038 | (defparameter *ordinal-ones* |
|---|
| 2039 | #(nil "first" "second" "third" "fourth" |
|---|
| 2040 | "fifth" "sixth" "seventh" "eighth" "ninth")) |
|---|
| 2041 | |
|---|
| 2042 | (defparameter *ordinal-tens* |
|---|
| 2043 | #(nil "tenth" "twentieth" "thirtieth" "fortieth" |
|---|
| 2044 | "fiftieth" "sixtieth" "seventieth" "eightieth" "ninetieth")) |
|---|
| 2045 | |
|---|
| 2046 | (defun format-print-small-cardinal (stream n) |
|---|
| 2047 | (multiple-value-bind (hundreds rem) (truncate n 100) |
|---|
| 2048 | (when (plusp hundreds) |
|---|
| 2049 | (write-string (svref *cardinal-ones* hundreds) stream) |
|---|
| 2050 | (write-string " hundred" stream) |
|---|
| 2051 | (when (plusp rem) |
|---|
| 2052 | (write-char #\space stream))) |
|---|
| 2053 | (when (plusp rem) |
|---|
| 2054 | (multiple-value-bind (tens ones) (truncate rem 10) |
|---|
| 2055 | (cond ((< 1 tens) |
|---|
| 2056 | (write-string (svref *cardinal-tens* tens) stream) |
|---|
| 2057 | (when (plusp ones) |
|---|
| 2058 | (write-char #\- stream) |
|---|
| 2059 | (write-string (svref *cardinal-ones* ones) stream))) |
|---|
| 2060 | ((= tens 1) |
|---|
| 2061 | (write-string (svref *cardinal-teens* ones) stream)) |
|---|
| 2062 | ((plusp ones) |
|---|
| 2063 | (write-string (svref *cardinal-ones* ones) stream))))))) |
|---|
| 2064 | |
|---|
| 2065 | (defun format-print-cardinal (stream n) |
|---|
| 2066 | (cond ((minusp n) |
|---|
| 2067 | (write-string "negative " stream) |
|---|
| 2068 | (format-print-cardinal-aux stream (- n) 0 n)) |
|---|
| 2069 | ((zerop n) |
|---|
| 2070 | (write-string "zero" stream)) |
|---|
| 2071 | (t |
|---|
| 2072 | (format-print-cardinal-aux stream n 0 n)))) |
|---|
| 2073 | |
|---|
| 2074 | (defun format-print-cardinal-aux (stream n period err) |
|---|
| 2075 | (multiple-value-bind (beyond here) (truncate n 1000) |
|---|
| 2076 | (unless (<= period 20) |
|---|
| 2077 | (error "number too large to print in English: ~:D" err)) |
|---|
| 2078 | (unless (zerop beyond) |
|---|
| 2079 | (format-print-cardinal-aux stream beyond (1+ period) err)) |
|---|
| 2080 | (unless (zerop here) |
|---|
| 2081 | (unless (zerop beyond) |
|---|
| 2082 | (write-char #\space stream)) |
|---|
| 2083 | (format-print-small-cardinal stream here) |
|---|
| 2084 | (write-string (svref *cardinal-periods* period) stream)))) |
|---|
| 2085 | |
|---|
| 2086 | (defun format-print-ordinal (stream n) |
|---|
| 2087 | (when (minusp n) |
|---|
| 2088 | (write-string "negative " stream)) |
|---|
| 2089 | (let ((number (abs n))) |
|---|
| 2090 | (multiple-value-bind (top bot) (truncate number 100) |
|---|
| 2091 | (unless (zerop top) |
|---|
| 2092 | (format-print-cardinal stream (- number bot))) |
|---|
| 2093 | (when (and (plusp top) (plusp bot)) |
|---|
| 2094 | (write-char #\space stream)) |
|---|
| 2095 | (multiple-value-bind (tens ones) (truncate bot 10) |
|---|
| 2096 | (cond ((= bot 12) (write-string "twelfth" stream)) |
|---|
| 2097 | ((= tens 1) |
|---|
| 2098 | (write-string (svref *cardinal-teens* ones) stream);;;RAD |
|---|
| 2099 | (write-string "th" stream)) |
|---|
| 2100 | ((and (zerop tens) (plusp ones)) |
|---|
| 2101 | (write-string (svref *ordinal-ones* ones) stream)) |
|---|
| 2102 | ((and (zerop ones)(plusp tens)) |
|---|
| 2103 | (write-string (svref *ordinal-tens* tens) stream)) |
|---|
| 2104 | ((plusp bot) |
|---|
| 2105 | (write-string (svref *cardinal-tens* tens) stream) |
|---|
| 2106 | (write-char #\- stream) |
|---|
| 2107 | (write-string (svref *ordinal-ones* ones) stream)) |
|---|
| 2108 | ((plusp number) |
|---|
| 2109 | (write-string "th" stream)) |
|---|
| 2110 | (t |
|---|
| 2111 | (write-string "zeroth" stream))))))) |
|---|
| 2112 | |
|---|
| 2113 | ;;; Print Roman numerals |
|---|
| 2114 | |
|---|
| 2115 | (defun format-print-old-roman (stream n) |
|---|
| 2116 | (unless (< 0 n 5000) |
|---|
| 2117 | (error "Number too large to print in old Roman numerals: ~:D" n)) |
|---|
| 2118 | (do ((char-list '(#\D #\C #\L #\X #\V #\I) (cdr char-list)) |
|---|
| 2119 | (val-list '(500 100 50 10 5 1) (cdr val-list)) |
|---|
| 2120 | (cur-char #\M (car char-list)) |
|---|
| 2121 | (cur-val 1000 (car val-list)) |
|---|
| 2122 | (start n (do ((i start (progn |
|---|
| 2123 | (write-char cur-char stream) |
|---|
| 2124 | (- i cur-val)))) |
|---|
| 2125 | ((< i cur-val) i)))) |
|---|
| 2126 | ((zerop start)))) |
|---|
| 2127 | |
|---|
| 2128 | (defun format-print-roman (stream n) |
|---|
| 2129 | (unless (< 0 n 4000) |
|---|
| 2130 | (error "Number too large to print in Roman numerals: ~:D" n)) |
|---|
| 2131 | (do ((char-list '(#\D #\C #\L #\X #\V #\I) (cdr char-list)) |
|---|
| 2132 | (val-list '(500 100 50 10 5 1) (cdr val-list)) |
|---|
| 2133 | (sub-chars '(#\C #\X #\X #\I #\I) (cdr sub-chars)) |
|---|
| 2134 | (sub-val '(100 10 10 1 1 0) (cdr sub-val)) |
|---|
| 2135 | (cur-char #\M (car char-list)) |
|---|
| 2136 | (cur-val 1000 (car val-list)) |
|---|
| 2137 | (cur-sub-char #\C (car sub-chars)) |
|---|
| 2138 | (cur-sub-val 100 (car sub-val)) |
|---|
| 2139 | (start n (do ((i start (progn |
|---|
| 2140 | (write-char cur-char stream) |
|---|
| 2141 | (- i cur-val)))) |
|---|
| 2142 | ((< i cur-val) |
|---|
| 2143 | (cond ((<= (- cur-val cur-sub-val) i) |
|---|
| 2144 | (write-char cur-sub-char stream) |
|---|
| 2145 | (write-char cur-char stream) |
|---|
| 2146 | (- i (- cur-val cur-sub-val))) |
|---|
| 2147 | (t i)))))) |
|---|
| 2148 | ((zerop start)))) |
|---|
| 2149 | |
|---|
| 2150 | ;;;; plural |
|---|
| 2151 | |
|---|
| 2152 | (def-format-interpreter #\P (colonp atsignp params) |
|---|
| 2153 | (interpret-bind-defaults () params |
|---|
| 2154 | (let ((arg (if colonp |
|---|
| 2155 | (if (eq orig-args args) |
|---|
| 2156 | (error 'format-error |
|---|
| 2157 | :complaint "no previous argument") |
|---|
| 2158 | (do ((arg-ptr orig-args (cdr arg-ptr))) |
|---|
| 2159 | ((eq (cdr arg-ptr) args) |
|---|
| 2160 | (car arg-ptr)))) |
|---|
| 2161 | (next-arg)))) |
|---|
| 2162 | (if atsignp |
|---|
| 2163 | (write-string (if (eql arg 1) "y" "ies") stream) |
|---|
| 2164 | (unless (eql arg 1) (write-char #\s stream)))))) |
|---|
| 2165 | |
|---|
| 2166 | ;;;; format interpreters and support functions for floating point output |
|---|
| 2167 | |
|---|
| 2168 | (defun decimal-string (n) |
|---|
| 2169 | (write-to-string n :base 10 :radix nil :escape nil)) |
|---|
| 2170 | |
|---|
| 2171 | (def-format-interpreter #\F (colonp atsignp params) |
|---|
| 2172 | (when colonp |
|---|
| 2173 | (error 'format-error |
|---|
| 2174 | :complaint |
|---|
| 2175 | "cannot specify the colon modifier with this directive")) |
|---|
| 2176 | (interpret-bind-defaults ((w nil) (d nil) (k nil) (ovf nil) (pad #\space)) |
|---|
| 2177 | params |
|---|
| 2178 | (format-fixed stream (next-arg) w d k ovf pad atsignp))) |
|---|
| 2179 | |
|---|
| 2180 | (defun format-fixed (stream number w d k ovf pad atsign) |
|---|
| 2181 | (if (numberp number) |
|---|
| 2182 | (if (floatp number) |
|---|
| 2183 | (format-fixed-aux stream number w d k ovf pad atsign) |
|---|
| 2184 | (if (rationalp number) |
|---|
| 2185 | (format-fixed-aux stream |
|---|
| 2186 | (coerce number 'single-float) |
|---|
| 2187 | w d k ovf pad atsign) |
|---|
| 2188 | (format-write-field stream |
|---|
| 2189 | (decimal-string number) |
|---|
| 2190 | w 1 0 #\space t))) |
|---|
| 2191 | (format-princ stream number nil nil w 1 0 pad))) |
|---|
| 2192 | |
|---|
| 2193 | ;;; We return true if we overflowed, so that ~G can output the overflow char |
|---|
| 2194 | ;;; instead of spaces. |
|---|
| 2195 | (defun format-fixed-aux (stream number w d k ovf pad atsign) |
|---|
| 2196 | (cond |
|---|
| 2197 | ((and (floatp number) |
|---|
| 2198 | (or (sys:float-infinity-p number) |
|---|
| 2199 | (sys:float-nan-p number))) |
|---|
| 2200 | (prin1 number stream) |
|---|
| 2201 | nil) |
|---|
| 2202 | (t |
|---|
| 2203 | (let ((spaceleft w)) |
|---|
| 2204 | (when (and w (or atsign (minusp (float-sign number)))) |
|---|
| 2205 | (decf spaceleft)) |
|---|
| 2206 | (multiple-value-bind (str len lpoint tpoint) |
|---|
| 2207 | (sys::flonum-to-string (abs number) spaceleft d k) |
|---|
| 2208 | ;;if caller specifically requested no fraction digits, suppress the |
|---|
| 2209 | ;;optional trailing zero |
|---|
| 2210 | (when (and d (zerop d)) |
|---|
| 2211 | (setf tpoint nil)) |
|---|
| 2212 | (when w |
|---|
| 2213 | (decf spaceleft len) |
|---|
| 2214 | ;;optional leading zero |
|---|
| 2215 | (when lpoint |
|---|
| 2216 | (if (or (> spaceleft 0) tpoint) ;force at least one digit |
|---|
| 2217 | (decf spaceleft) |
|---|
| 2218 | (setq lpoint nil))) |
|---|
| 2219 | ;;optional trailing zero |
|---|
| 2220 | (when tpoint |
|---|
| 2221 | (if (> spaceleft 0) |
|---|
| 2222 | (decf spaceleft) |
|---|
| 2223 | (setq tpoint nil)))) |
|---|
| 2224 | (cond ((and w (< spaceleft 0) ovf) |
|---|
| 2225 | ;;field width overflow |
|---|
| 2226 | (dotimes (i w) (write-char ovf stream)) |
|---|
| 2227 | t) |
|---|
| 2228 | (t |
|---|
| 2229 | (when w (dotimes (i spaceleft) (write-char pad stream))) |
|---|
| 2230 | (cond ((minusp (float-sign number)) |
|---|
| 2231 | (write-char #\- stream)) |
|---|
| 2232 | (atsign |
|---|
| 2233 | (write-char #\+ stream))) |
|---|
| 2234 | (when lpoint (write-char #\0 stream)) |
|---|
| 2235 | (write-string str stream) |
|---|
| 2236 | (when tpoint (write-char #\0 stream)) |
|---|
| 2237 | nil))))))) |
|---|
| 2238 | |
|---|
| 2239 | (def-format-interpreter #\E (colonp atsignp params) |
|---|
| 2240 | (when colonp |
|---|
| 2241 | (error 'format-error |
|---|
| 2242 | :complaint |
|---|
| 2243 | "cannot specify the colon modifier with this directive")) |
|---|
| 2244 | (interpret-bind-defaults |
|---|
| 2245 | ((w nil) (d nil) (e nil) (k 1) (ovf nil) (pad #\space) (mark nil)) |
|---|
| 2246 | params |
|---|
| 2247 | (format-exponential stream (next-arg) w d e k ovf pad mark atsignp))) |
|---|
| 2248 | |
|---|
| 2249 | (defun format-exponential (stream number w d e k ovf pad marker atsign) |
|---|
| 2250 | (if (numberp number) |
|---|
| 2251 | (if (floatp number) |
|---|
| 2252 | (format-exp-aux stream number w d e k ovf pad marker atsign) |
|---|
| 2253 | (if (rationalp number) |
|---|
| 2254 | (format-exp-aux stream |
|---|
| 2255 | (coerce number 'single-float) |
|---|
| 2256 | w d e k ovf pad marker atsign) |
|---|
| 2257 | (format-write-field stream |
|---|
| 2258 | (decimal-string number) |
|---|
| 2259 | w 1 0 #\space t))) |
|---|
| 2260 | (format-princ stream number nil nil w 1 0 pad))) |
|---|
| 2261 | |
|---|
| 2262 | (defun format-exponent-marker (number) |
|---|
| 2263 | (if (typep number *read-default-float-format*) |
|---|
| 2264 | #\e |
|---|
| 2265 | (typecase number |
|---|
| 2266 | (single-float #\f) |
|---|
| 2267 | (double-float #\d) |
|---|
| 2268 | (short-float #\s) |
|---|
| 2269 | (long-float #\l)))) |
|---|
| 2270 | |
|---|
| 2271 | ;;; Here we prevent the scale factor from shifting all significance out of |
|---|
| 2272 | ;;; a number to the right. We allow insignificant zeroes to be shifted in |
|---|
| 2273 | ;;; to the left right, athough it is an error to specify k and d such that this |
|---|
| 2274 | ;;; occurs. Perhaps we should detect both these condtions and flag them as |
|---|
| 2275 | ;;; errors. As for now, we let the user get away with it, and merely guarantee |
|---|
| 2276 | ;;; that at least one significant digit will appear. |
|---|
| 2277 | |
|---|
| 2278 | ;;; Raymond Toy writes: The Hyperspec seems to say that the exponent |
|---|
| 2279 | ;;; marker is always printed. Make it so. Also, the original version |
|---|
| 2280 | ;;; causes errors when printing infinities or NaN's. The Hyperspec is |
|---|
| 2281 | ;;; silent here, so let's just print out infinities and NaN's instead |
|---|
| 2282 | ;;; of causing an error. |
|---|
| 2283 | (defun format-exp-aux (stream number w d e k ovf pad marker atsign) |
|---|
| 2284 | (if (and (floatp number) |
|---|
| 2285 | (or (sys::float-infinity-p number) |
|---|
| 2286 | (sys::float-nan-p number))) |
|---|
| 2287 | (prin1 number stream) |
|---|
| 2288 | (multiple-value-bind (num expt) (sys::scale-exponent (abs number)) |
|---|
| 2289 | (let* ((expt (- expt k)) |
|---|
| 2290 | (estr (decimal-string (abs expt))) |
|---|
| 2291 | (elen (if e (max (length estr) e) (length estr))) |
|---|
| 2292 | (fdig (if d (if (plusp k) (1+ (- d k)) d) nil)) |
|---|
| 2293 | (fmin (if (minusp k) (- 1 k) nil)) |
|---|
| 2294 | (spaceleft (if w |
|---|
| 2295 | (- w 2 elen |
|---|
| 2296 | (if (or atsign (minusp number)) |
|---|
| 2297 | 1 0)) |
|---|
| 2298 | nil))) |
|---|
| 2299 | (if (and w ovf e (> elen e)) ;exponent overflow |
|---|
| 2300 | (dotimes (i w) (write-char ovf stream)) |
|---|
| 2301 | (multiple-value-bind (fstr flen lpoint) |
|---|
| 2302 | (sys::flonum-to-string num spaceleft fdig k fmin) |
|---|
| 2303 | (when w |
|---|
| 2304 | (decf spaceleft flen) |
|---|
| 2305 | (when lpoint |
|---|
| 2306 | (if (> spaceleft 0) |
|---|
| 2307 | (decf spaceleft) |
|---|
| 2308 | (setq lpoint nil)))) |
|---|
| 2309 | (cond ((and w (< spaceleft 0) ovf) |
|---|
| 2310 | ;;significand overflow |
|---|
| 2311 | (dotimes (i w) (write-char ovf stream))) |
|---|
| 2312 | (t (when w |
|---|
| 2313 | (dotimes (i spaceleft) (write-char pad stream))) |
|---|
| 2314 | (if (minusp number) |
|---|
| 2315 | (write-char #\- stream) |
|---|
| 2316 | (if atsign (write-char #\+ stream))) |
|---|
| 2317 | (when lpoint (write-char #\0 stream)) |
|---|
| 2318 | (write-string fstr stream) |
|---|
| 2319 | (write-char (if marker |
|---|
| 2320 | marker |
|---|
| 2321 | (format-exponent-marker number)) |
|---|
| 2322 | stream) |
|---|
| 2323 | (write-char (if (minusp expt) #\- #\+) stream) |
|---|
| 2324 | (when e |
|---|
| 2325 | ;;zero-fill before exponent if necessary |
|---|
| 2326 | (dotimes (i (- e (length estr))) |
|---|
| 2327 | (write-char #\0 stream))) |
|---|
| 2328 | (write-string estr stream))))))))) |
|---|
| 2329 | |
|---|
| 2330 | (def-format-interpreter #\G (colonp atsignp params) |
|---|
| 2331 | (when colonp |
|---|
| 2332 | (error 'format-error |
|---|
| 2333 | :complaint |
|---|
| 2334 | "cannot specify the colon modifier with this directive")) |
|---|
| 2335 | (interpret-bind-defaults |
|---|
| 2336 | ((w nil) (d nil) (e nil) (k nil) (ovf nil) (pad #\space) (mark nil)) |
|---|
| 2337 | params |
|---|
| 2338 | (format-general stream (next-arg) w d e k ovf pad mark atsignp))) |
|---|
| 2339 | |
|---|
| 2340 | (defun format-general (stream number w d e k ovf pad marker atsign) |
|---|
| 2341 | (if (numberp number) |
|---|
| 2342 | (if (floatp number) |
|---|
| 2343 | (format-general-aux stream number w d e k ovf pad marker atsign) |
|---|
| 2344 | (if (rationalp number) |
|---|
| 2345 | (format-general-aux stream |
|---|
| 2346 | (coerce number 'single-float) |
|---|
| 2347 | w d e k ovf pad marker atsign) |
|---|
| 2348 | (format-write-field stream |
|---|
| 2349 | (decimal-string number) |
|---|
| 2350 | w 1 0 #\space t))) |
|---|
| 2351 | (format-princ stream number nil nil w 1 0 pad))) |
|---|
| 2352 | |
|---|
| 2353 | ;;; Raymond Toy writes: same change as for format-exp-aux |
|---|
| 2354 | (defun format-general-aux (stream number w d e k ovf pad marker atsign) |
|---|
| 2355 | (if (and (floatp number) |
|---|
| 2356 | (or (sys::float-infinity-p number) |
|---|
| 2357 | (sys::float-nan-p number))) |
|---|
| 2358 | (prin1 number stream) |
|---|
| 2359 | (multiple-value-bind (ignore n) (sys::scale-exponent (abs number)) |
|---|
| 2360 | (declare (ignore ignore)) |
|---|
| 2361 | ;; KLUDGE: Default d if omitted. The procedure is taken directly from |
|---|
| 2362 | ;; the definition given in the manual, and is not very efficient, since |
|---|
| 2363 | ;; we generate the digits twice. Future maintainers are encouraged to |
|---|
| 2364 | ;; improve on this. -- rtoy?? 1998?? |
|---|
| 2365 | (unless d |
|---|
| 2366 | (multiple-value-bind (str len) |
|---|
| 2367 | (sys::flonum-to-string (abs number)) |
|---|
| 2368 | (declare (ignore str)) |
|---|
| 2369 | (let ((q (if (= len 1) 1 (1- len)))) |
|---|
| 2370 | (setq d (max q (min n 7)))))) |
|---|
| 2371 | (let* ((ee (if e (+ e 2) 4)) |
|---|
| 2372 | (ww (if w (- w ee) nil)) |
|---|
| 2373 | (dd (- d n))) |
|---|
| 2374 | (cond ((<= 0 dd d) |
|---|
| 2375 | (let ((char (if (format-fixed-aux stream number ww dd nil |
|---|
| 2376 | ovf pad atsign) |
|---|
| 2377 | ovf |
|---|
| 2378 | #\space))) |
|---|
| 2379 | (dotimes (i ee) (write-char char stream)))) |
|---|
| 2380 | (t |
|---|
| 2381 | (format-exp-aux stream number w d e (or k 1) |
|---|
| 2382 | ovf pad marker atsign))))))) |
|---|
| 2383 | |
|---|
| 2384 | (def-format-interpreter #\$ (colonp atsignp params) |
|---|
| 2385 | (interpret-bind-defaults ((d 2) (n 1) (w 0) (pad #\space)) params |
|---|
| 2386 | (format-dollars stream (next-arg) d n w pad colonp atsignp))) |
|---|
| 2387 | |
|---|
| 2388 | (defun format-dollars (stream number d n w pad colon atsign) |
|---|
| 2389 | (when (rationalp number) |
|---|
| 2390 | ;; This coercion to SINGLE-FLOAT seems as though it gratuitously |
|---|
| 2391 | ;; loses precision (why not LONG-FLOAT?) but it's the default |
|---|
| 2392 | ;; behavior in the ANSI spec, so in some sense it's the right |
|---|
| 2393 | ;; thing, and at least the user shouldn't be surprised. |
|---|
| 2394 | (setq number (coerce number 'single-float))) |
|---|
| 2395 | (if (floatp number) |
|---|
| 2396 | (let* ((signstr (if (minusp number) "-" (if atsign "+" ""))) |
|---|
| 2397 | (signlen (length signstr))) |
|---|
| 2398 | (multiple-value-bind (str strlen ig2 ig3 pointplace) |
|---|
| 2399 | (sys::flonum-to-string number nil d nil) |
|---|
| 2400 | (declare (ignore ig2 ig3 strlen)) |
|---|
| 2401 | (when colon |
|---|
| 2402 | (write-string signstr stream)) |
|---|
| 2403 | (dotimes (i (- w signlen (max n pointplace) 1 d)) |
|---|
| 2404 | (write-char pad stream)) |
|---|
| 2405 | (unless colon |
|---|
| 2406 | (write-string signstr stream)) |
|---|
| 2407 | (dotimes (i (- n pointplace)) |
|---|
| 2408 | (write-char #\0 stream)) |
|---|
| 2409 | (write-string str stream))) |
|---|
| 2410 | (format-write-field stream |
|---|
| 2411 | (decimal-string number) |
|---|
| 2412 | w 1 0 #\space t))) |
|---|
| 2413 | |
|---|
| 2414 | ;;;; FORMAT interpreters and support functions for line/page breaks etc. |
|---|
| 2415 | |
|---|
| 2416 | (def-format-interpreter #\% (colonp atsignp params) |
|---|
| 2417 | (when (or colonp atsignp) |
|---|
| 2418 | (error 'format-error |
|---|
| 2419 | :complaint |
|---|
| 2420 | "cannot specify either colon or atsign for this directive")) |
|---|
| 2421 | (interpret-bind-defaults ((count 1)) params |
|---|
| 2422 | (dotimes (i count) |
|---|
| 2423 | (terpri stream)))) |
|---|
| 2424 | |
|---|
| 2425 | (def-format-interpreter #\& (colonp atsignp params) |
|---|
| 2426 | (when (or colonp atsignp) |
|---|
| 2427 | (error 'format-error |
|---|
| 2428 | :complaint |
|---|
| 2429 | "cannot specify either colon or atsign for this directive")) |
|---|
| 2430 | (interpret-bind-defaults ((count 1)) params |
|---|
| 2431 | (fresh-line stream) |
|---|
| 2432 | (dotimes (i (1- count)) |
|---|
| 2433 | (terpri stream)))) |
|---|
| 2434 | |
|---|
| 2435 | (def-format-interpreter #\| (colonp atsignp params) |
|---|
| 2436 | (when (or colonp atsignp) |
|---|
| 2437 | (error 'format-error |
|---|
| 2438 | :complaint |
|---|
| 2439 | "cannot specify either colon or atsign for this directive")) |
|---|
| 2440 | (interpret-bind-defaults ((count 1)) params |
|---|
| 2441 | (dotimes (i count) |
|---|
| 2442 | (write-char (code-char sys::form-feed-char-code) stream)))) |
|---|
| 2443 | |
|---|
| 2444 | (def-format-interpreter #\~ (colonp atsignp params) |
|---|
| 2445 | (when (or colonp atsignp) |
|---|
| 2446 | (error 'format-error |
|---|
| 2447 | :complaint |
|---|
| 2448 | "cannot specify either colon or atsign for this directive")) |
|---|
| 2449 | (interpret-bind-defaults ((count 1)) params |
|---|
| 2450 | (dotimes (i count) |
|---|
| 2451 | (write-char #\~ stream)))) |
|---|
| 2452 | |
|---|
| 2453 | (def-complex-format-interpreter #\newline (colonp atsignp params directives) |
|---|
| 2454 | (when (and colonp atsignp) |
|---|
| 2455 | (error 'format-error |
|---|
| 2456 | :complaint |
|---|
| 2457 | "cannot specify both colon and atsign for this directive")) |
|---|
| 2458 | (interpret-bind-defaults () params |
|---|
| 2459 | (when atsignp |
|---|
| 2460 | (write-char #\newline stream))) |
|---|
| 2461 | (if (and (not colonp) |
|---|
| 2462 | directives |
|---|
| 2463 | (simple-string-p (car directives))) |
|---|
| 2464 | (cons (string-left-trim *format-whitespace-chars* |
|---|
| 2465 | (car directives)) |
|---|
| 2466 | (cdr directives)) |
|---|
| 2467 | directives)) |
|---|
| 2468 | |
|---|
| 2469 | ;;;; format interpreters and support functions for tabs and simple pretty |
|---|
| 2470 | ;;;; printing |
|---|
| 2471 | |
|---|
| 2472 | (def-format-interpreter #\T (colonp atsignp params) |
|---|
| 2473 | (if colonp |
|---|
| 2474 | (interpret-bind-defaults ((n 1) (m 1)) params |
|---|
| 2475 | (pprint-tab (if atsignp :section-relative :section) n m stream)) |
|---|
| 2476 | (if atsignp |
|---|
| 2477 | (interpret-bind-defaults ((colrel 1) (colinc 1)) params |
|---|
| 2478 | (format-relative-tab stream colrel colinc)) |
|---|
| 2479 | (interpret-bind-defaults ((colnum 1) (colinc 1)) params |
|---|
| 2480 | (format-absolute-tab stream colnum colinc))))) |
|---|
| 2481 | |
|---|
| 2482 | (defun output-spaces (stream n) |
|---|
| 2483 | (let ((spaces #.(make-string 100 :initial-element #\space))) |
|---|
| 2484 | (loop |
|---|
| 2485 | (when (< n (length spaces)) |
|---|
| 2486 | (return)) |
|---|
| 2487 | (write-string spaces stream) |
|---|
| 2488 | (decf n (length spaces))) |
|---|
| 2489 | (write-string spaces stream :end n))) |
|---|
| 2490 | |
|---|
| 2491 | (defun format-relative-tab (stream colrel colinc) |
|---|
| 2492 | (if (xp::xp-structure-p stream) |
|---|
| 2493 | (pprint-tab :line-relative colrel colinc stream) |
|---|
| 2494 | (let* ((cur (charpos stream)) |
|---|
| 2495 | (spaces (if (and cur (plusp colinc)) |
|---|
| 2496 | (- (* (ceiling (+ cur colrel) colinc) colinc) cur) |
|---|
| 2497 | colrel))) |
|---|
| 2498 | (output-spaces stream spaces)))) |
|---|
| 2499 | |
|---|
| 2500 | (defun format-absolute-tab (stream colnum colinc) |
|---|
| 2501 | (if (xp::xp-structure-p stream) |
|---|
| 2502 | (pprint-tab :line colnum colinc stream) |
|---|
| 2503 | (let ((cur (charpos stream))) |
|---|
| 2504 | (cond ((null cur) |
|---|
| 2505 | (write-string " " stream)) |
|---|
| 2506 | ((< cur colnum) |
|---|
| 2507 | (output-spaces stream (- colnum cur))) |
|---|
| 2508 | (t |
|---|
| 2509 | (unless (zerop colinc) |
|---|
| 2510 | (output-spaces stream |
|---|
| 2511 | (- colinc (rem (- cur colnum) colinc))))))))) |
|---|
| 2512 | |
|---|
| 2513 | (def-format-interpreter #\_ (colonp atsignp params) |
|---|
| 2514 | (interpret-bind-defaults () params |
|---|
| 2515 | (pprint-newline (if colonp |
|---|
| 2516 | (if atsignp |
|---|
| 2517 | :mandatory |
|---|
| 2518 | :fill) |
|---|
| 2519 | (if atsignp |
|---|
| 2520 | :miser |
|---|
| 2521 | :linear)) |
|---|
| 2522 | stream))) |
|---|
| 2523 | |
|---|
| 2524 | (def-format-interpreter #\I (colonp atsignp params) |
|---|
| 2525 | (when atsignp |
|---|
| 2526 | (error 'format-error |
|---|
| 2527 | :complaint "cannot specify the at-sign modifier")) |
|---|
| 2528 | (interpret-bind-defaults ((n 0)) params |
|---|
| 2529 | (pprint-indent (if colonp :current :block) n stream))) |
|---|
| 2530 | |
|---|
| 2531 | ;;;; format interpreter for ~* |
|---|
| 2532 | |
|---|
| 2533 | (def-format-interpreter #\* (colonp atsignp params) |
|---|
| 2534 | (if atsignp |
|---|
| 2535 | (if colonp |
|---|
| 2536 | (error 'format-error |
|---|
| 2537 | :complaint "cannot specify both colon and at-sign") |
|---|
| 2538 | (interpret-bind-defaults ((posn 0)) params |
|---|
| 2539 | (if (<= 0 posn (length orig-args)) |
|---|
| 2540 | (setf args (nthcdr posn orig-args)) |
|---|
| 2541 | (error 'format-error |
|---|
| 2542 | :complaint "Index ~W is out of bounds. (It should ~ |
|---|
| 2543 | have been between 0 and ~W.)" |
|---|
| 2544 | :args (list posn (length orig-args)))))) |
|---|
| 2545 | (if colonp |
|---|
| 2546 | (interpret-bind-defaults ((n 1)) params |
|---|
| 2547 | (do ((cur-posn 0 (1+ cur-posn)) |
|---|
| 2548 | (arg-ptr orig-args (cdr arg-ptr))) |
|---|
| 2549 | ((eq arg-ptr args) |
|---|
| 2550 | (let ((new-posn (- cur-posn n))) |
|---|
| 2551 | (if (<= 0 new-posn (length orig-args)) |
|---|
| 2552 | (setf args (nthcdr new-posn orig-args)) |
|---|
| 2553 | (error 'format-error |
|---|
| 2554 | :complaint |
|---|
| 2555 | "Index ~W is out of bounds. (It should |
|---|
| 2556 | have been between 0 and ~W.)" |
|---|
| 2557 | :args |
|---|
| 2558 | (list new-posn (length orig-args)))))))) |
|---|
| 2559 | (interpret-bind-defaults ((n 1)) params |
|---|
| 2560 | (dotimes (i n) |
|---|
| 2561 | (next-arg)))))) |
|---|
| 2562 | |
|---|
| 2563 | ;;;; format interpreter for indirection |
|---|
| 2564 | |
|---|
| 2565 | (def-format-interpreter #\? (colonp atsignp params string end) |
|---|
| 2566 | (when colonp |
|---|
| 2567 | (error 'format-error |
|---|
| 2568 | :complaint "cannot specify the colon modifier")) |
|---|
| 2569 | (interpret-bind-defaults () params |
|---|
| 2570 | (handler-bind |
|---|
| 2571 | ((format-error |
|---|
| 2572 | (lambda (condition) |
|---|
| 2573 | (error 'format-error |
|---|
| 2574 | :complaint |
|---|
| 2575 | "~A~%while processing indirect format string:" |
|---|
| 2576 | :args (list condition) |
|---|
| 2577 | :print-banner nil |
|---|
| 2578 | :control-string string |
|---|
| 2579 | :offset (1- end))))) |
|---|
| 2580 | (if atsignp |
|---|
| 2581 | (setf args (%format stream (next-arg) orig-args args)) |
|---|
| 2582 | (%format stream (next-arg) (next-arg)))))) |
|---|
| 2583 | |
|---|
| 2584 | ;;;; format interpreters for capitalization |
|---|
| 2585 | |
|---|
| 2586 | (def-complex-format-interpreter #\( (colonp atsignp params directives) |
|---|
| 2587 | (let ((close (find-directive directives #\) nil))) |
|---|
| 2588 | (unless close |
|---|
| 2589 | (error 'format-error |
|---|
| 2590 | :complaint "no corresponding close paren")) |
|---|
| 2591 | (interpret-bind-defaults () params |
|---|
| 2592 | (let* ((posn (position close directives)) |
|---|
| 2593 | (before (subseq directives 0 posn)) |
|---|
| 2594 | (after (nthcdr (1+ posn) directives)) |
|---|
| 2595 | (stream (sys::make-case-frob-stream |
|---|
| 2596 | (if (typep stream 'xp::xp-structure) |
|---|
| 2597 | (xp::base-stream stream) |
|---|
| 2598 | stream) |
|---|
| 2599 | (if colonp |
|---|
| 2600 | (if atsignp |
|---|
| 2601 | :upcase |
|---|
| 2602 | :capitalize) |
|---|
| 2603 | (if atsignp |
|---|
| 2604 | :capitalize-first |
|---|
| 2605 | :downcase))))) |
|---|
| 2606 | (setf args (interpret-directive-list stream before orig-args args)) |
|---|
| 2607 | after)))) |
|---|
| 2608 | |
|---|
| 2609 | (def-complex-format-interpreter #\) () |
|---|
| 2610 | (error 'format-error |
|---|
| 2611 | :complaint "no corresponding open paren")) |
|---|
| 2612 | |
|---|
| 2613 | ;;;; format interpreters and support functions for conditionalization |
|---|
| 2614 | |
|---|
| 2615 | (def-complex-format-interpreter #\[ (colonp atsignp params directives) |
|---|
| 2616 | (multiple-value-bind (sublists last-semi-with-colon-p remaining) |
|---|
| 2617 | (parse-conditional-directive directives) |
|---|
| 2618 | (setf args |
|---|
| 2619 | (if atsignp |
|---|
| 2620 | (if colonp |
|---|
| 2621 | (error 'format-error |
|---|
| 2622 | :complaint |
|---|
| 2623 | "cannot specify both the colon and at-sign modifiers") |
|---|
| 2624 | (if (cdr sublists) |
|---|
| 2625 | (error 'format-error |
|---|
| 2626 | :complaint |
|---|
| 2627 | "can only specify one section") |
|---|
| 2628 | (interpret-bind-defaults () params |
|---|
| 2629 | (let ((prev-args args) |
|---|
| 2630 | (arg (next-arg))) |
|---|
| 2631 | (if arg |
|---|
| 2632 | (interpret-directive-list stream |
|---|
| 2633 | (car sublists) |
|---|
| 2634 | orig-args |
|---|
| 2635 | prev-args) |
|---|
| 2636 | args))))) |
|---|
| 2637 | (if colonp |
|---|
| 2638 | (if (= (length sublists) 2) |
|---|
| 2639 | (interpret-bind-defaults () params |
|---|
| 2640 | (if (next-arg) |
|---|
| 2641 | (interpret-directive-list stream (car sublists) |
|---|
| 2642 | orig-args args) |
|---|
| 2643 | (interpret-directive-list stream (cadr sublists) |
|---|
| 2644 | orig-args args))) |
|---|
| 2645 | (error 'format-error |
|---|
| 2646 | :complaint |
|---|
| 2647 | "must specify exactly two sections")) |
|---|
| 2648 | (interpret-bind-defaults ((index (next-arg))) params |
|---|
| 2649 | (let* ((default (and last-semi-with-colon-p |
|---|
| 2650 | (pop sublists))) |
|---|
| 2651 | (last (1- (length sublists))) |
|---|
| 2652 | (sublist |
|---|
| 2653 | (if (<= 0 index last) |
|---|
| 2654 | (nth (- last index) sublists) |
|---|
| 2655 | default))) |
|---|
| 2656 | (interpret-directive-list stream sublist orig-args |
|---|
| 2657 | args)))))) |
|---|
| 2658 | remaining)) |
|---|
| 2659 | |
|---|
| 2660 | (def-complex-format-interpreter #\; () |
|---|
| 2661 | (error 'format-error |
|---|
| 2662 | :complaint |
|---|
| 2663 | "~~; not contained within either ~~[...~~] or ~~<...~~>")) |
|---|
| 2664 | |
|---|
| 2665 | (def-complex-format-interpreter #\] () |
|---|
| 2666 | (error 'format-error |
|---|
| 2667 | :complaint |
|---|
| 2668 | "no corresponding open bracket")) |
|---|
| 2669 | |
|---|
| 2670 | ;;;; format interpreter for up-and-out |
|---|
| 2671 | |
|---|
| 2672 | (defvar *outside-args*) |
|---|
| 2673 | |
|---|
| 2674 | (def-format-interpreter #\^ (colonp atsignp params) |
|---|
| 2675 | (when atsignp |
|---|
| 2676 | (error 'format-error |
|---|
| 2677 | :complaint "cannot specify the at-sign modifier")) |
|---|
| 2678 | (when (and colonp (not *up-up-and-out-allowed*)) |
|---|
| 2679 | (error 'format-error |
|---|
| 2680 | :complaint "attempt to use ~~:^ outside a ~~:{...~~} construct")) |
|---|
| 2681 | (when (interpret-bind-defaults ((arg1 nil) (arg2 nil) (arg3 nil)) params |
|---|
| 2682 | (cond (arg3 (<= arg1 arg2 arg3)) |
|---|
| 2683 | (arg2 (eql arg1 arg2)) |
|---|
| 2684 | (arg1 (eql arg1 0)) |
|---|
| 2685 | (t (if colonp |
|---|
| 2686 | (null *outside-args*) |
|---|
| 2687 | (null args))))) |
|---|
| 2688 | (throw (if colonp 'up-up-and-out 'up-and-out) |
|---|
| 2689 | args))) |
|---|
| 2690 | |
|---|
| 2691 | ;;;; format interpreters for iteration |
|---|
| 2692 | |
|---|
| 2693 | (def-complex-format-interpreter #\{ |
|---|
| 2694 | (colonp atsignp params string end directives) |
|---|
| 2695 | (let ((close (find-directive directives #\} nil))) |
|---|
| 2696 | (unless close |
|---|
| 2697 | (error 'format-error |
|---|
| 2698 | :complaint |
|---|
| 2699 | "no corresponding close brace")) |
|---|
| 2700 | (interpret-bind-defaults ((max-count nil)) params |
|---|
| 2701 | (let* ((closed-with-colon (format-directive-colonp close)) |
|---|
| 2702 | (posn (position close directives)) |
|---|
| 2703 | (insides (if (zerop posn) |
|---|
| 2704 | (next-arg) |
|---|
| 2705 | (subseq directives 0 posn))) |
|---|
| 2706 | (*up-up-and-out-allowed* colonp)) |
|---|
| 2707 | (labels |
|---|
| 2708 | ((do-guts (orig-args args) |
|---|
| 2709 | (if (zerop posn) |
|---|
| 2710 | (handler-bind |
|---|
| 2711 | ((format-error |
|---|
| 2712 | (lambda (condition) |
|---|
| 2713 | (error |
|---|
| 2714 | 'format-error |
|---|
| 2715 | :complaint |
|---|
| 2716 | "~A~%while processing indirect format string:" |
|---|
| 2717 | :args (list condition) |
|---|
| 2718 | :print-banner nil |
|---|
| 2719 | :control-string string |
|---|
| 2720 | :offset (1- end))))) |
|---|
| 2721 | (%format stream insides orig-args args)) |
|---|
| 2722 | (interpret-directive-list stream insides |
|---|
| 2723 | orig-args args))) |
|---|
| 2724 | (bind-args (orig-args args) |
|---|
| 2725 | (if colonp |
|---|
| 2726 | (let* ((arg (next-arg)) |
|---|
| 2727 | (*logical-block-popper* nil) |
|---|
| 2728 | (*outside-args* args)) |
|---|
| 2729 | (catch 'up-and-out |
|---|
| 2730 | (do-guts arg arg)) |
|---|
| 2731 | args) |
|---|
| 2732 | (do-guts orig-args args))) |
|---|
| 2733 | (do-loop (orig-args args) |
|---|
| 2734 | (catch (if colonp 'up-up-and-out 'up-and-out) |
|---|
| 2735 | (loop |
|---|
| 2736 | (when (and (not closed-with-colon) (null args)) |
|---|
| 2737 | (return)) |
|---|
| 2738 | (when (and max-count (minusp (decf max-count))) |
|---|
| 2739 | (return)) |
|---|
| 2740 | (setf args (bind-args orig-args args)) |
|---|
| 2741 | (when (and closed-with-colon (null args)) |
|---|
| 2742 | (return))) |
|---|
| 2743 | args))) |
|---|
| 2744 | (if atsignp |
|---|
| 2745 | (setf args (do-loop orig-args args)) |
|---|
| 2746 | (let ((arg (next-arg)) |
|---|
| 2747 | (*logical-block-popper* nil)) |
|---|
| 2748 | (do-loop arg arg))) |
|---|
| 2749 | (nthcdr (1+ posn) directives)))))) |
|---|
| 2750 | |
|---|
| 2751 | (def-complex-format-interpreter #\} () |
|---|
| 2752 | (error 'format-error |
|---|
| 2753 | :complaint "no corresponding open brace")) |
|---|
| 2754 | |
|---|
| 2755 | ;;;; format interpreters and support functions for justification |
|---|
| 2756 | |
|---|
| 2757 | (def-complex-format-interpreter #\< |
|---|
| 2758 | (colonp atsignp params string end directives) |
|---|
| 2759 | (multiple-value-bind (segments first-semi close remaining) |
|---|
| 2760 | (parse-format-justification directives) |
|---|
| 2761 | (setf args |
|---|
| 2762 | (if (format-directive-colonp close) |
|---|
| 2763 | (multiple-value-bind (prefix per-line-p insides suffix) |
|---|
| 2764 | (parse-format-logical-block segments colonp first-semi |
|---|
| 2765 | close params string end) |
|---|
| 2766 | (interpret-format-logical-block stream orig-args args |
|---|
| 2767 | prefix per-line-p insides |
|---|
| 2768 | suffix atsignp)) |
|---|
| 2769 | (let ((count (reduce #'+ (mapcar (lambda (x) (count-if #'illegal-inside-justification-p x)) segments)))) |
|---|
| 2770 | (when (> count 0) |
|---|
| 2771 | ;; ANSI specifies that "an error is signalled" in this |
|---|
| 2772 | ;; situation. |
|---|
| 2773 | (error 'format-error |
|---|
| 2774 | :complaint "~D illegal directive~:P found inside justification block" |
|---|
| 2775 | :args (list count))) |
|---|
| 2776 | (interpret-format-justification stream orig-args args |
|---|
| 2777 | segments colonp atsignp |
|---|
| 2778 | first-semi params)))) |
|---|
| 2779 | remaining)) |
|---|
| 2780 | |
|---|
| 2781 | (defun interpret-format-justification |
|---|
| 2782 | (stream orig-args args segments colonp atsignp first-semi params) |
|---|
| 2783 | (interpret-bind-defaults |
|---|
| 2784 | ((mincol 0) (colinc 1) (minpad 0) (padchar #\space)) |
|---|
| 2785 | params |
|---|
| 2786 | (let ((newline-string nil) |
|---|
| 2787 | (strings nil) |
|---|
| 2788 | (extra-space 0) |
|---|
| 2789 | (line-len 0)) |
|---|
| 2790 | (setf args |
|---|
| 2791 | (catch 'up-and-out |
|---|
| 2792 | (when (and first-semi (format-directive-colonp first-semi)) |
|---|
| 2793 | (interpret-bind-defaults |
|---|
| 2794 | ((extra 0) |
|---|
| 2795 | (len (or #-abcl(sb!impl::line-length stream) 72))) |
|---|
| 2796 | (format-directive-params first-semi) |
|---|
| 2797 | (setf newline-string |
|---|
| 2798 | (with-output-to-string (stream) |
|---|
| 2799 | (setf args |
|---|
| 2800 | (interpret-directive-list stream |
|---|
| 2801 | (pop segments) |
|---|
| 2802 | orig-args |
|---|
| 2803 | args)))) |
|---|
| 2804 | (setf extra-space extra) |
|---|
| 2805 | (setf line-len len))) |
|---|
| 2806 | (dolist (segment segments) |
|---|
| 2807 | (push (with-output-to-string (stream) |
|---|
| 2808 | (setf args |
|---|
| 2809 | (interpret-directive-list stream segment |
|---|
| 2810 | orig-args args))) |
|---|
| 2811 | strings)) |
|---|
| 2812 | args)) |
|---|
| 2813 | (format-justification stream newline-string extra-space line-len strings |
|---|
| 2814 | colonp atsignp mincol colinc minpad padchar))) |
|---|
| 2815 | args) |
|---|
| 2816 | |
|---|
| 2817 | (defun format-justification (stream newline-prefix extra-space line-len strings |
|---|
| 2818 | pad-left pad-right mincol colinc minpad padchar) |
|---|
| 2819 | (setf strings (reverse strings)) |
|---|
| 2820 | (let* ((num-gaps (+ (1- (length strings)) |
|---|
| 2821 | (if pad-left 1 0) |
|---|
| 2822 | (if pad-right 1 0))) |
|---|
| 2823 | (chars (+ (* num-gaps minpad) |
|---|
| 2824 | (loop |
|---|
| 2825 | for string in strings |
|---|
| 2826 | summing (length string)))) |
|---|
| 2827 | (length (if (> chars mincol) |
|---|
| 2828 | (+ mincol (* (ceiling (- chars mincol) colinc) colinc)) |
|---|
| 2829 | mincol)) |
|---|
| 2830 | (padding (+ (- length chars) (* num-gaps minpad)))) |
|---|
| 2831 | (when (and newline-prefix |
|---|
| 2832 | (> (+ (or (charpos stream) 0) |
|---|
| 2833 | length extra-space) |
|---|
| 2834 | line-len)) |
|---|
| 2835 | (write-string newline-prefix stream)) |
|---|
| 2836 | (flet ((do-padding () |
|---|
| 2837 | (let ((pad-len (if (zerop num-gaps) |
|---|
| 2838 | padding |
|---|
| 2839 | (truncate padding num-gaps)))) |
|---|
| 2840 | (decf padding pad-len) |
|---|
| 2841 | (decf num-gaps) |
|---|
| 2842 | (dotimes (i pad-len) (write-char padchar stream))))) |
|---|
| 2843 | (when (or pad-left |
|---|
| 2844 | (and (not pad-right) (null (cdr strings)))) |
|---|
| 2845 | (do-padding)) |
|---|
| 2846 | (when strings |
|---|
| 2847 | (write-string (car strings) stream) |
|---|
| 2848 | (dolist (string (cdr strings)) |
|---|
| 2849 | (do-padding) |
|---|
| 2850 | (write-string string stream))) |
|---|
| 2851 | (when pad-right |
|---|
| 2852 | (do-padding))))) |
|---|
| 2853 | |
|---|
| 2854 | (defun interpret-format-logical-block |
|---|
| 2855 | (stream orig-args args prefix per-line-p insides suffix atsignp) |
|---|
| 2856 | (let ((arg (if atsignp args (next-arg)))) |
|---|
| 2857 | (if per-line-p |
|---|
| 2858 | (pprint-logical-block |
|---|
| 2859 | (stream arg :per-line-prefix prefix :suffix suffix) |
|---|
| 2860 | (let ((*logical-block-popper* (lambda () (pprint-pop)))) |
|---|
| 2861 | (catch 'up-and-out |
|---|
| 2862 | (interpret-directive-list stream insides |
|---|
| 2863 | (if atsignp orig-args arg) |
|---|
| 2864 | arg)))) |
|---|
| 2865 | (pprint-logical-block (stream arg :prefix prefix :suffix suffix) |
|---|
| 2866 | (let ((*logical-block-popper* (lambda () (pprint-pop)))) |
|---|
| 2867 | (catch 'up-and-out |
|---|
| 2868 | (interpret-directive-list stream insides |
|---|
| 2869 | (if atsignp orig-args arg) |
|---|
| 2870 | arg)))))) |
|---|
| 2871 | (if atsignp nil args)) |
|---|
| 2872 | |
|---|
| 2873 | ;;;; format interpreter and support functions for user-defined method |
|---|
| 2874 | |
|---|
| 2875 | (def-format-interpreter #\/ (string start end colonp atsignp params) |
|---|
| 2876 | (let ((symbol (extract-user-fun-name string start end))) |
|---|
| 2877 | (collect ((args)) |
|---|
| 2878 | (dolist (param-and-offset params) |
|---|
| 2879 | (let ((param (cdr param-and-offset))) |
|---|
| 2880 | (case param |
|---|
| 2881 | (:arg (args (next-arg))) |
|---|
| 2882 | (:remaining (args (length args))) |
|---|
| 2883 | (t (args param))))) |
|---|
| 2884 | (apply (fdefinition symbol) stream (next-arg) colonp atsignp (args))))) |
|---|
| 2885 | |
|---|
| 2886 | (setf (symbol-function 'sys::simple-format) #'format) |
|---|
| 2887 | |
|---|
| 2888 | |
|---|
| 2889 | (provide 'format) |
|---|