(define (string-cursor-diff str start end) (if (string-cursor? start) (- (string-cursor->index str end) (string-cursor->index str start)) (- end start))) (define (string-unfold/aux k stop? mapper successor seed . o) (let ((base (if (pair? o) (car o) "")) (make-final (if (and (pair? o) (pair? (cdr o))) (cadr o) (lambda (x) "")))) (do ((acc seed (successor acc)) (ls '() (cons (mapper acc) ls))) ((stop? acc) (k base ls (make-final acc)))))) (define (string-unfold . o) (apply string-unfold/aux (lambda (base ls final) (string-append base (reverse-list->string ls) final)) o)) (define (string-unfold-right . o) (apply string-unfold/aux (lambda (base ls final) (string-append final (list->string ls) base)) o)) (define (string-tabulate proc len) (string-unfold (lambda (i) (= i len)) proc (lambda (i) (+ i 1)) 0)) (define (string->list/cursors str . o) (let ((start (if (pair? o) (car o) (string-cursor-start str))) (end (if (and (pair? o) (pair? (cdr o))) (cadr o) (string-cursor-end str)))) (let lp ((i end) (res '())) (if (string-cursor<=? i start) res (let ((i (string-cursor-prev str i))) (lp i (cons (string-cursor-ref str i) res))))))) (define (string->vector/cursors str . o) (list->vector (apply string->list/cursors str o))) (define (reverse-list->string ls) (list->string (reverse ls))) (define (string-join str-ls . o) (let ((sep (if (pair? o) (car o) "")) (grammar (if (and (pair? o) (pair? (cdr o))) (cadr o) 'infix))) (case grammar ((infix) (%string-join str-ls sep)) ((strict-infix) (if (null? str-ls) (error "string-join 'strict-infix called on an empty list") (%string-join str-ls sep))) ((prefix) (if (null? str-ls) "" (%string-join (cons "" str-ls) sep))) ((suffix) (if (null? str-ls) "" (string-append (%string-join str-ls sep) sep))) (else (error "unknown string-join grammar" grammar))))) (define (string-ref/cursor str x) (if (string-cursor? x) (string-cursor-ref str x) (string-ref str x))) (define (substring/cursors str start end) (if (string-cursor? start) (substring-cursor str start end) (substring str start end))) (define (string-copy/cursors str . o) (cond ((null? o) (substring-cursor str (string-cursor-start str))) ((string-cursor? (car o)) (apply substring-cursor str o)) (else (apply substring str o)))) (define (string-arg str o) (if (pair? o) (apply string-copy/cursors str o) str)) (define (cursor-arg str x) (if (string-cursor? x) x (string-index->cursor str x))) (define (cursor-args str o) (if (pair? o) (cons (cursor-arg str (car o)) (cursor-args str (cdr o))) '())) (define (string-take str n) (substring str 0 n)) (define (string-take-right str n) (let ((start (string-cursor-back str (string-cursor-end str) n))) (substring-cursor str start))) (define (string-drop str n) (substring str n)) (define (string-drop-right str n) (let ((end (string-cursor-back str (string-cursor-end str) n))) (substring-cursor str (string-cursor-start str) end))) (define (string-pad str len . o) (let* ((pad-char (if (pair? o) (car o) #\space)) (str (if (and (pair? o) (pair? (cdr o))) (string-arg str (cdr o)) str)) (str-len (string-length str))) (cond ((> str-len len) (string-take-right str len)) ((< str-len len) (string-append (make-string (- len str-len) pad-char) str)) (else str)))) (define (string-pad-right str len . o) (let* ((pad-char (if (pair? o) (car o) #\space)) (str (if (and (pair? o) (pair? (cdr o))) (string-arg str (cdr o)) str)) (str-len (string-length str))) (cond ((> str-len len) (string-take str len)) ((< str-len len) (string-append str (make-string (- len str-len) pad-char))) (else str)))) (define (string-trim str . o) (let ((pred (if (pair? o) (car o) char-whitespace?)) (str (if (and (pair? o) (pair? (cdr o))) (string-arg str (cdr o)) str))) (substring-cursor str (string-skip str pred)))) (define (string-trim-right str . o) (let ((pred (if (pair? o) (car o) char-whitespace?)) (str (if (and (pair? o) (pair? (cdr o))) (string-arg str (cdr o)) str))) (substring-cursor str (string-cursor-start str) (string-skip-right str pred)))) (define (string-trim-both str . o) (let ((pred (if (pair? o) (car o) char-whitespace?)) (str (if (and (pair? o) (pair? (cdr o))) (string-arg str (cdr o)) str))) (string-trim-right (string-trim str pred) pred))) (define (string-prefix-length s1 s2 . o) (let ((s1 (string-arg s1 o)) (s2 (if (and (pair? o) (pair? (cdr o))) (string-arg s2 (cddr o)) s2))) (call-with-values (lambda () (string-mismatch s1 s2)) (lambda (i j) (string-cursor->index s1 i))))) (define (string-suffix-length s1 s2 . o) (let* ((s1 (string-arg s1 o)) (s2 (if (and (pair? o) (pair? (cdr o))) (string-arg s2 (cddr o)) s2)) (mismatch (call-with-values (lambda () (string-mismatch-right s2 s1)) (lambda (i j) j)))) (string-cursor-diff s1 (string-cursor-next s1 mismatch) (string-cursor-end s1)))) (define (string-prefix? s1 s2 . o) (equal? (string-length s1) (apply string-prefix-length s1 s2 o))) (define (string-suffix? s1 s2 . o) (equal? (string-length s1) (apply string-suffix-length s1 s2 o))) (define (string-index str pred . o) (apply string-find str pred (cursor-args str o))) (define (string-index-right str pred . o) (apply string-find-right str pred (cursor-args str o))) (define (string-contains s1 s2 . o) (let ((start1 (cursor-arg s1 (if (pair? o) (car o) (string-cursor-start s1)))) (end1 (cursor-arg s1 (if (and (pair? o) (pair? (cdr o))) (cadr o) (string-cursor-end s1)))) (s2 (if (and (pair? o) (pair? (cdr o))) (string-arg s2 (cddr o)) s2))) (let ((res (%string-contains s1 s2 start1))) (and res (string-cursor<=? (string-cursor-forward s1 res (string-length s2)) end1) res)))) (define (string-contains-right s1 s2 . o) (let* ((start1 (cursor-arg s1 (if (pair? o) (car o) (string-cursor-start s1)))) (end1 (cursor-arg s1 (if (and (pair? o) (pair? (cdr o))) (cadr o) (string-cursor-end s1)))) (s2 (if (and (pair? o) (pair? (cdr o))) (string-arg s2 (cddr o)) s2)) (start2 (string-cursor-start s2)) (end2 (string-cursor-end s2))) (let lp ((sc1-base end1) (sc1 end1) (sc2-base end2) (sc2 end2)) (cond ((string-cursor=? sc2 start2) sc1) ((string-cursor=? sc1 start1) #f) (else (let ((sc1 (string-cursor-prev s1 sc1)) (sc2 (string-cursor-prev s2 sc2))) (if (eqv? (string-cursor-ref s1 sc1) (string-cursor-ref s2 sc2)) (lp sc1-base sc1 sc2-base sc2) (let ((sc1-base (string-cursor-prev s1 sc1-base))) (lp sc1-base sc1-base sc2-base sc2-base))))))))) (define (string-reverse str . o) (list->string (reverse (string->list/cursors (string-arg str o))))) (define string-concatenate %string-join) (define (string-concatenate-reverse str-ls . o) (let ((str-ls (if (pair? o) (cons (apply string-copy/cursors (car o) 0 (cdr o)) str-ls) str-ls))) (string-concatenate (reverse str-ls)))) (define (string-fold kons knil str . o) (%string-fold kons knil (string-arg str o))) (define (string-fold-right kons knil str . o) (%string-fold-right kons knil (string-arg str o))) (define (string-for-each-cursor proc str . o) (let ((end (cursor-arg str (if (and (pair? o) (pair? (cdr o))) (cadr o) (string-cursor-end str))))) (let lp ((i (cursor-arg str (if (pair? o) (car o) (string-cursor-start str))))) (when (string-cursor=? start end)) (error "string-split 'strict-infix called on an empty string")) (let lp ((sc start) (found? #f) (i 1) (res '())) (cond ((string-cursor>=? sc end) (if (and found? (not (eq? 'suffix grammar))) (reverse (cons "" res)) (reverse res))) ((string-contains str delim sc end) => (lambda (sc2) (let ((sc3 (string-cursor-forward str sc2 delim-len))) (cond ((>= i limit) (let* ((res (if (equal? "" delim) res (cons (substring-cursor str sc sc2) res))) (res (if (and (string-cursor=? sc3 end) (eq? 'suffix grammar)) res (cons (substring-cursor str sc3 end) res)))) (lp end #f i res))) ((equal? "" delim) (lp (string-cursor-forward str sc2 1) #f (+ i 1) (cons (string (string-cursor-ref str sc2)) res))) ((and (string-cursor=? sc2 start) (eq? 'prefix grammar)) (lp sc3 #t (+ i 1) res)) (else (lp sc3 #t (+ i 1) (cons (substring-cursor str sc sc2) res))))))) (else (lp end #f i (cons (substring-cursor str sc end) res))))))) (define (string-filter pred str . o) (let ((out (open-output-string))) (apply string-fold (lambda (ch acc) (if (pred ch) (write-char ch out))) #f str o) (get-output-string out))) (define (string-remove pred str . o) (apply string-filter (lambda (ch) (not (pred ch))) str o))