HTML: when's the last time anybody actually wrote it? I certainly didn't use it for this article, and why should my code be any different? My new lol.fm page needed a template where code and static data could mingle freely, so Scheme naturally came to mind. An s-expression→HTML DSL follows. Maybe it doesn't cover all cases called for in some spec somewhere. I don't care. I was able to whip it up in a few minutes with no external dependencies, which is a win in my book. It takes loose inspiration from Oleg Kiselyov's SXML format, where properties are presented in a list of key value pairs like so: (@ (k v) (k v) ...)
, but my implementation is from scratch and slightly more verbose in the name of simplicity. Makes use of Chicken's conc
builtin, which returns the string representation of all items in a heterogeneously-typed list.
(define ◇ conc)
(define (property key value)
(if (string? value)
(◇ " " key "=\"" value "\"")
(◇ " " key "=" value)))
(define-syntax ▽
(syntax-rules (! @)
((_ ! α (@ ) ω ...) (◇ ">" ω ... "</" (quote α) ">"))
((_ ! α (@ (β γ) δ ...) ω ...) (◇ (property (quote β) γ) (▽ ! α (@ δ ...) ω ...)))
((_ ! α (@ β δ ...) ω ...) (◇ (quote β) (▽ ! α (@ δ ...) ω ...)))
((_ α (@ β ...) ω ...) (◇ "<" (quote α) (▽ ! α (@ β ...) ω ...)))
((_ α ω ...) (◇ "<" (quote α) ">" ω ... "</" (quote α) ">"))))
That's it. Any HTML node can be represented with
(▽ tag (@ (property value) ...) children ...)
like
(▽ html
(▽ body
(▽ h1 (▽ a (@ (href "https://zombo.com/")) "Welcome"))))
The ▽
can obviously be factored out, but I found it nicer to implement this using recursive macro syntax rather than as a procedure over a plain symbolic list. The syntax-rules
cases that begin with !
are internal recursive patterns.
The ideal way, no, but my way. Done.