A function like Clojure's Hiccup - nested lists to HTML


#1

After seeing the recent WiFi demo I got thinking: has anybody written anything similar to Hiccup in Clojure, in uLisp? Using lists to represent HTML.

I’ve written a basic one in Chika:

(fn list->xml m
  (if (not (type m []))
    (return m))
  (do tag= (nth 0 m)
    (str \< tag \>
      (.. (map list->xml (sect m)))
      "</" tag \>)))

(list->xml
  ["body"
    ["h1" "Welcome"]
    ["p" "This is " ["i" "very"] " exciting."]])

And I’ve tried porting it to uLisp (my first uLisp function ever!):

(defun list->xml (m)
  (if (listp m)
    (let* ((m (second m))
           (tag (first m)))
      (concatenate 'string
        "<" tag ">"
        (apply #'concatenate 'string
          (mapcar list->xml (rest m)))
        "</" tag ">"))
    (princ-to-string m)))


(list->xml
  (quote '("body"
    '("h1" "Welcome")
    '("p" "This is " '("i" "very") " exciting."))))

Both return "<body><h1>Welcome</h1><p>This is <i>very</i> exciting.</p></body>".
Would there be a better way to do the uLisp version?
And Hiccup supports things like hash maps for attributes (e.g. {:class "bold"}), and I’m thinking perhaps that could be like '('("h1" "class" "bold") "Welcome") => "<h1 class="bold">Welcome</h1>".


#2

Nice example! But there seem to be some unnecessary quotes; it should be:

(defvar example
  '("body"
    ("h1" "Welcome")
    ("p" "This is " ("i" "very") " exciting.")))

Here’s a slightly simpler version of your function:

(defun list->xml (m)
  (cond
   ((atom m) m)
   (t (concatenate 'string
       "<" (first m) ">"
       (apply #'concatenate 'string (mapcar #'list->xml (cdr m)))
       "</" (first m) ">"))))

It works!

> (list->xml example)
"<body><h1>Welcome</h1><p>This is <i>very</i> exciting.</p></body>"

#3

Ahhhh, I was wondering why it was coming up with (quote ..) all the time. I haven’t got Common Lisp background and I’ve never played with homoiconicity but that makes sense 😅
Actually, what would be helpful is an example of a nested list in the tutorial, because I couldn’t find one :)


#4

You just need the one quote - nothing inside will get evaluated. Common Lisp has a more complicated (read: actually pretty hairy) syntax for a quote you can escape out of, but even then you rarely need to have multiple layers.

Incidentally, there are a number of different implementations of this idea in Common Lisp, with CL-WHO being a popular one. Because of the macro system, it produces most of the HTML string at compile time.


#5

Also, @johnsondavies, I haven’t tried it yet, but I think your corrected example would fall over if we include anything but strings. The idea of using nested lists isn’t just for asthetics but being able to include variables and other data, like '("p" "Number of visitors: " n-visitors). So, be sure to include the princ-to-string :)


#6

OK, good point - thanks!