about summary refs log tree commit diff stats
path: root/src/emit.scm
blob: aa36eb57e37e89aa719b9612d4b366077ec2c44a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
(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 (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-public filters
  `((para
     (line . ,(o (char->tag "*" "<strong>" "</strong>")
                 (char->tag "_" "<em>" "</em>")
                 (char->tag "`" "<code>" "</code>")
                 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)))))
                  (list (car ws) (string-join (cdr ws)))))))
    (default
      (line . ,list)
      (block . ,identity))))

(define (get-from from type subtype)
  (or (alist-walk from type subtype)
      (if (eq? subtype 'inline)
          (alist-walk from type 'list)
          (lambda _ '("")))))

(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)
    (set! line (list line)))
   ;; if it's a list of strings, join them together and filter them
   ((and (list? line)
         (string? (car line)))
    (set! line ((get-filter type 'line) line)))
   ;; if the car of LINE is a symbol, it's an inline thing.
   ((and (list? line)
         (symbol? (car line)))
    (set! line (format-line (get-format (car line) 'inline)
                            ((get-filter (car line) 'line) (cdr line))
                            type)))
   (else (error "Malformed line" line)))
  (apply sprintf fmt 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)))
      ""))