template — render a template from the command line.
template -f page.html -c ctx.json # render with a JSON context template -f page.html # empty context echo '{{ msg }}' | template -c ctx.json # template from stdin template -f page.html -c ctx.json -p views # partials dir for {% include %}
The context file must contain a JSON object; {% include 'name' %} resolves against --partials (every *.html in the dir, named by basename without the extension).
@ main → itemplate — an HTML template engine for NURL.
Jinja-flavoured syntax rendered against a stdlib Json context:
{{ user.name }} output, HTML-escaped by default {{ body | raw }} filters: raw / upper / lower / length / json {% if admin %} … {% elif x == 'y' %} … {% else %} … {% end %} {% for item in items %} {{ loop.index }}: {{ item.title }} {% end %} {% include 'partial' %} from a TplSet (tset_* below) {# comment #}
Expressions are a dotted path (user.name, rows.0.id, loop vars), a quoted string literal ('x' or "x"), a number, or true/false — optionally combined with == / != or prefixed with not. Inside a {% for %} body, loop.index (0-based), loop.first, loop.last and loop.length resolve from the innermost loop.
Missing keys render as empty and are falsy (mustache-style lenient); malformed tags are hard errors. Rendering is an interpreter over the source — a {% for %} body is re-scanned per item, so templates stay data, no AST to manage.
Surface: ( tpl_render src ctx ) → !String String one-shot render ( tset_new ) / ( tset_add t n s ) named-template set (enables include) ( tset_render t name ctx ) → !String String ( tpl_render_with t src ctx ) → !String String one-shot + includes ( tset_free t )
The error payload is an owned String: "template error at line L, col C: …". Known limits (v0.1): }}/%} inside a string literal ends the tag; error positions inside an included template are reported against the including template's source.
: TplSet: TplSet {
( Vec String ) names
( Vec String ) srcs
}
@ tset_new → *TplSet@ tset_add * TplSet t s name s tsrc → vRegister (or replace) a named template. Copies both arguments.
@ tset_has * TplSet t s name → b@ tset_free * TplSet t → v: TplR: TplR {
s src
i len
i pos
i setp // *TplSet as an integer; 0 = no set (include fails)
i depth // include nesting guard
i tag_a // {% elif %} expression span, handed to the if-handler
i tag_b
i e_end // one-past-end of the last parsed primary expression
b failed
i err_pos
String err
String out
String key // scratch: NUL-terminated path segment / include name
( Vec Json ) ctxv // [0] = root context (borrow)
( Vec String ) sc_names // loop-variable scope stack (owned names)
( Vec Json ) sc_vals // parallel values (borrows)
( Vec i ) lp_idx // loop frames, innermost last
( Vec i ) lp_len
i va_kind
b va_bool
f va_num
b va_raw
String va_str
( Vec Json ) va_node
i vb_kind
b vb_bool
f vb_num
String vb_str
( Vec Json ) vb_node
}
@ tpl_render s tsrc Json jctx → !String StringRender template source against a Json context. The error payload is an owned String ("template error at line L, col C: …").
@ tpl_render_with * TplSet t s tsrc Json jctx → !String StringSame, with a TplSet so {% include 'name' %} resolves.
@ tset_render * TplSet t s name Json jctx → !String StringRender a named member of the set.
loader — fill a TplSet from a directory of template files.
( tset_load_dir t dir ext ) loads every dir/*<ext> file (e.g. ext .html) into the set, named by its basename without the extension: views/index.html → index. Returns the number of templates loaded, or -1 when the directory glob fails. Individual unreadable files are skipped (counted out), not fatal.
Kept out of template.nu so the engine itself has no fs dependency.
@ tset_load_dir * TplSet t s dir s ext → i