;;; -*- Mode:LISP; Package:LISP-IO; Readtable:CL; Base:10 -*-
;;;
;;; FORMAT.LISP
;;;
;;; FORMAT Interpreter


;; Problems:  parsing ,,, 
;; cryptic error messages.  how should they work, anyway? nice errors.
;; check types of arguments
;; piggyback stream for column counting?


;;;----------------------------------------
;;; Entry
;;;----------------------------------------


(defun format (destination control-string &rest arguments)
  (let ((stream (cond ((streamp destination)
		       destination)
		      ((eq destination NIL)
		       (make-string-output-stream))
		      ((eq destination T)
		       *standard-output*)
		      ((stringp destination)
		       (error "Can't do strings yet"))
		      (t
		       (error "Bad destination to FORMAT.")))))
    (format-main (make-string-iterator control-string)
		 (make-format-args (copy-list arguments))
		 stream)
    (cond ((eq destination NIL)
	   (get-output-stream-string stream))
	  (t
	   NIL))))


;;;----------------------------------------
;;; Representation of format arguments
;;;----------------------------------------


(defstruct format-args-rep
  "ALL-ARGUMENTS is the complete list of format arguments.  NEXT-ARGUMENT is a
list of all the arguments remaining to be processed by format.  POSITION is the
number of arguments that have been processed so far.  To remain consistent,
NEXT-ARGUMENT = (NTHCDR POSITION ALL-ARGUMENTS)."
  position all-arguments next-argument)


(defun make-format-args (format-argument-list)
  "Represent the list of format arguments in a more convenient manner."
  (make-format-args-rep :position      0
			:all-arguments format-argument-list
			:next-argument format-argument-list))


(defun next-format-arg (format-args)
  "Return the next format argument available."
  (if (format-args-rep-next-argument format-args)
      (progn (incf (format-args-rep-position format-args))
	     (pop  (format-args-rep-next-argument format-args)))
      (error "No arguments left.")))


(defun any-more-format-args? (format-args)
  "Return T if there are more format arguments available, NIL if not."
  (and (format-args-rep-next-argument format-args) t))


(defun number-of-remaining-format-args (format-args)
  (length (format-args-rep-next-argument format-args)))


(defun back-up-one-format-arg (format-args)
  (if (zerop (format-args-rep-position format-args))
      (error "Can't back up.")
      (progn
	(decf (format-args-rep-position format-args))
	(setf (format-args-rep-next-argument format-args)
	      (nthcdr (format-args-rep-position format-args)
		      (format-args-rep-all-arguments format-args))))))
    

;;;----------------------------------------
;;; String element iteration
;;;----------------------------------------


(defstruct string-iterator-rep
  string length index most-recent-twiddle-index most-recent-open-index)


(defun make-string-iterator (string)
  (make-string-iterator-rep :string string
			    :length (length string)
			    :index  0
			    :most-recent-twiddle-index NIL
			    :most-recent-open-index NIL))
  
  


(defun next-character (string-iterator)
  (let ((index (string-iterator-rep-index string-iterator)))
    (if (>= index (string-iterator-rep-length string-iterator))
	(format-error string-iterator "Unexpected end of format string.")
	(prog1 (char (string-iterator-rep-string string-iterator) index)
	       (incf (string-iterator-rep-index string-iterator))))))


(defun peek-character (string-iterator)
  (let ((index (string-iterator-rep-index string-iterator)))
    (if (>= index (string-iterator-rep-length string-iterator))
	(format-error string-iterator "Unexpected end of format string.")
	(char (string-iterator-rep-string string-iterator) index))))


(defun more-characters? (string-iterator)
  (< (string-iterator-rep-index string-iterator)
     (string-iterator-rep-length string-iterator)))


(defun grab-current-index (string-iterator)
  (string-iterator-rep-index string-iterator))


(defun set-current-index (string-iterator index)
  (setf (string-iterator-rep-index string-iterator) index))


(defun note-most-recent-twiddle-index (string-iterator)
  (setf (string-iterator-rep-most-recent-twiddle-index string-iterator)
	(1- (string-iterator-rep-index string-iterator))))


(defun get-most-recent-twiddle-index (string-iterator)
  (string-iterator-rep-most-recent-twiddle-index string-iterator))


(defun note-most-recent-open-index (string-iterator)
  (setf (string-iterator-rep-most-recent-open-index string-iterator)
	(1- (string-iterator-rep-index string-iterator))))


(defun get-most-recent-open-index (string-iterator)
  (string-iterator-rep-most-recent-open-index string-iterator))


(defun get-entire-format-string (string-iterator)
  (string-iterator-rep-string string-iterator))


;;;----------------------------------------
;;; Main loop and dispatch
;;;----------------------------------------


(defun format-main (format-string-iterator format-args stream)
  (when (more-characters? format-string-iterator)
    (let ((character (next-character format-string-iterator)))
      (if (char= character #\~)
	  (multiple-value-bind (dispatch-char directive-args colonp atsignp)
	      (parse-after-twiddle format-string-iterator format-args nil)
	    (funcall (lookup-format-dispatch dispatch-char)
		     format-args
		     directive-args
		     colonp
		     atsignp
		     stream
		     format-string-iterator))
	  (write-char character stream)))
    (format-main format-string-iterator format-args stream)))


(defun lookup-format-dispatch (dispatch-char)
  (case dispatch-char
    (#\A 'format-A)
    (#\B 'format-B)
    (#\C 'format-C)
    (#\D 'format-D)
    (#\O 'format-O)
    (#\P 'format-P)
    (#\R 'format-R)
    (#\S 'format-S)
    (#\X 'format-X)
    (#\% 'format-%)
    (#\& 'format-&)
    (#\( 'format-\()
    (#\[ 'format-[)
    (#\{ 'format-{)
    (#\< 'format-<)
    (t
     (error "Unrecognized format dispatch character ~C." dispatch-char))))


;;;----------------------------------------
;;; Parsing the format string
;;;----------------------------------------


;(defun parse-after-twiddle (format-string-iterator format-args)
;  "Begin parsing the format string immediately after a ~.  Return four values:
;\(1) the format dispatch character, (2) a list of the prefix parameters, (3) a
;boolean value indicating whether or not a colon modifier was specified, and (4)
;a boolean value indicating whether or not an at-sign modifier was specified."
;  (let (dispatch-char parameters colonp at-sign-p)
;    (loop
;      (if (not (more-characters? format-string-iterator))
;	  (error "Unexpected end of format.")
;	  (let ((next-char 
;		  (char-upcase (next-character format-string-iterator))))
;	    (cond
;	      ((find next-char "+-0123456789")
;	       (push (pluck-integer next-char format-string-iterator) 
;		     parameters))
;	      ((char= next-char #\V)
;	       (push (next-format-arg format-args) parameters))
;	      ((char= next-char #\,)
;	       (when (char= (peek-character format-string-iterator) #\,)
;		 (push NIL parameters)))
;	      ((char= next-char #\#)
;	       (push (number-of-remaining-format-args format-args) parameters))
;	      ((char= next-char #\')
;	       (push (next-character format-string-iterator) parameters))
;	      ((char= next-char #\:)
;	       (if colonp
;		   (error "More than one colon in format directive.")
;		   (setq colonp t)))
;	      ((char= next-char #\@)
;	       (if at-sign-p
;		   (error "More than one at-sign in format directive.")
;		   (setq at-sign-p t)))
;	      (t
;	       (setq dispatch-char next-char)
;	       (return))))))
;    (values dispatch-char (nreverse parameters) colonp at-sign-p)))


(defun parse-after-twiddle (format-string-iterator format-args vacuous-p)
  "Parse a format directive, immediately following a twiddle.  If VACUOUS-P is
true, then treat all prefix parameters as NIL, and no side effects will happen
to FORMAT-ARGS.  Return five values:  (1) the format dispatch character, (2) a
list of the prefix parameters, (3) a boolean value indicating whether or not a
colon modifier was specified, (4) a boolean value indicating whether or not an
at-sign modifier was specified, and (5) a string containing the text of the
format directive."
  (note-most-recent-twiddle-index format-string-iterator)
  (let (dispatch-char parameters colonp at-sign-p text)
    (let ((type-of-last-thing :TWIDDLE))
      (loop
	(let ((next-char (char-upcase (next-character format-string-iterator))))
	  (push next-char text)
	  (cond
	    ((find next-char "+-0123456789V#'")
	     (require-can-follow :ARGUMENT type-of-last-thing)
	     (push 
	       (if vacuous-p
		   NIL
		   (case next-char
		     (#\V (next-format-arg format-args))
		     (#\# (number-of-remaining-format-args format-args))
		     (#\' (next-character format-string-iterator))
		     (t   (pluck-integer next-char format-string-iterator))))
	       parameters)
	     (setq type-of-last-thing :ARGUMENT))
	    ((char= next-char #\,)
	     (require-can-follow :COMMA type-of-last-thing)
	     (when (member type-of-last-thing '(:TWIDDLE :COMMA))
	       (push NIL parameters))
	     (setq type-of-last-thing :COMMA))
	    ((char= next-char #\:)
	     (require-can-follow :COLON type-of-last-thing)
	     (setq colonp t)
	     (setq type-of-last-thing :COLON))
	    ((char= next-char #\@)
	     (require-can-follow :AT-SIGN type-of-last-thing)
	     (setq at-sign-p t)
	     (setq type-of-last-thing :AT-SIGN))
	    (t
	     (require-can-follow :DIRECTIVE type-of-last-thing)
	     (setq dispatch-char next-char)
	     (return))))))
    (values dispatch-char 
	    (nreverse parameters)
	    colonp 
	    at-sign-p
	    (coerce (nreverse text) 'string))))

		      
(defun require-can-follow (foo bar)
  "Raise an error if FOO can't follow BAR."
  (unless 
    (member foo
	    (case bar
	      (:TWIDDLE  '(:ARGUMENT :COMMA :COLON :AT-SIGN :DIRECTIVE))
	      (:ARGUMENT '(:COMMA :COLON :AT-SIGN :DIRECTIVE))
	      (:COMMA    '(:ARGUMENT :COMMA))
	      (:COLON    '(:AT-SIGN :DIRECTIVE))
	      (:AT-SIGN  '(:DIRECTIVE))))
    (error "Bad parameter syntax.")))


(defun pluck-integer (first-char format-string-iterator)
  (let ((digits (list first-char)))
    (loop
      (let ((next-char (peek-character format-string-iterator)))
	(cond
	  ((digit-char-p next-char)
	   (next-character format-string-iterator)
	   (push next-char digits))
	  ((find next-char "+-Vv#'")
	   (error "Bad syntax after numeric argument."))
	  (t
	   (return)))))
    (parse-integer (coerce (nreverse digits) 'string))))


(defun scan-a-chunk (terminating-character format-string-iterator)
  "March along the format string until a twiddle-TERMINATING-CHARACTER or a
twiddle-semicolon directive is encountered.  Skip over any nested ~(-~), 
~[-~], ~{-~}, or ~<-~> pairs.  Return three values:  a string containing the
characters scanned over (without the directive that terminated it), the
character that terminated it, and a string containing the format directive
that terminated it."
  (let ((characters (make-string-output-stream))
	(actual-terminating-character NIL)
	(terminating-string)
	(paren-depth 0)
	(bracket-depth 0)
	(brace-depth 0)
	(angle-depth 0))
    (loop 
      (let ((character (next-character format-string-iterator)))
	(if (char= character #\~)
	    (multiple-value-bind 
	      (dispatch-char ignore ignore ignore directive-string)
		(parse-after-twiddle format-string-iterator nil t)
	      (if (and (or (char= dispatch-char terminating-character)
			   (char= dispatch-char #\;))
		       (zerop paren-depth)
		       (zerop bracket-depth)
		       (zerop brace-depth)
		       (zerop angle-depth))
		  (progn (setq actual-terminating-character dispatch-char)
			 (setq terminating-string directive-string)
			 (return))
		  (progn (write-char #\~ characters)
			 (write-string directive-string characters)
			 (case dispatch-char
			   (#\( (incf paren-depth))
			   (#\) (decf paren-depth))
			   (#\[ (incf bracket-depth))
			   (#\] (decf bracket-depth))
			   (#\{ (incf brace-depth))
			   (#\} (decf brace-depth))
			   (#\< (incf angle-depth))
			   (#\> (decf angle-depth))))))
	    (write-char character characters))))
    (values (get-output-stream-string characters) 
	    actual-terminating-character
	    terminating-string)))

		  
;;;----------------------------------------
;;; Error checking
;;;----------------------------------------


(defun maximum-prefix-arguments (maximum prefix-arguments 
				 format-string-iterator)
  "Raise an error if there are more than MAXIMUM elements in PREFIX-ARGUMENTS."
  (let ((length (length prefix-arguments)))
    (when (> length maximum)
      (format-error format-string-iterator
		    "More than ~S prefix parameters supplied to this format directive."
		    maximum))))


(defun forbid-atsign (atsignp format-string-iterator)
  "Raise an error if ATSIGNP is true."
  (when atsignp
    (format-error format-string-iterator
		  "A @ modifier is not allowed in this format directive.")))
 
 
(defun forbid-colon (colonp format-string-iterator)
  "Raise an error if COLONP is true."
  (when colonp
    (format-error format-string-iterator
		  "A : modifier is not allowed in this format directive.")))


(defun forbid-both (colonp atsignp format-string-iterator)
  "Raise an error if both COLONP and ATSIGNP are true."
  (when (and colonp atsignp)
    (format-error format-string-iterator
		  "A :@ modifier is not allowed in this format directive.")))


;;;----------------------------------------
;;; Ugly format directives
;;;----------------------------------------


(defun format-A (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (maximum-prefix-arguments 4 directive-args format-string-iterator)
  (let ((object (next-format-arg format-args)))
    (cond ((and (null object) (not colonp))
	   (setq object "nil"))
	  ((and (null object) colonp)
	   (setq object "()")))
    (if (not directive-args)
	(princ object stream) ;; the easy case
	(let ((mincol  (or (first directive-args) 0))
	      (colinc  (or (second directive-args) 1))
	      (minpad  (or (third directive-args) 0))
	      (padchar (or (fourth directive-args) #\Space))
	      (string  (princ-to-string object)))
	  (pad string atsignp stream mincol colinc minpad padchar)))))


(defun format-S (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (maximum-prefix-arguments 4 directive-args format-string-iterator)
  (let* ((object (next-format-arg format-args))
	 (string (if (and (null object) colonp)
		     "()"
		     (prin1-to-string object))))
    (if (not directive-args)
	(princ string stream) ;; the easy case
	(let ((mincol  (or (first directive-args) 0))
	      (colinc  (or (second directive-args) 1))
	      (minpad  (or (third directive-args) 0))
	      (padchar (or (fourth directive-args) #\Space)))
	  (pad string atsignp stream mincol colinc minpad padchar)))))


(defun pad (string pad-left-p stream mincol colinc minpad padchar)
  (let* 
    ((length         (length string))
     (space-to-fill  (max 0 (- mincol length)))
     (padding-length (max minpad (* colinc (ceiling space-to-fill colinc))))
     (padding-string (make-string padding-length :initial-element padchar)))
    (if pad-left-p
	(progn (princ padding-string stream)
	       (princ string stream))
	(progn (princ string stream)
	       (princ padding-string stream)))))


(defun integer-to-string (integer radix comma force-sign-p)
  "Return a string representing INTEGER in base RADIX.  If COMMA is not NIL, it
should be a character to use between groups of 3 digits.  If FORCE-SIGN-P is
true and the number is positive, print a + sign in front of it."
  (let ((digit-list NIL)
	(digit-count 0))
    (labels ((iter (integer)
	       (when (and comma (> digit-count 0) (= (mod digit-count 3) 0))
		 (push comma digit-list))
	       (multiple-value-bind (most-of-the-digits the-last-digit)
		   (floor integer radix)
		 (push (digit-char the-last-digit radix) digit-list)
		 (incf digit-count)
		 (when (> most-of-the-digits 0)
		   (iter most-of-the-digits)))))
      (iter (abs integer))
      (when (or force-sign-p (minusp integer))
	(push (if (minusp integer) #\- #\+) digit-list))
      (coerce digit-list 'string))))


(defun format-integer (object radix mincol padchar comma force-sign-p stream)
  (let ((string (if (integerp object)
		    (integer-to-string object radix comma force-sign-p)
		    (let ((*print-base* radix))
		      (princ-to-string object)))))
    (pad string t stream mincol 1 0 padchar)))


(defun format-D (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (maximum-prefix-arguments 3 directive-args format-string-iterator)
  (let* ((object    (next-format-arg format-args))
	 (mincol    (or (first directive-args)  0))
	 (padchar   (or (second directive-args) #\Space))
	 (commachar (or (third directive-args)  #\,)))
    (format-integer object 10. mincol padchar 
		    (and colonp commachar) atsignp stream)))


(defun format-B (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (maximum-prefix-arguments 3 directive-args format-string-iterator)
  (let* ((object    (next-format-arg format-args))
	 (mincol    (or (first directive-args)  0))
	 (padchar   (or (second directive-args) #\Space))
	 (commachar (or (third directive-args)  #\,)))
    (format-integer object 2. mincol padchar 
		    (and colonp commachar) atsignp stream)))


(defun format-O (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (maximum-prefix-arguments 3 directive-args format-string-iterator)
  (let* ((object    (next-format-arg format-args))
	 (mincol    (or (first directive-args)  0))
	 (padchar   (or (second directive-args) #\Space))
	 (commachar (or (third directive-args)  #\,)))
    (format-integer object 8. mincol padchar 
		    (and colonp commachar) atsignp stream)))


(defun format-X (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (maximum-prefix-arguments 3 directive-args format-string-iterator)
  (let* ((object    (next-format-arg format-args))
	 (mincol    (or (first directive-args)  0))
	 (padchar   (or (second directive-args) #\Space))
	 (commachar (or (third directive-args)  #\,)))
    (format-integer object 16. mincol padchar 
		    (and colonp commachar) atsignp stream)))


(defun format-R (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (maximum-prefix-arguments 4 directive-args format-string-iterator)
  (let ((object (next-format-arg format-args)))
    (if directive-args
	(let ((radix     (first directive-args))
	      (mincol    (or (second directive-args) 0))
	      (padchar   (or (third directive-args) #\Space))
	      (commachar (or (fourth directive-args) #\,)))
	  (format-integer object radix mincol padchar 
			  (and colonp commachar) atsignp stream))
	(cond ((and colonp atsignp)
	       (print-old-roman-numeral object stream))
	      (atsignp
	       (print-roman-numeral object stream))
	      (colonp
	       (print-ordinal-number object stream))
	      (t
	       (print-cardinal-number object stream))))))


(defun print-roman-numeral (x stream)
  (roman-numeral-loop x 0 nil stream))


(defun print-old-roman-numeral (x stream)
  (roman-numeral-loop x 0 t stream))


(defun roman-numeral-loop (x level oldp stream)
  (when (> x 9)
    (multiple-value-bind (more-significant-digits least-significant-digit)
	(floor x 10)
      (if (> level 1)
	  (dotimes (i more-significant-digits)
	    (write-char #\M stream))
	  (roman-numeral-loop more-significant-digits (1+ level) oldp stream))
      (setq x least-significant-digit)))
  (cond ((and (= x 9) (not oldp))
	 (print-roman-I-char level stream)
	 (print-roman-I-char (1+ level) stream))
	((= x 5)
	 (print-roman-V-char level stream))
	((and (= x 4) (not oldp))
	 (print-roman-I-char level stream)
	 (print-roman-V-char level stream))
	((> x 5)
	 (print-roman-V-char level stream)
	 (dotimes (i (- x 5))
	   (print-roman-I-char level stream)))
	(t
	 (dotimes (i x)
	   (print-roman-I-char level stream)))))


(defun print-roman-I-char (level stream)	 
  (write-char (char "IXCM" level) stream))


(defun print-roman-V-char (level stream)
  (write-char (char "VLD" level) stream))


(defun format-P (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (maximum-prefix-arguments 0 directive-args format-string-iterator)
  (when colonp (back-up-one-format-arg format-args))
  (let* ((next-arg (next-format-arg format-args))
	 (pluralp  (not (eql next-arg 1))))
    (write-string (cond ((and atsignp pluralp) "ies")
			(atsignp               "y")
			(pluralp               "s")
			(t		       ""))
		  stream)))
	

(defun format-C (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (maximum-prefix-arguments 0 directive-args format-string-iterator)
  (let ((next-arg (next-format-arg format-args)))
    (check-type next-arg character)
    (cond ((and colonp atsignp)
	   (print-redundantly-verbose-character next-arg stream))
	  (colonp
	   (print-verbose-character next-arg stream))
	  (atsignp
	   (prin1 next-arg stream))
	  (t
	   (princ next-arg stream)))))
  

(defun format-% (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (declare (ignore format-args))
  (maximum-prefix-arguments 1 directive-args format-string-iterator)
  (forbid-colon colonp format-string-iterator)
  (forbid-atsign atsignp format-string-iterator)
  (let ((number-of-times (if directive-args (first directive-args) 1)))
    (dotimes (i number-of-times)
      (terpri stream))))


(defun format-& (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (declare (ignore format-args))
  (maximum-prefix-arguments 1 directive-args format-string-iterator)
  (forbid-colon colonp format-string-iterator)
  (forbid-atsign atsignp format-string-iterator)
  (let ((number-of-times (if directive-args (first directive-args) 1)))
    (when (> number-of-times 0)
      (fresh-line stream))
    (dotimes (i (1- number-of-times))
      (terpri stream))))


(defun format-\| (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (declare (ignore format-args))
  (maximum-prefix-arguments 1 directive-args format-string-iterator)
  (forbid-colon colonp format-string-iterator)
  (forbid-atsign atsignp format-string-iterator)
  (let ((number-of-times (if directive-args (first directive-args) 1)))
    (dotimes (i number-of-times)
      (write-char #\Page stream))))


(defun format-~ (format-args directive-args colonp atsignp stream
                format-string-iterator)
  (declare (ignore format-args))
  (maximum-prefix-arguments 1 directive-args format-string-iterator)
  (forbid-colon colonp format-string-iterator)
  (forbid-atsign atsignp format-string-iterator)
  (let ((number-of-times (if directive-args (first directive-args) 1)))
    (dotimes (i number-of-times)
      (write-char #\~ stream))))


(defun format-NEWLINE (format-args directive-args colonp atsignp stream 
		       format-string-iterator)
  (declare (ignore format-args))
  (maximum-prefix-arguments 0 directive-args format-string-iterator)
  (forbid-both colonp atsignp format-string-iterator)
  (when atsignp
    (terpri stream))
  (unless colonp
    (loop
      (let ((next-character (peek-character format-string-iterator)))
	(if (eq (syntax-type next-character) :WHITESPACE)
	    (next-character format-string-iterator)
	    (return))))))
  

(defun format-[ (format-args directive-args colonp atsignp stream
		 format-string-iterator)
  (forbid-both colonp atsignp format-string-iterator)
  (note-most-recent-open-index format-string-iterator)
  (cond (colonp  (format-colon-[ format-args directive-args 
				 stream format-string-iterator))
	(atsignp (format-@[ format-args directive-args
			    stream format-string-iterator))
	(t       (format-n[ format-args directive-args
			    stream format-string-iterator))))


(defun format-n[ (format-args di



(defun format-colon-[ (format-args directive-args stream 
		       format-string-iterator)
  (maximum-prefix-arguments 0 directive-args format-string-iterator)
  (let (false-string true-string)
    (multiple-value-bind (string ignore terminating-directive)
	(scan-a-chunk #\] format-string-iterator)
      (require-twiddle-semicolon terminating-directive)
      (setq false-string string))
    (multiple-value-bind (string ignore terminating-directive)
	(scan-a-chunk #\] format-string-iterator)
      (require-twiddle-] terminating-directive)
      (setq true-string string))
    (format-main (make-string-iterator (if (next-format-arg format-args)
					   true-string
					   false-string))
		 format-args
		 stream)))

(defun require-twiddle-semicolon (ignore))
(defun require-twiddle-] (ignore))


;;;----------------------------------------
;;; Format error
;;;----------------------------------------


(defun format-error (format-string-iterator error-string &rest error-args)
  (error "~?~%~V@T~%~3@T\"~A\"~%"
	 error-string
	 error-args
	 (+ (get-most-recent-twiddle-index format-string-iterator) 4)
	 (get-entire-format-string format-string-iterator)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	    
	  
		


