a preface soo (23 Mar 2004 16:18 UTC)
|
Re: a preface
David Van Horn
(24 Mar 2004 07:38 UTC)
|
Re: a preface
soo
(24 Mar 2004 12:19 UTC)
|
Re: a preface
David Van Horn
(24 Mar 2004 19:38 UTC)
|
Re: a preface
soo
(25 Mar 2004 12:32 UTC)
|
Re: a preface
Francisco Solsona
(24 Mar 2004 16:45 UTC)
|
Hi. The current uploaded draft is old one. I submitted several revised proposals before drafting, but there was no response. (I have heard that Solsona had some problems with his machine.) The following is my last revised proposal. Title Formatting Author Joo ChurlSoo Abstract This SRFI introduces the FMT procedure that converts any object to a string. Unlike the procedure called FORMAT, this FMT procedure takes one object as the first argument and accepts several optional arguments. Rationale The FMT procedure provides a handy optional and functional interface. Specification (FMT <number> [[<width>] [<depth>] [<char>] [<radix>] [<plus>] [<exactness>] [<space>] [<string>] ...]) (FMT <others> [[<width>] [<count>] [<char>] [<show>] [<case>] [<space>] [<string>] ...]) * <number> is any numeric expression. * <others> are any expressions except number. * <width> is an exact integer whose absolute value specifies the width of the resulting string. When the resulting string has fewer characters than the absolute value of <width>, it is padded with <char>s, either on the left if <width> is positive, or on the right if <width> is negative. On the other hand, when the resulting string has more characters than the absolute value of <width>, the <width> is ignored. * <depth> is a non-negative exact integer that specifies the number of decimal digits after decimal point. * <count> is a non-negative exact integer that specifies the number of characters of the resulting string. * <char> is a padding character. * <radix> is a symbol: b (binary), d (decimal), o (octal), x (hexadecimal) * <show> is a procedure: display, write * If <plus> is a procedure + and <number> is a positive number without a positive sign, the positive sign is prefixed to the <number>. * <case> is a symbol: u (upcase), d (downcase), t (titlecase) * <exactness> is a symbol: e (exact), i (inexact) * <space> is a list whose elements are non-negative exact integer, and the number of elements of the list is 1 or 2. The resulting string is padded with space character on the left as much as the value of the first element and on the right as much as th value of the second element regardless of <width>. * <string>s are strings that are appended to the resulting string. The order of optional arguments is ignored except that <depth> or <count> can be defined only after <width> is defined. Examples (fmt 129.995) "129.995" (fmt 129.995 '(2)) " 129.995" (fmt 129.995 '(1 1)) " 129.995 " (fmt 129.995 10 2) " 130.00" (fmt 129.995 -10 2) "130.00 " (fmt 129.995 10 #\0 2) "0000130.00" (fmt 129.995 #\0 10 + 2) "+000130.00" (fmt 129.995 #\0 10 + 2 'o 'e) error (fmt 129.995 #\0 10 + 'o 'e) "+000000202" (fmt 129.995 #\0 10 + 'o 'e '(1 2)) " +000202 " (fmt 129.995 '(1) #\0 10 + 'o 'e) " +00000202" (fmt (sqrt -5) 10) "0.0+2.23606797749979i" (fmt (sqrt -5) 10 2) "0.00+2.24i" (fmt 3.14159e12 10 2 +) " +3.14e12" (fmt #x123 'o 10) " 443" (fmt #x123 -10 3 + #\*) "+291.000**" (fmt "string" 10) " string" (fmt "string" '(1 1)) " string " (fmt "string" -10 '(1 2)) " string " (fmt "The number is 3." 't 10) "The Number Is 3." (fmt "The number is 3." 'u '(1)) " THE NUMBER IS 3." (fmt "string" -10 write) "\"string\" " (fmt "string" 10 3) " str" (fmt "string" -10 3) "str " (fmt "string" #\- -10 3) "str-------" (fmt #\a write) "#\\a" (fmt #\a display) "a" (fmt #\a 10) " a" (fmt 'symbol 10) " symbol" (fmt '(1 #\a "str" sym '(a)) write) "(1 #\\a \"str\" sym (quote (a)))" (fmt '(1 #\a "str" sym '(a))) "(1 a str sym (quote (a)))" (fmt '(1 #\a "str" sym '(a)) 10) "(1 a str sym (quote (a)))" (fmt '(1 #\a "str" sym '(a)) 10 10) "(1 a str s" (fmt #(1 #\a "str" sym '(a)) 10) "#(1 a str sym (quote (a)))" (fmt #(1 #\a "str" sym '(a)) 10 write) "#(1 #\\a \"str\" sym (quote (a)))" (fmt 123 "is an integer." + 0 2 '(0 1)) "+123.00 is an integer." (fmt "this" 't (fmt "is" '(1 1)) "A.") "This is A." Implementation The implementation below requires SRFI-1 (List library), SRFI-6 (Basic string ports), SRFI-8 (Receive), SRFI-13 (String library), and SRFI-23 (Error reporting mechanism). (define (opt-values rest-list . default-list) (let loop ((rest-list rest-list) (default-list default-list) (result '())) (if (null? default-list) (apply values (append (reverse result) rest-list)) (let ((default (car default-list))) (let lp ((rest rest-list) (head '())) (if (null? rest) (loop (reverse head) (cdr default-list) (cons (car default) result)) (if (list? default) (if (member (car rest) default) (loop (append (reverse head) (cdr rest)) (cdr default-list) (cons (car rest) result)) (lp (cdr rest) (cons (car rest) head))) (if ((cdr default) (car rest)) (loop (append (reverse head) (cdr rest)) (cdr default-list) (cons (car rest) result)) (lp (cdr rest) (cons (car rest) head)))))))))) (define-syntax arg-or (syntax-rules() ((arg-or arg (a1 a2 ...) ...) (or (and (not (symbol? 'arg)) (error "bad syntax" 'arg '(symbol? 'arg) '(arg-or arg (a1 a2 ...) ...))) (and (a1 a2 ...) (error "incorrect argument" arg 'arg '(a1 a2 ...))) ...)) ((arg-or caller arg (a1 a2 ...) ...) (or (and (not (symbol? 'arg)) (error "bad syntax" 'arg '(symbol? 'arg) '(arg-or caller arg (a1 a2 ...) ...))) (and (a1 a2 ...) (if (string? caller) (error caller arg 'arg '(a1 a2 ...)) (error "incorrect argument" arg 'arg '(a1 a2 ...) caller))) ...)))) ;; accessory macro for arg-ors (define-syntax caller-arg-or (syntax-rules() ((caller-arg-or caller arg (a1 a2 ...) ...) (or (and (not (symbol? 'arg)) (error "bad syntax" 'arg '(symbol? 'arg) '(caller-arg-or caller arg (a1 a2 ...) ...))) (and (a1 a2 ...) (if (string? caller) (error caller arg 'arg '(a1 a2 ...)) (error "incorrect argument" arg 'arg '(a1 a2 ...) caller))) ...)) ((caller-arg-or null caller arg (a1 a2 ...) ...) (or (and (not (symbol? 'arg)) (error "bad syntax" 'arg '(symbol? 'arg) '(caller-arg-or caller arg (a1 a2 ...) ...))) (and (a1 a2 ...) (if (string? caller) (error caller arg 'arg '(a1 a2 ...)) (error "incorrect argument" arg 'arg '(a1 a2 ...) caller))) ...)))) (define-syntax arg-ors (syntax-rules (common) ((arg-ors (a1 a2 ...) ...) (or (arg-or a1 a2 ...) ...)) ((arg-ors common caller (a1 a2 ...) ...) (or (caller-arg-or caller a1 a2 ...) ...)))) (define (fmt expr . rest) (if (number? expr) (receive (width depth char radix plus exactness space . str-list) (opt-values rest (cons #f (lambda (x) (and (integer? x) (exact? x)))) (cons #f (lambda (x) (and (integer? x) (exact? x) (<= 0 x)))) (cons #f char?) (list 'd 'b 'o 'x) (cons #f (lambda (x) (eq? x +))) (cons #f (lambda (x) (memq x '(e i)))) (cons #f (lambda (x) (and (list? x) (<= 1 (length x) 2) (every (lambda (x) (and (integer? x) (exact? x) (<= 0 x))) x))))) (arg-ors ("fmt: bad argument" str-list (not (every string? str-list))) ("fmt: non-decimal cannot be inexact" radix (and (memq radix '(b o x)) (or depth (and (inexact? expr) (not (eq? exactness 'e))) (eq? exactness 'i)))) ("fmt: exact number cannot have a decimal point" depth (and depth (eq? exactness 'e))) ("fmt: unnecessary padding character" char (and char (not width)))) (let* ((width (or width 0)) (char (or char #\space)) (sign (if (< width 0) '- '+)) (str (number->string (if exactness (if (eq? exactness 'e) (if (inexact? expr) (inexact->exact expr) expr) (if (exact? expr) (exact->inexact expr) expr)) (if (and depth (exact? expr)) (exact->inexact expr) expr)) (cdr (assq radix '((b . 2) (d . 10) (o . 8) (x . 16)))))) (str (if depth (let ((e-index (or (string-index str #\e) (string-index str #\E))) (+-index (string-index str #\+ 1))) (define (mold str dep) (let ((len (string-length str)) (index (string-index str #\.))) (if index (let ((d-len (- len index 1))) (if (<= d-len dep) (string-append str (make-string (- dep d-len) #\0)) (mold (number->string (+ (string->number (substring str 0 (+ (if (= dep 0) 0 1) index dep))) (if (< 4 (string->number (string (string-ref str (+ 1 index dep))))) (expt 0.1 dep) 0))) dep))) (string-append str "." (make-string dep #\0))))) (cond (e-index (string-append (mold (substring str 0 e-index) depth) (substring str e-index (string-length str)))) (+-index (string-append (mold (substring str 0 +-index) depth) "+" (mold (substring str (+ 1 +-index) (- (string-length str) 1)) depth) (string (string-ref str (- (string-length str) 1))))) (else (mold str depth)))) str)) (str (if (and (< 0 (real-part expr)) (not (eqv? #\+ (string-ref str 0))) plus) (string-append "+" str) str)) (len (string-length str)) (lt (if space (car space) 0)) (rt (if (and space (not (null? (cdr space)))) (cadr space) 0)) (pad (- (abs width) len lt rt))) (apply string-append (make-string lt #\space) (cond ((<= pad 0) str) ((eq? sign '+) (if (and (eqv? char #\0) (or (eqv? #\+ (string-ref str 0)) (eqv? #\- (string-ref str 0)))) (string-append (string (string-ref str 0)) (make-string pad char) (substring str 1 len)) (string-append (make-string pad char) str))) (else (string-append str (make-string pad char)))) (make-string rt #\space) str-list))) (receive (width depth char show case space . str-list) (opt-values rest (cons #f (lambda (x) (and (integer? x) (exact? x)))) (cons #f (lambda (x) (and (integer? x) (exact? x) (<= 0 x)))) (cons #f char?) (list display write) (cons #f (lambda (x) (memq x '(d u t)))) (cons #f (lambda (x) (and (list? x) (<= 1 (length x) 2) (every (lambda (x) (and (integer? x) (exact? x) (<= 0 x))) x))))) (arg-ors ("fmt: bad argument" str-list (not (every string? str-list))) ("fmt: unnecessary padding character" char (and char (not width)))) (let* ((width (or width 0)) (char (or char #\space)) (sign (if (< width 0) '- '+)) (str (get-output-string (let ((str-port (open-output-string))) (show expr str-port) str-port))) (str (if (and depth (< depth (string-length str))) (substring str 0 depth) str)) (str (if case ((cdr (assq case `((d . ,string-downcase) (u . ,string-upcase) (t . ,string-titlecase)))) str) str)) (lt (if space (car space) 0)) (rt (if (and space (not (null? (cdr space)))) (cadr space) 0)) (pad (- (abs width) (string-length str) lt rt))) (apply string-append (make-string lt #\space) (cond ((<= pad 0) str) ((eq? sign '+) (string-append (make-string pad char) str)) (else (string-append str (make-string pad char)))) (make-string rt #\space) str-list))))) Copyright Copyright (C) Joo ChurlSoo (2004). All Rights Reserved. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Scheme Request For Implementation process or editors, except as needed for the purpose of developing SRFIs in which case the procedures for copyrights defined in the SRFI process must be followed, or as required to translate it into languages other than English. The limited permissions granted above are perpetual and will not be revoked by the authors or their successors or assigns. This document and the information contained herein is provided on an "AS IS" basis and THE AUTHOR AND THE SRFI EDITORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. A Note: I am on neither "format strings are wrong" side nor "format strings are right" side, and indeed have no knowledge to infer it. I am only a Scheme consumer. I've used FORMAT because of its functions such as left and right padding of numbers or strings and expressing the precision of number. As I thought the function of the current SRFI's FORMAT was a little insufficient (such as right padding), and the `former' side had not provided a real thing for Scheme users, I submitted this proposal. While defining the new procedure (FMT), I felt that the problem was handling its optional arguments, and REST-VALUES (a prodecure in SRFI-51 that I had submitted) was insufficient and inappropriate. So I appended the third mode of operation to the function of REST-VALUES, and submitted the revised SRFI-51 to SRFI editors. If the revised SRFI-51 is finalized, I'll substitute REST-VALUES for OPT-VALUES which is a temporary and accessory procedure for handling rest list (optional arguments). Thanks. -- INITTERM