(declare (module (jimmy emit))) (import scheme (chicken base) (jimmy util) (chicken format) (chicken irregex) (chicken string)) (define-public (emit document) (for-each display (map format-block document))) (define-public formats ;;; (type line-format block-format [line-in-block-format]) ;; these default to gemtext '((para (line . "~A ") (block . "~A~%~%")) (verb (line . "~A~%") (block . "```~%~A```~%~%")) (link (line . "=> ~A ~A~%") (block . "~A~%") (inline . "~%=> ~A ~A~%")) ;TODO: have 2 args to format (list (line . "* ~A~%") (block . "~A~%")) (quot (line . "~A ") (block . "> ~A~%~%")) (hdr1 (line . "# ~A~%") (block . "~A~%")) (hdr2 (line . "## ~A~%") (block . "~A~%")) (hdr3 (line . "### ~A~%") (block . "~A~%")) (default (line . "~A") (block . "~A~%~%")))) (define-public filters `((para (line . ,(o (char->tag "*" "" "") (char->tag "_" "" "") (char->tag "`" "" "") string-join)) (block . ,(lambda (ln) (irregex-replace/all '(: bol (* " ")) ln "")))) (link (line . ,(lambda (ln) (let ((ws (cond ((list? ln) ln) ((string? ln) (string-split ln))))) (let ((x (list (car ws) (string-join (cdr ws))))) x))))) (default (line . ,(lambda (x) (print x) (if (list? x) x (list x)))) (block . ,identity)))) (define (string-join ss #!optional sep) (if (string? ss) ss (string-intersperse ss (or sep " ")))) (define (char->tag char beg end) (lambda (str) (irregex-replace/all `(: ($ (or bos space)) ,char ($ (+ (~ ,char))) ,char ($ (or space eos))) str 1 beg 2 end 3))) (define (get-from from type subtype) (or (alist-walk from type subtype) (get-from from 'default subtype))) (define (get-format type subtype) (get-from formats type subtype)) (define (get-filter type subtype) (get-from filters type subtype)) (define (format-line fmt line type) (cond ;; if LINE is a string, wrap it in a list ((string? line) (sprintf (get-format type 'line) ((get-filter type 'line) line))) ;; if it's a list of strings, join them together and filter them ((and (list? line) (string? (car line))) (sprintf (get-format type 'line) (apply string-append ((get-filter type 'line) line)))) ;; if the car of LINE is a symbol, it's an inline thing. ((and (list? line) (symbol? (car line))) (sprintf* (get-format (car line) 'inline) ((get-filter (car line) 'line) (cdr line)))) (else (error "Malformed line" line)))) (define (format-block block) (if (assq (car block) formats) (let* ((type (car block)) (data (cdr block)) (text (cond ((string? data) data) ((list? data) (apply string-append (map (lambda (ln) (format-line (get-format type 'line) ln type)) data))) (else (error "Malformed block" block))))) (sprintf (get-format type 'block) ((get-filter type 'block) text))) "")) (define (sprintf* fmt lis) (let loop ((num (length (irregex-extract "~[aA]" fmt))) (lis lis) (out '())) (cond ((null? lis) (apply sprintf fmt (reverse out))) ((= 1 num) (loop 0 '() (cons (string-join lis) out))) (else (loop (- num 1) (cdr lis) (cons (car lis) out))))))