(define-module (uniseg graphemes) #:use-module (uniseg graphemes stream) #:use-module (uniseg graphemes iterator) #:use-module (ice-9 hash-table) #:use-module (ice-9 streams) #:use-module (srfi srfi-41) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9 gnu) #:export (make-grapheme grapheme? grapheme-width grapheme-delta-width grapheme-modification? grapheme-glyphs grapheme-glyphs-reverse grapheme-state grapheme-string string->grapheme string->grapheme-list char->grapheme)) (define-immutable-record-type (_make-grapheme width delta-width modification? state glyphs-reverse glyphs-promise string-promise) grapheme? (width grapheme-width) (delta-width grapheme-delta-width) (modification? grapheme-modification?) (state grapheme-state) (glyphs-reverse grapheme-glyphs-reverse) (glyphs-promise _grapheme-glyphs-promise) (string-promise _grapheme-string-promise)) (define (make-grapheme width delta modification? state glyphs-reverse) (_make-grapheme width delta modification? state glyphs-reverse (delay (reverse glyphs-reverse)) (delay (reverse-list->string glyphs-reverse)))) (define (grapheme-glyphs grapheme) "Return a lazily-constructed list of glyphs in the grapheme" (force (_grapheme-glyphs-promise grapheme))) (define (grapheme-string grapheme) "Return a lazily-constructed string of the glyphs in the grapheme." (force (_grapheme-string-promise grapheme))) (define (string->grapheme-list str) "Get a list of all the final graphemes (not including intermediates) in the provided string" (define stream (string->grapheme-stream str)) (let loop ((stream stream)) (define grapheme (stream-car stream)) (define next-stream (stream-cdr stream)) (define have-next? (stream-pair? next-stream)) (define next-modification? (and have-next? (grapheme-modification? (stream-car next-stream)))) (cond (next-modification? (loop next-stream)) (have-next? (cons grapheme (loop next-stream))) (else (list grapheme))))) (define (string->grapheme str) "Reads from `str' until we reach the end of the first grapheme cluster" (define stream (string->grapheme-stream str)) (define first-grapheme (stream-car stream)) ;; Skip first, we want to get the last entry that is a modification ;; (or the first entry, if there are no mods afterwards) (define last-grapheme (let loop ((stream stream)) (define grapheme (stream-car stream)) (define next-stream (stream-cdr stream)) (if (and (stream-pair? next-stream) (grapheme-modification? (stream-car next-stream))) (loop next-stream) grapheme))) (if (grapheme-modification? last-grapheme) last-grapheme first-grapheme)) (define (char->grapheme char) "Attempt to convert a char to a grapheme. Returns #f if no valid grapheme is formed from the single character." ((make-grapheme-iterator) char))