|
|
1.1 root 1: (setq rcs-format-
2: "$Header")
3:
4: ;;
5: ;; format.l -[Fri Mar 4 12:20:16 1983 by jkf]-
6: ;;
7: ;; This is a function for printing or creating nicely formatted strings.
8: ;; This file is a modified version of the format program which runs in
9: ;; the mit lisps. When converting to franz, compatibility was the
10: ;; major goal, thus we still use the \ character as a string delimiter
11: ;; within a command string, even though it must be doubled in Franz.
12: ;;
13: ;; The file contains the user callable functions:
14: ;; format - lexpr for doing formated printed output or creating
15: ;; strings
16: ;; defformat - macro for adding a format directive
17: ;;
18:
19: ; FORMAT prints several arguments according to a control argument.
20: ; The control argument is either a string or a list of strings and lists.
21: ; The strings and lists are interpreted consecutively.
22: ; Strings are for the most part just printed, except that the character ~
23: ; starts an escape sequence which directs other actions.
24: ; A ~ escape sequence has an (optional) numeric parameter followed by a
25: ; mode character.
26: ; These escape actions can use up one or more of the non-control arguments.
27: ; A list in the control-argument list is also interpreted as an escape.
28: ; Its first element is the mode, a symbol which may be any length,
29: ; and its remaining elements are parameters. The list (D 5) is equivalent
30: ; to the ~ escape "~5D"; similarly, each ~ escape has an equivalent list.
31: ; However, there are list escapes which have no ~ equivalent.
32:
33: ; Any undefined list escape is simply evaluated.
34:
35: ;These are the escape modes which are defined:
36: ; ~nD Takes any number and prints as a decimal integer. If no arg,
37: ; print without leading spaces. If arg and it fits in, put in leading
38: ; spaces; if it doesnt fit just print it. If second arg, use that
39: ; (or first char of STRING of it if not a number) instead of space
40: ; as a pad char.
41: ; ~nF Floating point
42: ; ~nE Exponential notation
43: ; ~nO Like D but octal
44: ; ~nA Character string. If there is an n then pad the string with spaces
45: ; on the right to make it n long. If it doesn't fit, ignore n.
46: ; ~n,m,minpad,padcharA Pad on the right to occupy at least
47: ; n columns, or if longer than that to begin with, pad to occupy
48: ; n+p*m columns for some nonnegative integer p.
49: ; at least minpad pad characters are produced in any case
50: ; (default if not supplied = 0).
51: ; padchar is used for padding purposes (default if not supplied = space).
52: ; if padchar is not a number, the first character in STRING of it is used.
53:
54: ; A mode can actually be used to PRINC anything, not just a string.
55: ; ~S Prin1 an object. Just like ~A (including parameters) but uses PRIN1.
56: ; ~C One character, in any acceptable form.
57: ; Control and meta bits print as alpha, beta, epsilon.
58: ; ~n* Ignore the next n args. n defaults to 1.
59: ; ~n% Insert n newlines. n defaults to 1.
60: ; ~n| Insert n formfeeds. n defaults to 1.
61: ; ~nX Insert n spaces. n defaults to 1.
62: ; ~n~ Insert n tildes. n defaults to 1.
63: ; ~& Perform the :FRESH-LINE operation on the stream.
64: ; ~n,mT Tab to column n+pm, for p an integer >= 0.
65: ; ~Q Apply the next arg to no arguments.
66: ; (Q ...) Apply the next arg to the (unevaluated) parameters following the Q.
67: ; ~P Insert an "s", unless its argument is a 1
68: ; ~nG Goto the nth argument (zero based). The next command will get that
69: ; argument, etc.
70: ; ~E and ~F are not implemented. ~T is not implemented.
71:
72: ; (FORMAT <stream> <control arg> <args>)
73: ; If <stream> is NIL, cons up and return a symbol.
74: ; If <stream> is T, use STANDARD-OUTPUT (saves typing).
75:
76: ;; defformat:
77: ;; to add a format handler, the defformat macro is used.
78: ;; the form is (defformat code args type . body)
79: ;; where
80: ;; code is the code this will handle. the code can be a multi
81: ;; character symbol, however it will have to be called with \\code\\.
82: ;; args is either a one or two symbol list, depending on type
83: ;; type is either: none, one, or many.
84: ;; none means that type handler will not use any argument (it may use
85: ;; use parameters however)
86: ;; one means that it takes exactly one argument
87: ;; many means that it may take from zero to ?? arguments.
88: ;; body is the body of the function. Its return value is only important
89: ;; in the case of 'many' handlers since these handlers must return the
90: ;; list of the arguments they didn't use.
91: ;;
92: ;; 'none' handlers get passes a hunk which contains the parameters provide
93: ;; for this format directive.
94: ;; 'one' handlers are passed the argument and the parameters.
95: ;; 'many' handlers are passed the list of remaining arguments and the
96: ;; parameters. they return the arguments they don't use.
97:
98: ;to do:
99: ; 3) make sure the semantics follows the lisp machine defs
100: ; 6) do exponential (~e) floating point formats correctly.
101: ; 7) move ferror elsewhere (near error would be a good place).
102: ; 8) document it.
103: ; 11) fix ~a to left justify if given correct flag
104: ; 13) make sure that multi character directives are lower cased
105: ; 14) make the 'x parameter work correctly
106: ; 15) fix the english printer (wrt stream arg) and add ordinal
107:
108:
109: ;;; Kludges to make MacLISP like some of the LISPM functions
110:
111: (declare (special Format-Standard-Output roman-old
112: format-params-supplied format format-handlers
113: format-sharpsign-vars))
114:
115: (setq format-sharpsign-vars 'franz-symbolic-character-names)
116:
117: ;; format-params-supplied : numbers of parameters to format parameter
118: ;; roman-old when t, the roman printer will print IIII instead of IV
119:
120: (or (boundp 'roman-old) (setq roman-old nil))
121:
122: (declare (setq defmacro-for-compiling nil defmacro-displace-call nil ))
123: (defmacro nsubstring (&rest w) `(format\:nsubstring ,.w))
124: (defmacro string-search-char (&rest w) `(format\:string-search-char ,.w))
125: (defmacro ar-1 (ar ind) `(cxr ,ind ,ar))
126: (defmacro as-1 (val ar ind) `(rplacx ,ind ,ar ,val))
127: (defmacro >= (x y) `(not (< ,x ,y)))
128: (defmacro <= (x y) `(not (> ,x ,y)))
129: (defmacro neq (x y) `(not (= ,x ,y)))
130: (defmacro pop (stack) `(prog1 (car ,stack) (setq ,stack (cdr ,stack))))
131: (declare (setq defmacro-for-compiling 't defmacro-displace-call 't))
132:
133:
134: (declare
135: (special ctl-string ;The control string.
136: ctl-length ;string-length of ctl-string.
137: ctl-index ;Our current index into the control string.
138: ; Used by the conditional command. (NYI)
139: atsign-flag ;Modifier
140: colon-flag ;Modifier
141: format-temporary-area ;For temporary consing
142: format-arglist ;The original arg list, for ~G.
143: arglist-index ;How far we are in the current arglist
144: float-format ; format used when printing floats
145: poport ; franz's standard output
146: ))
147:
148: (defun format (stream ctl-string &rest args)
149: (let (format-string Format-Standard-Output
150: (format-arglist args)
151: (arglist-index 0))
152: (setq stream (cond ((eq stream 't) poport )
153: ((null stream)
154: (setq format-string 't)
155: (list nil))
156: (t stream)))
157: (setq Format-Standard-Output stream)
158: (cond ((symbolp ctl-string)
159: (setq ctl-string (get_pname ctl-string))))
160: (cond ((stringp ctl-string)
161: (format-ctl-string args ctl-string))
162: (t (do ((ctl-string ctl-string (cdr ctl-string)))
163: ((null ctl-string))
164: (setq args
165: (cond ((symbolp (car ctl-string))
166: (format-ctl-string args (car ctl-string)))
167: (t (format-ctl-list args (car ctl-string))))))))
168: (and format-string
169: (setq format-string (maknam (nreverse (cdr stream)))))
170: format-string))
171:
172: (defun format-ctl-list (args ctl-list)
173: (format-ctl-op (car ctl-list) args (cdr ctl-list)))
174:
175: (defun format-ctl-string (args ctl-string)
176: (declare (fixnum ctl-index ctl-length))
177: (do ((ctl-index 0) (ch) (tem) (str) (sym)
178: (ctl-length (flatc ctl-string)))
179: ((>= ctl-index ctl-length) args)
180: (setq tem (cond ((string-search-char #/~ ctl-string ctl-index))
181: (t ctl-length)))
182: (cond ((neq tem ctl-index) ;Put out some literal string
183: (setq str (nsubstring ctl-string ctl-index tem))
184: (format:patom str)
185: (and (>= (setq ctl-index tem) ctl-length)
186: (return args))))
187: ;; (ar-1 ch ctl-index) is a tilde.
188: (do ((atsign-flag nil) ;Modifier
189: (colon-flag nil) ;Modifier
190: (params (makhunk 10))
191: (param-leader -1)
192: ;PARAMS contains the list of numeric parameters
193: (param-flag nil) ;If T, a parameter has been started in PARAM
194: (param)) ;PARAM is the parameter currently
195: ; being constructed
196: ((>= (setq ctl-index (1+ ctl-index)) ctl-length))
197: (setq ch (getcharn ctl-string (1+ ctl-index)))
198: (cond ((and (>= ch #/0) (<= ch #/9)) ;
199: (setq param (+ (* (or param 0) 10.) (- ch #/0)) ;
200: param-flag t))
201: ((= ch #/@) ;ascii @
202: (setq atsign-flag t))
203: ((= ch #/:) ;ascii :
204: (setq colon-flag t))
205: ((or (= ch #/v) (= ch #/V)) ;ascii v, v
206: (as-1 (pop args) params
207: (setq param-leader (1+ param-leader)))
208: (setq arglist-index (1+ arglist-index))
209: (setq param nil param-flag nil))
210: ((= ch #/#)
211: (as-1 (length args) params
212: (setq param-leader (1+ param-leader))))
213: ((= ch #/,)
214: ;comma, begin another parameter, ascii ,
215: (and param-flag (as-1 param params (setq param-leader
216: (1+ param-leader))))
217: (setq param nil param-flag t))
218: ;omitted arguments made manifest by the
219: ;presence of a comma come through as nil
220: (t ;must be a command character
221: ;upper case to lower
222: (and (>= ch #/A) (<= ch #/Z) (setq ch (+ ch (- #/a #/A))))
223: (setq ctl-index (1+ ctl-index)) ;advance past command char
224: (and param-flag (as-1 param params (setq param-leader
225: (1+ param-leader))))
226: (setq param-flag nil param nil tem nil)
227: ;str gets a string which is the name of the operation to do
228: (setq
229: str (cond ((= ch #/\ ) ;ascii \
230: (let ((i (string-search-char
231: #/\
232: ctl-string
233: (1+ ctl-index))))
234: (and (null i)
235: (ferror nil
236: '|Unmatched \\ in control string.|))
237: (prog1 ; don't uppercase! we are a two
238: ; case system
239: (setq tem
240: (nsubstring ctl-string
241: (1+ ctl-index)
242: i))
243: (setq ctl-index i))))
244: ; makes ~<newline> work! ;SMH@EMS
245: ((= ch #\newline) (concat "ch" ch)) ;SMH@EMS
246: (t (ascii ch))))
247: ;; SYM gets the symbol corresponding to STR
248: (cond ((setq sym str)
249: (setq format-params-supplied param-leader)
250: (setq args (format-ctl-op sym args params)))
251: (t (ferror nil '|~C is an unknown FORMAT op in \"~A\"|
252: tem ctl-string)))
253: (return nil))))))
254:
255: ;Perform a single formatted output operation on specified args.
256: ;Return the remaining args not used up by the operation.
257: (defun format-ctl-op (op args params &aux tem)
258: (cond ((stringp op) (setq op (concat op)))) ; make into a symbol
259: (cond ((setq tem (assq op format-handlers))
260: (cond ((eq 'one (cadr tem))
261: (or args
262: (ferror nil "arg required for ~a, but no more args" op))
263: (funcall (caddr tem) (car args) params)
264: (setq arglist-index (1+ arglist-index))
265: (cdr args))
266: ((eq 'none (cadr tem))
267: (funcall (caddr tem) params)
268: args)
269: ((eq 'many (cadr tem))
270: (funcall (caddr tem) args params))
271: (t (ferror nil "Illegal format handler: ~s" tem))))
272: (t (ferror nil '|\"~S\" is not defined as a FORMAT command.| op)
273: args)))
274:
275: (setq format-handlers nil)
276: ;; Format handlers
277: ;;
278: (defmacro defformat (name arglist type &rest body)
279: (let (newname)
280: ;; allow the name to be the fixnum rep of a character too.
281: (cond ((fixp name) (setq name (concat "ch" name))))
282:
283: (cond ((not (memq type '(none one many)))
284: (ferror nil "The format type, \"~a\" is not: none, one or many"
285: type)))
286: (cond ((or (not (symbolp name))
287: (not (dtpr arglist)))
288: (ferror nil "Bad form for name and/or arglist: ~a ~a"
289: name arglist)))
290: (cond ((memq type '(one many))
291: (cond ((not (= (length arglist) 2))
292: (ferror nil "There should be 2 arguments to ~a" name))))
293: (t (cond ((not (= (length arglist) 1))
294: (ferror nil "There should be 1 argument to ~a" name)))))
295: (setq newname (concat name ":format-handler"))
296: `(progn 'compile
297: (defun ,newname ,arglist ,@body)
298: (let ((handler (assq ',name format-handlers)))
299: (cond (handler (rplaca (cddr handler) ',newname))
300: (t (setq format-handlers (cons (list ',name
301: ',type
302: ',newname)
303: format-handlers))))))))
304:
305:
306:
307: (defformat d (arg params) one
308: (let ((width (cxr 0 params))
309: (padchar (cxr 1 params)))
310: (cond ((and colon-flag (< arg 4000.) (> arg 0))
311: (roman-step arg 0))
312: (atsign-flag (english-print arg 'cardinal))
313: ((let ((base 10.) (*nopoint t))
314: (cond ((null padchar) (setq padchar 32.))
315: ((not (numberp padchar))
316: (setq padchar (getcharn padchar 1))))
317: (and width (format-ctl-justify width (flatc arg) padchar))
318: (format:patom arg))))))
319:
320: (defformat f (arg params) one
321: (cond ((not (floatp arg)) (format:patom arg))
322: (t (let ((float-format "%.16g")
323: (prec (cxr 0 params)))
324: (cond ((and prec (fixp prec) (> prec 0) (< prec 16))
325: (setq float-format (concat "%" prec "g"))))
326: (format:patom arg)))))
327:
328: ; r format
329: ; no params and flags: print as cardinal (four)
330: ; no params and colon: print as ordinal (fourth)
331: ; no params and atsign: print as roman (IV)
332: ; no params and colon and atsign: print as old roman (IIII)
333: ; params: radix,mincol[0],padchar[<space>]
334: ; print in radix with at least mincol columns, padded on left
335: ; with padchar
336: ;
337: (defformat r (arg params) one
338: (format:anyradix-printer arg params nil))
339:
340: ; o format - like ~8r, but params are like ~d.
341: ;
342: (defformat o (arg params) one
343: (format:anyradix-printer arg params 8.))
344:
345: (defun format:anyradix-printer (arg params radix)
346: ; this is called by ~r and ~o. for ~r, the mincol parameter starts at
347: ; cxr 1, for ~o the mincol parameter starts at cxr 0. We compute
348: ; paramstart as either 0 or 1
349: ; radix is given as third argument iff this is ~o
350: (let ((paramstart (cond (radix 0)
351: (t 1))))
352: (cond ((null radix) (setq radix (cxr 0 params))))
353: (cond ((null radix) ; if not to any given base
354: (cond ((and (null colon-flag) (null atsign-flag))
355: (english-print arg 'cardinal))
356: ((and colon-flag (null atsign-flag))
357: (english-print arg 'ordinal))
358: ((and (null colon-flag) atsign-flag)
359: (roman-step arg 0))
360: ((and colon-flag atsign-flag)
361: (let ((roman-old t))
362: (roman-step arg 0)))))
363: (t (let ((mincol (cxr paramstart params))
364: (padchr (or (cxr (+ 1 paramstart) params) #\space))
365: (res))
366: (cond (mincol ;; if mincol specified
367: (let ((Format-Standard-Output (list nil)))
368: (format-binpr arg radix)
369: (setq res (cdr Format-Standard-Output)))
370: (format-ctl-justify mincol (length res) padchr)
371: (mapc 'format:tyo (nreverse res)))
372: (t (format-binpr arg radix))))))))
373:
374:
375: (defun format-binpr (x base)
376: (cond ((equal x 0)(format:patom 0))
377: ((or (> base 36.) (< base 2))
378: (ferror nil "\"~s\" is not a base between 2 and 36" base))
379: ((lessp x 0)
380: (format:patom '-)
381: (format-binpr1 (minus x) base))
382: (t (format-binpr1 x base)))
383: x)
384:
385:
386:
387: (defun format-binpr1 (x base)
388: (cond ((equal x 0))
389: (t (format-binpr1 (quotient x base) base)
390: (format-prc (remainder x base)))))
391:
392: (defun format-prc (x)
393: (cond ((< x 10.) (format:patom x))
394: (t (format:tyo (plus (- #/a 10.) x)))))
395: ; works for 10.=A, 35.=Z.
396:
397: ;; must get the width stuff to work!!
398: (defun format-ctl-octal (arg params)
399: (let ((width (cxr 0 params)) (padchar (cxr 1 params)))
400: (let ((base 8))
401: (cond ((null padchar)
402: (setq padchar 32.))
403: ((not (numberp padchar))
404: (setq padchar (getcharn padchar 1))))
405: (and width (format-ctl-justify width (flatc arg) padchar))
406: (format:patom arg))))
407:
408: (defformat a (arg params) one
409: (format-ctl-ascii arg params nil))
410:
411: (defun format-ctl-ascii (arg params prin1p)
412: (let ((edge (cxr 0 params))
413: (period (cxr 1 params))
414: (min (cxr 2 params))
415: (padchar (cxr 3 params)))
416: (cond ((null padchar)
417: (setq padchar #\space))
418: ((not (numberp padchar))
419: (setq padchar (getcharn padchar 1))))
420: (cond (prin1p (format:print arg))
421: (t (format:patom arg)))
422: (cond ((not (null edge))
423: (let ((width (cond (prin1p (flatsize arg)) ((flatc arg)))))
424: (cond ((not (null min))
425: (format-ctl-repeat-char min padchar)
426: (setq width (+ width min))))
427: (cond (period
428: (format-ctl-repeat-char
429: (- (+ edge (* (\\ (+ (- (max edge width) edge 1)
430: period)
431: period)
432: period))
433: width)
434: padchar))
435: (t (format-ctl-justify edge width padchar))))))))
436:
437: (defformat s (arg params) one
438: (format-ctl-ascii arg params t))
439:
440: (defformat c (arg params) one
441: (cond ((or (not (fixp arg))
442: (< arg 0)
443: (> arg 127))
444: (ferror nil "~s is not a legal character value" arg)))
445: (cond ((and (not colon-flag) (not atsign-flag))
446: ; just print out the character after converting to ascii
447: (format:patom (ascii arg)))
448: (t ; it may have an extended name, check for that first
449: (let (name)
450: (cond ((setq name (car
451: (rassq arg (symeval format-sharpsign-vars))))
452: ; it has an extended name.
453: ; if : flag, then print in human readable
454: (cond (colon-flag (format:patom name))
455: (atsign-flag (format:patom "#\\")
456: (format:patom name))))
457: ((< arg #\space)
458: ; convert from control to upper case
459: (setq arg (+ arg #/@))
460: (cond (colon-flag (format:patom "^")
461: (format:patom (ascii arg)))
462: (atsign-flag (format:patom "#^")
463: (format:patom (ascii arg)))))
464: (t (cond (colon-flag (format:patom (ascii arg)))
465: (atsign-flag (format:patom "#/")
466: (format:patom (ascii arg))))))))))
467:
468: (defformat p (args params) many
469: (let (arg)
470: (cond (colon-flag
471: (setq arg (nth (1- arglist-index) format-arglist)))
472: ((null args)
473: (ferror () "Argument required for p, but no more arguments"))
474: (t (setq arg (pop args)
475: arglist-index (1+ arglist-index))))
476: (if (= arg 1)
477: (if atsign-flag (format:tyo #/y))
478: (cond (atsign-flag
479: (format:tyo #/i)
480: (format:tyo #/e)
481: (format:tyo #/s))
482: (t (format:tyo #/s))))
483: args))
484:
485: (defformat * (args params) many
486: (let ((count (or (cxr 0 params) 1)))
487: (if colon-flag (setq count (minus count)))
488: (setq arglist-index (+ arglist-index count))
489: ;; (nthcdr count format-arglist) ;; ??? SMH@EMS
490: (nthcdr arglist-index format-arglist))) ;; SMH@EMS
491:
492: (defformat g (arg params) many
493: (let ((count (or (cxr 0 params) 1)))
494: (nthcdr count format-arglist)))
495:
496: (defformat % (params) none
497: (declare (fixnum i))
498: (let ((count (or (cxr 0 params) 1)))
499: (do i 0 (1+ i) (= i count)
500: (format:terpr))))
501:
502: ; ~ at the end of the line
503: ; no params: ignore newline and following whitespace
504: ; @ flag: leave the newline in the string but ignore whitespace
505: ; : flag: ignore newline but leave the whitespace
506: ; :@ flags: leave both newline and whitespace
507: ;
508: (defformat #\newline (params) none
509: (cond (atsign-flag
510: (format:tyo #\newline)))
511: (cond ((not colon-flag)
512: (setq ctl-index (1+ ctl-index))
513: (do ()
514: ((>= ctl-index ctl-length))
515: (cond ((memq (getcharn ctl-string ctl-index)
516: '(#\space #\tab))
517: (setq ctl-index (1+ ctl-index)))
518: (t (setq ctl-index (1- ctl-index))
519: (return)))))))
520:
521:
522: (defformat & (params) none
523: (format:fresh-line))
524:
525: (defformat x (params) none
526: (format-ctl-repeat-char (cxr 0 params) #\space))
527:
528: (defformat \| (params) none
529: (format-ctl-repeat-char (cxr 0 params) #\ff))
530:
531: (defformat ~ (params) none
532: (format-ctl-repeat-char (cxr 0 params) #/~))
533:
534: (defun format-ctl-repeat-char (count char)
535: (declare (fixnum i))
536: (cond ((null count) (setq count 1)))
537: (do i 0 (1+ i) (=& i count)
538: (format:tyo char)))
539:
540: ;; Several commands have a SIZE long object which they must print
541: ;; in a WIDTH wide field. If WIDTH is specified and is greater than
542: ;; the SIZE of the thing to be printed, this put out the right
543: ;; number of CHARs to fill the field. You can call this before
544: ;; or after printing the thing, to get leading or trailing padding.
545: (defun format-ctl-justify (width size &optional (char #\space))
546: (and width (> width size) (format-ctl-repeat-char (- width size) char)))
547:
548: (defformat q (arg params) one
549: ;; convert params given to a list
550: (do ((ii format-params-supplied (1- ii))
551: (params-given nil))
552: ((< ii 0) (apply arg params-given))
553: (setq params-given (cons (cxr ii params) params-given))))
554:
555: ;; Fixed nested ~[ ~] parser to handle ~:[ ~] and ~@:[ ~] as well. SMH@EMS
556: (defun case-scan (goal &optional (lim ctl-length) (times 1))
557: (declare (fixnum cnt lim times ctl-index))
558: (*catch 'case-scan
559: (do ((cnt 0 (1+ cnt)))
560: ((>= cnt times) t)
561: (do ((ch))
562: ((>= ctl-index lim)
563: (*throw 'case-scan nil))
564: (setq ch (getcharn ctl-string (1+ ctl-index))
565: ctl-index (1+ ctl-index))
566: (cond ((= ch #/~)
567: (setq ch (getcharn ctl-string (1+ ctl-index))
568: ctl-index (1+ ctl-index))
569: (cond ((= ch goal)
570: (return t))
571: ((or (= ch #/[) ;; SMH@EMS
572: (and (or (= ch #/:) (= ch #/@))
573: (= (getcharn ctl-string
574: (setq ctl-index (1+ ctl-index)))
575: #/[))) ;; #/] fakeout emacs
576: (case-scan #/] lim)))))))))
577:
578: ; [ format
579: ; the case selector is the first parameter given, and if no parameter
580: ; is given, then it is the next argument
581: ;
582: (defformat \[ (args params) many
583: (let ((start ctl-index)
584: (num (cond ((> format-params-supplied -1)
585: (cxr 0 params))
586: (t (cond ((null args)
587: (error "the [ format requires an argument")))
588: (prog1 (car args)
589: (setq args (cdr args))
590: (setq arglist-index (1+ arglist-index)))))))
591: (and colon-flag (setq num (cond (num 1) (t 0))))
592: (and (null num)
593: (ferror nil
594: "The FORMAT \"[\" command must be given a numeric parameter"))
595: (cond ((>= num 0)
596: (or (case-scan #/])
597: (ferror nil "Unbalanced conditional in FORMAT control string"))
598: (let ((i ctl-index))
599: (setq ctl-index start)
600: (case-scan #/; i num))))
601: args))
602:
603: (defformat \] (params) none nil)
604:
605: (defformat \; (params) none
606: (case-scan #/]))
607:
608: ;; FIXTHIS:
609: ;; The following doesn't bind format-arglist and arglist-index properly.
610: ;; Added return-* stuff, also fixing above(?). SMH@EMS
611: (defformat \{ (args params) many
612: (let ((loop-times (or (cxr 0 params) -1))
613: (loop-string)
614: (at-least-once nil)
615: (return-args) ;; SMH@EMS
616: (return-format-arglist) ;; SMH@EMS
617: (return-arglist-index)) ;; SMH@EMS
618: (do ((i (format\:string-search-char #/~ ctl-string ctl-index)
619: (format\:string-search-char #/~ ctl-string (1+ i))))
620: ((or (null i) (= (1+ i) ctl-length))
621: (ferror () "No matching \"}\" for \"{\" in format"))
622: (cond ((= #/} (getcharn ctl-string (+ 2 i)))
623: (setq loop-string
624: (format\:nsubstring ctl-string ctl-index i)
625: ctl-index (+ 2 i))
626: (return t))
627: ((and (= #/: (getcharn ctl-string (+ 2 i)))
628: (= #/} (getcharn ctl-string (+ 3 i))))
629: (setq loop-string
630: (format\:nsubstring ctl-string ctl-index i)
631: ctl-index (+ 3 i)
632: at-least-once t)
633: (return t))))
634: (if (= 0 (flatc loop-string))
635: (setq loop-string (pop args)
636: arglist-index (1+ arglist-index)))
637: (if (null atsign-flag)
638: (setq return-args (cdr args) ;; SMH@EMS
639: return-arglist-index arglist-index ;; SMH@EMS
640: arglist-index 0 ;; SMH@EMS
641: return-format-arglist format-arglist ;; SMH@EMS
642: format-arglist (car args) ;; SMH@EMS
643: args format-arglist))
644: (*catch '(loop-stop loop-abort)
645: (do ((i loop-times (1- i)))
646: ((and (null at-least-once)
647: (or (null args) (= i 0))))
648: (setq at-least-once nil)
649: (cond ((null colon-flag)
650: (setq args (format-ctl-string args loop-string)))
651: (t (*catch 'loop-stop
652: (format-ctl-string (car args) loop-string))
653: (setq args (cdr args)
654: arglist-index (1+ arglist-index))))))
655: (cond (return-arglist-index ;; SMH@EMS
656: (setq args return-args ;; SMH@EMS
657: arglist-index (1+ return-arglist-index) ;; SMH@EMS
658: format-arglist return-format-arglist))) ;; SMH@EMS
659: args))
660:
661: (defformat \} (params) none nil)
662:
663: (defformat \^ (args params) many
664: (let ((terminate nil))
665: (cond ((null (cxr 0 params))
666: (setq terminate (null args)))
667: ((null (cxr 1 params))
668: (setq terminate (zerop (cxr 0 params))))
669: ((null (cxr 2 params))
670: (setq terminate (equal (cxr 1 params) (cxr 0 params))))
671: (t (setq terminate (and (< (cxr 0 params) (cxr 1 params))
672: (< (cxr 1 params) (cxr 2 params))))))
673: (if terminate
674: (if colon-flag (*throw 'loop-abort t) (*throw 'loop-stop t))
675: args)))
676:
677:
678: (declare (special english-small english-medium english-large))
679:
680: (defun make-list-array (list)
681: (let ((a (makhunk (length list))))
682: (do ((i 0 (1+ i))
683: (ll list (cdr ll)))
684: ((null ll))
685: (rplacx i a (car ll)))
686: a))
687:
688: (setq english-small
689: (make-list-array '(|one| |two| |three| |four| |five| |six|
690: |seven| |eight| |nine| |ten| |eleven| |twelve|
691: |thirteen| |fourteen| |fifteen| |sixteen|
692: |seventeen| |eighteen| |nineteen|)))
693:
694: (setq english-medium
695: (make-list-array '(|twenty| |thirty| |forty| |fifty| |sixty| |seventy|
696: |eighty| |ninty|)))
697:
698: (setq english-large
699: (make-list-array '(|thousand| |million| |billion| |trillion| |quadrillion|
700: |quintillion|)))
701:
702:
703: (defun english-print (n type)
704: (declare (fixnum i n limit))
705: (cond ((zerop n)
706: (cond ((eq type 'cardinal) (format:patom "zero"))
707: (t (format:patom "zeroth"))))
708: ((< n 0)
709: (format:patom '|minus|)
710: (format:tyo #\space)
711: (english-print (minus n) type))
712: (t
713: (do ((n n)
714: (p)
715: (flag)
716: (limit 1000000.
717: (quotient limit 1000.))
718: (i 1 (1- i)))
719: ((< i 0)
720: (cond ((> n 0)
721: (and flag (format:tyo #\space))
722: (english-print-thousand n))))
723: (cond ((not (< n limit))
724: (setq p (quotient n limit)
725: n (remainder n limit))
726: (cond (flag (format:tyo #\space))
727: (t (setq flag t)))
728: (english-print-thousand p)
729: (format:tyo #\space)
730: (format:patom (ar-1 english-large i))))))))
731:
732: (defun english-print-thousand (n)
733: (declare (fixnum i n limit))
734: (let ((n (remainder n 100.))
735: (h (quotient n 100.)))
736: (cond ((> h 0)
737: (format:patom (ar-1 english-small (1- h)))
738: (format:tyo #\space)
739: (format:patom '|hundred|)
740: (and (> n 0) (format:tyo #\space))))
741: (cond ((= n 0))
742: ((< n 20.)
743: (format:patom (ar-1 english-small (1- n))))
744: (t
745: (format:patom (ar-1 english-medium
746: (- (quotient n 10.) 2)))
747: (cond ((zerop (setq h (remainder n 10.))))
748: (t
749: (format:tyo #/-) ;ascii -
750: (format:patom (ar-1 english-small (1- h)))))))))
751:
752: (defun roman-step (x n)
753: (cond ((> x 9.)
754: (roman-step (quotient x 10.) (1+ n))
755: (setq x (remainder x 10.))))
756: (cond ((and (= x 9) (not roman-old))
757: (roman-char 0 n)
758: (roman-char 0 (1+ n)))
759: ((= x 5)
760: (roman-char 1 n))
761: ((and (= x 4) (not roman-old))
762: (roman-char 0 n)
763: (roman-char 1 n))
764: (t (cond ((> x 5)
765: (roman-char 1 n)
766: (setq x (- x 5))))
767: (do i 0 (1+ i) (>= i x)
768: (roman-char 0 n)))))
769:
770: (defun roman-char (i x)
771: (format:tyo (car (nthcdr (+ i x x) '(#/I #/V #/X #/L #/C #/D #/M)))
772: ; i v x l c d m
773: ))
774:
775: ;;; Kludges to make MacLISP like some of the LISPM functions
776:
777:
778: (defun format:tyo (char)
779: (cond ((dtpr Format-Standard-Output)
780: (rplacd Format-Standard-Output
781: (cons char (cdr Format-Standard-Output))))
782: (t (tyo char Format-Standard-Output))))
783:
784: (defun format:patom (arg)
785: (format:printorpatom arg nil))
786:
787: (defun format:print (arg)
788: (format:printorpatom arg t))
789:
790: (defun format:printorpatom (argument slashify)
791: (cond ((dtpr Format-Standard-Output)
792: (rplacd Format-Standard-Output
793: (nreconc (cond (slashify
794: (mapcar '(lambda (x)
795: (getcharn x 1))
796: (explode argument)))
797: ((exploden argument)))
798: (cdr Format-Standard-Output))))
799: (t (cond (slashify (print argument Format-Standard-Output))
800: (t (patom argument Format-Standard-Output))))))
801:
802: (defun format:terpr nil
803: (cond ((dtpr Format-Standard-Output)
804: (rplacd Format-Standard-Output
805: (cons #\newline (cdr Format-Standard-Output))))
806: (t (terpr Format-Standard-Output))))
807:
808: (defun format:fresh-line nil
809: (cond ((dtpr Format-Standard-Output)
810: (cond ((and (cdr Format-Standard-Output)
811: (not (= (cadr Format-Standard-Output) #\newline)))
812: (rplacd Format-Standard-Output
813: (cons #\newline (cdr Format-Standard-Output))))))
814: (t (and (not (= 0 (nwritn Format-Standard-Output)))
815: (terpr Format-Standard-Output)))))
816:
817:
818:
819:
820: (defun format\:string-search-char (char str start-pos)
821: (declare (fixnum i start-pos str-len))
822: (do ((i start-pos (1+ i))
823: (str-len (flatc str)))
824: ((>& i str-len) nil)
825: (and (=& char (getcharn str (1+ i))) (return i))))
826:
827: (defun format\:nsubstring (str from to)
828: (declare (fixnum i from to))
829: (substring str (+ 1 from) (- to from))) ;substring is 1 based
830:
831: (defun ferror (&rest args)
832: (let (str)
833: ; if the first arg to ferror is a string we assume that it is the
834: ; format control string, otherwise we assume that it is a port
835: ; specification, and we ignore it since we want to build a string.
836: (if (stringp (car args))
837: then (setq str (lexpr-funcall 'format nil args))
838: else (setq str (lexpr-funcall 'format nil (cdr args))))
839: (error str)))
840:
841:
842: (defun format-test nil
843: (format t "Start test, newline:~%freshline:~&")
844: (format t "decimal:~d, width=5:~5d~%" 10 10)
845: (format t "decimal pad with period:~10,vd~%" #/. 12)
846: (format t "char normal:~c, as # would read:~@c, human read:~:c~%"
847: #\space #\space #\space)
848: (format t "cardinal:~r, roman new:~@r, roman-old:~:@r~
849: <same line I hope>~@
850: new line but at beginning~:
851: same line, but spaced out~:@
852: new line and over two tabs~%" 4 4 4))
853:
854: (putprop 'format t 'version)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.