(DEFINE-FILE-INFO PACKAGE "INTERLISP" READTABLE "INTERLISP" BASE 10) (FILECREATED "18-Oct-93 12:11:00" "{Pele:mv:envos}Sources>CLTL2>CMLFORMAT.;2" 102570 previous date%: "25-Oct-91 16:43:17" "{Pele:mv:envos}Sources>CLTL2>CMLFORMAT.;1") (* ; " Copyright (c) 1986, 1987, 1988, 1989, 1990, 1991, 1993 by Venue & Xerox Corporation. All rights reserved. ") (PRETTYCOMPRINT CMLFORMATCOMS) (RPAQQ CMLFORMATCOMS ( (* ;; "The FORMAT facility") (STRUCTURES FORMAT-ERROR) (FUNCTIONS MAKE-DISPATCH-VECTOR SCALE-EXPONENT SCALE-EXPT-AUX) (FUNCTIONS FORMAT-ERROR) (VARIABLES *DIGIT-STRING* *DIGITS*) (FUNCTIONS FLONUM-TO-STRING FORMAT-WITH-CONTROL-STRING FORMAT-STRINGIFY-OUTPUT POP-FORMAT-ARG WITH-FORMAT-PARAMETERS NEXTCHAR FORMAT-PEEK FORMAT-FIND-CHAR) (FUNCTIONS FORMAT-GET-PARAMETER PARSE-FORMAT-OPERATION FORMAT-FIND-COMMAND LISP:FORMAT FORMAT-MAYBE-PPRINT SUB-FORMAT) (* ;; "Top-level entries in *FORMAT-DISPATCH-TABLE*") (FUNCTIONS FORMAT-PRINT-BINARY FORMAT-PRINT-OCTAL FORMAT-PRINT-HEXADECIMAL FORMAT-PRINT-RADIX FORMAT-FIXED FORMAT-EXPONENTIAL FORMAT-GENERAL-FLOAT FORMAT-PRINC FORMAT-PRINT-CHARACTER FORMAT-PLURAL FORMAT-PRIN1 FORMAT-TAB FORMAT-TERPRI FORMAT-FRESHLINE FORMAT-SKIP-ARGUMENTS FORMAT-PAGE FORMAT-TILDE FORMAT-DOLLARS FORMAT-INDIRECTION FORMAT-ESCAPE FORMAT-SEMICOLON-ERROR FORMAT-CONDITION FORMAT-ITERATION FORMAT-JUSTIFICATION FORMAT-CAPITALIZATION FORMAT-NEWLINE FORMAT-JUST-WRITE FORMAT-PPRINT-NEWLINE FORMAT-PPRINT-INDENT FORMAT-CALL-FUNCTION) (* ;; "Direct support for top-level entries") (FUNCTIONS FORMAT-ROUND-COLUMNS FORMAT-EAT-WHITESPACE FORMAT-PRINT-NAMED-CHARACTER FORMAT-ADD-COMMAS FORMAT-WRITE-FIELD FORMAT-PRINT-NUMBER FORMAT-PRINT-SMALL-CARDINAL FORMAT-PRINT-CARDINAL FORMAT-PRINT-CARDINAL-AUX FORMAT-PRINT-ORDINAL FORMAT-PRINT-OLD-ROMAN FORMAT-PRINT-ROMAN FORMAT-PRINT-DECIMAL FORMAT-PRINT-RADIX-AUX FORMAT-FIXED-AUX FORMAT-EXPONENT-MARKER FORMAT-EXP-AUX FORMAT-GENERAL-AUX FORMAT-UNTAGGED-CONDITION FORMAT-FUNNY-CONDITION FORMAT-BOOLEAN-CONDITION FORMAT-DO-ITERATION FORMAT-GET-TRAILING-SEGMENTS FORMAT-GET-SEGMENTS FORMAT-PPRINT-LOGICAL-BLOCK FORMAT-CHECK-JUSTIFY) (FUNCTIONS CHARPOS WHITESPACE-CHAR-P MAKE-PAD-SEGS FORMAT-LOGICAL-FILL) (FUNCTIONS NAME-ARRAY) (VARIABLES *FORMAT-ARGUMENTS* *OUTER-FORMAT-ARGUMENTS* *FORMAT-CONTROL-STRING* *FORMAT-DISPATCH-TABLE* *FORMAT-INDEX* *FORMAT-LENGTH* *FORMAT-ORIGINAL-ARGUMENTS* *FORMAT-LOGICAL-BLOCK* *FORMAT-JUSTIFICATION* *FORMAT-INCOMPATIBLE-JUSTIFICATION* *FORMAT-COLON-ITERATION* CARDINAL-ONES CARDINAL-TENS CARDINAL-TEENS CARDINAL-PERIODS ORDINAL-ONES ORDINAL-TENS) (DECLARE%: DONTEVAL@LOAD DOEVAL@COMPILE DONTCOPY COMPILERVARS (ADDVARS (NLAMA) (NLAML) (LAMA))) (* ;; "Arrange to use the correct compiler.") (PROP FILETYPE CMLFORMAT))) (* ;; "The FORMAT facility") (DEFINE-CONDITION FORMAT-ERROR (LISP:ERROR) (ARGS) [:REPORT (LISP:LAMBDA (CONDITION *STANDARD-OUTPUT*) (LISP:FORMAT T "~%%~:{~@?~%%~}" (FORMAT-ERROR-ARGS CONDITION]) (DEFMACRO MAKE-DISPATCH-VECTOR (&BODY ENTRIES) (* ;; "Hairy dispatch-table initialization macro. Takes a list of two-element lists ( ) and returns a vector char-code-limit elements in length, where the Ith element is the function associated with the character with char-code I. If the character is case-convertible, it must be given in only one case however, an entry in the vector will be made for both.") [LET ((ENTRIES (LISP:MAPCAN #'[LISP:LAMBDA (X) (LET [(LOWER (LISP:CHAR-DOWNCASE (CAR X))) (UPPER (LISP:CHAR-UPCASE (CAR X] (LISP:IF (LISP:CHAR= LOWER UPPER) (LIST X) (LIST (CONS UPPER (CDR X)) (CONS LOWER (CDR X))))] ENTRIES))) (LISP:DO ([ENTRIES (SORT ENTRIES #'(LISP:LAMBDA (X Y) (LISP:CHAR< (CAR X) (CAR Y] (CHARIDX 0 (LISP:1+ CHARIDX)) (COMTAB NIL (CONS (LISP:IF ENTRIES (LISP:IF (= (LISP:CHAR-CODE (CAAR ENTRIES)) CHARIDX) (CADR (pop ENTRIES)) NIL) NIL) COMTAB))) [(= CHARIDX 256) (LISP:IF ENTRIES (LISP:ERROR "Garbage in dispatch vector - ~S" ENTRIES)) `(LISP:MAKE-ARRAY '(256) :ELEMENT-TYPE T :INITIAL-CONTENTS ',(LISP:NREVERSE COMTAB])]) (LISP:DEFUN SCALE-EXPONENT (X) (SCALE-EXPT-AUX X 0.0 1.0 10.0 0.1 (CONSTANT (LISP:LOG 2.0 10.0)))) (LISP:DEFUN SCALE-EXPT-AUX (X ZERO ONE TEN ONE-TENTH LOG10-OF-2) [LISP:MULTIPLE-VALUE-BIND (SIG EXPONENT) (LISP:DECODE-FLOAT X) (DECLARE (IGNORE SIG)) (LISP:IF (= X ZERO) (LISP:VALUES ZERO 1) [LET* [(E (ROUND (LISP:* EXPONENT LOG10-OF-2))) (NEWX (LISP:IF (MINUSP E) (LISP:* X TEN (LISP:EXPT TEN (- -1 E))) (/ X TEN (LISP:EXPT TEN (LISP:1- E))))] (LISP:DO ((D TEN (LISP:* D TEN)) (Y NEWX (/ NEWX D)) (E E (LISP:1+ E))) [(< Y ONE) (LISP:DO ((M TEN (LISP:* M TEN)) (Z Y (LISP:* Z M)) (E E (LISP:1- E))) ((>= Z ONE-TENTH) (LISP:VALUES (/ X (LISP:EXPT 10 E)) E)))])])]) (LISP:DEFUN FORMAT-ERROR (COMPLAINT &REST FORMAT-ARGS) [LISP:ERROR 'FORMAT-ERROR :ARGS (LIST (LIST "~?~%%~S~%%~V@T^" COMPLAINT FORMAT-ARGS *FORMAT-CONTROL-STRING* (LISP:1+ *FORMAT-INDEX*]) (LISP:DEFVAR *DIGIT-STRING* (LISP:MAKE-ARRAY 50 :ELEMENT-TYPE 'LISP:STRING-CHAR :FILL-POINTER 0 :ADJUSTABLE T)) (LISP:DEFCONSTANT *DIGITS* "0123456789") (LISP:DEFUN FLONUM-TO-STRING (X &OPTIONAL WIDTH DECPLACES SCALE FMIN) (* ;; "Returns FIVE values: a string of digits with one decimal point, the string's length, T if the point is at the front, T if the point is at the end, the index of the point in the string") (LISP:IF (ZEROP X) (LISP:VALUES "." 1 T T) [LET* ((REALDP (COND (DECPLACES (LISP:IF FMIN (MAX DECPLACES FMIN) DECPLACES)) (FMIN))) [ROUND (COND [REALDP (* ;  "Foo! Compute rounding place based on size of number and scale factor") (MIN 9 (+ (DIGITSBDP X) REALDP (OR SCALE 0] (WIDTH (MAX 1 (MIN 9 (LISP:1- WIDTH] MANTSTR INTEXP) (LISP:MULTIPLE-VALUE-SETQ (MANTSTR INTEXP) (FLTSTR X ROUND)) (LISP:IF SCALE (LISP:INCF INTEXP SCALE)) (* ;;  "OK, now copy the digit string into *digit-string* with the decimal point set appropriately") (LISP:MACROLET [(STRPUT (C) `(LISP:VECTOR-PUSH-EXTEND ,C *DIGIT-STRING*] (LET* ((DIGITS (LISP:LENGTH MANTSTR)) (INDEX -1) (POINTPLACE (+ DIGITS INTEXP)) DECPNT) (* ;; "MANTSTR may have more digits than necessary; prune off its zeros. Doing this will lose if X is zero.") (IF (NOT (ZEROP X)) THEN (WHILE (AND (LISP:PLUSP DIGITS) (LISP:CHAR= (LISP:CHAR MANTSTR (LISP:1- DIGITS )) #\0)) DO (LISP:DECF DIGITS) (LISP:INCF INTEXP))) (LISP:SETF (LISP:FILL-POINTER *DIGIT-STRING*) 0) [COND ((NOT (LISP:PLUSP POINTPLACE)) (* ; " .") (STRPUT #\.) (LISP:DOTIMES (I (- POINTPLACE)) (STRPUT #\0)) (LISP:DOTIMES (I DIGITS) (STRPUT (LISP:CHAR MANTSTR I))) (SETQ DECPNT 0)) ((MINUSP INTEXP) (* ; ".") (LISP:DOTIMES (I POINTPLACE) (STRPUT (LISP:CHAR MANTSTR (LISP:INCF INDEX)))) (STRPUT #\.) (LISP:DOTIMES (I (- INTEXP)) (STRPUT (LISP:CHAR MANTSTR (LISP:INCF INDEX)))) (SETQ DECPNT (+ DIGITS INTEXP))) (T (* ; "00.") (LISP:DOTIMES (I DIGITS) (STRPUT (LISP:CHAR MANTSTR I))) (LISP:DOTIMES (I INTEXP) (STRPUT #\0)) (STRPUT #\.) (SETQ DECPNT (+ DIGITS INTEXP] (SETQ DIGITS (LISP:1- (LISP:LENGTH *DIGIT-STRING*))) (IF DECPLACES THEN (* ;; "Need extra 0s to get enough decimal places") (LISP:DOTIMES (I (- DECPLACES (- DIGITS DECPNT))) (STRPUT #\0) (LISP:INCF DIGITS))) (LISP:VALUES *DIGIT-STRING* (LISP:1+ DIGITS) (= DECPNT 0) (= DECPNT DIGITS) DECPNT])) (DEFMACRO FORMAT-WITH-CONTROL-STRING (CONTROL-STRING &BODY FORMS) (* ;; "This macro establishes the correct environment for processing an indirect control string. CONTROL-STRING is the string to process, and FORMS are the forms to do the processing. They invariably will involve a call to SUB-FORMAT. CONTROL-STRING is guaranteed to be evaluated exactly once.") `[LET ((STRING ,CONTROL-STRING)) (CONDITION-CASE (LET ((*FORMAT-CONTROL-STRING* STRING) (*FORMAT-LENGTH* (LISP:LENGTH STRING)) (*FORMAT-INDEX* 0)) ,@FORMS) (FORMAT-ERROR (C) (LISP:ERROR 'FORMAT-ERROR :ARGS (CONS (LIST "While processing indirect control string~%%~S~%%~V@T^" *FORMAT-CONTROL-STRING* (LISP:1+ *FORMAT-INDEX*)) (FORMAT-ERROR-ARGS C]) (DEFMACRO FORMAT-STRINGIFY-OUTPUT (&BODY FORMS) (* ;; "This macro collects output to the standard output stream in a string. It used to try to avoid consing new string streams if possible.") `(LISP:WITH-OUTPUT-TO-STRING (*STANDARD-OUTPUT*) ,@FORMS)) (LISP:DEFUN POP-FORMAT-ARG () (* ;; "Pops an argument from the current argument list. This is either the list of arguments given to the top-level call to FORMAT, or the argument list for the current iteration in a ~{~} construct. An error is signalled if the argument list is empty. if we're under a ~<...~:> logical block, we have to do a PPRINT-POP equivalent") (LISP:IF (AND *FORMAT-LOGICAL-BLOCK* (XP::XP-STRUCTURE-P *STANDARD-OUTPUT*) (XP::PPRINT-POP-CHECK+ *FORMAT-ARGUMENTS* *STANDARD-OUTPUT*)) (LISP:THROW 'XP::LOGICAL-BLOCK NIL) (LISP:IF *FORMAT-ARGUMENTS* (LISP:POP *FORMAT-ARGUMENTS*) (FORMAT-ERROR "Missing argument")))) (DEFMACRO WITH-FORMAT-PARAMETERS (PARMVAR PARMDEFS &BODY FORMS) (* ;; "This macro decomposes the argument list returned by PARSE-FORMAT-OPERATION. PARMVAR is the list of parameters. PARMDEFS is a list of lists of the form ( ) . The FORMS are evaluated in an environment where each is bound to either the value of the parameter supplied in the parameter list, or to its value if the parameter was omitted or explicitly defaulted.") `(LET ,[FOR PARMDEF IN PARMDEFS COLLECT `(,(LISP:FIRST PARMDEF) (OR (LISP:IF ,PARMVAR (POP ,PARMVAR)) ,(LISP:SECOND PARMDEF] (LISP:WHEN ,PARMVAR (FORMAT-ERROR "Too many parameters")) ,@FORMS)) (DEFMACRO NEXTCHAR () (* ;; "Gets the next character from the current control string. It is an error if there is none. Leave *format-index* pointing to the character returned. *") '(LISP:IF (< (LISP:INCF *FORMAT-INDEX*) *FORMAT-LENGTH*) (LISP:CHAR *FORMAT-CONTROL-STRING* *FORMAT-INDEX*) (FORMAT-ERROR "Syntax error"))) (DEFMACRO FORMAT-PEEK () (* ;; "Returns the current character, i.e. the one pointed to by *format-index*.") '(LISP:CHAR *FORMAT-CONTROL-STRING* *FORMAT-INDEX*)) (DEFMACRO FORMAT-FIND-CHAR (CHAR START END) (* ;; "Returns the index of the first occurrence of the specified character between indices START (inclusive) and END (exclusive) in the control string.") `(LISP:POSITION ,CHAR *FORMAT-CONTROL-STRING* :START ,START :END ,END :TEST 'LISP:CHAR=)) (LISP:DEFUN FORMAT-GET-PARAMETER () (* ;; "Attempts to parse a parameter, starting at the current index. Returns the value of the parameter, or NIL if none is found. On exit, *format-index* points to the first character which is not a part of the recognized parameter.") (LET [(NUMSIGN (CASE (FORMAT-PEEK) (#\+ (NEXTCHAR) NIL) (#\- (NEXTCHAR) T) (T NIL))] (CASE (FORMAT-PEEK) (#\# (NEXTCHAR) (LISP:LENGTH *FORMAT-ARGUMENTS*)) ((#\V #\v) (PROG1 (POP-FORMAT-ARG) (NEXTCHAR))) (#\' (PROG1 (NEXTCHAR) (NEXTCHAR))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (LISP:DO* [(LISP:NUMBER (LISP:DIGIT-CHAR-P (FORMAT-PEEK)) (+ (LISP:* 10 LISP:NUMBER) (LISP:DIGIT-CHAR-P (  FORMAT-PEEK ] ((NOT (LISP:DIGIT-CHAR-P (NEXTCHAR ))) (LISP:IF NUMSIGN (- LISP:NUMBER) LISP:NUMBER)))) (T NIL)))) (LISP:DEFUN PARSE-FORMAT-OPERATION () (* amd " 1-May-86 14:33") (* ;; "Parses a format directive, including flags and parameters. On entry, *format-index* should point to the '~' preceding the command. On exit, *format-index* points to the command character itself. Returns the list of parameters, the ':' flag, the '@' flag, and the command character as multiple values. Explicitly defaulted parameters appear in the list of parameters as NIL. Omitted parameters are simply not included in the list at all. *") (LET ((CH (NEXTCHAR)) PARMS COLON ATSIGN) (* ;; "First get the parameters") (SETQ PARMS (LISP:IF (OR (LISP:DIGIT-CHAR-P CH) (LISP:MEMBER CH '(#\, #\# #\V #\v #\') :TEST (FUNCTION LISP:CHAR=))) (LISP:DO ((PARMS (LIST (FORMAT-GET-PARAMETER)) (CONS (FORMAT-GET-PARAMETER) PARMS))) ((LISP:CHAR/= (FORMAT-PEEK) #\,) (LISP:NREVERSE PARMS)) (NEXTCHAR)) 'NIL)) (* ;; "Then check for : and @ (not necessarily in that order)") [LISP:LOOP (CASE (FORMAT-PEEK) (#\: (LISP:IF COLON (RETURN NIL) (SETQ COLON (NEXTCHAR)))) (#\@ (LISP:IF ATSIGN (RETURN NIL) (SETQ ATSIGN (NEXTCHAR)))) (T (RETURN NIL)))] (LISP:VALUES PARMS COLON ATSIGN (FORMAT-PEEK)))) (LISP:DEFUN FORMAT-FIND-COMMAND (COMMAND-LIST) (* ;; "Starting at the current value of *format-index*, finds the first occurrence of one of the specified directives. Embedded constructs, i.e. those inside ~ (~) %%, ~[~], ~{~}, or ~<~>, are ignored. And error is signalled if no satisfactory command is found. Otherwise, the following are returned as multiple values: The value of *format-index* at the start of the search The index of the '~' character preceding the command The parameter list of the command The ':' flag The '@' flag The command character Implementation note: The present implementation is not particulary careful with storage allocation. It would be a good idea to have a separate function for skipping embedded constructs which did not bother to cons parameter lists and then throw them away. We go to some trouble here to use POSITION for most of the searching.") [LET ((START *FORMAT-INDEX*)) (LISP:DO ((PLACE START *FORMAT-INDEX*) (TILDE (FORMAT-FIND-CHAR #\~ START *FORMAT-LENGTH*) (FORMAT-FIND-CHAR #\~ PLACE *FORMAT-LENGTH*))) ((NOT TILDE) (FORMAT-ERROR "Expecting one of ~S" COMMAND-LIST)) (SETQ *FORMAT-INDEX* TILDE) [LISP:MULTIPLE-VALUE-BIND (PARMS COLON ATSIGN COMMAND) (PARSE-FORMAT-OPERATION) (LISP:WHEN (MEMBER COMMAND COMMAND-LIST :TEST (FUNCTION LISP:CHAR=)) (RETURN (LISP:VALUES START TILDE PARMS COLON ATSIGN COMMAND))) NIL (CASE COMMAND (#\{ (NEXTCHAR) (FORMAT-FIND-COMMAND '(#\}))) (#\< (NEXTCHAR) (FORMAT-FIND-COMMAND '(#\>))) (#\( (NEXTCHAR) (FORMAT-FIND-COMMAND '(#\)))) (#\[ (NEXTCHAR) (FORMAT-FIND-COMMAND '(#\]))) ((#\} #\> #\) #\]) (FORMAT-ERROR "No matching bracket")))])]) (LISP:DEFUN LISP:FORMAT (LISP::DESTINATION LISP::CONTROL-STRING &REST LISP::FORMAT-ARGUMENTS) [LET ((*FORMAT-ORIGINAL-ARGUMENTS* LISP::FORMAT-ARGUMENTS) (*FORMAT-ARGUMENTS* LISP::FORMAT-ARGUMENTS) (*FORMAT-CONTROL-STRING* LISP::CONTROL-STRING) *FORMAT-LOGICAL-BLOCK* *FORMAT-JUSTIFICATION* *FORMAT-INCOMPATIBLE-JUSTIFICATION*) (LISP:UNLESS (EQ LISP:*PRINT-READABLY* 'XCL::PRINTING-READABLY) (LISP::CHECK-READABLY "Arbitrary FORMAT control strings" 'LISP:FORMAT)) (COND ((NOT LISP::DESTINATION) (LISP:WITH-OUTPUT-TO-STRING (*STANDARD-OUTPUT*) (FORMAT-MAYBE-PPRINT))) ((LISP:STRINGP LISP::DESTINATION) (LISP:WITH-OUTPUT-TO-STRING (*STANDARD-OUTPUT* LISP::DESTINATION) (FORMAT-MAYBE-PPRINT)) NIL) (T (LET [(*STANDARD-OUTPUT* (LISP:IF (EQ LISP::DESTINATION T) *STANDARD-OUTPUT* (* ;; " FORMAT extension - IL:DESTINATION may be anything that IL:GETSTREAM can coerce into being a stream") (GETSTREAM LISP::DESTINATION 'OUTPUT))] (FORMAT-MAYBE-PPRINT) NIL]) (LISP:DEFUN FORMAT-MAYBE-PPRINT () (LISP:CATCH 'FORMAT-ESCAPE (LISP:CATCH 'FORMAT-COLON-ESCAPE (LISP:IF (STRINGP *FORMAT-CONTROL-STRING*) (SUB-FORMAT 0 (LISP:LENGTH *FORMAT-CONTROL-STRING*)) (LISP:APPLY *FORMAT-CONTROL-STRING* *STANDARD-OUTPUT* *FORMAT-ARGUMENTS*))))) (LISP:DEFUN SUB-FORMAT (START END) (* ;; "This function does the real work of format. The segment of the control string between indiced START (inclusive) and END (exclusive) is processed as follows: Text not part of a directive is output without further processing (except if *FORMAT-LOGICAL-BLOCK* is :FILL; blank sequences must then be followed by fill-style conditional newlines). Directives are parsed along with their parameters and flags, and the appropriate handlers invoked with the arguments COLON, ATSIGN, and PARMS. Implementation Note: FORMAT-FIND-CHAR uses the POSITION stream operation for speed. This is potentially faster than character-at-a-time searching.") [LET ((*FORMAT-INDEX* START) (*FORMAT-LENGTH* END)) (DECLARE (LISP:SPECIAL *FORMAT-INDEX* *FORMAT-LENGTH*)) (LISP:DO* ((PLACE START *FORMAT-INDEX*) (TILDE (FORMAT-FIND-CHAR #\~ START END) (FORMAT-FIND-CHAR #\~ PLACE END)) (LAST-CMD-NEWLINE?)) ((NOT TILDE) (LISP:IF (EQ *FORMAT-LOGICAL-BLOCK* :FILL) (LISP:WHEN (FORMAT-LOGICAL-FILL *FORMAT-CONTROL-STRING* *STANDARD-OUTPUT* PLACE END LAST-CMD-NEWLINE?) (XP:PPRINT-NEWLINE :FILL *STANDARD-OUTPUT*)) (WRITE-STRING* *FORMAT-CONTROL-STRING* *STANDARD-OUTPUT* PLACE END))) (LISP:WHEN (> TILDE PLACE) (LISP:IF (EQ *FORMAT-LOGICAL-BLOCK* :FILL) (FORMAT-LOGICAL-FILL *FORMAT-CONTROL-STRING* *STANDARD-OUTPUT* PLACE TILDE LAST-CMD-NEWLINE?) (WRITE-STRING* *FORMAT-CONTROL-STRING* *STANDARD-OUTPUT* PLACE TILDE))) (SETQ *FORMAT-INDEX* TILDE) [LISP:MULTIPLE-VALUE-BIND (PARMS COLON ATSIGN COMMAND) (PARSE-FORMAT-OPERATION) (SETQ LAST-CMD-NEWLINE? (LISP:CHAR= COMMAND #\Newline)) (LET [(CMDFUN (LISP:AREF *FORMAT-DISPATCH-TABLE* (LISP:CHAR-CODE COMMAND] (LISP:IF CMDFUN (LISP:FUNCALL CMDFUN COLON ATSIGN PARMS) (FORMAT-ERROR "Illegal FORMAT command ~~~C" COMMAND))] (LISP:UNLESS (< (LISP:INCF *FORMAT-INDEX*) END) (RETURN)))]) (* ;; "Top-level entries in *FORMAT-DISPATCH-TABLE*") (LISP:DEFUN FORMAT-PRINT-BINARY (COLON ATSIGN PARMS) (* ;; "Binary ~B") (FORMAT-PRINT-NUMBER (POP-FORMAT-ARG) 2 COLON ATSIGN PARMS)) (LISP:DEFUN FORMAT-PRINT-OCTAL (COLON ATSIGN PARMS) (* ;; "Octal ~O") (FORMAT-PRINT-NUMBER (POP-FORMAT-ARG) 8 COLON ATSIGN PARMS)) (LISP:DEFUN FORMAT-PRINT-HEXADECIMAL (COLON ATSIGN PARMS) (* ;; "Hexadecimal ~X") (FORMAT-PRINT-NUMBER (POP-FORMAT-ARG) 16 COLON ATSIGN PARMS)) (LISP:DEFUN FORMAT-PRINT-RADIX (COLON ATSIGN PARMS) (* ;; "Radix ~R") [LET ((LISP:NUMBER (POP-FORMAT-ARG))) (LISP:IF (CAR PARMS) (FORMAT-PRINT-NUMBER LISP:NUMBER (pop PARMS) COLON ATSIGN PARMS) (LISP:IF PARMS (FORMAT-WRITE-FIELD (FORMAT-STRINGIFY-OUTPUT (FORMAT-PRINT-RADIX-AUX LISP:NUMBER COLON ATSIGN)) (CADR PARMS) 1 0 (COND ((CADDR PARMS)) (T #\Space) NIL) T) (FORMAT-PRINT-RADIX-AUX LISP:NUMBER COLON ATSIGN)))]) (LISP:DEFUN FORMAT-FIXED (COLON ATSIGN PARMS) (* ;; "Fixed-format floating point ~F") (LISP:WHEN COLON (FORMAT-ERROR "Colon flag not allowed")) [WITH-FORMAT-PARAMETERS PARMS ((W NIL) (D NIL) (K NIL) (OVF NIL) (PAD #\Space)) (* ;; "Note that the scale factor k defaults to nil. This is interpreted as zero by flonum-to-string, but more efficiently.") (LET ((LISP:NUMBER (POP-FORMAT-ARG)) (*PRINT-ESCAPE* NIL)) (LISP:IF (FLOATP LISP:NUMBER) (FORMAT-FIXED-AUX LISP:NUMBER W D K OVF PAD ATSIGN) (LISP:IF (LISP:RATIONALP LISP:NUMBER) (FORMAT-FIXED-AUX (COERCE LISP:NUMBER 'FLOAT) W D K OVF PAD ATSIGN) (LET ((*PRINT-BASE* 10)) (FORMAT-WRITE-FIELD (LISP:PRINC-TO-STRING LISP:NUMBER) W 1 0 #\Space T))))]) (LISP:DEFUN FORMAT-EXPONENTIAL (COLON ATSIGN PARMS) (* ;; "Exponential-format floating point ~E") (LISP:WHEN COLON (FORMAT-ERROR "Colon flag not allowed")) [WITH-FORMAT-PARAMETERS PARMS ((W NIL) (D NIL) (E NIL) (K 1) (OVF NIL) (PAD #\Space) (MARKER NIL)) (LET ((LISP:NUMBER (POP-FORMAT-ARG)) (*PRINT-ESCAPE* NIL)) (LISP:IF (FLOATP LISP:NUMBER) (FORMAT-EXP-AUX LISP:NUMBER W D E K OVF PAD MARKER ATSIGN) (LISP:IF (LISP:RATIONALP LISP:NUMBER) (FORMAT-EXP-AUX (COERCE LISP:NUMBER 'FLOAT) W D E K OVF PAD MARKER ATSIGN) (LET ((*PRINT-BASE* 10)) (FORMAT-WRITE-FIELD (LISP:PRINC-TO-STRING LISP:NUMBER) W 1 0 #\Space T))))]) (LISP:DEFUN FORMAT-GENERAL-FLOAT (COLON ATSIGN PARMS) (* ;; "General Floating Point --- ~G") (LISP:WHEN COLON (FORMAT-ERROR "Colon flag not allowed")) [WITH-FORMAT-PARAMETERS PARMS ((W NIL) (D NIL) (E NIL) (K NIL) (OVF #\*) (PAD #\Space) (MARKER NIL)) (LET ((LISP:NUMBER (POP-FORMAT-ARG)) (*PRINT-ESCAPE* NIL)) (* ;; "The Excelsior edition does not say what to do if the argument is not a float. Here, we adopt the conventions used by ~F and ~E.") (LISP:IF (FLOATP LISP:NUMBER) (FORMAT-GENERAL-AUX LISP:NUMBER W D E K OVF PAD MARKER ATSIGN) (LISP:IF (LISP:RATIONALP LISP:NUMBER) (FORMAT-GENERAL-AUX (COERCE LISP:NUMBER 'FLOAT) W D E K OVF PAD MARKER ATSIGN) (LET ((*PRINT-BASE* 10)) (FORMAT-WRITE-FIELD (LISP:PRINC-TO-STRING LISP:NUMBER) W 1 0 #\Space T))))]) (LISP:DEFUN FORMAT-PRINC (COLON ATSIGN PARMS) (* ;; "Ascii ~A *") [LET ((ARG (POP-FORMAT-ARG))) (LISP:IF (NULL PARMS) (LISP:IF ARG (LISP:PRINC ARG) (LISP:IF COLON (WRITE-STRING* "()") (LISP:PRINC NIL))) (WITH-FORMAT-PARAMETERS PARMS ((MINCOL 0) (COLINC 1) (MINPAD 0) (PADCHAR #\Space)) (FORMAT-WRITE-FIELD (LISP:IF ARG (LISP:PRINC-TO-STRING ARG) (LISP:IF COLON "()" (LISP:PRINC-TO-STRING NIL))) MINCOL COLINC MINPAD PADCHAR ATSIGN)))]) (LISP:DEFUN FORMAT-PRINT-CHARACTER (COLON ATSIGN PARMS) (* ;; "Character ~C") [WITH-FORMAT-PARAMETERS PARMS NIL (LET ((LISP:CHAR (POP-FORMAT-ARG))) (LISP:UNLESS (LISP:CHARACTERP LISP:CHAR) (FORMAT-ERROR "Argument must be a character")) (COND ((AND (NOT COLON) (NOT ATSIGN)) (LISP:WRITE-CHAR LISP:CHAR)) ((AND ATSIGN (NOT COLON)) (LISP:PRIN1 LISP:CHAR)) (T (FORMAT-PRINT-NAMED-CHARACTER LISP:CHAR COLON]) (LISP:DEFUN FORMAT-PLURAL (COLON ATSIGN PARMS) (* ;; "Pluralize word ~P") (LISP:WHEN PARMS (FORMAT-ERROR "Parameters not allowed")) (LISP:WHEN COLON (* ;; "Back up one argument first ") [LET ((CDRS (- (LISP:LENGTH *FORMAT-ORIGINAL-ARGUMENTS*) (LISP:LENGTH *FORMAT-ARGUMENTS*) 1))) (LISP:IF (MINUSP CDRS) (FORMAT-ERROR "No previous argument") (SETQ *FORMAT-ARGUMENTS* (LISP:NTHCDR CDRS *FORMAT-ORIGINAL-ARGUMENTS*)))]) (LISP:IF (EQL (POP-FORMAT-ARG) 1) (WRITE-STRING* (LISP:IF ATSIGN "y" "")) (WRITE-STRING* (LISP:IF ATSIGN "ies" "s")))) (LISP:DEFUN FORMAT-PRIN1 (COLON ATSIGN PARMS) (* ;; "S-expression ~S") [LET ((ARG (POP-FORMAT-ARG))) (LISP:IF (NULL PARMS) (LISP:IF ARG (LISP:PRIN1 ARG) (LISP:IF COLON (WRITE-STRING* "()") (LISP:PRIN1 NIL))) (WITH-FORMAT-PARAMETERS PARMS ((MINCOL 0) (COLINC 1) (MINPAD 0) (PADCHAR #\Space)) (FORMAT-WRITE-FIELD (LISP:IF ARG (LISP:PRIN1-TO-STRING ARG) (LISP:IF COLON "()" (LISP:PRIN1-TO-STRING NIL))) MINCOL COLINC MINPAD PADCHAR ATSIGN)))]) (LISP:DEFUN FORMAT-TAB (COLON ATSIGN PARMS) (* ;; "Tabulation ~T") [WITH-FORMAT-PARAMETERS PARMS ((COLNUM 1) (COLINC 1)) (IF COLON THEN (LISP:WHEN *FORMAT-JUSTIFICATION* (FORMAT-ERROR "~:T not allowed under vanilla justification" )) (LISP:UNLESS (XP::XP-STRUCTURE-P *STANDARD-OUTPUT*) (FORMAT-ERROR "Can't PPRINT-TAB a non-XP stream")) (LISP:IF (EQ *FORMAT-INCOMPATIBLE-JUSTIFICATION* :JUSTIFY) (FORMAT-ERROR "Pretty-printer directive and ~<...~:;...~> in same format string") (SETQ *FORMAT-INCOMPATIBLE-JUSTIFICATION* :XP)) (LISP:IF ATSIGN (XP:PPRINT-TAB :SECTION-RELATIVE COLNUM COLINC) (XP:PPRINT-TAB :SECTION COLNUM COLINC)) ELSE (LET ((POSITION (POSITION *STANDARD-OUTPUT*)) SPACES) (IF ATSIGN THEN (SETQ SPACES COLNUM) [IF POSITION THEN (IF (NOT (ZEROP COLINC)) THEN (LISP:MULTIPLE-VALUE-BIND (Q R) (LISP:CEILING (+ POSITION COLNUM) COLINC) (LISP:DECF SPACES R] ELSE (IF POSITION THEN [IF (< POSITION COLNUM) THEN (SETQ SPACES (- COLNUM POSITION)) ELSE (IF (ZEROP COLINC) THEN (SETQ SPACES 0) ELSE (LISP:MULTIPLE-VALUE-BIND (Q R) (LISP:CEILING (- POSITION COLNUM) COLINC) (SETQ SPACES (IF (ZEROP R) THEN COLINC ELSE (- R] ELSE (SETQ SPACES 2))) (LISP:DOTIMES (S SPACES) (LISP:WRITE-CHAR #\Space *STANDARD-OUTPUT*))]) (LISP:DEFUN FORMAT-TERPRI (COLON ATSIGN PARMS) (* ;; "Newline ~&") (LISP:WHEN (OR COLON ATSIGN) (FORMAT-ERROR "Flags not allowed")) (WITH-FORMAT-PARAMETERS PARMS ((REPEAT-COUNT 1)) (LISP:DOTIMES (I REPEAT-COUNT) (LISP:TERPRI *STANDARD-OUTPUT*)))) (LISP:DEFUN FORMAT-FRESHLINE (COLON ATSIGN PARMS) (* ;; "Fresh-line ~%%") (LISP:WHEN (OR COLON ATSIGN) (FORMAT-ERROR "Flags not allowed")) (WITH-FORMAT-PARAMETERS PARMS ((REPEAT-COUNT 1)) (LISP:FRESH-LINE *STANDARD-OUTPUT*) (LISP:DOTIMES (I (LISP:1- REPEAT-COUNT)) (LISP:TERPRI *STANDARD-OUTPUT*)))) (LISP:DEFUN FORMAT-SKIP-ARGUMENTS (COLON ATSIGN PARMS) (* ;; "Skip arguments (relative goto) ~*") [LISP:FLET [(RECONS? (L) (COND ((OR (NOT XP::*CIRCULARITY-HASH-TABLE*) (EQ L *FORMAT-ARGUMENTS*)) L) [(TAILP *FORMAT-ARGUMENTS* L) (* ;; "We've gone backwards and need to recons") (LISP:DO* ((RESULT (CONS (CAR L) NIL)) (RP RESULT (CDR RP)) (LP (CDR L) (CDR LP))) ((EQ LP *FORMAT-ARGUMENTS*) (RPLACD RP LP) RESULT) (RPLACD RP (CONS (CAR LP) NIL)))] (T L] (WITH-FORMAT-PARAMETERS PARMS ((LISP:COUNT (LISP:IF ATSIGN 0 1))) (COND [ATSIGN (LISP:WHEN (OR (MINUSP LISP:COUNT) (> LISP:COUNT (LISP:LENGTH *FORMAT-ORIGINAL-ARGUMENTS*))) (FORMAT-ERROR "Illegal to go to non-existant argument")) (SETQ *FORMAT-ARGUMENTS* (RECONS? (LISP:NTHCDR LISP:COUNT *FORMAT-ORIGINAL-ARGUMENTS*] [COLON (LET ((CDRS (- (LISP:LENGTH *FORMAT-ORIGINAL-ARGUMENTS*) (LISP:LENGTH *FORMAT-ARGUMENTS*) LISP:COUNT))) (LISP:IF (MINUSP CDRS) (FORMAT-ERROR "Skip to nonexistant argument") (SETQ *FORMAT-ARGUMENTS* (RECONS? (LISP:NTHCDR CDRS *FORMAT-ORIGINAL-ARGUMENTS* ))))] (T (LISP:IF (> LISP:COUNT (LISP:LENGTH *FORMAT-ARGUMENTS*)) (FORMAT-ERROR "Skip to nonexistant argument") (SETQ *FORMAT-ARGUMENTS* (LISP:NTHCDR LISP:COUNT *FORMAT-ARGUMENTS*)))]) (LISP:DEFUN FORMAT-PAGE (COLON ATSIGN PARMS) (* ;; " Page ~|") (LISP:WHEN (OR COLON ATSIGN) (FORMAT-ERROR "Flags not allowed")) (WITH-FORMAT-PARAMETERS PARMS ((REPEAT-COUNT 1)) (LISP:DOTIMES (I REPEAT-COUNT) (LISP:WRITE-CHAR #\Page)))) (LISP:DEFUN FORMAT-TILDE (COLON ATSIGN PARMS) (* ;; "Print a tilde ~~") (LISP:WHEN (OR COLON ATSIGN) (FORMAT-ERROR "Flags not allowed")) (WITH-FORMAT-PARAMETERS PARMS ((REPEAT-COUNT 1)) (LISP:DOTIMES (I REPEAT-COUNT) (LISP:WRITE-CHAR #\~)))) (LISP:DEFUN FORMAT-DOLLARS (COLON ATSIGN PARMS) (* ;; "Dollars floating-point format ~$") [WITH-FORMAT-PARAMETERS PARMS ((D 2) (N 1) (FW 0) (PAD #\Space)) (LET* ((LISP:NUMBER (POP-FORMAT-ARG)) (SIGNSTR (LISP:IF (MINUSP LISP:NUMBER) "-" (LISP:IF ATSIGN "+" ""))) (*PRINT-ESCAPE* NIL)) (LISP:MULTIPLE-VALUE-BIND (STR NUMLENGTH IG2 IG3 POINTPLACE) (FLONUM-TO-STRING (ABS LISP:NUMBER) NIL D NIL) (DECLARE (IGNORE IG2 IG3)) (LISP:WHEN COLON (WRITE-STRING* SIGNSTR)) (LISP:DOTIMES [I (- FW NUMLENGTH (LISP:LENGTH SIGNSTR) (MAX 0 (- N POINTPLACE] (LISP:WRITE-CHAR PAD)) (LISP:UNLESS COLON (WRITE-STRING* SIGNSTR)) (LISP:DOTIMES (I (- N POINTPLACE)) (LISP:WRITE-CHAR #\0)) (WRITE-STRING* STR]) (LISP:DEFUN FORMAT-INDIRECTION (COLON ATSIGN PARMS) (* ;; "Indirection ~?") (LISP:WHEN COLON (FORMAT-ERROR "Colon modifier not allowed")) (LISP:WHEN PARMS (FORMAT-ERROR "Parameters not allowed")) [LET ((STRING (POP-FORMAT-ARG))) (* ;; "I don't understand; it should obviously be legal to pass a FORMATTER-generated function as an indirect %"string%" argument, but CLtL2 says no.") (LISP:UNLESS (LISP:STRINGP STRING) (FORMAT-ERROR "Indirected control string is not a string")) (FORMAT-WITH-CONTROL-STRING STRING (LET (*FORMAT-INCOMPATIBLE-JUSTIFICATION*) (* ;;  "New foramt string, so new scope for incompatible justifications") (LISP:IF ATSIGN (SUB-FORMAT 0 *FORMAT-LENGTH*) (LET ((*FORMAT-ARGUMENTS* (POP-FORMAT-ARG ))) (SUB-FORMAT 0 *FORMAT-LENGTH*)))]) (LISP:DEFUN FORMAT-ESCAPE (COLON ATSIGN PARMS) (* ;; "Up and Out (Escape) ~^") (* ;; "I THINK this does the right thing under a ~<...~:> logical-block; we'll see...") (LISP:WHEN ATSIGN (FORMAT-ERROR "FORMAT command ~~~:[~;:~]@^ is undefined" COLON)) (LISP:WHEN (AND COLON (NOT *FORMAT-COLON-ITERATION*)) (FORMAT-ERROR "FORMAT command ~~:^ is illegal outside a ~~:{...~~}")) (LISP:WHEN (LISP:IF (LISP:FIRST PARMS) (LISP:IF (LISP:SECOND PARMS) (LISP:IF (LISP:THIRD PARMS) (LISP:TYPECASE (LISP:SECOND PARMS) (INTEGER (<= (LISP:FIRST PARMS) (LISP:SECOND PARMS) (LISP:THIRD PARMS))) (LISP:CHARACTER (LISP:CHAR< (LISP:FIRST PARMS) (LISP:SECOND PARMS) (LISP:THIRD PARMS))) (T NIL)) (EQUAL (LISP:FIRST PARMS) (LISP:SECOND PARMS))) (ZEROP (LISP:FIRST PARMS))) (NOT (LISP:IF COLON *OUTER-FORMAT-ARGUMENTS* *FORMAT-ARGUMENTS*))) (* ;; "(THROW (IF IL:COLON 'IL:FORMAT-COLON-ESCAPE 'IL:FORMAT-ESCAPE) NIL)") (LISP:THROW (LISP:IF COLON 'FORMAT-COLON-ESCAPE (LISP:IF *FORMAT-COLON-ITERATION* 'FORMAT-ESCAPE 'FORMAT-COLON-ESCAPE)) NIL))) (LISP:DEFUN FORMAT-SEMICOLON-ERROR (COLON ATSIGN PARAMS) (DECLARE (IGNORE COLON ATSIGN PARAMS)) (FORMAT-ERROR "Unexpected semicolon (probably a missing ~~ somewhere).")) (LISP:DEFUN FORMAT-CONDITION (COLON ATSIGN PARMS) (LISP:WHEN PARMS (LISP:PUSH (POP PARMS) *FORMAT-ARGUMENTS*) (LISP:UNLESS (NULL PARMS) (FORMAT-ERROR "Too many parameters to ~["))) (NEXTCHAR) (COND (COLON (LISP:WHEN ATSIGN (FORMAT-ERROR "~~:@[ undefined")) (FORMAT-BOOLEAN-CONDITION)) (ATSIGN (FORMAT-FUNNY-CONDITION)) (T (FORMAT-UNTAGGED-CONDITION)))) (LISP:DEFUN FORMAT-ITERATION (COLON ATSIGN PARMS) (* ;; "Iteration ~{ ... ~}") [WITH-FORMAT-PARAMETERS PARMS ((MAX-ITER -1)) (NEXTCHAR) (LISP:MULTIPLE-VALUE-BIND (PREV TILDE END-PARMS END-COLON END-ATSIGN) (FORMAT-FIND-COMMAND '(#\})) (LISP:WHEN (OR END-ATSIGN END-PARMS) (FORMAT-ERROR "Illegal terminator for ~~{")) (LISP:IF (= PREV TILDE) (LET ((STRING (POP-FORMAT-ARG))) (* ;; "Use an argument as the control string if ~{~} is empty") (LISP:UNLESS (LISP:STRINGP STRING) (FORMAT-ERROR "Control string is not a string")) (FORMAT-WITH-CONTROL-STRING STRING (FORMAT-DO-ITERATION 0 *FORMAT-LENGTH* MAX-ITER COLON ATSIGN END-COLON))) (FORMAT-DO-ITERATION PREV TILDE MAX-ITER COLON ATSIGN END-COLON))]) (LISP:DEFUN FORMAT-JUSTIFICATION (COLON ATSIGN PARMS) [LISP:MULTIPLE-VALUE-BIND (PREV TILDE ENDPARMS ENDCOLON ENDATSIGN) (FORMAT-FIND-COMMAND '(#\>)) (LISP:IF ENDCOLON (PROGN (* ;; "May as well trap a few errors early") (LISP:WHEN PARMS (SETQ *FORMAT-INDEX* PREV) (FORMAT-ERROR "Logical-block does not accept parameters")) (LISP:WHEN ENDPARMS (FORMAT-ERROR "Logical-block close does not accept parameters" )) (LISP:WHEN *FORMAT-JUSTIFICATION* (FORMAT-ERROR "~<...~:> not allowed under vanilla justification" )) (FORMAT-CHECK-JUSTIFY) (FORMAT-PPRINT-LOGICAL-BLOCK COLON ATSIGN ENDATSIGN (LISP:1+ PREV) TILDE)) [WITH-FORMAT-PARAMETERS PARMS ((MINCOL 0) (COLINC 1) (MINPAD 0) (PADCHAR #\Space)) (LISP:UNLESS (AND (LISP:INTEGERP MINCOL) (NOT (MINUSP MINCOL))) (FORMAT-ERROR "Mincol must be a non-negative integer - ~S" MINCOL)) (LISP:UNLESS (AND (LISP:INTEGERP COLINC) (LISP:PLUSP COLINC)) (FORMAT-ERROR "Colinc must be a positive integer - ~S" COLINC)) (LISP:UNLESS (AND (LISP:INTEGERP MINPAD) (NOT (MINUSP MINPAD))) (FORMAT-ERROR "Minpad must be a non-negative integer - ~S" MINPAD)) (LISP:UNLESS (LISP:CHARACTERP PADCHAR) (FORMAT-ERROR "Padchar must be a character - ~S" PADCHAR)) (SETQ *FORMAT-INDEX* PREV) (NEXTCHAR) (LISP:MULTIPLE-VALUE-BIND (SPECIAL-ARG SPECIAL-PARMS SEGMENTS NUMSEGS NUMCHARS) (FORMAT-GET-SEGMENTS) (LISP:IF (EQ *FORMAT-INCOMPATIBLE-JUSTIFICATION* :XP) (FORMAT-ERROR "Pretty-printer directive and ~<...~:;...~> in same format string") (SETQ *FORMAT-INCOMPATIBLE-JUSTIFICATION* :JUSTIFY)) (LET* ((*FORMAT-JUSTIFICATION* T) [PADSEGS (LISP:IF (= NUMSEGS 1) (LISP:IF (AND COLON ATSIGN) 2 1) (+ (LISP:IF COLON 1 0) (LISP:1- NUMSEGS) (LISP:IF ATSIGN 1 0)))] (WIDTH (FORMAT-ROUND-COLUMNS (+ NUMCHARS (LISP:* MINPAD PADSEGS)) MINCOL COLINC)) (SPACES (MAKE-PAD-SEGS (- WIDTH NUMCHARS) PADSEGS))) (LISP:IF (= NUMSEGS 1) [COND ((AND ATSIGN (NOT COLON)) (LISP:PUSH '0 SPACES)) ((OR (AND COLON (NOT ATSIGN)) (AND (NOT ATSIGN) (NOT COLON))) (NCONC SPACES '(0] (PROGN (LISP:IF (OR (AND COLON (NOT ATSIGN)) (AND (NOT ATSIGN) (NOT COLON))) (NCONC SPACES '(0))) (LISP:IF (OR (AND ATSIGN (NOT COLON)) (AND (NOT ATSIGN) (NOT COLON))) (LISP:PUSH '0 SPACES)))) (LISP:WHEN SPECIAL-ARG [WITH-FORMAT-PARAMETERS SPECIAL-PARMS ((SPARE 0) (LINEL (OR (LINELENGTH) 72))) (LET ((POS (OR (CHARPOS *STANDARD-OUTPUT*) 0))) (LISP:WHEN (> (+ POS WIDTH SPARE) LINEL) (WRITE-STRING* SPECIAL-ARG]) (LISP:DO ((SEGS SEGMENTS (CDR SEGS)) (SPCS SPACES (CDR SPCS))) ((NULL SEGS) (LISP:DOTIMES (I (CAR SPCS)) (LISP:WRITE-CHAR PADCHAR))) (LISP:DOTIMES (I (CAR SPCS)) (LISP:WRITE-CHAR PADCHAR)) (WRITE-STRING* (CAR SEGS)))])]) (LISP:DEFUN FORMAT-CAPITALIZATION (COLON ATSIGN PARMS) (* ;; "Capitalize ~(") (LISP:WHEN PARMS (FORMAT-ERROR "No parameters allowed to ~~(")) (NEXTCHAR) [LISP:MULTIPLE-VALUE-BIND (PREV TILDE END-PARMS END-COLON END-ATSIGN) (FORMAT-FIND-COMMAND '(#\))) (LISP:WHEN (OR END-PARMS END-COLON END-ATSIGN) (FORMAT-ERROR "Flags or parameters not allowed")) (* ;; "The messy CL:CATCH nesting below and the funky ESCAPE and SUB-ESCAPE stuff is to record any escape attempts done during the processing of the stuff to be capitalized, so the escape can be continued after doing the WRITE-STRING*. This can ALMOST be done with an UNWIND-PROTECT, but not quite.") (LET* [(ESCAPE NIL) (STRING (FORMAT-STRINGIFY-OUTPUT (SETQ ESCAPE 'FORMAT-COLON-ESCAPE) (LISP:CATCH 'FORMAT-COLON-ESCAPE (LET ((SUB-ESCAPE 'FORMAT-ESCAPE)) (LISP:CATCH 'FORMAT-ESCAPE (SUB-FORMAT PREV TILDE) (SETQ SUB-ESCAPE NIL)) (LISP:SETQ ESCAPE SUB-ESCAPE)))] [WRITE-STRING* (COND ((AND ATSIGN COLON) (LISP:NSTRING-UPCASE STRING)) (COLON (LISP:NSTRING-CAPITALIZE STRING)) [ATSIGN (* ; "Capitalize the first word only") (LET ((STRLEN (LISP:LENGTH STRING))) (LISP:NSTRING-DOWNCASE STRING) (LISP:DO ((I 0 (LISP:1+ I))) ((OR (<= STRLEN I) (LISP:ALPHA-CHAR-P (LISP:CHAR STRING I))) (LISP:SETF (LISP:CHAR STRING I) (LISP:CHAR-UPCASE (LISP:CHAR STRING I) )) STRING))] (T (LISP:NSTRING-DOWNCASE STRING] (AND ESCAPE (LISP:THROW ESCAPE NIL]) (LISP:DEFUN FORMAT-NEWLINE (COLON ATSIGN PARMS) (LISP:WHEN PARMS (FORMAT-ERROR "Parameters not allowed")) (COND (COLON (LISP:WHEN ATSIGN (FORMAT-ERROR "~:@ is undefined"))) (ATSIGN (LISP:TERPRI *STANDARD-OUTPUT*) (FORMAT-EAT-WHITESPACE)) (T (FORMAT-EAT-WHITESPACE)))) (LISP:DEFUN FORMAT-JUST-WRITE (COLON ATSIGN PARMS) (* ;; "Unless I'm REALLY confused, *PRINT-LEVEL* wil be handled correctly without major magic.") [COND (PARMS (FORMAT-ERROR "Parameters not allowed")) ((AND *FORMAT-JUSTIFICATION* (OR (NOT *CLTL2-PEDANTIC*) COLON *PRINT-PRETTY*)) (* ;; "Minor extension; CLtL2 says ~W barfs under ~<...~> regardless. I think ~W should be OK under it unless it will force pretty-printing") (FORMAT-ERROR "~W not allowed under vanilla justification")) (T (* ;; "We don't use FORMAT-CHECK-JUSTIFY here because of the *CLTL2-PEDANTIC* check") (LISP:IF (AND (EQ *FORMAT-INCOMPATIBLE-JUSTIFICATION* :JUSTIFY) (OR (NOT *CLTL2-PEDANTIC*) COLON *PRINT-PRETTY*)) (FORMAT-ERROR "Pretty-printer directive and ~<...~:;...~> in same format string") (SETQ *FORMAT-INCOMPATIBLE-JUSTIFICATION* :XP)) (WRITE (POP-FORMAT-ARG) :PRETTY (OR *PRINT-PRETTY* COLON) :LEVEL (LISP:IF ATSIGN NIL *PRINT-LEVEL*) :LENGTH (LISP:IF ATSIGN NIL *PRINT-LENGTH*)]) (LISP:DEFUN FORMAT-PPRINT-NEWLINE (COLON ATSIGN PARMS) [COND (PARMS (FORMAT-ERROR "Parameters not allowed")) (*FORMAT-JUSTIFICATION* (FORMAT-ERROR "~_ not allowed under vanilla justification")) (T (FORMAT-CHECK-JUSTIFY) (LISP:IF COLON (LISP:IF ATSIGN (XP:PPRINT-NEWLINE :MANDATORY) (XP:PPRINT-NEWLINE :FILL)) (LISP:IF ATSIGN (XP:PPRINT-NEWLINE :MISER) (XP:PPRINT-NEWLINE :LINEAR)))]) (LISP:DEFUN FORMAT-PPRINT-INDENT (COLON ATSIGN PARMS) [COND (ATSIGN (FORMAT-ERROR "@ flag not allowed")) (*FORMAT-JUSTIFICATION* (FORMAT-ERROR "~I not allowed under vanilla justification")) (T (FORMAT-CHECK-JUSTIFY) (WITH-FORMAT-PARAMETERS PARMS ((N 0)) (LISP:IF COLON (XP:PPRINT-INDENT :CURRENT N) (XP:PPRINT-INDENT :BLOCK N))]) (LISP:DEFUN FORMAT-CALL-FUNCTION (COLON ATSIGN PARMS) (* ;; "First eat the leading /") (NEXTCHAR) (* ;; "Then find the function name, being paranoid about package-qualifiers") (LET ((CLOSING/ (FORMAT-FIND-CHAR #\/ *FORMAT-INDEX* *FORMAT-LENGTH*)) AFTER-COLONS BEFORE-COLONS FN-TO-CALL) (LISP:UNLESS CLOSING/ (FORMAT-ERROR "Missing closing /")) (* ;; "Move AFTER-COLONS to after the colon seperator ") (LISP:WHEN (SETQ AFTER-COLONS (FORMAT-FIND-CHAR #\: *FORMAT-INDEX* CLOSING/)) (SETQ BEFORE-COLONS AFTER-COLONS) (LISP:WHEN (EQL #\: (LISP:CHAR *FORMAT-CONTROL-STRING* (LISP:INCF AFTER-COLONS))) (LISP:WHEN (EQL #\: (LISP:CHAR *FORMAT-CONTROL-STRING* (LISP:INCF AFTER-COLONS))) (FORMAT-ERROR "Too many colons in function-name")))) (* ;; "Look up the function symbol") [SETQ FN-TO-CALL (LISP:IF BEFORE-COLONS [LISP:FIND-SYMBOL (LISP:STRING-UPCASE (LISP:SUBSEQ *FORMAT-CONTROL-STRING* :START AFTER-COLONS :END CLOSING/)) (LISP:FIND-PACKAGE (LISP:STRING-UPCASE (LISP:SUBSEQ *FORMAT-CONTROL-STRING* :START *FORMAT-INDEX* :END BEFORE-COLONS] (LISP:FIND-SYMBOL (LISP:STRING-UPCASE (LISP:SUBSEQ *FORMAT-CONTROL-STRING* :START *FORMAT-INDEX* :END CLOSING/) (LISP:FIND-PACKAGE "USER"))))] (* ;; "And call the damned thing (bumping past the argument in the control string first)") (SETQ *FORMAT-INDEX* (ADD1 CLOSING/)) (LISP:APPLY FN-TO-CALL *STANDARD-OUTPUT* (POP-FORMAT-ARG) COLON ATSIGN PARMS))) (* ;; "Direct support for top-level entries") (LISP:DEFUN FORMAT-ROUND-COLUMNS (WIDTH MINCOL COLINC) (* ;; "Determine the actual width to be used for a field requiring WIDTH characters according to the following rule: If WIDTH is less than or equal to MINCOL, use WIDTH as the actual width. Otherwise, round up to MINCOL + k * COLINC for the smallest possible positive integer k.") (LISP:IF (> WIDTH MINCOL) WIDTH (+ WIDTH (LISP:* COLINC (LISP:CEILING (- MINCOL WIDTH) COLINC))))) (LISP:DEFUN FORMAT-EAT-WHITESPACE () (* ;; "Continue control string on next line ~") (NEXTCHAR) [SETQ *FORMAT-INDEX* (LET ((NEXT-NON-WHITE (LISP:POSITION-IF-NOT (FUNCTION WHITESPACE-CHAR-P) *FORMAT-CONTROL-STRING* :START *FORMAT-INDEX*))) (LISP:IF NEXT-NON-WHITE (LISP:1- NEXT-NON-WHITE) (LISP:LENGTH *FORMAT-CONTROL-STRING*))]) (LISP:DEFUN FORMAT-PRINT-NAMED-CHARACTER (CHAR LONGP) [LET* ((CH (LISP:CODE-CHAR (LISP:CHAR-CODE CHAR))) (NAME (LISP:CHAR-NAME CH))) (* ;  "The calls to CODE-CHAR and CHAR-CODE strip funny bits") (COND [NAME (WRITE-STRING* (LISP:STRING-CAPITALIZE (LISP:PRINC-TO-STRING NAME] [(<= 0 (LISP:CHAR-CODE CHAR) 31) (* ;  "Print control characters as '^' ") (LISP:WRITE-CHAR #\^) (LISP:WRITE-CHAR (LISP:CODE-CHAR (+ 64 (LISP:CHAR-CODE CHAR] (T (LISP:WRITE-CHAR CH]) (LISP:DEFUN FORMAT-ADD-COMMAS (STRING COMMACHAR COMMA-INTERVAL) (* ;; "Insert commas after every COMMA-INTERVALth digit, scanning from right to left. Signs don't count in the final length.") (LISP:DO* ((LENGTH (LISP:LENGTH (THE STRING STRING))) (NEW-LENGTH (+ LENGTH (LISP:FLOOR (- LENGTH (LISP:IF (OR (EQL (LISP:CHAR STRING 0) #\+) (EQL (LISP:CHAR STRING 0) #\-)) 2 1)) COMMA-INTERVAL))) (NEW-STRING (LISP:MAKE-STRING NEW-LENGTH :INITIAL-ELEMENT COMMACHAR) (LISP:REPLACE (THE STRING NEW-STRING) (THE STRING STRING) :START1 (MAX 0 (- NEW-POS COMMA-INTERVAL)) :END1 NEW-POS :START2 (MAX 0 (- POS COMMA-INTERVAL)) :END2 POS)) (POS LENGTH (- POS COMMA-INTERVAL)) (NEW-POS NEW-LENGTH (- NEW-POS COMMA-INTERVAL 1))) ((NOT (LISP:PLUSP POS)) (* ;; "If there was a sign, put it back now") (LISP:IF (OR (EQL (LISP:CHAR STRING 0) #\+) (EQL (LISP:CHAR STRING 0) #\-)) (LISP:SETF (LISP:CHAR NEW-STRING 0) (LISP:CHAR STRING 0))) NEW-STRING))) (LISP:DEFUN FORMAT-WRITE-FIELD (STRING MINCOL COLINC MINPAD PADCHAR PADLEFT) (* ;; "Output a string in a field at MINCOL wide, padding with PADCHAR. Pads on the left if PADLEFT is true, else on the right. If the length of the string plus the minimum permissible padding, MINPAD, is greater than MINCOL, the actual field size is rounded up to MINCOL + k * COLINC for the smallest possible positive integer k.") (LISP:UNLESS (AND (LISP:INTEGERP MINCOL) (NOT (MINUSP MINCOL))) (FORMAT-ERROR "Mincol must be a non-negative integer - ~S" MINCOL)) (LISP:UNLESS (AND (LISP:INTEGERP COLINC) (LISP:PLUSP COLINC)) (FORMAT-ERROR "Colinc must be a positive integer - ~S" COLINC)) (LISP:UNLESS (AND (LISP:INTEGERP MINPAD) (NOT (MINUSP MINPAD))) (FORMAT-ERROR "Minpad must be a non-negative integer - ~S" MINPAD)) (LISP:UNLESS (LISP:CHARACTERP PADCHAR) (FORMAT-ERROR "Padchar must be a character - ~S" PADCHAR)) [LET* ((STRLEN (LISP:LENGTH (THE STRING STRING))) (WIDTH (FORMAT-ROUND-COLUMNS (+ STRLEN MINPAD) MINCOL COLINC))) (COND (PADLEFT (LISP:DOTIMES (I (- WIDTH STRLEN)) (LISP:WRITE-CHAR PADCHAR)) (WRITE-STRING* STRING)) (T (WRITE-STRING* STRING) (LISP:DOTIMES (I (- WIDTH STRLEN)) (LISP:WRITE-CHAR PADCHAR))]) (LISP:DEFUN FORMAT-PRINT-NUMBER (NUMBER RADIX PRINT-COMMAS-P PRINT-SIGN-P PARMS) (* ;; "This functions does most of the work for the numeric printing directives. The parameters are interpreted as defined for ~D.") [WITH-FORMAT-PARAMETERS PARMS ((MINCOL 0) (PADCHAR #\Space) (COMMACHAR #\,) (COMMA-INTERVAL 3)) (* ;  "comma-interval is an XCL extension.") (LET* ((*PRINT-BASE* RADIX) (*PRINT-RADIX* NIL) (*PRINT-ESCAPE* NIL) (TEXT (LISP:PRINC-TO-STRING NUMBER))) (LISP:IF (LISP:INTEGERP NUMBER) (PROGN (* ;; "colinc = 1, minpad = 0, padleft = t ") (FORMAT-WRITE-FIELD (LISP:IF (AND (LISP:PLUSP NUMBER) PRINT-SIGN-P) (LISP:IF PRINT-COMMAS-P (LISP:CONCATENATE 'STRING "+" (FORMAT-ADD-COMMAS TEXT COMMACHAR COMMA-INTERVAL)) (LISP:CONCATENATE 'STRING "+" TEXT)) (LISP:IF PRINT-COMMAS-P (FORMAT-ADD-COMMAS TEXT COMMACHAR COMMA-INTERVAL) TEXT)) MINCOL 1 0 PADCHAR T)) (WRITE-STRING* TEXT))]) (LISP:DEFUN FORMAT-PRINT-SMALL-CARDINAL (N) [LISP:MULTIPLE-VALUE-BIND (HUNDREDS REM) (LISP:TRUNCATE N 100) (LISP:WHEN (LISP:PLUSP HUNDREDS) (WRITE-STRING* (LISP:SVREF CARDINAL-ONES HUNDREDS)) (WRITE-STRING* " hundred") (LISP:WHEN (LISP:PLUSP REM) (LISP:WRITE-CHAR #\Space))) (LISP:WHEN (LISP:PLUSP REM) [LISP:MULTIPLE-VALUE-BIND (TENS ONES) (LISP:TRUNCATE REM 10) (COND [(< 1 TENS) (WRITE-STRING* (LISP:SVREF CARDINAL-TENS TENS)) (LISP:WHEN (LISP:PLUSP ONES) (LISP:WRITE-CHAR #\-) (WRITE-STRING* (LISP:SVREF CARDINAL-ONES ONES)))] ((= TENS 1) (WRITE-STRING* (LISP:SVREF CARDINAL-TEENS ONES))) ((LISP:PLUSP ONES) (WRITE-STRING* (LISP:SVREF CARDINAL-ONES ONES])]) (LISP:DEFUN FORMAT-PRINT-CARDINAL (N &OPTIONAL ERR) [COND ((MINUSP N) (WRITE-STRING* "negative ") (FORMAT-PRINT-CARDINAL-AUX (- N) 0 (OR ERR N))) ((ZEROP N) (WRITE-STRING* "zero")) (T (FORMAT-PRINT-CARDINAL-AUX N 0 (OR ERR N]) (LISP:DEFUN FORMAT-PRINT-CARDINAL-AUX (N PERIOD ERR) [LISP:MULTIPLE-VALUE-BIND (BEYOND HERE) (LISP:TRUNCATE N 1000) (LISP:UNLESS (<= PERIOD 10) (FORMAT-ERROR "Number too large to print in English: ~:D" ERR)) (LISP:UNLESS (ZEROP BEYOND) (FORMAT-PRINT-CARDINAL-AUX BEYOND (LISP:1+ PERIOD) ERR)) (LISP:UNLESS (ZEROP HERE) (LISP:UNLESS (ZEROP BEYOND) (LISP:WRITE-CHAR #\Space)) (FORMAT-PRINT-SMALL-CARDINAL HERE) (WRITE-STRING* (LISP:SVREF CARDINAL-PERIODS PERIOD)))]) (LISP:DEFUN FORMAT-PRINT-ORDINAL (N) (LISP:WHEN (MINUSP N) (WRITE-STRING* "negative ")) [LET ((LISP:NUMBER (ABS N))) (LISP:MULTIPLE-VALUE-BIND (TOP BOT) (LISP:TRUNCATE LISP:NUMBER 100) (LISP:UNLESS (ZEROP TOP) (FORMAT-PRINT-CARDINAL (- LISP:NUMBER BOT) LISP:NUMBER)) (LISP:WHEN (AND (LISP:PLUSP TOP) (LISP:PLUSP BOT)) (LISP:WRITE-CHAR #\Space)) (LISP:MULTIPLE-VALUE-BIND (TENS ONES) (LISP:TRUNCATE BOT 10) (COND ((= BOT 12) (WRITE-STRING* "twelfth")) ((= TENS 1) (WRITE-STRING* (LISP:SVREF CARDINAL-TEENS ONES)) (WRITE-STRING* "th")) ((AND (ZEROP TENS) (LISP:PLUSP ONES)) (WRITE-STRING* (LISP:SVREF ORDINAL-ONES ONES))) ((AND (ZEROP ONES) (LISP:PLUSP TENS)) (WRITE-STRING* (LISP:SVREF ORDINAL-TENS TENS))) ((LISP:PLUSP BOT) (WRITE-STRING* (LISP:SVREF CARDINAL-TENS TENS)) (LISP:WRITE-CHAR #\-) (WRITE-STRING* (LISP:SVREF ORDINAL-ONES ONES))) ((LISP:PLUSP LISP:NUMBER) (WRITE-STRING* "th")) (T (WRITE-STRING* "zeroeth"]) (LISP:DEFUN FORMAT-PRINT-OLD-ROMAN (N) (* ;; "Print Roman numerals") (LISP:UNLESS (< 0 N 5000) (FORMAT-ERROR "Number too large to print in old Roman numerals: ~:D" N)) (LISP:DO [(CHAR-LIST '(#\D #\C #\L #\X #\V #\I) (CDR CHAR-LIST)) (VAL-LIST '(500 100 50 10 5 1) (CDR VAL-LIST)) (CUR-CHAR #\M (CAR CHAR-LIST)) (CUR-VAL 1000 (CAR VAL-LIST)) (START N (LISP:DO [(I START (PROGN (LISP:WRITE-CHAR CUR-CHAR) (- I CUR-VAL] ((< I CUR-VAL) I))] ((ZEROP START)))) (LISP:DEFUN FORMAT-PRINT-ROMAN (N) (LISP:UNLESS (< 0 N 4000) (FORMAT-ERROR "Number too large to print in Roman numerals: ~:D" N)) (LISP:DO [(CHAR-LIST '(#\D #\C #\L #\X #\V #\I) (CDR CHAR-LIST)) (VAL-LIST '(500 100 50 10 5 1) (CDR VAL-LIST)) (SUB-CHARS '(#\C #\X #\X #\I #\I) (CDR SUB-CHARS)) (SUB-VAL '(100 10 10 1 1 0) (CDR SUB-VAL)) (CUR-CHAR #\M (CAR CHAR-LIST)) (CUR-VAL 1000 (CAR VAL-LIST)) (CUR-SUB-CHAR #\C (CAR SUB-CHARS)) (CUR-SUB-VAL 100 (CAR SUB-VAL)) (START N (LISP:DO [(I START (PROGN (LISP:WRITE-CHAR CUR-CHAR) (- I CUR-VAL] ((< I CUR-VAL) (COND ((<= (- CUR-VAL CUR-SUB-VAL) I) (LISP:WRITE-CHAR CUR-SUB-CHAR) (LISP:WRITE-CHAR CUR-CHAR) (- I (- CUR-VAL CUR-SUB-VAL))) (T I))))] ((ZEROP START)))) (LISP:DEFUN FORMAT-PRINT-DECIMAL (COLON ATSIGN PARMS) (* ;; "Decimal ~D") (FORMAT-PRINT-NUMBER (POP-FORMAT-ARG) 10 COLON ATSIGN PARMS)) (LISP:DEFUN FORMAT-PRINT-RADIX-AUX (LISP:NUMBER COLON ATSIGN) (LISP:IF (TYPEP LISP:NUMBER 'INTEGER) (LISP:IF ATSIGN (LISP:IF COLON (FORMAT-PRINT-OLD-ROMAN LISP:NUMBER) (FORMAT-PRINT-ROMAN LISP:NUMBER)) (LISP:IF COLON (FORMAT-PRINT-ORDINAL LISP:NUMBER) (FORMAT-PRINT-CARDINAL LISP:NUMBER))) (FORMAT-ERROR "Non-integer ~S can't be FORMATted ~~~:[~;:~]~:[~;@~]R" LISP:NUMBER COLON ATSIGN))) (LISP:DEFUN FORMAT-FIXED-AUX (NUMBER W D K OVF PAD ATSIGN) (LISP:IF (NOT (OR W D K)) (PROGN (* ;; "Code snarfed from Spice printer OUTPUT-FLOAT") (LISP:WHEN (MINUSP NUMBER) (LISP:WRITE-CHAR #\-) (LISP:SETQ NUMBER (- NUMBER))) (* ;;  "When number is reasonable size, use FLONUM-TO-STRING, otherwise punt and PRINC it") (LISP:IF (AND (>= NUMBER 0.001) (<= NUMBER 1.0E+7)) (LISP:MULTIPLE-VALUE-BIND (STR LEN LPOINT TPOINT) (FLONUM-TO-STRING NUMBER) (LISP:WHEN LPOINT (LISP:WRITE-CHAR #\0)) (WRITE-STRING* STR) (LISP:WHEN TPOINT (LISP:WRITE-CHAR #\0))) (LISP:PRINC NUMBER))) [LET ((SPACELEFT W)) (LISP:WHEN (AND W (OR ATSIGN (MINUSP NUMBER))) (LISP:DECF SPACELEFT)) (LISP:MULTIPLE-VALUE-BIND (STR LEN LPOINT TPOINT) (FLONUM-TO-STRING (ABS NUMBER) SPACELEFT D K) (* ;;  "if caller specifically requested no fraction digits, suppress the optional trailing zero") (LISP:WHEN (AND D (ZEROP D)) (SETQ TPOINT NIL)) (LISP:WHEN W (LISP:DECF SPACELEFT LEN) (* ;; "optional leading zero force at least one digit") (LISP:WHEN LPOINT (LISP:IF (OR (> SPACELEFT 0) TPOINT) (LISP:DECF SPACELEFT) (SETQ LPOINT NIL))) (* ;; "optional trailing zero") (LISP:WHEN TPOINT (LISP:IF (> SPACELEFT 0) (LISP:DECF SPACELEFT) (SETQ TPOINT NIL)))) (COND ((AND W (< SPACELEFT 0) OVF) (* ;; "field width overflow") (LISP:DOTIMES (I W) (LISP:WRITE-CHAR OVF))) (T (LISP:WHEN W (LISP:DOTIMES (I SPACELEFT) (LISP:WRITE-CHAR PAD))) (LISP:IF (MINUSP NUMBER) (LISP:WRITE-CHAR #\-) (LISP:IF ATSIGN (LISP:WRITE-CHAR #\+))) (LISP:WHEN LPOINT (LISP:WRITE-CHAR #\0)) (WRITE-STRING* STR) (LISP:WHEN TPOINT (LISP:WRITE-CHAR #\0])) (LISP:DEFUN FORMAT-EXPONENT-MARKER (LISP:NUMBER) (LISP:IF (TYPEP LISP:NUMBER *READ-DEFAULT-FLOAT-FORMAT*) #\E (LISP:ETYPECASE LISP:NUMBER (LISP:SHORT-FLOAT #\S) (LISP:SINGLE-FLOAT #\F)))) (LISP:DEFUN FORMAT-EXP-AUX (NUMBER W D E K OVF PAD MARKER ATSIGN) (* ;; "Here we prevent the scale factor from shifting all significance out of a number to the right. We allow insignificant zeroes to be shifted in to the left right, athough it is an error to specify k and d such that this occurs. Perhaps we should detect both these conditions and flag them as errors. As for now, we let the user get away with it, and merely guarantee that at least one significant digit will appear.") (LISP:IF (NOT (OR W D)) (LISP:PRIN1 NUMBER) [LISP:MULTIPLE-VALUE-BIND (NUM EXPT) (SCALE-EXPONENT (ABS NUMBER)) (LET* ((EXPT (- EXPT K)) (ESTR (LISP:PRINC-TO-STRING (ABS EXPT))) (ELEN (LISP:IF E (MAX (LISP:LENGTH ESTR) E) (LISP:LENGTH ESTR))) (FDIG (LISP:IF D (LISP:IF (LISP:PLUSP K) (LISP:1+ (- D K)) D) NIL)) (FMIN (LISP:IF (MINUSP K) (- 1 K) NIL)) (SPACELEFT (LISP:IF W (- W 2 ELEN) NIL))) (LISP:WHEN (OR ATSIGN (MINUSP NUMBER)) (LISP:DECF SPACELEFT)) (LISP:IF (AND W E OVF (> ELEN E)) (PROGN (* ;; "exponent overflow") (LISP:DOTIMES (I W) (LISP:WRITE-CHAR OVF))) [LISP:MULTIPLE-VALUE-BIND (FSTR FLEN LPOINT TPOINT) (FLONUM-TO-STRING NUM SPACELEFT FDIG K FMIN) (LISP:WHEN W (LISP:DECF SPACELEFT FLEN) (LISP:WHEN LPOINT (LISP:IF (> SPACELEFT 0) (LISP:DECF SPACELEFT) (SETQ LPOINT NIL)))) (COND ((AND W (< SPACELEFT 0) OVF) (* ;; "significand overflow") (LISP:DOTIMES (I W) (LISP:WRITE-CHAR OVF))) (T (LISP:WHEN W (LISP:DOTIMES (I SPACELEFT) (LISP:WRITE-CHAR PAD))) (LISP:IF (MINUSP NUMBER) (LISP:WRITE-CHAR #\-) (LISP:IF ATSIGN (LISP:WRITE-CHAR #\+))) (LISP:WHEN LPOINT (LISP:WRITE-CHAR #\0)) (WRITE-STRING* FSTR) (* ;; "(cl:when tpoint (cl:write-char #\0))") (LISP:WRITE-CHAR (LISP:IF MARKER MARKER (FORMAT-EXPONENT-MARKER NUMBER))) (LISP:WRITE-CHAR (LISP:IF (MINUSP EXPT) #\- #\+)) (LISP:WHEN E (* ;; "zero-fill before exponent if necessary") (LISP:DOTIMES (I (- E (LISP:LENGTH ESTR))) (LISP:WRITE-CHAR #\0))) (WRITE-STRING* ESTR])])) (LISP:DEFUN FORMAT-GENERAL-AUX (LISP:NUMBER W D E K OVF PAD MARKER ATSIGN) [LISP:MULTIPLE-VALUE-BIND (IGNORE N) (SCALE-EXPONENT (ABS LISP:NUMBER)) (DECLARE (IGNORE IGNORE)) (* ;; "Default d if omitted. The procedure is taken directly from the definition given in the manual, and is not very efficient, since we generate the digits twice. Future maintainers are encouraged to improve on this.") (LISP:UNLESS D [LISP:MULTIPLE-VALUE-BIND (STR LEN) (FLONUM-TO-STRING (ABS LISP:NUMBER)) (DECLARE (IGNORE STR)) (LET [(Q (LISP:IF (= LEN 1) 1 (LISP:1- LEN))] (SETQ D (MAX Q (MIN N 7]) (LET* ((EE (LISP:IF E (+ E 2) 4)) (WW (LISP:IF W (- W EE) NIL)) (DD (- D N))) (COND ((<= 0 DD D) (FORMAT-FIXED-AUX LISP:NUMBER WW DD NIL OVF PAD ATSIGN) (LISP:DOTIMES (I EE) (LISP:WRITE-CHAR #\Space))) (T (FORMAT-EXP-AUX LISP:NUMBER W D E (OR K 1) OVF PAD MARKER ATSIGN]) (LISP:DEFUN FORMAT-UNTAGGED-CONDITION () (* ;; "~[") [LET ((TEST (POP-FORMAT-ARG))) (LISP:UNLESS (LISP:INTEGERP TEST) (FORMAT-ERROR "Argument to ~~[ must be integer - ~S" TEST)) (LISP:DO ((LISP:COUNT 0 (LISP:1+ LISP:COUNT))) [(= LISP:COUNT TEST) (LISP:MULTIPLE-VALUE-BIND (PREV TILDE PARMS COLON ATSIGN CMD) (FORMAT-FIND-COMMAND '(#\; #\])) (DECLARE (IGNORE COLON)) (LISP:WHEN ATSIGN (FORMAT-ERROR "Atsign flag not allowed")) (LISP:WHEN PARMS (FORMAT-ERROR "No parameters allowed")) (SUB-FORMAT PREV TILDE) (LISP:UNLESS (LISP:CHAR= CMD #\]) (FORMAT-FIND-COMMAND '(#\])))] (LISP:MULTIPLE-VALUE-BIND (PREV TILDE PARMS COLON ATSIGN CMD) (FORMAT-FIND-COMMAND '(#\; #\])) (DECLARE (IGNORE PREV TILDE)) (LISP:WHEN ATSIGN (FORMAT-ERROR "Atsign flag not allowed")) (LISP:WHEN PARMS (FORMAT-ERROR "Parameters not allowed")) (LISP:WHEN (LISP:CHAR= CMD #\]) (RETURN)) (LISP:WHEN COLON (NEXTCHAR) [LISP:MULTIPLE-VALUE-BIND (PREV TILDE PARMS COLON ATSIGN CMD) (FORMAT-FIND-COMMAND '(#\; #\])) (DECLARE (IGNORE PARMS COLON ATSIGN)) (SUB-FORMAT PREV TILDE) (LISP:UNLESS (LISP:CHAR= CMD #\]) (FORMAT-FIND-COMMAND '(#\])))] (RETURN)) (NEXTCHAR)))]) (LISP:DEFUN FORMAT-FUNNY-CONDITION () (* ;; "~@[ ") (LISP:MULTIPLE-VALUE-BIND (PREV TILDE PARMS COLON ATSIGN) (FORMAT-FIND-COMMAND '(#\])) (LISP:WHEN (OR COLON ATSIGN PARMS) (FORMAT-ERROR "Flags or arguments not allowed")) (LISP:IF *FORMAT-ARGUMENTS* (LISP:IF (CAR *FORMAT-ARGUMENTS*) (SUB-FORMAT PREV TILDE) (LISP:POP *FORMAT-ARGUMENTS*)) (FORMAT-ERROR "Missing argument")))) (LISP:DEFUN FORMAT-BOOLEAN-CONDITION () (* ;; "~:[") [LISP:MULTIPLE-VALUE-BIND (PREV TILDE PARMS COLON ATSIGN) (FORMAT-FIND-COMMAND '(#\;)) (LISP:WHEN (OR PARMS COLON ATSIGN) (FORMAT-ERROR "Flags or parameters not allowed")) (NEXTCHAR) (LISP:IF (POP-FORMAT-ARG) (LISP:MULTIPLE-VALUE-BIND (PREV TILDE PARMS COLON ATSIGN) (FORMAT-FIND-COMMAND '(#\])) (LISP:WHEN (OR COLON ATSIGN PARMS) (FORMAT-ERROR "Flags or parameters not allowed")) (SUB-FORMAT PREV TILDE)) [PROGN (SUB-FORMAT PREV TILDE) (FORMAT-FIND-COMMAND '(#\]])]) (LISP:DEFUN FORMAT-DO-ITERATION (START END MAX-ITER COLON ATSIGN AT-LEAST-ONCE-P) (* ;; "The two catch tags FORMAT-ESCAPE and FORMAT-COLON-ESCAPE are needed here to correctly implement ~^ and ~:^. The former aborts only the current iteration, but the latter aborts the entire iteration process.") [LET ((*FORMAT-COLON-ITERATION* COLON)) (LISP:CATCH 'FORMAT-COLON-ESCAPE (LISP:CATCH 'FORMAT-ESCAPE [LISP:FLET [(REALLY-ITERATE (ERRMSG) (LISP:DO ((LISP:COUNT 0 (LISP:1+ LISP:COUNT))) [(OR (= LISP:COUNT MAX-ITER) (AND (NULL *FORMAT-ARGUMENTS*) (LISP:IF (= LISP:COUNT 0) (NOT AT-LEAST-ONCE-P) T)] (LISP:CATCH 'FORMAT-ESCAPE (LISP:IF COLON (LET* ((*FORMAT-ORIGINAL-ARGUMENTS* (POP-FORMAT-ARG )) (*OUTER-FORMAT-ARGUMENTS* *FORMAT-ARGUMENTS*) (*FORMAT-ARGUMENTS* *FORMAT-ORIGINAL-ARGUMENTS* )) (LISP:UNLESS (LISP:LISTP *FORMAT-ARGUMENTS*) (FORMAT-ERROR ERRMSG)) (SUB-FORMAT START END)) (SUB-FORMAT START END))))] (LISP:IF ATSIGN (REALLY-ITERATE "Argument must be a list") (LET* ((*FORMAT-ORIGINAL-ARGUMENTS* (POP-FORMAT-ARG)) (*FORMAT-ARGUMENTS* *FORMAT-ORIGINAL-ARGUMENTS*)) (LISP:UNLESS (LISP:LISTP *FORMAT-ARGUMENTS*) (FORMAT-ERROR "Argument must be a list")) (REALLY-ITERATE "Argument must be a list of lists")))]))]) (LISP:DEFUN FORMAT-GET-TRAILING-SEGMENTS () (* ;; "Parses a list of clauses delimited by ~ and terminated by ~>. Recursively invoke SUB-FORMAT to process them, and return a list of the results, the length of this list, and the total number of characters in the strings composing the list.") (NEXTCHAR) [LISP:MULTIPLE-VALUE-BIND (PREV TILDE COLON ATSIGN PARMS CMD) (FORMAT-FIND-COMMAND '(#\; #\>)) (LISP:WHEN COLON (FORMAT-ERROR "~~:; allowed only after first segment in ~~<")) (LISP:WHEN (OR ATSIGN PARMS) (FORMAT-ERROR "Flags and parameters not allowed")) (LET [(STR (LISP:CATCH 'FORMAT-ESCAPE (FORMAT-STRINGIFY-OUTPUT (SUB-FORMAT PREV TILDE)))] (LISP:IF STR (LISP:IF (LISP:CHAR= CMD #\;) [LISP:MULTIPLE-VALUE-BIND (SEGMENTS NUMSEGS NUMCHARS) (FORMAT-GET-TRAILING-SEGMENTS) (LISP:VALUES (CONS STR SEGMENTS) (LISP:1+ NUMSEGS) (+ NUMCHARS (LISP:LENGTH STR] (LISP:VALUES (LIST STR) 1 (LISP:LENGTH STR))) (LISP:VALUES NIL 0 0))]) (LISP:DEFUN FORMAT-GET-SEGMENTS () (* ;; "Gets the first segment, which is treated specially. Call FORMAT-GET-TRAILING-SEGMENTS to get the rest.") [LISP:MULTIPLE-VALUE-BIND (PREV TILDE PARMS COLON ATSIGN CMD) (FORMAT-FIND-COMMAND '(#\; #\>)) (LISP:WHEN ATSIGN (FORMAT-ERROR "Atsign flag not allowed")) (LET [(FIRST-SEG (FORMAT-STRINGIFY-OUTPUT (SUB-FORMAT PREV TILDE] (LISP:IF (LISP:CHAR= CMD #\;) [LISP:MULTIPLE-VALUE-BIND (SEGMENTS NUMSEGS NUMCHARS) (FORMAT-GET-TRAILING-SEGMENTS) (LISP:IF COLON (LISP:VALUES FIRST-SEG PARMS SEGMENTS NUMSEGS NUMCHARS) (LISP:VALUES NIL NIL (CONS FIRST-SEG SEGMENTS) (LISP:1+ NUMSEGS) (+ (LISP:LENGTH FIRST-SEG) NUMCHARS)))] (LISP:VALUES NIL NIL (LIST FIRST-SEG) 1 (LISP:LENGTH FIRST-SEG)))]) (LISP:DEFUN FORMAT-PPRINT-LOGICAL-BLOCK (COLON ATSIGN ENDATSIGN START END) (* ;;; "The amusement potential is near-infinite here; what we're doing is setting up the traditional format argument-eating mechanism to work like PPRINT-POP") [LET ((*FORMAT-LOGICAL-BLOCK* (LISP:IF ENDATSIGN :FILL T)) (*FORMAT-INDEX* START) (PREFIX (LISP:IF COLON "(")) (SUFFIX (LISP:IF COLON ")")) PER-LINE-PREFIX?) (* ;; "First grind through the string finding segments; when we're finished, START and END will point to the body, PREFIX, SUFFIX, and PER-LINE-PREFIX? will be set right") [LISP:MULTIPLE-VALUE-BIND (PREV TILDE PARMS COLON ATSIGN CMD) (FORMAT-FIND-COMMAND '(#\; #\>)) (IF (LISP:CHAR= CMD #\;) THEN (* ; "At least two segments") (LISP:WHEN PARMS (FORMAT-ERROR "Parameters not allowed for logical-block seperator" )) (LISP:WHEN COLON (FORMAT-ERROR "Colon not allowed for logical-block seperator")) (SETQ PREFIX (LISP:SUBSEQ *FORMAT-CONTROL-STRING* PREV TILDE)) (LISP:WHEN ATSIGN (SETQ PER-LINE-PREFIX? T)) (SETQ START (LISP:1+ *FORMAT-INDEX*)) [LISP:MULTIPLE-VALUE-SETQ (PREV TILDE PARMS COLON ATSIGN CMD) (FORMAT-FIND-COMMAND '(#\; #\>] (IF (LISP:CHAR= CMD #\;) THEN (* ; "At least three segments") (LISP:WHEN PARMS (FORMAT-ERROR "Parameters not allowed for logical-block seperator" )) (LISP:WHEN COLON (FORMAT-ERROR "Colon not allowed for logical-block seperator" )) (LISP:WHEN ATSIGN (FORMAT-ERROR "Atsign not allowed for this logical-block seperator" )) (SETQ SUFFIX (LISP:SUBSEQ *FORMAT-CONTROL-STRING* PREV TILDE)) (SETQ START (LISP:1+ *FORMAT-INDEX*)) [LISP:MULTIPLE-VALUE-SETQ (PREV TILDE PARMS COLON ATSIGN CMD) (FORMAT-FIND-COMMAND '(#\; #\>] (LISP:WHEN (LISP:CHAR= CMD #\;) (FORMAT-ERROR "No more than three segments allowed in a logical-block" ] (* ;;  "We are supposedly ready to do it. The code below is a massively hacked macroexpansion of") (* ;; "(PPRINT-LOGICAL-BLOCK (*STANDARD-OUTPUT* *FORMAT-ARGUMENTS* :PREFIX PREFIX :PER-LINE-PREFIX PER-LINE-PREFIX? :SUFFIX SUFFIX) (SUB-FORMAT START END))") (XP::MAYBE-INITIATE-XP-PRINTING #'[LISP:LAMBDA (*STANDARD-OUTPUT* ARGLIST) (LET* ((*FORMAT-ORIGINAL-ARGUMENTS* ARGLIST) (*FORMAT-ARGUMENTS* *FORMAT-ORIGINAL-ARGUMENTS*)) (* ;; "NOTE: the LET below is not a LET* for a reason; CIRCLE-CHECK must depend on the unbound value of XP::*CURRENT-LENGTH*, according to the convoluted code of XP::PPRINT-LOGICAL-BLOCK+. Yeesh...") (LET ((CIRCLE-CHECK (LISP:IF ATSIGN (LISP:PLUSP XP::*CURRENT-LENGTH*) T)) (XP::*CURRENT-LEVEL* (LISP:1+ XP::*CURRENT-LEVEL*)) (XP::*CURRENT-LENGTH* -1) (XP::*PARENTS* XP::*PARENTS*)) (LISP:UNLESS (XP::CHECK-BLOCK-ABBREVIATION *STANDARD-OUTPUT* *FORMAT-ARGUMENTS* CIRCLE-CHECK) (LISP:CATCH 'XP::LOGICAL-BLOCK (XP::START-BLOCK *STANDARD-OUTPUT* PREFIX (NOT (NULL PER-LINE-PREFIX? )) SUFFIX) (LISP:UNWIND-PROTECT (SUB-FORMAT START END) (XP::END-BLOCK *STANDARD-OUTPUT* SUFFIX))))] *STANDARD-OUTPUT* (LISP:IF ATSIGN *FORMAT-ARGUMENTS* (POP-FORMAT-ARG))]) (LISP:DEFUN FORMAT-CHECK-JUSTIFY () (LISP:IF (EQ *FORMAT-INCOMPATIBLE-JUSTIFICATION* :JUSTIFY) (FORMAT-ERROR "Pretty-printer directive and ~<...~:;...~> in same format string") (SETQ *FORMAT-INCOMPATIBLE-JUSTIFICATION* :XP))) (LISP:DEFUN CHARPOS (STREAM) (LISP:UNLESS (STREAMP STREAM) (LISP:ERROR "CHARPOS: ~A isn't a stream" STREAM)) (fetch (STREAM CHARPOSITION) of STREAM)) (LISP:DEFUN WHITESPACE-CHAR-P (CH) (LISP:MEMBER CH '(#\Tab #\Page #\Space #\Backspace #\Newline #\Linefeed) :TEST (FUNCTION EQL))) (LISP:DEFUN MAKE-PAD-SEGS (SPACES PADDINGS) (* ;; "Given the total number of SPACES needed for padding, and the number of padding segments needed (PADDINGS) , returns a list of such segments. We try to allocate the spaces equally to each segment. When this is not possible, we allocate the left-over spaces randomly, to improve the appearance of many successive lines of justified text.") (* ;; "Query: Is this right? Perhaps consistency might be better for the kind of applications ~<~> is used for.") (LISP:DO* ([EXTRA-SPACE NIL (AND (LISP:PLUSP EXTRA-SPACES) (< (RAND 0 (FLOAT 1)) (/ SEGS EXTRA-SPACES] (RESULT NIL (CONS (LISP:IF EXTRA-SPACE (LISP:1+ MIN-SPACE) MIN-SPACE) RESULT)) (MIN-SPACE (LISP:TRUNCATE SPACES PADDINGS)) (EXTRA-SPACES (- SPACES (LISP:* PADDINGS MIN-SPACE)) (LISP:IF EXTRA-SPACE (LISP:1- EXTRA-SPACES) EXTRA-SPACES)) (SEGS PADDINGS (LISP:1- SEGS))) ((ZEROP SEGS) RESULT))) (LISP:DEFUN FORMAT-LOGICAL-FILL (STRING STREAM START END FIRST?) (* ;; "Output STRING's characters between START and END to STREAM; blank sequences in STRING are followed by fill-style conditional newlines. If FIRST? is true, the last directive was a ~ and doesn't get one.") (LISP:DO ((CP START (LISP:INCF CP)) (C)) ((IGEQ CP END)) (IF (LISP:CHAR= (SETQ C (LISP:CHAR STRING CP)) #\Space) THEN (* ;; "Look for the end of this run of spaces; when you find it or the end of the sequence, check FIRST? and do the newline. Go to a little trouble to only look at things in string once.") (LISP:WRITE-CHAR #\Space STREAM) (LISP:INCF CP) (LISP:DO NIL ((OR (IGEQ CP END) (NOT (LISP:CHAR= (SETQ C (LISP:CHAR STRING CP)) #\Space))) (LISP:UNLESS FIRST? (XP:PPRINT-NEWLINE :FILL)) (SETQ FIRST? NIL) (LISP:WHEN (NOT (LISP:CHAR= C #\Space)) (LISP:WRITE-CHAR C STREAM))) (LISP:WRITE-CHAR #\Space STREAM) (LISP:INCF CP)) ELSE (LISP:WRITE-CHAR C STREAM)))) (DEFMACRO NAME-ARRAY (CONTENTS) `(LISP:MAKE-ARRAY ,(LENGTH CONTENTS) :ELEMENT-TYPE T :INITIAL-CONTENTS ',CONTENTS)) (LISP:DEFVAR *FORMAT-ARGUMENTS* NIL "List of FORMAT args yet unprocessed") (LISP:DEFVAR *OUTER-FORMAT-ARGUMENTS* NIL "Bound by FORMAT-DO-ITERATION so FORMAT-ESCAPE can see if there are any arguments left outside the ones *FORMAT-ARGUMENTS* are bound to" ) (LISP:DEFVAR *FORMAT-CONTROL-STRING* NIL "Bound to FORMAT control string") (LISP:DEFVAR *FORMAT-DISPATCH-TABLE* (MAKE-DISPATCH-VECTOR (#\B FORMAT-PRINT-BINARY) (#\O FORMAT-PRINT-OCTAL) (#\D FORMAT-PRINT-DECIMAL) (#\X FORMAT-PRINT-HEXADECIMAL) (#\R FORMAT-PRINT-RADIX) (#\F FORMAT-FIXED) (#\E FORMAT-EXPONENTIAL) (#\G FORMAT-GENERAL-FLOAT) (#\A FORMAT-PRINC) (#\C FORMAT-PRINT-CHARACTER) (#\P FORMAT-PLURAL) (#\S FORMAT-PRIN1) (#\T FORMAT-TAB) (#\% FORMAT-TERPRI) (#\& FORMAT-FRESHLINE) (#\* FORMAT-SKIP-ARGUMENTS) (#\| FORMAT-PAGE) (#\~ FORMAT-TILDE) (#\$ FORMAT-DOLLARS) (#\? FORMAT-INDIRECTION) (#\^ FORMAT-ESCAPE) (#\; FORMAT-SEMICOLON-ERROR) (#\[ FORMAT-CONDITION) (#\{ FORMAT-ITERATION) (#\< FORMAT-JUSTIFICATION) (#\( FORMAT-CAPITALIZATION) (#\Newline FORMAT-NEWLINE) (#\W FORMAT-JUST-WRITE) (#\_ FORMAT-PPRINT-NEWLINE) (#\I FORMAT-PPRINT-INDENT) (#\/ FORMAT-CALL-FUNCTION)) "Table of functions called by SUB-FORMAT to process ~foo stuff") (LISP:DEFVAR *FORMAT-INDEX* NIL "Index into current control string") (LISP:DEFVAR *FORMAT-LENGTH* NIL "Length of current control string") (LISP:DEFVAR *FORMAT-ORIGINAL-ARGUMENTS* NIL "List of original FORMAT arguments") (LISP:DEFVAR *FORMAT-LOGICAL-BLOCK* NIL "Bound to T under FORMAT-PPRINT-LOGICAL-BLOCK") (LISP:DEFVAR *FORMAT-JUSTIFICATION* NIL "Bound to T under FORMAT-JUSTIFICATION") (LISP:DEFVAR *FORMAT-INCOMPATIBLE-JUSTIFICATION* NIL "Bound by CL:FORMAT and FORMAT-INDIRECTION; set to :XP by ~_ and friends, set to :JUSTIFY by ~<...~:;...~>" ) (LISP:DEFVAR *FORMAT-COLON-ITERATION* NIL "Bound in FORMAT-DO-ITERATION to let FORMAT-ESCAPE know whether it is under a ~:{ or not") (LISP:DEFVAR CARDINAL-ONES (NAME-ARRAY (NIL "one" "two" "three" "four" "five" "six" "seven" "eight" "nine")) "Table of strings used by ~R") (LISP:DEFVAR CARDINAL-TENS (NAME-ARRAY (NIL NIL "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" "ninety")) "Table of strings used by ~R") (LISP:DEFVAR CARDINAL-TEENS (NAME-ARRAY ("ten" "eleven" "twelve" "thirteen" "fourteen" "fifteen" "sixteen" "seventeen" "eighteen" "nineteen")) "Table of strings used by ~R") (LISP:DEFVAR CARDINAL-PERIODS (NAME-ARRAY ("" " thousand" " million" " billion" " trillion" " quadrillion" " quintillion" " sextillion" " septillion" " octillion" " nonillion" " decillion")) "Table of strings used by ~R") (LISP:DEFVAR ORDINAL-ONES (NAME-ARRAY (NIL "first" "second" "third" "fourth" "fifth" "sixth" "seventh" "eighth" "ninth")) "Table of strings used by ~R") (LISP:DEFVAR ORDINAL-TENS (NAME-ARRAY (NIL "tenth" "twentieth" "thirtieth" "fourtieth" "fiftieth" "sixtieth" "seventieth" "eightieth" "ninetieth")) "Table of strings used by ~R") (DECLARE%: DONTEVAL@LOAD DOEVAL@COMPILE DONTCOPY COMPILERVARS (ADDTOVAR NLAMA ) (ADDTOVAR NLAML ) (ADDTOVAR LAMA ) ) (* ;; "Arrange to use the correct compiler.") (PUTPROPS CMLFORMAT FILETYPE LISP:COMPILE-FILE) (PUTPROPS CMLFORMAT COPYRIGHT ("Venue & Xerox Corporation" 1986 1987 1988 1989 1990 1991 1993)) (DECLARE%: DONTCOPY (FILEMAP (NIL))) STOP