Here is a code sample pair from SRFI-13. This is MUCH larger than previous code samples. SRFI-13 is one of the more popular SRFIs according to: https://spreadsheets.google.com/pub?key=tRCHK6jWXuKMABKAfoOwWqw&output=html As with the others, I translated the s-expressions using "sweeten" and then tweaked the results. I've also added to the "readable" git repository ("develop" branch) a new tool, "diff-s-sweet", that can compare an s-expression and sweet-expression to report semantic differences. I used that new tool to get more confidence that my tweaks were correct. I list the sweet-expressions first.... then the s-expressions. If you want the files, please go to the sourceforge readable.sourceforge.net "git" repository, "develop" branch. --- David A. Wheeler ; sweet-expressions ; Sample SRFI-13 implementation, from: ; http://srfi.schemers.org/srfi-13/srfi-13.scm ;;; SRFI 13 string library reference implementation -*- Scheme -*- ;;; Olin Shivers 7/2000 ;;; ;;; Copyright (c) 1988-1994 Massachusetts Institute of Technology. ;;; Copyright (c) 1998, 1999, 2000 Olin Shivers. All rights reserved. ;;; The details of the copyrights appear at the end of the file. Short ;;; summary: BSD-style open source. ; Since we only care about the *format*, nearly all ;-comments have been ; removed from this version, to shorten it (see the original file for them). define-syntax let-string-start+end ! syntax-rules () ! let-string-start+end(start(end) proc s-exp args-exp body ...) ! receive ! start end ! string-parse-final-start+end proc s-exp args-exp ! body ! ... ! let-string-start+end(start(end rest) proc s-exp args-exp body ...) ! receive ! rest start end ! string-parse-start+end proc s-exp args-exp ! body ! ... define-syntax let-string-start+end2 ! syntax-rules () ! l-s-s+e2(start1(end1 start2 end2) proc s1 s2 args body ...) ! let <* procv proc *> ! let-string-start+end ! start1 end1 rest ! procv ! s1 ! args ! let-string-start+end start2(end2) procv s2 rest body ... define string-parse-start+end(proc s args) ! if not(string?(s)) error("Non-string value" proc s) ! let <* slen $ string-length s *> ! if pair?(args) ! let <* start $ car args \\ args $ cdr args *> ! if {integer?(start) and exact?(start) and {start >= 0}} ! ! receive ! ! end args ! ! if pair?(args) ! ! ! let <* end $ car args \\ args $ cdr args *> ! ! ! if {integer?(end) and exact?(end) and {end <= slen}} ! ! ! values end args ! ! ! error "Illegal substring END spec" proc end s ! ! ! values slen args ! ! if {start <= end} ! ! ! values args start end ! ! ! error "Illegal substring START/END spec" proc start end s ! ! error "Illegal substring START spec" proc start s ! values '() 0 slen define string-parse-final-start+end(proc s args) ! receive ! rest start end ! string-parse-start+end proc s args ! if pair?(rest) ! error "Extra arguments to procedure" proc rest ! values start end define substring-spec-ok?(s start end) ! and ! string? s ! integer? start ! exact? start ! integer? end ! exact? end ! {0 <= start} ! {start <= end} ! {end <= string-length(s)} define check-substring-spec(proc s start end) ! if not(substring-spec-ok?(s start end)) ! error "Illegal substring spec." proc s start end define substring/shared(s start . maybe-end) ! check-arg string? s substring/shared ! let <* slen $ string-length s *> ! check-arg ! lambda start() ! {integer?(start) and exact?(start) and {0 <= start}} ! start ! substring/shared ! %substring/shared ! s ! start ! :optional ! maybe-end ! slen ! lambda end() ! and ! integer? end ! exact? end ! {start <= end} ! {end <= slen} define %substring/shared(s start end) ! if {zero?(start) and {end = string-length(s)}} ! s ! substring s start end define string-copy(s . maybe-start+end) ! let-string-start+end ! start end ! string-copy ! s ! maybe-start+end ! substring s start end define string-map(proc s . maybe-start+end) ! check-arg procedure? proc string-map ! let-string-start+end ! start end ! string-map ! s ! maybe-start+end ! %string-map proc s start end define %string-map(proc s start end) ! let* <* len {end - start} \\ ans $ make-string len *> ! do <* i {end - 1} {i - 1} \\ j {len - 1} {j - 1} *> ! $ {j < 0} ! string-set! ans j proc(string-ref(s i)) ! ans define string-map!(proc s . maybe-start+end) ! check-arg procedure? proc string-map! ! let-string-start+end ! start end ! string-map! ! s ! maybe-start+end ! %string-map! proc s start end define %string-map!(proc s start end) ! do <* i {end - 1} {i - 1} *> ! $ {i < start} ! string-set! s i proc(string-ref(s i)) define string-fold(kons knil s . maybe-start+end) ! check-arg procedure? kons string-fold ! let-string-start+end ! start end ! string-fold ! s ! maybe-start+end ! let lp <* v knil \\ i start *> ! if {i < end} ! lp kons(string-ref(s i) v) {i + 1} ! v define string-fold-right(kons knil s . maybe-start+end) ! check-arg procedure? kons string-fold-right ! let-string-start+end ! start end ! string-fold-right ! s ! maybe-start+end ! let lp <* v knil \\ i {end - 1} *> ! if {i >= start} ! lp kons(string-ref(s i) v) {i - 1} ! v define string-unfold(p f g seed . base+make-final) ! check-arg procedure? p string-unfold ! check-arg procedure? f string-unfold ! check-arg procedure? g string-unfold ! let-optionals* ! base+make-final ! base("" string?(base)) ! make-final lambda(x() "") procedure?(make-final) ! let lp ! chunks('()) ! nchars 0 ! chunk make-string(40) ! chunk-len 40 ! i 0 ! seed seed ! let lp2 <* i i \\ seed seed *> ! if not(p(seed)) ! ! let <* c $ f seed \\ seed $ g seed *> ! ! if {i < chunk-len} ! ! ! begin string-set!(chunk i c) lp2({i + 1} seed) ! ! ! let* ! ! ! \\ ! ! ! nchars2 {chunk-len + nchars} ! ! ! chunk-len2 min(4096 nchars2) ! ! ! new-chunk make-string(chunk-len2) ! ! ! string-set! new-chunk 0 c ! ! ! lp ! ! ! cons chunk chunks ! ! ! {nchars + chunk-len} ! ! ! new-chunk ! ! ! chunk-len2 ! ! ! 1 ! ! ! seed ! ! let* ! ! \\ ! ! final make-final(seed) ! ! flen string-length(final) ! ! base-len string-length(base) ! ! j {base-len + nchars + i} ! ! ans make-string{j + flen} ! ! %string-copy! ans j final 0 flen ! ! let <* j {j - i} *> ! ! ! %string-copy! ans j chunk 0 i ! ! ! let lp <* j j \\ chunks chunks *> ! ! ! if pair?(chunks) ! ! ! ! let* ! ! ! ! \\ ! ! ! ! chunk car(chunks) ! ! ! ! chunks cdr(chunks) ! ! ! ! chunk-len string-length(chunk) ! ! ! ! j {j - chunk-len} ! ! ! ! %string-copy! ans j chunk 0 chunk-len ! ! ! ! lp j chunks ! ! %string-copy! ans 0 base 0 base-len ! ! ans define string-unfold-right(p f g seed . base+make-final) ! let-optionals* ! base+make-final ! base("" string?(base)) ! make-final lambda(x() "") procedure?(make-final) ! let lp ! chunks('()) ! nchars 0 ! chunk make-string(40) ! chunk-len 40 ! i 40 ! seed seed ! let lp2 ! i(i) seed(seed) ! if not(p(seed)) ! ! let <* c $ f seed \\ seed $ g seed *> ! ! if {i > 0} ! ! ! let <* i {i - 1} *> ! ! ! string-set! chunk i c ! ! ! lp2 i seed ! ! ! let* ! ! ! \\ ! ! ! nchars2 {chunk-len + nchars} ! ! ! chunk-len2 min(4096 nchars2) ! ! ! new-chunk make-string(chunk-len2) ! ! ! i {chunk-len2 - 1} ! ! ! string-set! new-chunk i c ! ! ! lp ! ! ! cons chunk chunks ! ! ! {nchars + chunk-len} ! ! ! new-chunk ! ! ! chunk-len2 ! ! ! i ! ! ! seed ! ! let* ! ! \\ ! ! final make-final(seed) ! ! flen string-length(final) ! ! base-len string-length(base) ! ! chunk-used {chunk-len - i} ! ! j {base-len + nchars + chunk-used} ! ! ans make-string{j + flen} ! ! %string-copy! ans 0 final 0 flen ! ! %string-copy! ans flen chunk i chunk-len ! ! let lp <* j {flen + chunk-used} \\ chunks chunks *> ! ! ! if pair?(chunks) ! ! ! let* ! ! ! \\ ! ! ! chunk car(chunks) ! ! ! chunks cdr(chunks) ! ! ! chunk-len string-length(chunk) ! ! ! %string-copy! ans j chunk 0 chunk-len ! ! ! lp {j + chunk-len} chunks ! ! ! %string-copy! ans j base 0 base-len ! ! ans define string-for-each(proc s . maybe-start+end) ! check-arg procedure? proc string-for-each ! let-string-start+end ! start end ! string-for-each ! s ! maybe-start+end ! let lp <* i start *> ! if {i < end} ! begin proc(string-ref(s i)) lp{i + 1} define string-for-each-index(proc s . maybe-start+end) ! check-arg procedure? proc string-for-each-index ! let-string-start+end ! start end ! string-for-each-index ! s ! maybe-start+end ! let lp <* i start *> ! if {i < end} ! begin proc(i) lp{i + 1} define string-every(criterion s . maybe-start+end) ! let-string-start+end ! start end ! string-every ! s ! maybe-start+end ! cond ! char?(criterion) ! let lp <* i start *> ! ! or ! ! {i >= end} ! ! {char=?(criterion string-ref(s i)) and lp{i + 1}} ! char-set?(criterion) ! let lp <* i start *> ! ! or ! ! {i >= end} ! ! and ! ! char-set-contains? criterion string-ref(s i) ! ! lp {i + 1} ! procedure?(criterion) ! or ! {start = end} ! let lp <* i start *> ! ! let <* c $ string-ref s i \\ i1 {i + 1} *> ! ! if {i1 = end} ! ! ! criterion c ! ! ! {criterion(c) and lp(i1)} ! else ! error ! "Second param is neither char-set, char, or predicate procedure." ! string-every ! criterion define string-any(criterion s . maybe-start+end) ! let-string-start+end ! start end ! string-any ! s ! maybe-start+end ! cond ! char?(criterion) ! let lp <* i start *> ! ! and ! ! {i < end} ! ! {char=?(criterion string-ref(s i)) or lp{i + 1}} ! char-set?(criterion) ! let lp <* i start *> ! ! and ! ! {i < end} ! ! or ! ! char-set-contains? criterion string-ref(s i) ! ! lp {i + 1} ! procedure?(criterion) ! and ! {start < end} ! let lp <* i start *> ! ! let <* c $ string-ref s i \\ i1 {i + 1} *> ! ! if {i1 = end} ! ! ! criterion c ! ! ! {criterion(c) or lp(i1)} ! else ! error ! "Second param is neither char-set, char, or predicate procedure." ! string-any ! criterion define string-tabulate(proc len) ! check-arg procedure? proc string-tabulate ! check-arg ! lambda val() ! {integer?(val) and exact?(val) and {0 <= val}} ! len ! string-tabulate ! let <* s $ make-string len *> ! do <* i {len - 1} {i - 1} *> ! $ {i < 0} ! string-set! s i proc(i) ! s define %string-prefix-length(s1 start1 end1 s2 start2 end2) ! let* ! \\ ! delta min({end1 - start1} {end2 - start2}) ! end1 {start1 + delta} ! if {eq?(s1 s2) and {start1 = start2}} ! delta ! let lp <* i start1 \\ j start2 *> ! if {{i >= end1} or not(char=?(string-ref(s1 i) string-ref(s2 j)))} ! ! {i - start1} ! ! lp {i + 1} {j + 1} define %string-suffix-length(s1 start1 end1 s2 start2 end2) ! let* ! \\ ! delta min({end1 - start1} {end2 - start2}) ! start1 {end1 - delta} ! if {eq?(s1 s2) and {end1 = end2}} ! delta ! let lp <* i {end1 - 1} \\ j {end2 - 1} *> ! if {{i < start1} or not(char=?(string-ref(s1 i) string-ref(s2 j)))} ! ! {{end1 - i} - 1} ! ! lp {i - 1} {j - 1} define %string-prefix-length-ci(s1 start1 end1 s2 start2 end2) ! let* ! \\ ! delta min({end1 - start1} {end2 - start2}) ! end1 {start1 + delta} ! if {eq?(s1 s2) and {start1 = start2}} ! delta ! let lp <* i start1 \\ j start2 *> ! if {{i >= end1} or not(char-ci=?(string-ref(s1 i) string-ref(s2 j)))} ! ! {i - start1} ! ! lp {i + 1} {j + 1} define %string-suffix-length-ci(s1 start1 end1 s2 start2 end2) ! let* ! \\ ! delta min({end1 - start1} {end2 - start2}) ! start1 {end1 - delta} ! if {eq?(s1 s2) and {end1 = end2}} ! delta ! let lp <* i {end1 - 1} \\ j {end2 - 1} *> ! if ! or ! {i < start1} ! not char-ci=?(string-ref(s1 i) string-ref(s2 j)) ! {{end1 - i} - 1} ! lp {i - 1} {j - 1} define string-prefix-length(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-prefix-length ! s1 ! s2 ! maybe-starts+ends ! %string-prefix-length s1 start1 end1 s2 start2 end2 define string-suffix-length(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-suffix-length ! s1 ! s2 ! maybe-starts+ends ! %string-suffix-length s1 start1 end1 s2 start2 end2 define string-prefix-length-ci(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-prefix-length-ci ! s1 ! s2 ! maybe-starts+ends ! %string-prefix-length-ci s1 start1 end1 s2 start2 end2 define string-suffix-length-ci(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-suffix-length-ci ! s1 ! s2 ! maybe-starts+ends ! %string-suffix-length-ci s1 start1 end1 s2 start2 end2 define string-prefix?(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-prefix? ! s1 ! s2 ! maybe-starts+ends ! %string-prefix? s1 start1 end1 s2 start2 end2 define string-suffix?(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-suffix? ! s1 ! s2 ! maybe-starts+ends ! %string-suffix? s1 start1 end1 s2 start2 end2 define string-prefix-ci?(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-prefix-ci? ! s1 ! s2 ! maybe-starts+ends ! %string-prefix-ci? s1 start1 end1 s2 start2 end2 define string-suffix-ci?(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-suffix-ci? ! s1 ! s2 ! maybe-starts+ends ! %string-suffix-ci? s1 start1 end1 s2 start2 end2 ;;; Here are the internal routines that do the real work. define %string-prefix?(s1 start1 end1 s2 start2 end2) ! let <* len1 {end1 - start1} *> ! and ! {len1 <= {end2 - start2}} ! {%string-prefix-length(s1 start1 end1 s2 start2 end2) = len1} define %string-suffix?(s1 start1 end1 s2 start2 end2) ! let <* len1 {end1 - start1} *> ! and ! {len1 <= {end2 - start2}} ! {len1 = %string-suffix-length(s1 start1 end1 s2 start2 end2)} define %string-prefix-ci?(s1 start1 end1 s2 start2 end2) ! let <* len1 {end1 - start1} *> ! and ! {len1 <= {end2 - start2}} ! {len1 = %string-prefix-length-ci(s1 start1 end1 s2 start2 end2)} define %string-suffix-ci?(s1 start1 end1 s2 start2 end2) ! let <* len1 {end1 - start1} *> ! and ! {len1 <= {end2 - start2}} ! {len1 = %string-suffix-length-ci(s1 start1 end1 s2 start2 end2)} define %string-compare(s1 start1 end1 s2 start2 end2 proc< proc= proc>) ! let <* size1 {end1 - start1} \\ size2 {end2 - start2} *> ! let <* match $ %string-prefix-length s1 start1 end1 s2 start2 end2 *> ! if {match = size1} ! if({match = size2} proc= proc<) end1 ! \\ ! if {match = size2} ! ! proc> ! ! if ! ! char<? ! ! string-ref s1 {start1 + match} ! ! string-ref s2 {start2 + match} ! ! proc< ! ! proc> ! {match + start1} define %string-compare-ci(s1 start1 end1 s2 start2 end2 proc< proc= proc>) ! let <* size1 {end1 - start1} \\ size2 {end2 - start2} *> ! let <* match $ %string-prefix-length-ci s1 start1 end1 s2 start2 end2 *> ! if {match = size1} ! if({match = size2} proc= proc<) end1 ! \\ ! if {match = size2} ! ! proc> ! ! if ! ! char-ci<? ! ! string-ref s1 {start1 + match} ! ! string-ref s2 {start2 + match} ! ! proc< ! ! proc> ! {start1 + match} define string-compare(s1 s2 proc< proc= proc> . maybe-starts+ends) ! check-arg procedure? proc< string-compare ! check-arg procedure? proc= string-compare ! check-arg procedure? proc> string-compare ! let-string-start+end2 ! start1 end1 start2 end2 ! string-compare ! s1 ! s2 ! maybe-starts+ends ! %string-compare ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! proc< ! proc= ! proc> define string-compare-ci(s1 s2 proc< proc= proc> . maybe-starts+ends) ! check-arg procedure? proc< string-compare-ci ! check-arg procedure? proc= string-compare-ci ! check-arg procedure? proc> string-compare-ci ! let-string-start+end2 ! start1 end1 start2 end2 ! string-compare-ci ! s1 ! s2 ! maybe-starts+ends ! %string-compare-ci ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! proc< ! proc= ! proc> define string=(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string= ! s1 ! s2 ! maybe-starts+ends ! and ! {{end1 - start1} = {end2 - start2}} ! or ! {eq?(s1 s2) and {start1 = start2}} ! %string-compare ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! lambda i() #f ! values ! lambda i() #f define string<>(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string<> ! s1 ! s2 ! maybe-starts+ends ! or ! not {{end1 - start1} = {end2 - start2}} ! and ! not {eq?(s1 s2) and {start1 = start2}} ! %string-compare ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! values ! lambda i() #f ! values define string<(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string< ! s1 ! s2 ! maybe-starts+ends ! if {eq?(s1 s2) and {start1 = start2}} ! {end1 < end2} ! %string-compare ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! values ! lambda i() #f ! lambda i() #f define string>(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string> ! s1 ! s2 ! maybe-starts+ends ! if {eq?(s1 s2) and {start1 = start2}} ! {end1 > end2} ! %string-compare ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! lambda i() #f ! lambda i() #f ! values define string<=(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string<= ! s1 ! s2 ! maybe-starts+ends ! if {eq?(s1 s2) and {start1 = start2}} ! {end1 <= end2} ! %string-compare ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! values ! values ! lambda i() #f define string>=(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string>= ! s1 ! s2 ! maybe-starts+ends ! if {eq?(s1 s2) and {start1 = start2}} ! {end1 >= end2} ! %string-compare ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! lambda i() #f ! values ! values define string-ci=(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-ci= ! s1 ! s2 ! maybe-starts+ends ! and ! {{end1 - start1} = {end2 - start2}} ! or ! {eq?(s1 s2) and {start1 = start2}} ! %string-compare-ci ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! lambda i() #f ! values ! lambda i() #f define string-ci<>(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-ci<> ! s1 ! s2 ! maybe-starts+ends ! or ! not {{end1 - start1} = {end2 - start2}} ! and ! not {eq?(s1 s2) and {start1 = start2}} ! %string-compare-ci ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! values ! lambda i() #f ! values define string-ci<(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-ci< ! s1 ! s2 ! maybe-starts+ends ! if {eq?(s1 s2) and {start1 = start2}} ! {end1 < end2} ! %string-compare-ci ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! values ! lambda i() #f ! lambda i() #f define string-ci>(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-ci> ! s1 ! s2 ! maybe-starts+ends ! if {eq?(s1 s2) and {start1 = start2}} ! {end1 > end2} ! %string-compare-ci ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! lambda i() #f ! lambda i() #f ! values define string-ci<=(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-ci<= ! s1 ! s2 ! maybe-starts+ends ! if {eq?(s1 s2) and {start1 = start2}} ! {end1 <= end2} ! %string-compare-ci ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! values ! values ! lambda i() #f define string-ci>=(s1 s2 . maybe-starts+ends) ! let-string-start+end2 ! start1 end1 start2 end2 ! string-ci>= ! s1 ! s2 ! maybe-starts+ends ! if {eq?(s1 s2) and {start1 = start2}} ! {end1 >= end2} ! %string-compare-ci ! s1 ! start1 ! end1 ! s2 ! start2 ! end2 ! lambda i() #f ! values ! values define %string-hash(s char->int bound start end) ! let ! \\ ! iref $ lambda s(i) char->int(string-ref(s i)) ! mask let(lp (i(65536)) if({i >= bound} {i - 1} lp{i + i})) ! let lp <* i start \\ ans 0 *> ! if {i >= end} ! modulo ans bound ! lp {i + 1} bitwise-and(mask {{37 * ans} + iref(s i)}) define string-hash(s . maybe-bound+start+end) ! let-optionals* ! maybe-bound+start+end ! bound(4194304 {integer?(bound) and exact?(bound) and {0 <= bound}}) ! rest ! let <* bound $ if zero?(bound) 4194304 bound *> ! let-string-start+end ! start end ! string-hash ! s ! rest ! %string-hash s char->integer bound start end define string-hash-ci(s . maybe-bound+start+end) ! let-optionals* ! maybe-bound+start+end ! bound(4194304 {integer?(bound) and exact?(bound) and {0 <= bound}}) ! rest ! let <* bound $ if zero?(bound) 4194304 bound *> ! let-string-start+end ! start end ! string-hash-ci ! s ! rest ! %string-hash ! s ! lambda c() char->integer(char-downcase(c)) ! bound ! start ! end define string-upcase(s . maybe-start+end) ! let-string-start+end ! start end ! string-upcase ! s ! maybe-start+end ! %string-map char-upcase s start end define string-upcase!(s . maybe-start+end) ! let-string-start+end ! start end ! string-upcase! ! s ! maybe-start+end ! %string-map! char-upcase s start end define string-downcase(s . maybe-start+end) ! let-string-start+end ! start end ! string-downcase ! s ! maybe-start+end ! %string-map char-downcase s start end define string-downcase!(s . maybe-start+end) ! let-string-start+end ! start end ! string-downcase! ! s ! maybe-start+end ! %string-map! char-downcase s start end define %string-titlecase!(s start end) ! let lp <* i start *> ! cond ! string-index(s char-cased? i end) ! => ! lambda i() ! ! string-set! s i char-titlecase(string-ref(s i)) ! ! let <* i1 {i + 1} *> ! ! cond ! ! string-skip(s char-cased? i1 end) ! ! => ! ! lambda j() ! ! ! string-downcase! s i1 j ! ! ! lp {j + 1} ! ! else string-downcase!(s i1 end) define string-titlecase!(s . maybe-start+end) ! let-string-start+end ! start end ! string-titlecase! ! s ! maybe-start+end ! %string-titlecase! s start end define string-titlecase(s . maybe-start+end) ! let-string-start+end ! start end ! string-titlecase! ! s ! maybe-start+end ! let <* ans $ substring s start end *> ! %string-titlecase! ans 0 {end - start} ! ans define string-take(s n) ! check-arg string? s string-take ! check-arg ! lambda val() ! and ! integer? n ! exact? n ! {0 <= n <= string-length(s)} ! n ! string-take ! %substring/shared s 0 n define string-take-right(s n) ! check-arg string? s string-take-right ! let <* len $ string-length s *> ! check-arg ! lambda val() ! {integer?(n) and exact?(n) and {0 <= n <= len}} ! n ! string-take-right ! %substring/shared s {len - n} len define string-drop(s n) ! check-arg string? s string-drop ! let <* len $ string-length s *> ! check-arg ! lambda val() ! {integer?(n) and exact?(n) and {0 <= n <= len}} ! n ! string-drop ! %substring/shared s n len define string-drop-right(s n) ! check-arg string? s string-drop-right ! let <* len $ string-length s *> ! check-arg ! lambda val() ! {integer?(n) and exact?(n) and {0 <= n <= len}} ! n ! string-drop-right ! %substring/shared s 0 {len - n} define string-trim(s . criterion+start+end) ! let-optionals* ! criterion+start+end ! criterion(char-set:whitespace) rest ! let-string-start+end ! start end ! string-trim ! s ! rest ! cond ! string-skip(s criterion start end) ! => ! lambda i() %substring/shared(s i end) ! else "" define string-trim-right(s . criterion+start+end) ! let-optionals* ! criterion+start+end ! criterion(char-set:whitespace) rest ! let-string-start+end ! start end ! string-trim-right ! s ! rest ! cond ! string-skip-right(s criterion start end) ! => ! lambda i() %substring/shared(s 0 {1 + i}) ! else "" define string-trim-both(s . criterion+start+end) ! let-optionals* ! criterion+start+end ! criterion(char-set:whitespace) rest ! let-string-start+end ! start end ! string-trim-both ! s ! rest ! cond ! string-skip(s criterion start end) ! => ! lambda i() ! ! %substring/shared ! ! s ! ! i ! ! {1 + string-skip-right(s criterion i end)} ! else "" define string-pad-right(s n . char+start+end) ! let-optionals* ! char+start+end ! char(#\space char?(char)) rest ! let-string-start+end ! start end ! string-pad-right ! s ! rest ! check-arg ! lambda n() ! ! {integer?(n) and exact?(n) and {0 <= n}} ! n ! string-pad-right ! let <* len {end - start} *> ! if {n <= len} ! ! %substring/shared s start {start + n} ! ! let <* ans $ make-string n char *> ! ! %string-copy! ans 0 s start end ! ! ans define string-pad(s n . char+start+end) ! let-optionals* ! char+start+end ! char(#\space char?(char)) rest ! let-string-start+end ! start end ! string-pad ! s ! rest ! check-arg ! lambda n() ! ! {integer?(n) and exact?(n) and {0 <= n}} ! n ! string-pad ! let <* len {end - start} *> ! if {n <= len} ! ! %substring/shared s {end - n} end ! ! let <* ans $ make-string n char *> ! ! %string-copy! ans {n - len} s start end ! ! ans define string-delete(criterion s . maybe-start+end) ! let-string-start+end ! start end ! string-delete ! s ! maybe-start+end ! if procedure?(criterion) ! let* ! \\ ! slen {end - start} ! temp make-string(slen) ! ans-len ! string-fold ! lambda c(i) ! ! if criterion(c) ! ! i ! ! begin string-set!(temp i c) {i + 1} ! 0 ! s ! start ! end ! if {ans-len = slen} temp substring(temp 0 ans-len) ! let* ! \\ ! cset ! cond ! char-set?(criterion) criterion ! char?(criterion) char-set(criterion) ! else ! error ! "string-delete criterion not predicate, char or char-set" ! criterion ! len ! string-fold ! lambda c(i) ! ! if char-set-contains?(cset c) i {i + 1} ! 0 ! s ! start ! end ! ans make-string(len) ! string-fold ! lambda c(i) ! ! if char-set-contains?(cset c) ! ! i ! ! begin string-set!(ans i c) {i + 1} ! 0 ! s ! start ! end ! ans define string-filter(criterion s . maybe-start+end) ! let-string-start+end ! start end ! string-filter ! s ! maybe-start+end ! if procedure?(criterion) ! let* ! \\ ! slen {end - start} ! temp make-string(slen) ! ans-len ! string-fold ! lambda c(i) ! ! if criterion(c) ! ! begin string-set!(temp i c) {i + 1} ! ! i ! 0 ! s ! start ! end ! if {ans-len = slen} temp substring(temp 0 ans-len) ! let* ! \\ ! cset ! cond ! char-set?(criterion) criterion ! char?(criterion) char-set(criterion) ! else ! error ! "string-delete criterion not predicate, char or char-set" ! criterion ! len ! string-fold ! lambda c(i) ! ! if char-set-contains?(cset c) {i + 1} i ! 0 ! s ! start ! end ! ans make-string(len) ! string-fold ! lambda c(i) ! ! if char-set-contains?(cset c) ! ! begin string-set!(ans i c) {i + 1} ! ! i ! 0 ! s ! start ! end ! ans define string-index(str criterion . maybe-start+end) ! let-string-start+end ! start end ! string-index ! str ! maybe-start+end ! cond ! char?(criterion) ! let lp <* i start *> ! ! and ! ! {i < end} ! ! if char=?(criterion string-ref(str i)) i lp{i + 1} ! char-set?(criterion) ! let lp <* i start *> ! ! and ! ! {i < end} ! ! if char-set-contains?(criterion string-ref(str i)) ! ! ! i ! ! ! lp {i + 1} ! procedure?(criterion) ! let lp <* i start *> ! ! and ! ! {i < end} ! ! if criterion(string-ref(str i)) i lp{i + 1} ! else ! error ! "Second param is neither char-set, char, or predicate procedure." ! string-index ! criterion define string-index-right(str criterion . maybe-start+end) ! let-string-start+end ! start end ! string-index-right ! str ! maybe-start+end ! cond ! char?(criterion) ! let lp <* i {end - 1} *> ! ! and ! ! {i >= start} ! ! if char=?(criterion string-ref(str i)) i lp{i - 1} ! char-set?(criterion) ! let lp <* i {end - 1} *> ! ! and ! ! {i >= start} ! ! if char-set-contains?(criterion string-ref(str i)) ! ! ! i ! ! ! lp {i - 1} ! procedure?(criterion) ! let lp <* i {end - 1} *> ! ! and ! ! {i >= start} ! ! if criterion(string-ref(str i)) i lp{i - 1} ! else ! error ! "Second param is neither char-set, char, or predicate procedure." ! string-index-right ! criterion define string-skip(str criterion . maybe-start+end) ! let-string-start+end ! start end ! string-skip ! str ! maybe-start+end ! cond ! char?(criterion) ! let lp <* i start *> ! ! and ! ! {i < end} ! ! if char=?(criterion string-ref(str i)) lp{i + 1} i ! char-set?(criterion) ! let lp <* i start *> ! ! and ! ! {i < end} ! ! if char-set-contains?(criterion string-ref(str i)) ! ! ! lp {i + 1} ! ! ! i ! procedure?(criterion) ! let lp <* i start *> ! ! and ! ! {i < end} ! ! if criterion(string-ref(str i)) lp{i + 1} i ! else ! error ! "Second param is neither char-set, char, or predicate procedure." ! string-skip ! criterion define string-skip-right(str criterion . maybe-start+end) ! let-string-start+end ! start end ! string-skip-right ! str ! maybe-start+end ! cond ! char?(criterion) ! let lp <* i {end - 1} *> ! ! and ! ! {i >= start} ! ! if char=?(criterion string-ref(str i)) lp{i - 1} i ! char-set?(criterion) ! let lp <* i {end - 1} *> ! ! and ! ! {i >= start} ! ! if char-set-contains?(criterion string-ref(str i)) ! ! ! lp {i - 1} ! ! ! i ! procedure?(criterion) ! let lp <* i {end - 1} *> ! ! and ! ! {i >= start} ! ! if criterion(string-ref(str i)) lp{i - 1} i ! else ! error ! "CRITERION param is neither char-set or char." ! string-skip-right ! criterion define string-count(s criterion . maybe-start+end) ! let-string-start+end ! start end ! string-count ! s ! maybe-start+end ! cond ! char?(criterion) ! do ! i(start {i + 1}) ! count ! 0 ! if char=?(criterion string-ref(s i)) {count + 1} count ! {i >= end} count ! char-set?(criterion) ! do ! i(start {i + 1}) ! count ! 0 ! if char-set-contains?(criterion string-ref(s i)) ! ! {count + 1} ! ! count ! {i >= end} count ! procedure?(criterion) ! do ! i(start {i + 1}) ! count 0 if(criterion(string-ref(s i)) {count + 1} count) ! {i >= end} count ! else ! error ! "CRITERION param is neither char-set or char." ! string-count ! criterion define string-fill!(s char . maybe-start+end) ! check-arg char? char string-fill! ! let-string-start+end ! start end ! string-fill! ! s ! maybe-start+end ! do <* i {end - 1} {i - 1} *> ! $ {i < start} ! string-set! s i char define string-copy!(to tstart from . maybe-fstart+fend) ! let-string-start+end ! fstart fend ! string-copy! ! from ! maybe-fstart+fend ! check-arg integer? tstart string-copy! ! check-substring-spec ! string-copy! ! to ! tstart ! {tstart + {fend - fstart}} ! %string-copy! to tstart from fstart fend ;;; Library-internal routine define %string-copy!(to tstart from fstart fend) ! if {fstart > tstart} ! do <* i fstart {i + 1} \\ j tstart {j + 1} *> ! $ {i >= fend} ! string-set! to j string-ref(from i) ! do ! \\ ! i {fend - 1} {i - 1} ! j {-1 + tstart + {fend - fstart}} {j - 1} ! $ {i < fstart} ! string-set! to j string-ref(from i) define string-contains(text pattern . maybe-starts+ends) ! let-string-start+end2 ! t-start t-end p-start p-end ! string-contains ! text ! pattern ! maybe-starts+ends ! %kmp-search ! pattern ! text ! char=? ! p-start ! p-end ! t-start ! t-end define string-contains-ci(text pattern . maybe-starts+ends) ! let-string-start+end2 ! t-start t-end p-start p-end ! string-contains-ci ! text ! pattern ! maybe-starts+ends ! %kmp-search ! pattern ! text ! char-ci=? ! p-start ! p-end ! t-start ! t-end ;;; Knuth-Morris-Pratt string searching define %kmp-search(pattern text c= p-start p-end t-start t-end) ! let ! \\ ! plen {p-end - p-start} ! rv make-kmp-restart-vector(pattern c= p-start p-end) ! let lp ! ti(t-start) pi(0) tj{t-end - t-start} pj(plen) ! if {pi = plen} ! {ti - plen} ! and ! {pj <= tj} ! if c=(string-ref(text ti) string-ref(pattern {p-start + pi})) ! ! lp {1 + ti} {1 + pi} {tj - 1} {pj - 1} ! ! let <* pi $ vector-ref rv pi *> ! ! if {pi = -1} ! ! ! lp {ti + 1} 0 {tj - 1} plen ! ! ! lp ti pi tj {plen - pi} define make-kmp-restart-vector(pattern . maybe-c=+start+end) ! let-optionals* ! maybe-c=+start+end ! c=(char=? procedure?(c=)) ! start(end) ! lambda args() ! ! string-parse-start+end make-kmp-restart-vector pattern args ! let* <* rvlen {end - start} \\ rv $ make-vector rvlen -1 *> ! if {rvlen > 0} ! let <* rvlen-1 {rvlen - 1} \\ c0 $ string-ref pattern start *> ! ! let lp1 ! ! i(0) j(-1) k(start) ! ! if {i < rvlen-1} ! ! ! let lp2 ! ! ! (j(j)) ! ! ! cond ! ! ! {j = -1} ! ! ! let <* i1 {1 + i} *> ! ! ! ! if not(c=(string-ref(pattern {k + 1}) c0)) ! ! ! ! vector-set! rv i1 0 ! ! ! ! lp1 i1 0 {k + 1} ! ! ! c=(string-ref(pattern k) string-ref(pattern {j + start})) ! ! ! let* <* i1 {1 + i} \\ j1 {1 + j} *> ! ! ! ! vector-set! rv i1 j1 ! ! ! ! lp1 i1 j1 {k + 1} ! ! ! else lp2(vector-ref(rv j)) ! rv define kmp-step(pat rv c i c= p-start) ! let lp <* i i *> ! if c=(c string-ref(pat {i + p-start})) ! {i + 1} ! let <* i $ vector-ref rv i *> ! if {i = -1} 0 lp(i) define string-kmp-partial-search(pat rv s i . c=+p-start+s-start+s-end) ! check-arg vector? rv string-kmp-partial-search ! let-optionals* ! c=+p-start+s-start+s-end ! c=(char=? procedure?(c=)) ! p-start ! 0 ! {integer?(p-start) and exact?(p-start) and {0 <= p-start}} ! s-start(s-end) ! lambda args() ! ! string-parse-start+end string-kmp-partial-search s args ! let <* patlen $ vector-length rv *> ! check-arg ! lambda i() ! ! {integer?(i) and exact?(i) and {0 <= i} and {i < patlen}} ! i ! string-kmp-partial-search ! let lp <* si s-start \\ vi i *> ! cond ! {vi = patlen} -(si) ! {si = s-end} vi ! else ! let <* c $ string-ref s si *> ! ! lp ! ! {si + 1} ! ! let lp2 <* vi vi *> ! ! ! if c=(c string-ref(pat {vi + p-start})) ! ! ! {vi + 1} ! ! ! let <* vi $ vector-ref rv vi *> ! ! ! ! if {vi = -1} 0 lp2(vi) define string-null?(s) zero?(string-length(s)) define string-reverse(s . maybe-start+end) ! let-string-start+end ! start end ! string-reverse ! s ! maybe-start+end ! let* <* len {end - start} \\ ans $ make-string len *> ! do <* i start {i + 1} \\ j {len - 1} {j - 1} *> ! $ {j < 0} ! string-set! ans j string-ref(s i) ! ans define string-reverse!(s . maybe-start+end) ! let-string-start+end ! start end ! string-reverse! ! s ! maybe-start+end ! do <* i {end - 1} {i - 1} \\ j start {j + 1} *> ! $ {i <= j} ! let <* ci $ string-ref s i *> ! string-set! s i string-ref(s j) ! string-set! s j ci define reverse-list->string(clist) ! let* <* len $ length clist \\ s $ make-string len *> ! do <* i {len - 1} {i - 1} \\ clist clist cdr(clist) *> ! $ not(pair?(clist)) ! string-set! s i car(clist) ! s define string->list(s . maybe-start+end) ! let-string-start+end ! start end ! string->list ! s ! maybe-start+end ! do <* i {end - 1} {i - 1} \\ ans '() cons(string-ref(s i) ans) *> ! {i < start} ans define string-append/shared(. strings) ! string-concatenate/shared strings define string-concatenate/shared(strings) ! let lp <* strings strings \\ nchars 0 \\ first #f *> ! cond ! pair?(strings) ! let* ! \\ ! string car(strings) ! tail cdr(strings) ! slen string-length(string) ! if zero?(slen) ! ! lp tail nchars first ! ! lp tail {nchars + slen} {first or strings} ! zero?(nchars) "" ! {nchars = string-length(car(first))} car(first) ! else ! let <* ans $ make-string nchars *> ! ! let lp <* strings first \\ i 0 *> ! ! if pair?(strings) ! ! ! let* <* s $ car strings \\ slen $ string-length s *> ! ! ! %string-copy! ans i s 0 slen ! ! ! lp cdr(strings) {i + slen} ! ! ans define string-concatenate(strings) ! let* ! \\ ! total ! do ! strings(strings cdr(strings)) ! i 0 {i + string-length(car(strings))} ! not(pair?(strings)) i ! ans make-string(total) ! let lp <* i 0 \\ strings strings *> ! if pair?(strings) ! let* <* s $ car strings \\ slen $ string-length s *> ! ! %string-copy! ans i s 0 slen ! ! lp {i + slen} cdr(strings) ! ans define string-concatenate-reverse(string-list . maybe-final+end) ! let-optionals* ! maybe-final+end ! final("" string?(final)) ! end ! string-length final ! and ! integer? end ! exact? end ! {0 <= end <= string-length(final)} ! let ! \\ ! len ! let lp <* sum 0 \\ lis string-list *> ! ! if pair?(lis) ! ! lp {sum + string-length(car(lis))} cdr(lis) ! ! sum ! %finish-string-concatenate-reverse ! len ! string-list ! final ! end define string-concatenate-reverse/shared(string-list . maybe-final+end) ! let-optionals* ! maybe-final+end ! final("" string?(final)) ! end ! string-length final ! and ! integer? end ! exact? end ! {0 <= end <= string-length(final)} ! let lp <* len 0 \\ nzlist #f \\ lis string-list *> ! if pair?(lis) ! let <* slen $ string-length car(lis) *> ! ! lp ! ! {len + slen} ! ! if {nzlist or zero?(slen)} nzlist lis ! ! cdr lis ! cond ! zero?(len) substring/shared(final 0 end) ! {zero?(end) and {len = string-length(car(nzlist))}} ! car nzlist ! else ! %finish-string-concatenate-reverse len nzlist final end define %finish-string-concatenate-reverse(len string-list final end) ! let <* ans $ make-string {end + len} *> ! %string-copy! ans len final 0 end ! let lp <* i len \\ lis string-list *> ! if pair?(lis) ! let* ! ! \\ ! ! s car(lis) ! ! lis cdr(lis) ! ! slen string-length(s) ! ! i {i - slen} ! ! %string-copy! ans i s 0 slen ! ! lp i lis ! ans define string-replace(s1 s2 start1 end1 . maybe-start+end) ! check-substring-spec string-replace s1 start1 end1 ! let-string-start+end ! start2 end2 ! string-replace ! s2 ! maybe-start+end ! let* ! \\ ! slen1 string-length(s1) ! sublen2 {end2 - start2} ! alen {{slen1 - {end1 - start1}} + sublen2} ! ans make-string(alen) ! %string-copy! ans 0 s1 0 start1 ! %string-copy! ans start1 s2 start2 end2 ! %string-copy! ans {start1 + sublen2} s1 end1 slen1 ! ans define string-tokenize(s . token-chars+start+end) ! let-optionals* ! token-chars+start+end ! token-chars(char-set:graphic char-set?(token-chars)) rest ! let-string-start+end ! start end ! string-tokenize ! s ! rest ! let lp <* i end \\ ans '() *> ! cond ! {{start < i} and string-index-right(s token-chars start i)} ! => ! lambda tend-1() ! ! let <* tend {1 + tend-1} *> ! ! cond ! ! string-skip-right(s token-chars start tend-1) ! ! => ! ! lambda tstart-1() ! ! ! lp tstart-1 cons(substring(s {1 + tstart-1} tend) ans) ! ! else cons(substring(s start tend) ans) ! else ans define xsubstring(s from . maybe-to+start+end) ! check-arg ! lambda val() {integer?(val) and exact?(val)} ! from ! xsubstring ! receive ! to start end ! if pair?(maybe-to+start+end) ! let-string-start+end ! start end ! xsubstring ! s ! cdr maybe-to+start+end ! let <* to $ car maybe-to+start+end *> ! ! check-arg ! ! lambda val() ! ! ! {integer?(val) and exact?(val) and {from <= val}} ! ! to ! ! xsubstring ! ! values to start end ! let <* slen $ string-length check-arg(string? s xsubstring) *> ! values {from + slen} 0 slen ! let <* slen {end - start} \\ anslen {to - from} *> ! cond ! zero?(anslen) "" ! zero?(slen) ! error ! "Cannot replicate empty (sub)string" ! xsubstring ! s ! from ! to ! start ! end ! {1 = slen} make-string(anslen string-ref(s start)) ! {floor{from / slen} = floor{to / slen}} ! substring ! s ! {start + modulo(from slen)} ! {start + modulo(to slen)} ! else ! let <* ans $ make-string anslen *> ! ! %multispan-repcopy! ! ! ans ! ! 0 ! ! s ! ! from ! ! to ! ! start ! ! end ! ! ans define string-xcopy!(target tstart s sfrom . maybe-sto+start+end) ! check-arg ! lambda val() {integer?(val) and exact?(val)} ! sfrom ! string-xcopy! ! receive ! sto start end ! if pair?(maybe-sto+start+end) ! let-string-start+end ! start end ! string-xcopy! ! s ! cdr maybe-sto+start+end ! let <* sto $ car maybe-sto+start+end *> ! ! check-arg ! ! lambda val() {integer?(val) and exact?(val)} ! ! sto ! ! string-xcopy! ! ! values sto start end ! let <* slen $ string-length s *> values({sfrom + slen} 0 slen) ! let* ! \\ ! tocopy {sto - sfrom} ! tend {tstart + tocopy} ! slen {end - start} ! check-substring-spec string-xcopy! target tstart tend ! cond ! (zero?(tocopy)) ! zero?(slen) ! error ! "Cannot replicate empty (sub)string" ! string-xcopy! ! target ! tstart ! s ! sfrom ! sto ! start ! end ! {1 = slen} ! string-fill! target string-ref(s start) tstart tend ! {floor{sfrom / slen} = floor{sto / slen}} ! %string-copy! ! target ! tstart ! s ! {start + modulo(sfrom slen)} ! {start + modulo(sto slen)} ! else ! %multispan-repcopy! ! target ! tstart ! s ! sfrom ! sto ! start ! end define %multispan-repcopy!(target tstart s sfrom sto start end) ! let* ! \\ ! slen {end - start} ! i0 {start + modulo(sfrom slen)} ! total-chars {sto - sfrom} ! %string-copy! target tstart s i0 end ! let* ! \\ ! ncopied {end - i0} ! nleft {total-chars - ncopied} ! nspans quotient(nleft slen) ! do <* i {tstart + ncopied} {i + slen} \\ nspans nspans {nspans - 1} *> ! zero?(nspans) ! %string-copy! target i s start ! {start + {total-chars - {i - tstart}}} ! %string-copy! target i s start end; Copy a whole span. define string-join(strings . delim+grammar) ! let-optionals* ! delim+grammar ! delim(" " string?(delim)) grammar('infix) ! let ! \\ ! buildit ! lambda lis(final) ! ! let recur ! ! (lis(lis)) ! ! if pair?(lis) ! ! ! cons delim cons(car(lis) recur(cdr(lis))) ! ! ! final ! cond ! pair?(strings) ! string-concatenate ! case grammar ! ! infix(strict-infix) ! ! cons car(strings) buildit(cdr(strings) '()) ! ! prefix() buildit(strings '()) ! ! suffix() ! ! cons car(strings) buildit(cdr(strings) list(delim)) ! ! else error("Illegal join grammar" grammar string-join) ! not(null?(strings)) ! error "STRINGS parameter not list." strings string-join ! eq?(grammar 'strict-infix) ! error ! "Empty list cannot be joined with STRICT-INFIX grammar." ! string-join ! else ""; Special-cased for infix grammar. ;;; Copyright details ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; The prefix/suffix and comparison routines in this code had (extremely ;;; distant) origins in MIT Scheme's string lib, and was substantially ;;; reworked by Olin Shivers (xxxxxx@ai.mit.edu) 9/98. As such, it is ;;; covered by MIT Scheme's open source copyright. See below for details. ;;; ;;; The KMP string-search code was influenced by implementations written ;;; by Stephen Bevan, Brian Dehneyer and Will Fitzgerald. However, this ;;; version was written from scratch by myself. ;;; ;;; The remainder of this code was written from scratch by myself for scsh. ;;; The scsh copyright is a BSD-style open source copyright. See below for ;;; details. ;;; -Olin Shivers ;;; MIT Scheme copyright terms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; This material was developed by the Scheme project at the Massachusetts ;;; Institute of Technology, Department of Electrical Engineering and ;;; Computer Science. Permission to copy and modify this software, to ;;; redistribute either the original software or a modified version, and ;;; to use this software for any purpose is granted, subject to the ;;; following restrictions and understandings. ;;; ;;; 1. Any copy made of this software must include this copyright notice ;;; in full. ;;; ;;; 2. Users of this software agree to make their best efforts (a) to ;;; return to the MIT Scheme project any improvements or extensions that ;;; they make, so that these may be included in future releases; and (b) ;;; to inform MIT of noteworthy uses of this software. ;;; ;;; 3. All materials developed as a consequence of the use of this ;;; software shall duly acknowledge such use, in accordance with the usual ;;; standards of acknowledging credit in academic research. ;;; ;;; 4. MIT has made no warrantee or representation that the operation of ;;; this software will be error-free, and MIT is under no obligation to ;;; provide any services, by way of maintenance, update, or otherwise. ;;; ;;; 5. In conjunction with products arising from the use of this material, ;;; there shall be no use of the name of the Massachusetts Institute of ;;; Technology nor of any adaptation thereof in any advertising, ;;; promotional, or sales literature without prior written consent from ;;; MIT in each case. ;;; Scsh copyright terms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; All rights reserved. ;;; ;;; Redistribution and use in source and binary forms, with or without ;;; modification, are permitted provided that the following conditions ;;; are met: ;;; 1. Redistributions of source code must retain the above copyright ;;; notice, this list of conditions and the following disclaimer. ;;; 2. Redistributions in binary form must reproduce the above copyright ;;; notice, this list of conditions and the following disclaimer in the ;;; documentation and/or other materials provided with the distribution. ;;; 3. The name of the authors may not be used to endorse or promote products ;;; derived from this software without specific prior written permission. ;;; ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR ;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, ;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ; s-expressions ; Sample SRFI-13 implementation, from: ; http://srfi.schemers.org/srfi-13/srfi-13.scm ;;; SRFI 13 string library reference implementation -*- Scheme -*- ;;; Olin Shivers 7/2000 ;;; ;;; Copyright (c) 1988-1994 Massachusetts Institute of Technology. ;;; Copyright (c) 1998, 1999, 2000 Olin Shivers. All rights reserved. ;;; The details of the copyrights appear at the end of the file. Short ;;; summary: BSD-style open source. ; Since we only care about the *format*, nearly all ;-comments have been ; removed from this version, to shorten it (see the original file for them). (define-syntax let-string-start+end (syntax-rules () ((let-string-start+end (start end) proc s-exp args-exp body ...) (receive (start end) (string-parse-final-start+end proc s-exp args-exp) body ...)) ((let-string-start+end (start end rest) proc s-exp args-exp body ...) (receive (rest start end) (string-parse-start+end proc s-exp args-exp) body ...)))) (define-syntax let-string-start+end2 (syntax-rules () ((l-s-s+e2 (start1 end1 start2 end2) proc s1 s2 args body ...) (let ((procv proc)) ; Make sure PROC is only evaluated once. (let-string-start+end (start1 end1 rest) procv s1 args (let-string-start+end (start2 end2) procv s2 rest body ...)))))) (define (string-parse-start+end proc s args) (if (not (string? s)) (error "Non-string value" proc s)) (let ((slen (string-length s))) (if (pair? args) (let ((start (car args)) (args (cdr args))) (if (and (integer? start) (exact? start) (>= start 0)) (receive (end args) (if (pair? args) (let ((end (car args)) (args (cdr args))) (if (and (integer? end) (exact? end) (<= end slen)) (values end args) (error "Illegal substring END spec" proc end s))) (values slen args)) (if (<= start end) (values args start end) (error "Illegal substring START/END spec" proc start end s))) (error "Illegal substring START spec" proc start s))) (values '() 0 slen)))) (define (string-parse-final-start+end proc s args) (receive (rest start end) (string-parse-start+end proc s args) (if (pair? rest) (error "Extra arguments to procedure" proc rest) (values start end)))) (define (substring-spec-ok? s start end) (and (string? s) (integer? start) (exact? start) (integer? end) (exact? end) (<= 0 start) (<= start end) (<= end (string-length s)))) (define (check-substring-spec proc s start end) (if (not (substring-spec-ok? s start end)) (error "Illegal substring spec." proc s start end))) (define (substring/shared s start . maybe-end) (check-arg string? s substring/shared) (let ((slen (string-length s))) (check-arg (lambda (start) (and (integer? start) (exact? start) (<= 0 start))) start substring/shared) (%substring/shared s start (:optional maybe-end slen (lambda (end) (and (integer? end) (exact? end) (<= start end) (<= end slen))))))) (define (%substring/shared s start end) (if (and (zero? start) (= end (string-length s))) s (substring s start end))) (define (string-copy s . maybe-start+end) (let-string-start+end (start end) string-copy s maybe-start+end (substring s start end))) (define (string-map proc s . maybe-start+end) (check-arg procedure? proc string-map) (let-string-start+end (start end) string-map s maybe-start+end (%string-map proc s start end))) (define (%string-map proc s start end) ; Internal utility (let* ((len (- end start)) (ans (make-string len))) (do ((i (- end 1) (- i 1)) (j (- len 1) (- j 1))) ((< j 0)) (string-set! ans j (proc (string-ref s i)))) ans)) (define (string-map! proc s . maybe-start+end) (check-arg procedure? proc string-map!) (let-string-start+end (start end) string-map! s maybe-start+end (%string-map! proc s start end))) (define (%string-map! proc s start end) (do ((i (- end 1) (- i 1))) ((< i start)) (string-set! s i (proc (string-ref s i))))) (define (string-fold kons knil s . maybe-start+end) (check-arg procedure? kons string-fold) (let-string-start+end (start end) string-fold s maybe-start+end (let lp ((v knil) (i start)) (if (< i end) (lp (kons (string-ref s i) v) (+ i 1)) v)))) (define (string-fold-right kons knil s . maybe-start+end) (check-arg procedure? kons string-fold-right) (let-string-start+end (start end) string-fold-right s maybe-start+end (let lp ((v knil) (i (- end 1))) (if (>= i start) (lp (kons (string-ref s i) v) (- i 1)) v)))) (define (string-unfold p f g seed . base+make-final) (check-arg procedure? p string-unfold) (check-arg procedure? f string-unfold) (check-arg procedure? g string-unfold) (let-optionals* base+make-final ((base "" (string? base)) (make-final (lambda (x) "") (procedure? make-final))) (let lp ((chunks '()) ; Previously filled chunks (nchars 0) ; Number of chars in CHUNKS (chunk (make-string 40)) ; Current chunk into which we write (chunk-len 40) (i 0) ; Number of chars written into CHUNK (seed seed)) (let lp2 ((i i) (seed seed)) (if (not (p seed)) (let ((c (f seed)) (seed (g seed))) (if (< i chunk-len) (begin (string-set! chunk i c) (lp2 (+ i 1) seed)) (let* ((nchars2 (+ chunk-len nchars)) (chunk-len2 (min 4096 nchars2)) (new-chunk (make-string chunk-len2))) (string-set! new-chunk 0 c) (lp (cons chunk chunks) (+ nchars chunk-len) new-chunk chunk-len2 1 seed)))) ;; We're done. Make the answer string & install the bits. (let* ((final (make-final seed)) (flen (string-length final)) (base-len (string-length base)) (j (+ base-len nchars i)) (ans (make-string (+ j flen)))) (%string-copy! ans j final 0 flen) ; Install FINAL. (let ((j (- j i))) (%string-copy! ans j chunk 0 i) ; Install CHUNK[0,I). (let lp ((j j) (chunks chunks)) ; Install CHUNKS. (if (pair? chunks) (let* ((chunk (car chunks)) (chunks (cdr chunks)) (chunk-len (string-length chunk)) (j (- j chunk-len))) (%string-copy! ans j chunk 0 chunk-len) (lp j chunks))))) (%string-copy! ans 0 base 0 base-len) ; Install BASE. ans)))))) (define (string-unfold-right p f g seed . base+make-final) (let-optionals* base+make-final ((base "" (string? base)) (make-final (lambda (x) "") (procedure? make-final))) (let lp ((chunks '()) ; Previously filled chunks (nchars 0) ; Number of chars in CHUNKS (chunk (make-string 40)) ; Current chunk into which we write (chunk-len 40) (i 40) ; Number of chars available in CHUNK (seed seed)) (let lp2 ((i i) (seed seed)) ; Fill up CHUNK from right (if (not (p seed)) ; to left. (let ((c (f seed)) (seed (g seed))) (if (> i 0) (let ((i (- i 1))) (string-set! chunk i c) (lp2 i seed)) (let* ((nchars2 (+ chunk-len nchars)) (chunk-len2 (min 4096 nchars2)) (new-chunk (make-string chunk-len2)) (i (- chunk-len2 1))) (string-set! new-chunk i c) (lp (cons chunk chunks) (+ nchars chunk-len) new-chunk chunk-len2 i seed)))) ;; We're done. Make the answer string & install the bits. (let* ((final (make-final seed)) (flen (string-length final)) (base-len (string-length base)) (chunk-used (- chunk-len i)) (j (+ base-len nchars chunk-used)) (ans (make-string (+ j flen)))) (%string-copy! ans 0 final 0 flen) ; Install FINAL. (%string-copy! ans flen chunk i chunk-len); Install CHUNK[I,). (let lp ((j (+ flen chunk-used)) ; Install CHUNKS. (chunks chunks)) (if (pair? chunks) (let* ((chunk (car chunks)) (chunks (cdr chunks)) (chunk-len (string-length chunk))) (%string-copy! ans j chunk 0 chunk-len) (lp (+ j chunk-len) chunks)) (%string-copy! ans j base 0 base-len))); Install BASE. ans)))))) (define (string-for-each proc s . maybe-start+end) (check-arg procedure? proc string-for-each) (let-string-start+end (start end) string-for-each s maybe-start+end (let lp ((i start)) (if (< i end) (begin (proc (string-ref s i)) (lp (+ i 1))))))) (define (string-for-each-index proc s . maybe-start+end) (check-arg procedure? proc string-for-each-index) (let-string-start+end (start end) string-for-each-index s maybe-start+end (let lp ((i start)) (if (< i end) (begin (proc i) (lp (+ i 1))))))) (define (string-every criterion s . maybe-start+end) (let-string-start+end (start end) string-every s maybe-start+end (cond ((char? criterion) (let lp ((i start)) (or (>= i end) (and (char=? criterion (string-ref s i)) (lp (+ i 1)))))) ((char-set? criterion) (let lp ((i start)) (or (>= i end) (and (char-set-contains? criterion (string-ref s i)) (lp (+ i 1)))))) ((procedure? criterion) ; Slightly funky loop so that (or (= start end) ; final (PRED S[END-1]) call (let lp ((i start)) ; is a tail call. (let ((c (string-ref s i)) (i1 (+ i 1))) (if (= i1 end) (criterion c) ; Tail call. (and (criterion c) (lp i1))))))) (else (error "Second param is neither char-set, char, or predicate procedure." string-every criterion))))) (define (string-any criterion s . maybe-start+end) (let-string-start+end (start end) string-any s maybe-start+end (cond ((char? criterion) (let lp ((i start)) (and (< i end) (or (char=? criterion (string-ref s i)) (lp (+ i 1)))))) ((char-set? criterion) (let lp ((i start)) (and (< i end) (or (char-set-contains? criterion (string-ref s i)) (lp (+ i 1)))))) ((procedure? criterion) ; Slightly funky loop so that (and (< start end) ; final (PRED S[END-1]) call (let lp ((i start)) ; is a tail call. (let ((c (string-ref s i)) (i1 (+ i 1))) (if (= i1 end) (criterion c) ; Tail call (or (criterion c) (lp i1))))))) (else (error "Second param is neither char-set, char, or predicate procedure." string-any criterion))))) (define (string-tabulate proc len) (check-arg procedure? proc string-tabulate) (check-arg (lambda (val) (and (integer? val) (exact? val) (<= 0 val))) len string-tabulate) (let ((s (make-string len))) (do ((i (- len 1) (- i 1))) ((< i 0)) (string-set! s i (proc i))) s)) (define (%string-prefix-length s1 start1 end1 s2 start2 end2) (let* ((delta (min (- end1 start1) (- end2 start2))) (end1 (+ start1 delta))) (if (and (eq? s1 s2) (= start1 start2)) ; EQ fast path delta (let lp ((i start1) (j start2)) ; Regular path (if (or (>= i end1) (not (char=? (string-ref s1 i) (string-ref s2 j)))) (- i start1) (lp (+ i 1) (+ j 1))))))) (define (%string-suffix-length s1 start1 end1 s2 start2 end2) (let* ((delta (min (- end1 start1) (- end2 start2))) (start1 (- end1 delta))) (if (and (eq? s1 s2) (= end1 end2)) ; EQ fast path delta (let lp ((i (- end1 1)) (j (- end2 1))) ; Regular path (if (or (< i start1) (not (char=? (string-ref s1 i) (string-ref s2 j)))) (- (- end1 i) 1) (lp (- i 1) (- j 1))))))) (define (%string-prefix-length-ci s1 start1 end1 s2 start2 end2) (let* ((delta (min (- end1 start1) (- end2 start2))) (end1 (+ start1 delta))) (if (and (eq? s1 s2) (= start1 start2)) ; EQ fast path delta (let lp ((i start1) (j start2)) ; Regular path (if (or (>= i end1) (not (char-ci=? (string-ref s1 i) (string-ref s2 j)))) (- i start1) (lp (+ i 1) (+ j 1))))))) (define (%string-suffix-length-ci s1 start1 end1 s2 start2 end2) (let* ((delta (min (- end1 start1) (- end2 start2))) (start1 (- end1 delta))) (if (and (eq? s1 s2) (= end1 end2)) ; EQ fast path delta (let lp ((i (- end1 1)) (j (- end2 1))) ; Regular path (if (or (< i start1) (not (char-ci=? (string-ref s1 i) (string-ref s2 j)))) (- (- end1 i) 1) (lp (- i 1) (- j 1))))))) (define (string-prefix-length s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-prefix-length s1 s2 maybe-starts+ends (%string-prefix-length s1 start1 end1 s2 start2 end2))) (define (string-suffix-length s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-suffix-length s1 s2 maybe-starts+ends (%string-suffix-length s1 start1 end1 s2 start2 end2))) (define (string-prefix-length-ci s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-prefix-length-ci s1 s2 maybe-starts+ends (%string-prefix-length-ci s1 start1 end1 s2 start2 end2))) (define (string-suffix-length-ci s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-suffix-length-ci s1 s2 maybe-starts+ends (%string-suffix-length-ci s1 start1 end1 s2 start2 end2))) (define (string-prefix? s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-prefix? s1 s2 maybe-starts+ends (%string-prefix? s1 start1 end1 s2 start2 end2))) (define (string-suffix? s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-suffix? s1 s2 maybe-starts+ends (%string-suffix? s1 start1 end1 s2 start2 end2))) (define (string-prefix-ci? s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-prefix-ci? s1 s2 maybe-starts+ends (%string-prefix-ci? s1 start1 end1 s2 start2 end2))) (define (string-suffix-ci? s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-suffix-ci? s1 s2 maybe-starts+ends (%string-suffix-ci? s1 start1 end1 s2 start2 end2))) ;;; Here are the internal routines that do the real work. (define (%string-prefix? s1 start1 end1 s2 start2 end2) (let ((len1 (- end1 start1))) (and (<= len1 (- end2 start2)) ; Quick check (= (%string-prefix-length s1 start1 end1 s2 start2 end2) len1)))) (define (%string-suffix? s1 start1 end1 s2 start2 end2) (let ((len1 (- end1 start1))) (and (<= len1 (- end2 start2)) ; Quick check (= len1 (%string-suffix-length s1 start1 end1 s2 start2 end2))))) (define (%string-prefix-ci? s1 start1 end1 s2 start2 end2) (let ((len1 (- end1 start1))) (and (<= len1 (- end2 start2)) ; Quick check (= len1 (%string-prefix-length-ci s1 start1 end1 s2 start2 end2))))) (define (%string-suffix-ci? s1 start1 end1 s2 start2 end2) (let ((len1 (- end1 start1))) (and (<= len1 (- end2 start2)) ; Quick check (= len1 (%string-suffix-length-ci s1 start1 end1 s2 start2 end2))))) (define (%string-compare s1 start1 end1 s2 start2 end2 proc< proc= proc>) (let ((size1 (- end1 start1)) (size2 (- end2 start2))) (let ((match (%string-prefix-length s1 start1 end1 s2 start2 end2))) (if (= match size1) ((if (= match size2) proc= proc<) end1) ((if (= match size2) proc> (if (char<? (string-ref s1 (+ start1 match)) (string-ref s2 (+ start2 match))) proc< proc>)) (+ match start1)))))) (define (%string-compare-ci s1 start1 end1 s2 start2 end2 proc< proc= proc>) (let ((size1 (- end1 start1)) (size2 (- end2 start2))) (let ((match (%string-prefix-length-ci s1 start1 end1 s2 start2 end2))) (if (= match size1) ((if (= match size2) proc= proc<) end1) ((if (= match size2) proc> (if (char-ci<? (string-ref s1 (+ start1 match)) (string-ref s2 (+ start2 match))) proc< proc>)) (+ start1 match)))))) (define (string-compare s1 s2 proc< proc= proc> . maybe-starts+ends) (check-arg procedure? proc< string-compare) (check-arg procedure? proc= string-compare) (check-arg procedure? proc> string-compare) (let-string-start+end2 (start1 end1 start2 end2) string-compare s1 s2 maybe-starts+ends (%string-compare s1 start1 end1 s2 start2 end2 proc< proc= proc>))) (define (string-compare-ci s1 s2 proc< proc= proc> . maybe-starts+ends) (check-arg procedure? proc< string-compare-ci) (check-arg procedure? proc= string-compare-ci) (check-arg procedure? proc> string-compare-ci) (let-string-start+end2 (start1 end1 start2 end2) string-compare-ci s1 s2 maybe-starts+ends (%string-compare-ci s1 start1 end1 s2 start2 end2 proc< proc= proc>))) (define (string= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string= s1 s2 maybe-starts+ends (and (= (- end1 start1) (- end2 start2)) ; Quick filter (or (and (eq? s1 s2) (= start1 start2)) ; Fast path (%string-compare s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) values (lambda (i) #f)))))) (define (string<> s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string<> s1 s2 maybe-starts+ends (or (not (= (- end1 start1) (- end2 start2))) ; Fast path (and (not (and (eq? s1 s2) (= start1 start2))) ; Quick filter (%string-compare s1 start1 end1 s2 start2 end2 ; Real test values (lambda (i) #f) values))))) (define (string< s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string< s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (< end1 end2) (%string-compare s1 start1 end1 s2 start2 end2 ; Real test values (lambda (i) #f) (lambda (i) #f))))) (define (string> s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string> s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (> end1 end2) (%string-compare s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) (lambda (i) #f) values)))) (define (string<= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string<= s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (<= end1 end2) (%string-compare s1 start1 end1 s2 start2 end2 ; Real test values values (lambda (i) #f))))) (define (string>= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string>= s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (>= end1 end2) (%string-compare s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) values values)))) (define (string-ci= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci= s1 s2 maybe-starts+ends (and (= (- end1 start1) (- end2 start2)) ; Quick filter (or (and (eq? s1 s2) (= start1 start2)) ; Fast path (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) values (lambda (i) #f)))))) (define (string-ci<> s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci<> s1 s2 maybe-starts+ends (or (not (= (- end1 start1) (- end2 start2))) ; Fast path (and (not (and (eq? s1 s2) (= start1 start2))) ; Quick filter (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test values (lambda (i) #f) values))))) (define (string-ci< s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci< s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (< end1 end2) (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test values (lambda (i) #f) (lambda (i) #f))))) (define (string-ci> s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci> s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (> end1 end2) (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) (lambda (i) #f) values)))) (define (string-ci<= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci<= s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (<= end1 end2) (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test values values (lambda (i) #f))))) (define (string-ci>= s1 s2 . maybe-starts+ends) (let-string-start+end2 (start1 end1 start2 end2) string-ci>= s1 s2 maybe-starts+ends (if (and (eq? s1 s2) (= start1 start2)) ; Fast path (>= end1 end2) (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test (lambda (i) #f) values values)))) (define (%string-hash s char->int bound start end) (let ((iref (lambda (s i) (char->int (string-ref s i)))) ;; Compute a 111...1 mask that will cover BOUND-1: (mask (let lp ((i #x10000)) ; Let's skip first 16 iterations, eh? (if (>= i bound) (- i 1) (lp (+ i i)))))) (let lp ((i start) (ans 0)) (if (>= i end) (modulo ans bound) (lp (+ i 1) (bitwise-and mask (+ (* 37 ans) (iref s i)))))))) (define (string-hash s . maybe-bound+start+end) (let-optionals* maybe-bound+start+end ((bound 4194304 (and (integer? bound) (exact? bound) (<= 0 bound))) rest) (let ((bound (if (zero? bound) 4194304 bound))) ; 0 means default. (let-string-start+end (start end) string-hash s rest (%string-hash s char->integer bound start end))))) (define (string-hash-ci s . maybe-bound+start+end) (let-optionals* maybe-bound+start+end ((bound 4194304 (and (integer? bound) (exact? bound) (<= 0 bound))) rest) (let ((bound (if (zero? bound) 4194304 bound))) ; 0 means default. (let-string-start+end (start end) string-hash-ci s rest (%string-hash s (lambda (c) (char->integer (char-downcase c))) bound start end))))) (define (string-upcase s . maybe-start+end) (let-string-start+end (start end) string-upcase s maybe-start+end (%string-map char-upcase s start end))) (define (string-upcase! s . maybe-start+end) (let-string-start+end (start end) string-upcase! s maybe-start+end (%string-map! char-upcase s start end))) (define (string-downcase s . maybe-start+end) (let-string-start+end (start end) string-downcase s maybe-start+end (%string-map char-downcase s start end))) (define (string-downcase! s . maybe-start+end) (let-string-start+end (start end) string-downcase! s maybe-start+end (%string-map! char-downcase s start end))) (define (%string-titlecase! s start end) (let lp ((i start)) (cond ((string-index s char-cased? i end) => (lambda (i) (string-set! s i (char-titlecase (string-ref s i))) (let ((i1 (+ i 1))) (cond ((string-skip s char-cased? i1 end) => (lambda (j) (string-downcase! s i1 j) (lp (+ j 1)))) (else (string-downcase! s i1 end))))))))) (define (string-titlecase! s . maybe-start+end) (let-string-start+end (start end) string-titlecase! s maybe-start+end (%string-titlecase! s start end))) (define (string-titlecase s . maybe-start+end) (let-string-start+end (start end) string-titlecase! s maybe-start+end (let ((ans (substring s start end))) (%string-titlecase! ans 0 (- end start)) ans))) (define (string-take s n) (check-arg string? s string-take) (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n (string-length s)))) n string-take) (%substring/shared s 0 n)) (define (string-take-right s n) (check-arg string? s string-take-right) (let ((len (string-length s))) (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len))) n string-take-right) (%substring/shared s (- len n) len))) (define (string-drop s n) (check-arg string? s string-drop) (let ((len (string-length s))) (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len))) n string-drop) (%substring/shared s n len))) (define (string-drop-right s n) (check-arg string? s string-drop-right) (let ((len (string-length s))) (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len))) n string-drop-right) (%substring/shared s 0 (- len n)))) (define (string-trim s . criterion+start+end) (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest) (let-string-start+end (start end) string-trim s rest (cond ((string-skip s criterion start end) => (lambda (i) (%substring/shared s i end))) (else ""))))) (define (string-trim-right s . criterion+start+end) (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest) (let-string-start+end (start end) string-trim-right s rest (cond ((string-skip-right s criterion start end) => (lambda (i) (%substring/shared s 0 (+ 1 i)))) (else ""))))) (define (string-trim-both s . criterion+start+end) (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest) (let-string-start+end (start end) string-trim-both s rest (cond ((string-skip s criterion start end) => (lambda (i) (%substring/shared s i (+ 1 (string-skip-right s criterion i end))))) (else ""))))) (define (string-pad-right s n . char+start+end) (let-optionals* char+start+end ((char #\space (char? char)) rest) (let-string-start+end (start end) string-pad-right s rest (check-arg (lambda (n) (and (integer? n) (exact? n) (<= 0 n))) n string-pad-right) (let ((len (- end start))) (if (<= n len) (%substring/shared s start (+ start n)) (let ((ans (make-string n char))) (%string-copy! ans 0 s start end) ans)))))) (define (string-pad s n . char+start+end) (let-optionals* char+start+end ((char #\space (char? char)) rest) (let-string-start+end (start end) string-pad s rest (check-arg (lambda (n) (and (integer? n) (exact? n) (<= 0 n))) n string-pad) (let ((len (- end start))) (if (<= n len) (%substring/shared s (- end n) end) (let ((ans (make-string n char))) (%string-copy! ans (- n len) s start end) ans)))))) (define (string-delete criterion s . maybe-start+end) (let-string-start+end (start end) string-delete s maybe-start+end (if (procedure? criterion) (let* ((slen (- end start)) (temp (make-string slen)) (ans-len (string-fold (lambda (c i) (if (criterion c) i (begin (string-set! temp i c) (+ i 1)))) 0 s start end))) (if (= ans-len slen) temp (substring temp 0 ans-len))) (let* ((cset (cond ((char-set? criterion) criterion) ((char? criterion) (char-set criterion)) (else (error "string-delete criterion not predicate, char or char-set" criterion)))) (len (string-fold (lambda (c i) (if (char-set-contains? cset c) i (+ i 1))) 0 s start end)) (ans (make-string len))) (string-fold (lambda (c i) (if (char-set-contains? cset c) i (begin (string-set! ans i c) (+ i 1)))) 0 s start end) ans)))) (define (string-filter criterion s . maybe-start+end) (let-string-start+end (start end) string-filter s maybe-start+end (if (procedure? criterion) (let* ((slen (- end start)) (temp (make-string slen)) (ans-len (string-fold (lambda (c i) (if (criterion c) (begin (string-set! temp i c) (+ i 1)) i)) 0 s start end))) (if (= ans-len slen) temp (substring temp 0 ans-len))) (let* ((cset (cond ((char-set? criterion) criterion) ((char? criterion) (char-set criterion)) (else (error "string-delete criterion not predicate, char or char-set" criterion)))) (len (string-fold (lambda (c i) (if (char-set-contains? cset c) (+ i 1) i)) 0 s start end)) (ans (make-string len))) (string-fold (lambda (c i) (if (char-set-contains? cset c) (begin (string-set! ans i c) (+ i 1)) i)) 0 s start end) ans)))) (define (string-index str criterion . maybe-start+end) (let-string-start+end (start end) string-index str maybe-start+end (cond ((char? criterion) (let lp ((i start)) (and (< i end) (if (char=? criterion (string-ref str i)) i (lp (+ i 1)))))) ((char-set? criterion) (let lp ((i start)) (and (< i end) (if (char-set-contains? criterion (string-ref str i)) i (lp (+ i 1)))))) ((procedure? criterion) (let lp ((i start)) (and (< i end) (if (criterion (string-ref str i)) i (lp (+ i 1)))))) (else (error "Second param is neither char-set, char, or predicate procedure." string-index criterion))))) (define (string-index-right str criterion . maybe-start+end) (let-string-start+end (start end) string-index-right str maybe-start+end (cond ((char? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (char=? criterion (string-ref str i)) i (lp (- i 1)))))) ((char-set? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (char-set-contains? criterion (string-ref str i)) i (lp (- i 1)))))) ((procedure? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (criterion (string-ref str i)) i (lp (- i 1)))))) (else (error "Second param is neither char-set, char, or predicate procedure." string-index-right criterion))))) (define (string-skip str criterion . maybe-start+end) (let-string-start+end (start end) string-skip str maybe-start+end (cond ((char? criterion) (let lp ((i start)) (and (< i end) (if (char=? criterion (string-ref str i)) (lp (+ i 1)) i)))) ((char-set? criterion) (let lp ((i start)) (and (< i end) (if (char-set-contains? criterion (string-ref str i)) (lp (+ i 1)) i)))) ((procedure? criterion) (let lp ((i start)) (and (< i end) (if (criterion (string-ref str i)) (lp (+ i 1)) i)))) (else (error "Second param is neither char-set, char, or predicate procedure." string-skip criterion))))) (define (string-skip-right str criterion . maybe-start+end) (let-string-start+end (start end) string-skip-right str maybe-start+end (cond ((char? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (char=? criterion (string-ref str i)) (lp (- i 1)) i)))) ((char-set? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (char-set-contains? criterion (string-ref str i)) (lp (- i 1)) i)))) ((procedure? criterion) (let lp ((i (- end 1))) (and (>= i start) (if (criterion (string-ref str i)) (lp (- i 1)) i)))) (else (error "CRITERION param is neither char-set or char." string-skip-right criterion))))) (define (string-count s criterion . maybe-start+end) (let-string-start+end (start end) string-count s maybe-start+end (cond ((char? criterion) (do ((i start (+ i 1)) (count 0 (if (char=? criterion (string-ref s i)) (+ count 1) count))) ((>= i end) count))) ((char-set? criterion) (do ((i start (+ i 1)) (count 0 (if (char-set-contains? criterion (string-ref s i)) (+ count 1) count))) ((>= i end) count))) ((procedure? criterion) (do ((i start (+ i 1)) (count 0 (if (criterion (string-ref s i)) (+ count 1) count))) ((>= i end) count))) (else (error "CRITERION param is neither char-set or char." string-count criterion))))) (define (string-fill! s char . maybe-start+end) (check-arg char? char string-fill!) (let-string-start+end (start end) string-fill! s maybe-start+end (do ((i (- end 1) (- i 1))) ((< i start)) (string-set! s i char)))) (define (string-copy! to tstart from . maybe-fstart+fend) (let-string-start+end (fstart fend) string-copy! from maybe-fstart+fend (check-arg integer? tstart string-copy!) (check-substring-spec string-copy! to tstart (+ tstart (- fend fstart))) (%string-copy! to tstart from fstart fend))) ;;; Library-internal routine (define (%string-copy! to tstart from fstart fend) (if (> fstart tstart) (do ((i fstart (+ i 1)) (j tstart (+ j 1))) ((>= i fend)) (string-set! to j (string-ref from i))) (do ((i (- fend 1) (- i 1)) (j (+ -1 tstart (- fend fstart)) (- j 1))) ((< i fstart)) (string-set! to j (string-ref from i))))) (define (string-contains text pattern . maybe-starts+ends) (let-string-start+end2 (t-start t-end p-start p-end) string-contains text pattern maybe-starts+ends (%kmp-search pattern text char=? p-start p-end t-start t-end))) (define (string-contains-ci text pattern . maybe-starts+ends) (let-string-start+end2 (t-start t-end p-start p-end) string-contains-ci text pattern maybe-starts+ends (%kmp-search pattern text char-ci=? p-start p-end t-start t-end))) ;;; Knuth-Morris-Pratt string searching (define (%kmp-search pattern text c= p-start p-end t-start t-end) (let ((plen (- p-end p-start)) (rv (make-kmp-restart-vector pattern c= p-start p-end))) ;; The search loop. TJ & PJ are redundant state. (let lp ((ti t-start) (pi 0) (tj (- t-end t-start)) ; (- tlen ti) -- how many chars left. (pj plen)) ; (- plen pi) -- how many chars left. (if (= pi plen) (- ti plen) ; Win. (and (<= pj tj) ; Lose. (if (c= (string-ref text ti) ; Search. (string-ref pattern (+ p-start pi))) (lp (+ 1 ti) (+ 1 pi) (- tj 1) (- pj 1)) ; Advance. (let ((pi (vector-ref rv pi))) ; Retreat. (if (= pi -1) (lp (+ ti 1) 0 (- tj 1) plen) ; Punt. (lp ti pi tj (- plen pi)))))))))) (define (make-kmp-restart-vector pattern . maybe-c=+start+end) (let-optionals* maybe-c=+start+end ((c= char=? (procedure? c=)) ((start end) (lambda (args) (string-parse-start+end make-kmp-restart-vector pattern args)))) (let* ((rvlen (- end start)) (rv (make-vector rvlen -1))) (if (> rvlen 0) (let ((rvlen-1 (- rvlen 1)) (c0 (string-ref pattern start))) ;; Here's the main loop. We have set rv[0] ... rv[i]. ;; K = I + START -- it is the corresponding index into PATTERN. (let lp1 ((i 0) (j -1) (k start)) (if (< i rvlen-1) ;; lp2 invariant: ;; pat[(k-j) .. k-1] matches pat[start .. start+j-1] ;; or j = -1. (let lp2 ((j j)) (cond ((= j -1) (let ((i1 (+ 1 i))) (if (not (c= (string-ref pattern (+ k 1)) c0)) (vector-set! rv i1 0)) (lp1 i1 0 (+ k 1)))) ;; pat[(k-j) .. k] matches pat[start..start+j]. ((c= (string-ref pattern k) (string-ref pattern (+ j start))) (let* ((i1 (+ 1 i)) (j1 (+ 1 j))) (vector-set! rv i1 j1) (lp1 i1 j1 (+ k 1)))) (else (lp2 (vector-ref rv j))))))))) rv))) (define (kmp-step pat rv c i c= p-start) (let lp ((i i)) (if (c= c (string-ref pat (+ i p-start))) ; Match => (+ i 1) ; Done. (let ((i (vector-ref rv i))) ; Back up in PAT. (if (= i -1) 0 ; Can't back up further. (lp i)))))) ; Keep trying for match. (define (string-kmp-partial-search pat rv s i . c=+p-start+s-start+s-end) (check-arg vector? rv string-kmp-partial-search) (let-optionals* c=+p-start+s-start+s-end ((c= char=? (procedure? c=)) (p-start 0 (and (integer? p-start) (exact? p-start) (<= 0 p-start))) ((s-start s-end) (lambda (args) (string-parse-start+end string-kmp-partial-search s args)))) (let ((patlen (vector-length rv))) (check-arg (lambda (i) (and (integer? i) (exact? i) (<= 0 i) (< i patlen))) i string-kmp-partial-search) ;; Enough prelude. Here's the actual code. (let lp ((si s-start) ; An index into S. (vi i)) ; An index into RV. (cond ((= vi patlen) (- si)) ; Win. ((= si s-end) vi) ; Ran off the end. (else ; Match s[si] & loop. (let ((c (string-ref s si))) (lp (+ si 1) (let lp2 ((vi vi)) ; This is just KMP-STEP. (if (c= c (string-ref pat (+ vi p-start))) (+ vi 1) (let ((vi (vector-ref rv vi))) (if (= vi -1) 0 (lp2 vi))))))))))))) (define (string-null? s) (zero? (string-length s))) (define (string-reverse s . maybe-start+end) (let-string-start+end (start end) string-reverse s maybe-start+end (let* ((len (- end start)) (ans (make-string len))) (do ((i start (+ i 1)) (j (- len 1) (- j 1))) ((< j 0)) (string-set! ans j (string-ref s i))) ans))) (define (string-reverse! s . maybe-start+end) (let-string-start+end (start end) string-reverse! s maybe-start+end (do ((i (- end 1) (- i 1)) (j start (+ j 1))) ((<= i j)) (let ((ci (string-ref s i))) (string-set! s i (string-ref s j)) (string-set! s j ci))))) (define (reverse-list->string clist) (let* ((len (length clist)) (s (make-string len))) (do ((i (- len 1) (- i 1)) (clist clist (cdr clist))) ((not (pair? clist))) (string-set! s i (car clist))) s)) (define (string->list s . maybe-start+end) (let-string-start+end (start end) string->list s maybe-start+end (do ((i (- end 1) (- i 1)) (ans '() (cons (string-ref s i) ans))) ((< i start) ans)))) (define (string-append/shared . strings) (string-concatenate/shared strings)) (define (string-concatenate/shared strings) (let lp ((strings strings) (nchars 0) (first #f)) (cond ((pair? strings) ; Scan the args, add up total (let* ((string (car strings)) ; length, remember 1st (tail (cdr strings)) ; non-empty string. (slen (string-length string))) (if (zero? slen) (lp tail nchars first) (lp tail (+ nchars slen) (or first strings))))) ((zero? nchars) "") ;; Just one non-empty string! Return it. ((= nchars (string-length (car first))) (car first)) (else (let ((ans (make-string nchars))) (let lp ((strings first) (i 0)) (if (pair? strings) (let* ((s (car strings)) (slen (string-length s))) (%string-copy! ans i s 0 slen) (lp (cdr strings) (+ i slen))))) ans))))) (define (string-concatenate strings) (let* ((total (do ((strings strings (cdr strings)) (i 0 (+ i (string-length (car strings))))) ((not (pair? strings)) i))) (ans (make-string total))) (let lp ((i 0) (strings strings)) (if (pair? strings) (let* ((s (car strings)) (slen (string-length s))) (%string-copy! ans i s 0 slen) (lp (+ i slen) (cdr strings))))) ans)) (define (string-concatenate-reverse string-list . maybe-final+end) (let-optionals* maybe-final+end ((final "" (string? final)) (end (string-length final) (and (integer? end) (exact? end) (<= 0 end (string-length final))))) (let ((len (let lp ((sum 0) (lis string-list)) (if (pair? lis) (lp (+ sum (string-length (car lis))) (cdr lis)) sum)))) (%finish-string-concatenate-reverse len string-list final end)))) (define (string-concatenate-reverse/shared string-list . maybe-final+end) (let-optionals* maybe-final+end ((final "" (string? final)) (end (string-length final) (and (integer? end) (exact? end) (<= 0 end (string-length final))))) (let lp ((len 0) (nzlist #f) (lis string-list)) (if (pair? lis) (let ((slen (string-length (car lis)))) (lp (+ len slen) (if (or nzlist (zero? slen)) nzlist lis) (cdr lis))) (cond ((zero? len) (substring/shared final 0 end)) ;; LEN > 0, so NZLIST is non-empty. ((and (zero? end) (= len (string-length (car nzlist)))) (car nzlist)) (else (%finish-string-concatenate-reverse len nzlist final end))))))) (define (%finish-string-concatenate-reverse len string-list final end) (let ((ans (make-string (+ end len)))) (%string-copy! ans len final 0 end) (let lp ((i len) (lis string-list)) (if (pair? lis) (let* ((s (car lis)) (lis (cdr lis)) (slen (string-length s)) (i (- i slen))) (%string-copy! ans i s 0 slen) (lp i lis)))) ans)) (define (string-replace s1 s2 start1 end1 . maybe-start+end) (check-substring-spec string-replace s1 start1 end1) (let-string-start+end (start2 end2) string-replace s2 maybe-start+end (let* ((slen1 (string-length s1)) (sublen2 (- end2 start2)) (alen (+ (- slen1 (- end1 start1)) sublen2)) (ans (make-string alen))) (%string-copy! ans 0 s1 0 start1) (%string-copy! ans start1 s2 start2 end2) (%string-copy! ans (+ start1 sublen2) s1 end1 slen1) ans))) (define (string-tokenize s . token-chars+start+end) (let-optionals* token-chars+start+end ((token-chars char-set:graphic (char-set? token-chars)) rest) (let-string-start+end (start end) string-tokenize s rest (let lp ((i end) (ans '())) (cond ((and (< start i) (string-index-right s token-chars start i)) => (lambda (tend-1) (let ((tend (+ 1 tend-1))) (cond ((string-skip-right s token-chars start tend-1) => (lambda (tstart-1) (lp tstart-1 (cons (substring s (+ 1 tstart-1) tend) ans)))) (else (cons (substring s start tend) ans)))))) (else ans)))))) (define (xsubstring s from . maybe-to+start+end) (check-arg (lambda (val) (and (integer? val) (exact? val))) from xsubstring) (receive (to start end) (if (pair? maybe-to+start+end) (let-string-start+end (start end) xsubstring s (cdr maybe-to+start+end) (let ((to (car maybe-to+start+end))) (check-arg (lambda (val) (and (integer? val) (exact? val) (<= from val))) to xsubstring) (values to start end))) (let ((slen (string-length (check-arg string? s xsubstring)))) (values (+ from slen) 0 slen))) (let ((slen (- end start)) (anslen (- to from))) (cond ((zero? anslen) "") ((zero? slen) (error "Cannot replicate empty (sub)string" xsubstring s from to start end)) ((= 1 slen) ; Fast path for 1-char replication. (make-string anslen (string-ref s start))) ;; Selected text falls entirely within one span. ((= (floor (/ from slen)) (floor (/ to slen))) (substring s (+ start (modulo from slen)) (+ start (modulo to slen)))) ;; Selected text requires multiple spans. (else (let ((ans (make-string anslen))) (%multispan-repcopy! ans 0 s from to start end) ans)))))) (define (string-xcopy! target tstart s sfrom . maybe-sto+start+end) (check-arg (lambda (val) (and (integer? val) (exact? val))) sfrom string-xcopy!) (receive (sto start end) (if (pair? maybe-sto+start+end) (let-string-start+end (start end) string-xcopy! s (cdr maybe-sto+start+end) (let ((sto (car maybe-sto+start+end))) (check-arg (lambda (val) (and (integer? val) (exact? val))) sto string-xcopy!) (values sto start end))) (let ((slen (string-length s))) (values (+ sfrom slen) 0 slen))) (let* ((tocopy (- sto sfrom)) (tend (+ tstart tocopy)) (slen (- end start))) (check-substring-spec string-xcopy! target tstart tend) (cond ((zero? tocopy)) ((zero? slen) (error "Cannot replicate empty (sub)string" string-xcopy! target tstart s sfrom sto start end)) ((= 1 slen) ; Fast path for 1-char replication. (string-fill! target (string-ref s start) tstart tend)) ;; Selected text falls entirely within one span. ((= (floor (/ sfrom slen)) (floor (/ sto slen))) (%string-copy! target tstart s (+ start (modulo sfrom slen)) (+ start (modulo sto slen)))) ;; Multi-span copy. (else (%multispan-repcopy! target tstart s sfrom sto start end)))))) (define (%multispan-repcopy! target tstart s sfrom sto start end) (let* ((slen (- end start)) (i0 (+ start (modulo sfrom slen))) (total-chars (- sto sfrom))) (%string-copy! target tstart s i0 end) (let* ((ncopied (- end i0)) ; We've copied this many. (nleft (- total-chars ncopied)) ; # chars left to copy. (nspans (quotient nleft slen))) ; # whole spans to copy (do ((i (+ tstart ncopied) (+ i slen)) ; Current target index. (nspans nspans (- nspans 1))) ; # spans to copy ((zero? nspans) ;; Copy the partial-span @ the end & we're done. (%string-copy! target i s start (+ start (- total-chars (- i tstart))))) (%string-copy! target i s start end))))); Copy a whole span. (define (string-join strings . delim+grammar) (let-optionals* delim+grammar ((delim " " (string? delim)) (grammar 'infix)) (let ((buildit (lambda (lis final) (let recur ((lis lis)) (if (pair? lis) (cons delim (cons (car lis) (recur (cdr lis)))) final))))) (cond ((pair? strings) (string-concatenate (case grammar ((infix strict-infix) (cons (car strings) (buildit (cdr strings) '()))) ((prefix) (buildit strings '())) ((suffix) (cons (car strings) (buildit (cdr strings) (list delim)))) (else (error "Illegal join grammar" grammar string-join))))) ((not (null? strings)) (error "STRINGS parameter not list." strings string-join)) ;; STRINGS is () ((eq? grammar 'strict-infix) (error "Empty list cannot be joined with STRICT-INFIX grammar." string-join)) (else ""))))) ; Special-cased for infix grammar. ;;; Copyright details ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; The prefix/suffix and comparison routines in this code had (extremely ;;; distant) origins in MIT Scheme's string lib, and was substantially ;;; reworked by Olin Shivers (xxxxxx@ai.mit.edu) 9/98. As such, it is ;;; covered by MIT Scheme's open source copyright. See below for details. ;;; ;;; The KMP string-search code was influenced by implementations written ;;; by Stephen Bevan, Brian Dehneyer and Will Fitzgerald. However, this ;;; version was written from scratch by myself. ;;; ;;; The remainder of this code was written from scratch by myself for scsh. ;;; The scsh copyright is a BSD-style open source copyright. See below for ;;; details. ;;; -Olin Shivers ;;; MIT Scheme copyright terms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; This material was developed by the Scheme project at the Massachusetts ;;; Institute of Technology, Department of Electrical Engineering and ;;; Computer Science. Permission to copy and modify this software, to ;;; redistribute either the original software or a modified version, and ;;; to use this software for any purpose is granted, subject to the ;;; following restrictions and understandings. ;;; ;;; 1. Any copy made of this software must include this copyright notice ;;; in full. ;;; ;;; 2. Users of this software agree to make their best efforts (a) to ;;; return to the MIT Scheme project any improvements or extensions that ;;; they make, so that these may be included in future releases; and (b) ;;; to inform MIT of noteworthy uses of this software. ;;; ;;; 3. All materials developed as a consequence of the use of this ;;; software shall duly acknowledge such use, in accordance with the usual ;;; standards of acknowledging credit in academic research. ;;; ;;; 4. MIT has made no warrantee or representation that the operation of ;;; this software will be error-free, and MIT is under no obligation to ;;; provide any services, by way of maintenance, update, or otherwise. ;;; ;;; 5. In conjunction with products arising from the use of this material, ;;; there shall be no use of the name of the Massachusetts Institute of ;;; Technology nor of any adaptation thereof in any advertising, ;;; promotional, or sales literature without prior written consent from ;;; MIT in each case. ;;; Scsh copyright terms ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; All rights reserved. ;;; ;;; Redistribution and use in source and binary forms, with or without ;;; modification, are permitted provided that the following conditions ;;; are met: ;;; 1. Redistributions of source code must retain the above copyright ;;; notice, this list of conditions and the following disclaimer. ;;; 2. Redistributions in binary form must reproduce the above copyright ;;; notice, this list of conditions and the following disclaimer in the ;;; documentation and/or other materials provided with the distribution. ;;; 3. The name of the authors may not be used to endorse or promote products ;;; derived from this software without specific prior written permission. ;;; ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR ;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, ;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.