(defun escape (s)
(or (and (or (number? s) (bool? s)) (string s))
(and (symbol? s) (escape (string s)))
(let ((out (vec)))
(dolist (x s)
(push out
(cond ((= x (chr "<")) "<")
((= x (chr ">")) ">")
((= x (chr "&")) "&")
(true x))))
(apply concat out))))
(defun escape-attr (s)
(or (and (or (number? s) (bool? s)) s)
(and (symbol? s) s)
(let ((out (vec)))
(dolist (x s)
(push out
(cond ((= x (chr "<")) "<")
((= x (chr ">")) ">")
((= x (chr "&")) "&")
((= x (chr "\"")) """)
((= x (chr "'")) "'")
(true x))))
(apply concat out))))
(defmacro html (&body b)
(let ((s (vec 'vec)))
(dolist (elem b)
(if (cons? elem)
(progn
(if (symbol? (car elem))
(let ((tag (string (car elem))))
(push s "<")
(push s tag)
(while (keyword? (cadr elem))
(push s " ")
(push s (keyword-name (cadr elem)))
(set elem (cdr elem))
(push s "=\"")
(if (cons? (cadr elem))
(push s (cadr elem))
(push s (escape-attr (cadr elem))))
(set elem (cdr elem))
(push s "\""))
(push s ">")
(push s `(html ,@(cdr elem)))
(push s "</")
(push s tag)
(push s ">"))
(push s elem)))
(if (symbol? elem)
(push s elem)
(push s (escape elem)))))
`(apply concat
,(apply (lambda (&rest r) r) s))))