; -*- Scheme -*-
;
; $Id: string23.scm,v 1.1 1998/03/16 07:59:43 foner Exp $

;+fs
; procedure: string:translate
; arguments: source from to
; signature: string x string x string -> string
; pre:       (= (string-length from) (string-length to))
;
; Replaces all occurences of characters in FROM in SOURCE with
; corresponding characters in TO.
;
; This is an based on the Icon function `map(s1, s2, s3)'.
; The description of how to use this function covers 7 pages in the
; Icon book I have, rather than repeat it all, I've just copied most
; of the examples.  If you can't understand what it is doing from
; the examples, then either get a book on Icon or mail me and I will
; try to put together a coherent explanation.
;
; > (string:translate "bevan" "aeiou" "+%#@&")
; "b%v+n"
;
; > (string:translate "bevan" "aeiou" "*****")
; "b*v*n"
;
; > (string:translate "654321" "123456" "quotas")
; "satouq"
;
; > (string:translate "124578" "12345678" "03:56:42")
; "035642"
;
; > (string:translate "hxmysz" "hx:my:sz" "03:56:42")
; "035642"
;
; > (string:translate "hx:my:sz" "hxmysz" "035642")
; "03:56:42"
;
; > (string:translate "123321" "123" "-*|")
; "-*||*-"
;-doc

(define string:translate
  (lambda (source from to)
    
    (let* ((mapping-vector (make-vector 256))
	   (defined?-vector (make-vector 256 #f))
	   (from-length (string-length from))
	   (source-length (string-length source))
	   (result-string (make-string source-length)))
      
      (let loop ((from-pos 0))
	(if (not (= from-pos from-length))
	    (let ((pos (char->integer (string-ref from from-pos))))
	      (vector-set! mapping-vector pos (string-ref to from-pos))
	      (vector-set! defined?-vector pos #t)
	      (loop (+ from-pos 1)))))
      
      (let loop ((source-pos 0) (from-pos 0))
	(if (= source-pos source-length)
	    result-string
	    (let ((pos (char->integer (string-ref source source-pos))))
	      (string-set!
	       result-string
	       from-pos
	       (if (vector-ref defined?-vector pos)
		   (vector-ref mapping-vector pos)
		   (string-ref source source-pos)))
	      (loop (+ 1 source-pos) (+ 1 from-pos))))))))

; You might notice that two vectors are used to create the mapping
; from one set of characters to another. This is hardly the most
; efficient way of doing it, but it works!  In a C implementation,
; you could have just one vector and use NULL to represent
; characters that are present in the source but not the from string.
; However, this function is meant to be used for all characters
; (including NULL) and so you can't use this.  The alternative
; approaches seem to be either two vectors (as I have used) or one
; vector either containing a tuple (defined/not-defined . character)
; or the integer representation of the character.  This would allow
; an integer out of the bounds of the character set to be used as
; the `not-defined' marker.  Which is the most efficient in terms of
; time/space depends on your implementation.  If Scheme ever has
; some sort of conditional evaluation (a la Common-lisp +/-
; features), I will stuff all the implementations in here and you
; will be able to chose which one is best.

;------------

;+doc
; procedure: string:icon:map
; arguments: source from to
; signature: string x string x string -> string
; pre:       (= (string-length from) (string-length to))
;
; This is just a synonym for STRING:TRANSLATE
;-doc

(define string:icon:map string:translate)


; eof
