(define (html-escape writer value) (define str-value (let ((out (open-output-string))) (writer value out) (get-output-string out))) (define out (open-output-string)) (string-for-each (lambda (char) (case char ((#\&) (write-string "&" out)) ((#\<) (write-string "<" out)) ((#\>) (write-string ">" out)) ((#\") (write-string """ out)) (else (write-char char out)))) str-value) (get-output-string out)) (define (lookup-in-stack-single name objs-stack lookup) (let loop ((objs objs-stack)) (if (null? objs) (values objs #f) (lookup (car objs) name (lambda (value) (values objs value)) (lambda () (loop (cdr objs))))))) (define (lookup-in-stack name-lst objs-stack lookup) (define-values (objs value) (lookup-in-stack-single (car name-lst) objs-stack lookup)) (cond ((not value) #f) ((null? (cdr name-lst)) value) (else (lookup-in-stack (cdr name-lst) (list value) lookup)))) (define (execute template objs-stack partials out lookup collection? collection-empty? collection-for-each writer) (define (execute-h template indent objs-stack) (for-each (lambda (fragment) (cond ((string? fragment) (write-string fragment out)) ((new-line? fragment) (begin (write-string (new-line-content fragment) out) (write-string (make-string indent #\space) out))) ((interp? fragment) (let* ((name (interp-ref fragment)) (value (if (equal? '(".") name) (car objs-stack) (lookup-in-stack name objs-stack lookup)))) (if (interp-escape? fragment) (write-string (html-escape writer value) out) (writer value out)))) ((section? fragment) (let ((value (lookup-in-stack (section-ref fragment) objs-stack lookup)) (inner-template (section-content fragment))) (cond ((not value) (when (section-invert? fragment) (execute-h inner-template indent objs-stack))) ((not (collection? value)) (unless (section-invert? fragment) (execute-h inner-template indent (cons value objs-stack)))) (else (if (section-invert? fragment) (when (collection-empty? value) (execute-h inner-template indent objs-stack)) (collection-for-each (lambda (el) (execute-h inner-template indent (cons el objs-stack))) value)))))) ((partial? fragment) (let () (define partial-tpl (cond ((assoc (partial-name fragment) partials) => cdr) (else #f))) (when partial-tpl (execute-h partial-tpl (+ indent (partial-indent fragment)) objs-stack) ))) (else (error "Unknown fragment")))) template)) (execute-h template 0 objs-stack))