urly

A toy template language

Curly is the small and fun template engine which was created with only one goal in mind: simplicity of implementation.

It should not be used anywhere in production (you may do it, of course, it is bug free and so on): there is a number of more convenient and smart choices: from Jinja2 or Mako if you are pythonista to Mustache and Handlebars if you are polyglot.

Curly is easily extendable to the state of Mustache and Handlebars (actually, to implement Handlebars, you need just a couple of hours including testing, coffee drinking, serfing Reddit or Hacker News): a completeness is not a goal. The highly documented source code in Python can be found on GitHub . Documentation is available on ReadTheDocs. The goal is to have a small but useful toy templating language which can be expressed in any language < 500 lines overall.

In real world, there are a number of different lexer and parser generators like yacc and lex. PLY is a good example how to do such things. The main intention is to give an example on how to do lexing and parsing of the text, converting into abstract syntax tree and calculating template with a context after. So, like a study. That's why I do not want to extend the language further: it will complicate lexing (well, lexing here cannot be complicated) and parsing.

Language Description

Like Mustache, the templating is done with special construction enclosed with curly braces. There are only 2 such constructions: print and block.

More or less everything in tag (except of function name) is an expression. In reference implementation expression is how to resolve certain context variables. It is possible to implement your own DSL here. Consider it as a homework. The main difference between Curly and other template languages that reference implementation is really liberal: if you misprint something, in most cases it will be considered as a literal text, syntax errors are really such errors where it is impossible to recover to literal text. If you open a tag like {% but forget to close it, no worries: it will be considered as a text.

Printing

To print variable x from the context, just put it in template like {{ x }}: this will do the trick. If you have a big nested context, it is totally fine to access variables with dot notation. Let's assume that user context variable is following:

{
    "first_name": "Sergey",
    "last_name": "Arkhipov",
    "roles": {
        "team": [
            {
                "id": "user",
                "read": true,
                "write": true
            },
            {
                "id": "execution",
                "read": true,
                "write": false
            }
        ]
    }
}

So, to access execution read permission, just use following template:

{{ user.roles.team.1.read }}

Block tags

Blocks are everything what has some content it has to process. Loops are looping through the lists, conditional output is depended on context input.

For example, if you want to print a work place only if available, do following:

{{ name }}{% if work %} ({{ work.job_title }} at {{ work.company_name }}){% /if %}

elifs and elses are also supported:

{% if work %}
    {{ work.job_title }}
{% elif study %}
    {{ university }}
{% else %}
    unemployed
{% /if %}

If you loop over dictionary (map, object, you name it), the item has key and value fields. In all other case item is literally the item of an iterable. To loop over some variable:

{% loop tags %}
    - {{ item }}
{% /loop %}