1

I guess I should have pre-faced this with: Yes, I know there is no need for a new templating language, but I want to make a new one anyway, because I'm a fool. That aside, how can I improve my language:

Let's start with an example:

using "html5"
using "extratags"

html {
    head {
        title "Ordering Notice"
        jsinclude "jquery.js"
    }
    body {
        h1 "Ordering Notice"
        p "Dear @name,"
        p "Thanks for placing your order with @company. It's scheduled to ship on {@ship_date|dateformat}."
        p "Here are the items you've ordered:"
        table {
            tr {
                th "name"
                th "price"
            }
            for(@item in @item_list) {
                tr {
                    td @item.name
                    td @item.price
                }
            }
        }
        if(@ordered_warranty)
            p "Your warranty information will be included in the packaging."
        p(class="footer") {
            "Sincerely," br @company
        }
    }
}

The "using" keyword indicates which tags to use. "html5" might include all the html5 standard tags, but your tags names wouldn't have to be based on their HTML counter-parts at all if you didn't want to.

The "extratags" library for example might add an extra tag, called "jsinclude" which gets replaced with something like <script type="text/javascript" src="@content"></script>

Tags can be optionally be followed by an opening brace. They will automatically be closed at the closing brace. If no brace is used, they will be closed after taking one element.

Variables are prefixed with the @ symbol. They may be used inside double-quoted strings. I think I'll use single-quotes to indicate "no variable substitution" like PHP does.

Filter functions can be applied to variables like @variable|filter. Arguments can be passed to the filter @variable|filter:@arg1,arg2="y"

Attributes can be passed to tags by including them in (), like p(class="classname").

You will also be able to include partial templates like:

for(@item in @item_list)
    include("item_partial", item=@item)

Something like that I'm thinking. The first argument will be the name of the template file, and subsequent ones will be named arguments where @item gets the variable name "item" inside that template. I also want to have a collection version like RoR has, so you don't even have to write the loop. Thoughts on this and exact syntax would be helpful :)

Some questions:

  • Which symbol should I use to prefix variables? @ (like Razor), $ (like PHP), or something else?
  • Should the @ symbol be necessary in "for" and "if" statements? It's kind of implied that those are variables.
  • Tags and controls (like if,for) presently have the exact same syntax. Should I do something to differentiate the two? If so, what?
    • This would make it more clear that the "tag" isn't behaving like just a normal tag that will get replaced with content, but controls the flow. Also, it would allow name-reuse.
    • Like tag would be a normal tag, but @tag would be a directive like @for, @if, @include, @using
  • Do you like the attribute syntax? (round brackets)
  • How should I do template inheritance/layouts?
    • In Django, the first line of the file has to include the layout file, and then you delimit blocks of code which get stuffed into that layout.
    • In CakePHP, it's kind of backwards, you specify the layout in the controller.view function, the layout gets a special $content_for_layout variable, and then the entire template gets stuffed into that, and you don't need to delimit any blocks of code.
    • I guess Django's is a little more powerful because you can have multiple code blocks, but it makes your templates more verbose... trying to decide what approach to take
  • Filtered variables inside quotes:
    • "xxx {@var|filter} yyy"
    • "xxx @{var|filter} yyy"
    • "xxx @var|filter yyy"
    • i.e, @ inside, @ outside, or no braces at all. I think no-braces might cause problems, especially when you try adding arguments, like @var|filter:arg="x", then the quotes would get confused. But perhaps a braceless version could work for when there are no quotes...? Still, which option for braces, first or second? I think the first one might be better because then we're consistent... the @ is always nudged up against the variable.

I'll add more questions in a few minutes, once I get some feedback.


Semi-colons:

I'm going to use semi-colons to close tags without content. For example, div; would output and wouldn't eat the next token like it normally would.

Namespaces:

Since I'll be defining the tags in straight C#, and C# supports namespaces, I think I'll just use that namespace and it'll work seamlessly. namespace.tagname, but it won't be necessary to include the namespace unless you want to disambiguate. The most recently included (via @using directive) will take precedence.


@Tom Anderson:

div {
    p "This is a paragraph"
    p "This is another paragraph"
}

span(class="error") "This is going to be bright red"

img(src="smiley.png", alt=":)")

p {
    "This " a(href="about-paragraphs.html") "paragraph" " contains a link."
}
mpen
  • 1,889

3 Answers3

4

First thought: "Oh no, not another one!" Second thought: "Well, at least that was a unique concept".

You make a "template" language that in fact isn't a template language at all, as it bears no relation to the output. It more strictly a HTML generating language.

The idea is interesting, mostly because it has a much cleaner syntax than HTML. But it has one major drawback: No HTML-editor would understand it. Therefore it is only useful for HTML that is more generated than templated. That's definitely an interesting concept, as HTML technology is going more and more towards having HTML only doing content, and designing either with CSS or XSLT or both.

But do you really need a language for this? Building HTML is possible with modules in most languages, and although the syntax would be different, the code would end up looking quite similar. It wouldn't be to difficult to do something similar with say lxml or BeautifulSoup in Python. Maybe it would be less clean, but it would also not require you to learn another language.

My preferred technique is still to gather all data in the programming language (python in my case) and have a rather stupid template language that just formats it into HTML. (Unfortunately I never get to use my favorite: Templess, so it remains a favorite in theory only.)

In any case I'd prefer using a more Pythonic syntax. It's even cleaner:

html:
  head:
    title: "Ordering Notice"
    jsinclude: "jquery.js"
  body:
    h1: "Ordering Notice"
    p: "Dear @name,"
    p: "Thanks for placing your order with @company. It's scheduled to ship on {@ship_date|dateformat}."
    p: "Here are the items you've ordered:"
    table:
        tr:
            th: "name"
            th: "price"
        for item in item_list:
            tr:
              th: item.name
              td: item.price
    if ordered_warranty:
        p: "Your warranty information will be included in the packaging."
        p(class="footer"):
          "Sincerely," br @company # I don't understand this line
1

As Lennart says, this isn't a templating language. Rather, it's a domain-specific programming language, with primitives (or perhaps library functions, i'm not clear how the tags are defined) for producing HTML.

I wrote something similar a while ago, which i have sadly lost. However, rather than defining a new language, i simply wrote some functions in Python. Really, i needed to write a translator that turned DTDs into Python, but that's another story. Anyway, i had functions named for each tag, which would return an object representing an element with that tag, and which would accept various arguments according to the content model of that element. Attributes were handled with keyword arguments. So, you could write:

div(
    p("This is a paragraph"),
    p("This is another paragraph - div takes varargs")
)

span("This is going to be bright red", class="error")

img(src="smiley.png", alt=":)")

p("This ", a("paragraph", href="about-paragraphs.html"), "contains a link."),

But you couldn't write any of:

img("throws an exception because img elements can't have content")

span(p("throws an exception because spans can't contain block-level elements"))

p(nosuch="throws an exception because there's no such attribute")

img(alt="throws an exception because it lacks the required src attribute")

The objects constructed were lightweight, but knew how to write themselves to a file-like object, or assemble themselves into a string.

Because this is just ordinary Python code, you have the full power of the language - a language which already exists, is well-developed, powerful, and widely known and documented - at your disposal. If you want a loop, you use for, or even better, a list comprehension or generator expression. If you want a conditional block, you use an if. If you want to store a fragment of HTML and use it later, use a variable. If you want to put the values of variables into strings, use %. If you want a metaclass which applies a decorator to produce a generator from a lambda, go crazy. You can't do template inheritance as such, but the existing facilities of the language let you make a function which passes its arguments into a template:

def standardPageContainer(pageTitle, pageText):
    return html(
        head(
            title(pageTitle)),
        body(
            h1(pageTitle),
            hr(),
            pageText))

And you're away.

I will bet pounds to pork pies that my approach will deliver a cleaner, more consistent, easier to use, and more powerful language than yours in under a tenth of the lines of code. I can make that bet because i'm cheating like a swine: i'm stealing everything the Python guys have done.

Anyway, the moral of the story is: kids, just say no to domain-specific languages.

Tom Anderson
  • 3,082
1

I like the syntax.

I don't like the idea of creating something just "because we can", without a precise need. But, well, it doesn't matter.

What matters is another thing I want to notice, which is sometimes forgotten by the people who create new languages, template systems, etc.:

  1. Do you intend to create tools to support this template language? I mean, when I develop an ASP.NET MVC website with Razor, I have a very helpful Visual Studio 2010 IntelliSense. If one day I need to use PHP (which is a template language), I can use Zend IDE and other helpful editors for it. If one day I start using your template language, do I have something better than notepad?

  2. Do you intend to provide help and support? Imagine one day I start using your language. And something wrong happens. What can I do?

You may say that it doesn't matter since your intent is not to create something to be used by hundreds of thousands of developers all around the world. But if you will one day use it in your websites, be aware that a website which uses some custom template language without tools or support can be maintained only by you.

This means that you will have problems if you are a professional developer and you use it inside your customers/your company website. This also means that you will have problems if one day some of your home-made websites will become famous or you will decide to invite somebody to work on your code.