summary refs log tree commit diff stats
path: root/lib/schmaltz.sls
blob: 623191ff40e2d81318080161ac9641804428c7bc (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
;;; (boudin schmaltz) --- transform almost-html plus scheme into html

;;; Embedded scheme code

(define (->string x)
  (call-with-port (open-output-string)
    (lambda (port)
      (display x port)
      (get-output-string port))))

(define render-string
  (case-lambda
    ((s) (render-string s (interaction-environment)))
    ((s env)
     (call-with-port (open-input-string s)
       (lambda (port)
         (render port env))))))

(define (render port env)
  ;; A few rough edges:
  ;; #, x will try to render x
  (define (burn-char)
    ;; Throw a character away.  I've defined this for clarity below.
    (read-char port))

  (let loop ((ch (read-char port))
             (acc '()))
    (define (proceed) (loop (read-char port) (cons ch acc)))
    (cond
     ((not ch)
      (loop (read-char port) acc))
     ((eof-object? ch)
      (list->string (reverse acc)))
     ((eq? ch #\#)                      ; special processing to come
      (case (peek-char port)
        ((#\\)                          ; inhibit processing of the next char
         (burn-char)
         (loop (read-char port) (cons ch acc)))
        ((#\,)                          ; scheme eval expansion
         (burn-char)
         (loop #f
               (append (let ((s (->string
                                 (eval (read port)
                                       env))))
                         (cond
                          ((equal? s "#<unspecified>") ; XXX NOT PORTABLE
                           '())
                          ((equal? s "#!eof") ; XXX NOT PORTABLE
                           '(#\, #\#))
                          (else (reverse (string->list s)))))
                       acc)))
        ((#\@)                          ; embedded sxml
         (burn-char)
         (loop #f
               (append (let ((h (eval `(sxml->html ,(list 'quasiquote
                                                          (read port)))
                                      env)))
                         (cond
                          ((equal? h "#!eof") ; XXX NOT PORTABLE
                           '(#\@ #\#))
                          (else (reverse (string->list h)))))
                       acc)))
        (else (proceed))))
     (else (proceed)))))

;;; Wrap paragraphs

(define (split-paragraphs str)
  (let loop ((lines (string-split str "\n"))
             (par '())
             (acc '()))
    (cond
     ((and (null? lines)                ; base case: no more lines
           (null? par))                 ; ... or pending paragraph
      (reverse acc))
     ((null? lines)                     ; add the final paragraph
      (loop '() '() (cons (apply string-append (reverse par)) acc)))
     ((equal? (car lines) "")           ; paragraph break
      (loop (cdr lines)
            '()
            (cons (apply string-append (reverse par)) acc)))
     (else                              ; line break
      (loop (cdr lines)
            (cons (string-append (car lines) "\n") par)
            acc)))))

(define (wrap-paragraphs str)
  (let loop ((pars (split-paragraphs str))
             (acc '()))
    (cond
     ((null? pars)
      (apply string-append (reverse acc)))
     ((zero? (string-length (car pars)))
      (loop (cdr pars)
            acc))
     ((eq? #\< (string-ref (string-trim (car pars)) 0))
      (loop (cdr pars)
            (cons (car pars)
                  acc)))
     (else
      (loop (cdr pars)
            (cons (string-append "<p>" (car pars) "</p>\n")
                  acc))))))