(define (list->html-text lst)
(let loop ((lst lst)
(res #f))
(cond
((null? lst) res)
(else
(let ((e (car lst)))
(loop (cdr lst)
(string-append
(if res res "")
(cond
((list? e) (list->html-text e))
((and (string? e) (not res)) e)
((string? e)
(string-append
(if (char=? (string-ref e 0) #\<) "" " ")
e))
(else (format (if res " ~a" "~a") e))))))))))
(define (escape str)
(let loop ((result "")
(last-start 0)
(cur 0))
(if (>= cur (string-length str))
(string-append result (substring str last-start cur))
(let* ((ch (string-ref str cur))
(entity (case ch
((#\&) "&")
((#\<) "<")
((#\>) ">")
((#\") """)
((#\') "'")
(else #f))))
(if entity
(loop (string-append
result
(substring str last-start cur)
entity)
(+ cur 1)
(+ cur 1))
(loop result last-start (+ cur 1)))))))
(define (tag name is-void escape-needed)
(lambda args
(let loop ((attrs "")
(content "")
(attr #f)
(args args))
(cond
((null? args)
(string-append
"<"name attrs
(if attr
(string-append "=\""(symbol->string attr)"\"")
"")
(if is-void
" />"
(string-append
">"
(if (zero? (string-length content))
""
(if escape-needed (escape content) content))
""name">"))))
((symbol? (car args))
(loop (string-append
attrs
(if attr
(string-append "=\""(symbol->string attr)"\" ")
" ")
(symbol->string (car args)))
content
(car args)
(cdr args)))
(attr
(loop (string-append
attrs"=\""
(escape
(let ((cattr (car args)))
(if (list? cattr)
(apply string-append cattr)
cattr)))
"\"")
content
#f
(cdr args)))
(is-void
(error "tag" (string-append "<"name attrs" />: That is void-tag, it can't have any children")))
(else
(loop attrs
(string-append
content
(if (and (not (string=? content ""))
(not (char=? #\> (string-ref content (- (string-length content) 1)))))
" "
"")
(list->html-text (list (car args))))
#f
(cdr args)))))))
(define-syntax html-tag
(lambda (stx)
(syntax-case stx ()
((_ name)
(let ((name* (datum->syntax #'name
(string->symbol
(string-append (symbol->string (syntax->datum #'name)) "*")))))
#`(begin
(define name (tag (symbol->string 'name) #f #f))
(define #,name* (tag (symbol->string 'name) #f #t))))))))
(define-syntax html-tags
(syntax-rules ()
((_ name ...)
(begin (html-tag name) ...))))
(define-syntax html-tags-void
(syntax-rules ()
((_ name ...)
(begin (define name (tag (symbol->string 'name) #t #f)) ...))))
(html-tags
html body head title style details summary marquee textarea
p a i b h1 h2 h3 h4 h5 h6 sup sub div span section ul li ol
main footer header nav canvas button form script noscript
table tr td th tbody caption q s pre code label center font)
(html-tags-void
link meta img input br hr wbr)
(define (comment . content)
(string-append
""))
;; those tags are mostly used without any attributes
(define br/ "
")
(define hr/ "