Skip to content

Instantly share code, notes, and snippets.

@nulldatamap
Created July 23, 2014 00:47
Show Gist options
  • Select an option

  • Save nulldatamap/783c620453c2b3599666 to your computer and use it in GitHub Desktop.

Select an option

Save nulldatamap/783c620453c2b3599666 to your computer and use it in GitHub Desktop.

The Templator format

The Templator foramt is a format for generating source code files from a set of structured data. It's relatively flexible making it applicable for a large amount of different input data.

Contents:

  • API

  • Syntax

  • Semantics

  • Examples

API

render_template_file( f, values, outfile=None )

Takes a path to a template source file (f), the input data that should be applied to the template (values) and if specified will write the rendered template to outfile, otherwise to the original template file itself. Returns 0 on success and 1 on error. If an error occurs it will be printed to stdout.

render_template( src, values, keep_original=True )

Renders the given template source src with the given values. If keep_original is true the template part of the template file will be returned concatenated with the rendered template. Returns 0 on success and 1 on error. If an error occurs it will be printed to stdout.

Syntax

The Templator file consists of two segments, the definition segment, and the body segment. Both are enclosed with two lines only containing /* amd */ respectively. The syntax is defined in EBNF as:

SPACING := TAB | " " 

WHITESPACE := ( NEWLINE | SPACING )+ ;

NAME := -( WHITESPACE | ESCAPE | "${" | "%{" | "?{" | "}" | "." ) + ;

COMMENT := "#", (-NEWLINE)+, NEWLINE ;

ESCAPE = "$$" | "$/" |"$." | "$n" | "$%" ;

SUBTEMP_TAG := "%{", SPACEING*, NAME, ( "with" | "for", SPACING+, NAME )?, SPACING*, "}" ;

BODY_SEGMENT := ( SUBTEMP_TAG | ESCAPE | -"*/" )* ;

IF_TAG := "?{", SPACEING*, "if", SPACING+, "not"?, SPACING+, "first"|"last", ":", ( ESCAPE | (-NEWLINE) + ) ;

VAR_TAG := "${", SPACING*, NAME, SPACING*, "}" ;

TAG := VAR_TAG | IF_TAG | SUBTEMP_TAG ;

SUBTEMPLATE := NAME, NEWLINE, ( TAG | ESCAPE | -NEWLINE, NEWLINE )+, "." ;

DEFINITION_SEGMENT := ( WHITESPACE | SUBTEMPLATE | COMMENT )*, "@BODY" ;

TEMPLATE := "/*", NEWLINE, DEFINITION_SEGMENT, BODY_SEGMENT, "*/", ANYTHING ;

*Note that ANYTHING is not greedy, else this the syntax would be broken.

Semantics

In the defition segment subtemplates are defined. Subtemplates consists of thier name, then their body and a . to terminate them. The body is raw text as-is except for the tags inside.

Tags come in 3 flavours:

  • Variable tag: ${ variable_name } or ${}, which replaces itself with the giving variable if present in the current value. If the variable is empty ( ${} ) then the current topvalue is inserted instead of a subvalue of it.

  • If tag: ?{ if not last:raw text here} or ?{ if first:also raw text}, which if the condition is met, replaces itself with the text directly following the : and trailing the }. Current a condition can be either if first or if last, these can also be negated with if not last and if not first. Note that any condition with uses first or last is always false if it's note a part of an iterable.

  • Subtemplate tag: %{ foo }, %{ foo with var_bar } or %{ foo for var_bar }, which replaces itself with the result of the specified subtemplate being rendered. Passing the values that the subtemplate will be rendered can be done in 3 different ways:

    • %{ subtemp }, which just passes the current value along (on top-level that is always the data structure passed to the render_template function) to the given subtemplate.
    • %{ subtemp with val }, which passes val (which belongs the current value) on to the specified subtemplate.
    • %{ subtemp for vals }, which iterates over lists and tuples calling the specified subtemplate with each element. If the specified iterable value is a dict, the subtemplate will be called with a key value structure: { "key": ..., "value": ... }. If the specified value isn't iterable the program will fail with an error.

After the definition of subtemplates comes the body of the template. The body may contain subtemplate tags which are then resolved. This is done multiple time until the rendered body can't be resolved any further. This enables all kinds of weird recursion, though the recursion limit is a 100.

Escape codes

In order to avoid plain-text source code to clash with the syntax of Templator a set of escape codes are available:

$$$

$/*/

$..

$n\n(newline)

$%%

Examples

These examples are here to demonstrate the semantics of Templator.

#1:

/*
# This takes the top-value given, and resovle to the value displayed with
# indentation and a trailing comma and newline, but only if it's not the
# last element in the list.
list_stuff
  ${}?{ if not last:,$n}
.
@BODY
My list of stuff:
%{list_stuff}
*/

Given [ 1, 2, 3, 4 ] as it's data, it would resolve to:

My list of stuff:
  1,
  2,
  3,
  4

#2:

/*
# Here, we are going to list some more complex data.
list_developer
${name}:
  Job: ${job}
  Previous projects: %{list_projects for projects}
?{ if not last:$n}
.

# Here we basically do the same as in #1
list_projects
${}?{ if not last:, }
.

header
${studio}'s is working on their game ${game}.
The devlopment team is:
%{list_developer for developers}
.
@BODY
%{header}
They are hiring QAs though.
*/

Given the following data:

{ "studio": "Lioncloth Intertainment"
, "game": "Mogworld"
, "developers": [ { "name": "Simon Northbridge"
                  , "job": "Programmer"
                  , "projects": [ "Interstellar Bum Pirates" ] }
                
                , { "name": "Sasha Caldwell"
                  , "job": "Lead artist"
                  , "projects": [ "Skywards", "Call of Duty 39" ] }
                
                , { "name": "Don Sonderland"
                  , "job": "Lead programmer"
                  , "projects": [ "Interstellar Bum Pirates"
                                , "Project 11"
                                , "Bullet Madness 3"
                                , "Magizalius" ] } ] }
                  

Resolves to:

Lioncloth Intertainment's is working on their game Mogworld.
The devlopment team is:
Simon Northbridge:
  Job: Programmer
  Previous projects: Interstellar Bum Pirates

Sasha Caldwell:
  Job: Lead artist
  Previous projects: Skywards, Call of Duty 39

Don Sonderland:
  Job: Lead programmer
  Previous projects: Interstellar Bum Pirates, Project 11, Bullet Madness 3, Magizalius

They are hiring QAs though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment