diff options
Diffstat (limited to 'lib/schmaltz.sls')
-rw-r--r-- | lib/schmaltz.sls | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/lib/schmaltz.sls b/lib/schmaltz.sls new file mode 100644 index 0000000..623191f --- /dev/null +++ b/lib/schmaltz.sls | |||
@@ -0,0 +1,103 @@ | |||
1 | ;;; (boudin schmaltz) --- transform almost-html plus scheme into html | ||
2 | |||
3 | ;;; Embedded scheme code | ||
4 | |||
5 | (define (->string x) | ||
6 | (call-with-port (open-output-string) | ||
7 | (lambda (port) | ||
8 | (display x port) | ||
9 | (get-output-string port)))) | ||
10 | |||
11 | (define render-string | ||
12 | (case-lambda | ||
13 | ((s) (render-string s (interaction-environment))) | ||
14 | ((s env) | ||
15 | (call-with-port (open-input-string s) | ||
16 | (lambda (port) | ||
17 | (render port env)))))) | ||
18 | |||
19 | (define (render port env) | ||
20 | ;; A few rough edges: | ||
21 | ;; #, x will try to render x | ||
22 | (define (burn-char) | ||
23 | ;; Throw a character away. I've defined this for clarity below. | ||
24 | (read-char port)) | ||
25 | |||
26 | (let loop ((ch (read-char port)) | ||
27 | (acc '())) | ||
28 | (define (proceed) (loop (read-char port) (cons ch acc))) | ||
29 | (cond | ||
30 | ((not ch) | ||
31 | (loop (read-char port) acc)) | ||
32 | ((eof-object? ch) | ||
33 | (list->string (reverse acc))) | ||
34 | ((eq? ch #\#) ; special processing to come | ||
35 | (case (peek-char port) | ||
36 | ((#\\) ; inhibit processing of the next char | ||
37 | (burn-char) | ||
38 | (loop (read-char port) (cons ch acc))) | ||
39 | ((#\,) ; scheme eval expansion | ||
40 | (burn-char) | ||
41 | (loop #f | ||
42 | (append (let ((s (->string | ||
43 | (eval (read port) | ||
44 | env)))) | ||
45 | (cond | ||
46 | ((equal? s "#<unspecified>") ; XXX NOT PORTABLE | ||
47 | '()) | ||
48 | ((equal? s "#!eof") ; XXX NOT PORTABLE | ||
49 | '(#\, #\#)) | ||
50 | (else (reverse (string->list s))))) | ||
51 | acc))) | ||
52 | ((#\@) ; embedded sxml | ||
53 | (burn-char) | ||
54 | (loop #f | ||
55 | (append (let ((h (eval `(sxml->html ,(list 'quasiquote | ||
56 | (read port))) | ||
57 | env))) | ||
58 | (cond | ||
59 | ((equal? h "#!eof") ; XXX NOT PORTABLE | ||
60 | '(#\@ #\#)) | ||
61 | (else (reverse (string->list h))))) | ||
62 | acc))) | ||
63 | (else (proceed)))) | ||
64 | (else (proceed))))) | ||
65 | |||
66 | ;;; Wrap paragraphs | ||
67 | |||
68 | (define (split-paragraphs str) | ||
69 | (let loop ((lines (string-split str "\n")) | ||
70 | (par '()) | ||
71 | (acc '())) | ||
72 | (cond | ||
73 | ((and (null? lines) ; base case: no more lines | ||
74 | (null? par)) ; ... or pending paragraph | ||
75 | (reverse acc)) | ||
76 | ((null? lines) ; add the final paragraph | ||
77 | (loop '() '() (cons (apply string-append (reverse par)) acc))) | ||
78 | ((equal? (car lines) "") ; paragraph break | ||
79 | (loop (cdr lines) | ||
80 | '() | ||
81 | (cons (apply string-append (reverse par)) acc))) | ||
82 | (else ; line break | ||
83 | (loop (cdr lines) | ||
84 | (cons (string-append (car lines) "\n") par) | ||
85 | acc))))) | ||
86 | |||
87 | (define (wrap-paragraphs str) | ||
88 | (let loop ((pars (split-paragraphs str)) | ||
89 | (acc '())) | ||
90 | (cond | ||
91 | ((null? pars) | ||
92 | (apply string-append (reverse acc))) | ||
93 | ((zero? (string-length (car pars))) | ||
94 | (loop (cdr pars) | ||
95 | acc)) | ||
96 | ((eq? #\< (string-ref (string-trim (car pars)) 0)) | ||
97 | (loop (cdr pars) | ||
98 | (cons (car pars) | ||
99 | acc))) | ||
100 | (else | ||
101 | (loop (cdr pars) | ||
102 | (cons (string-append "<p>" (car pars) "</p>\n") | ||
103 | acc)))))) | ||