;;; (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 "#") ; 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 "" (car pars) "
\n")
acc))))))