You're viewing all posts tagged with html

Html engine for arc

I was looking at the coffekup html engine, and as I was trying to use it, I realized something: it’s not really extensible. The same thing is true for Haml, and probably some other html DSLs out there.

While it’s interesting that:

.book
   .title = "Something"

compiles into:

<div class="book"><div class="title">Something</div></div>

It’s not awfully useful for large projects, and its only advantage is a nicer syntax.

The same goes for coffeekup. It’s certainly nice that one can write:

ul ->
   li "Item1"
   li "Item2"
   li "Item3"

But ultimately, I want to write something more like:

ulist "item1" "item2" "item3"

or, in s-expression syntax, I want to write:

(ulist "item1" "item2" "item3")

and have it automatically produce:

(ul (li "item1") (li "item2") (li "item3"))

In other words, I want to be able to define custom tags that blend in naturally with the rest of the syntax and don’t look any different; as if they were a natural part of the language:

 (div 'id "menu"
    (list "item1" "item2" "item3"))

The nice things about s-expressions is they’re perfect for building tree structures. But there’s a slight problem in that html trees are not pure trees of nodes only; each node can have attributes.

But I want to write trees with the least amount of ( parenthesis ) possible, so for a good syntax, I propose that symbols should be used to denote attributes, and then whatever follows the 'attr value pairs should be considered as child nodes; like this:

(node-name 'attr value 'attr value 'attr value node node node node)

There’s no ambiguity here, you know what are the attributes and what are the child nodes.

Once we have that, we’re gonna need a mechanism to define custom tags.

Something like mylist above could be defined like this:

(deftag mylist
    (ul (map li children)))

Here, children is implicit, it’s created by the deftag statement. deftag automatically parses the arguments passed to mylist and creates 'attrs and 'children variables. This means that custom tags can also take attributes and use them.

For example, mylist can specify a class for the ul element by using the 'class attribute passed to it:

(deftag mylist
    (ul 'class attr!class (map li children)))

This way, (mylist 'class "menu" "item1" "item2") is a short-hand for:

(ul 'class "menu" (li "item1") (li "item2))

To make this tolerant and robust, we can make nil attributes not count, so that (div 'class nil content) produces the same result as (div content)

These tags like (div ... stuff ..) don’t have to return strings; they can return tag objects. Then these tag objects can be traversed and the final html can be produced by printing to stdout, like the vanilla html.arc does.

I started working yesterday on implementing these ideas, and I already have a basic working version locally.

I can tell it:

arc> (render-html (div 'class "floating" (span "Piece of text") (p "Some paragraph")))
<div class="floating">
  <span>
    Piece of text
  </span>
  <p>
    Some paragraph
  </p>
</div>
nil

And I can define custom tags:

For example, a ul list:

(deftag items
      (e "ul" 'class attr!class
        (map [e "il" 'class attr!itemclass _] children)))

And usage:

arc> (render-html (items 'class "small_menu" 'itemclass "tiny_item" "item1" "item2" "item3"))
<ul class="small_menu">
  <il class="tiny_item">
    item1
  </il>
  <il class="tiny_item">
    item2
  </il>
  <il class="tiny_item">
    item3
  </il>
</ul>
nil

Notice how you can use arbitrary custom attributes.

And here’s a slightly more useful example:

(deftag csslink
    "takes a list of css files and creates tags to include them"
    (map [e "link" 'rel "stylesheet" 'type "text/css" 'href _] (flat children)))

And here it is in action:

arc> (render-html (csslink "s1.css" "s2.css" "s3.css"))
<link rel="stylesheet" type="text/css" href="s1.css">
</link>
<link rel="stylesheet" type="text/css" href="s2.css">
</link>
<link rel="stylesheet" type="text/css" href="s3.css">
</link>
nil

This is all good, but I wanted a way to make it templatize-able. What I mean is, I want to define a page template, that defines the general structure of the page, and put some placeholders in it, which can be populated later. Further more, it should be able to create new templates based on already existing ones.

And as I was thinking about how to do that, it hit me: this is already possible. Templates are nothing more than custom tags! The placeholders are the custom attributes! And since custom tags are based on other tags, then it’s already possible to create a template based on another template.

Here’s a little demonstration:

(deftag page
    (html
      (head (title attr!title)
            (apply jscript attr!js)
            (apply csslink attr!css))
      (body children)))

Here’s an example of how to use it:

(render-html 
     (page 'title "Html template" 
              'js '("first.js" "second.js") 
          (div "This is my content")))

And here’s the output:

<html>
  <head>
    <title>
      Html template
    </title>
    <script type="text/javascript" src="first.js">
    </script>
    <script type="text/javascript" src="second.js">
    </script>
  </head>
  <body>
    <div>
      This is my content
    </div>
  </body>
</html>

Here’s an example of a template based on the “page” template:

(deftag blogpage
    (page 'title "Blog post" 'js "blog.js" (div 'class "blogpost" children)))

(render-html (blogpage "This is my post"))

And the result, as expected:

<html>
  <head>
    <title>
      Blog post
    </title>
    <script type="text/javascript" src="blog.js">
    </script>
  </head>
  <body>
    <div class="blogpost">
      This is my post
    </div>
  </body>
</html>

This project is now 3 days old, I’m putting on github, and naming it “sehm”, as in S-Expression Html Markup.

https://github.com/hasenj/sehm