> I've found string hacking to be a fundamental component of many different
> kinds of hacking I've done -- web servers and scripting in particular. So
> I'm very interested in developing a rich, carefully-thought-out library
> that will assist this kind of programming.
In my web server scripting I often have to escape strings into HTML or
LaTeX. I understand some people have to escape strings to safely pass
to a Unix shell. All of these are cases of escaping single characters
into multiple characters for the output string. I think the string
library would be more useful with some efficient mechanism for
implementing such escapes.
Here's a sample implementation to help fuel discussion:
;; examples of use
(define html-escape (string-escaper '((#\< . "<")
(#\> . ">")
(#\& . "&"))))
(define scheme-escape (string-escaper '((#\\ . "\\\\")
(#\" . "\\\""))))
(define latex-escape (string-escaper '((#\\ . "\\\\")
(#\~ . "\\~")
(#\# . "\\#")
(#\$ . "\\$")
(#\% . "\\%")
(#\^ . "\\^")
(#\& . "\\&")
(#\{ . "\\{")
(#\} . "\\}")
(#\_ . "\\_"))))
;; example implementation
(define (string-escaper esc)
(let ((spec (char-escape-spec esc)))
(lambda (str) (string-escape str spec))))
(define (string-needs-escape? str esc)
(let ((len (string-length str)))
(let loop ((i 0))
(if (= i len)
#f
(let ((c (string-ref str i)))
(if (and (char>=? c (car esc))
(char<=? c (cadr esc)))
#t
(loop (+ 1 i))))))))
(define (string-escape str esc)
(if (string-needs-escape? str esc)
(list->string
(reverse
(let ((len (string-length str)))
(let loop ((i 0)
(li '()))
(if (= i len)
li
(loop (+ 1 i)
(let ((c (string-ref str i)))
(if (and (char>=? c (car esc))
(char<=? c (cadr esc)))
(let ((li2 (vector-ref
(caddr esc)
(- (char->integer c)
(char->integer (car esc))))))
(if li2
(append li2 li)
(cons c li)))
(cons c li)))))))))
str))
(define (char-escape-spec speclist)
(let ((minchar (caar speclist))
(maxchar (caar speclist)))
(let loop ((li (cdr speclist)))
(if (not (null? li))
(begin
(let ((testchar (caar li)))
(if (char<? testchar minchar)
(set! minchar testchar))
(if (char>? testchar maxchar)
(set! maxchar testchar)))
(loop (cdr li)))))
(list
minchar
maxchar
(let ((specv (make-vector (+ 1 (- (char->integer maxchar)
(char->integer minchar))) #f)))
(map (lambda (specpair)
(vector-set! specv
(- (char->integer (car specpair))
(char->integer minchar))
(reverse (string->list (cdr specpair)))))
speclist)
specv))))