build | statistics | meta | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
- simple c++ example
- jinja template examples
- json
- unicode
- expressions
- statements
- filter
- tests
- introspection
- custom procs
in the following example a datastructure is created and put into the engine's context. the subsequent call of templ_render
is given the created datastructure to render the string template.
#include "templ.cpp"
int
main(int argc, char **argv) {
using namespace templ::api;
templ_init(MB(100), MB(100), MB(100));
Templ_Vars vars = templ_vars();
Templ_Var *name = templ_var("name", "noob");
templ_vars_add(&vars, name);
Templ *templ = templ_compile_string("hello {{ name }}");
char *result = templ_render(templ, &vars);
os_file_write("test.html", result, utf8_strlen(result));
if ( status_is_error() ) {
for ( int i = 0; i < status_num_errors(); ++i ) {
Status *error = status_error_get(i);
fprintf(stderr, "%s in %s line %lld\n", status_message(error),
status_filename(error), status_line(error));
}
for ( int i = 0; i < status_num_warnings(); ++i ) {
Status *warning = status_warning_get(i);
fprintf(stderr, "%s in %s line %lld\n", status_message(warning),
status_filename(warning), status_line(warning));
}
status_reset();
}
templ_reset();
return 0;
}
data folder contains a couple jinja templates with statements that are supported by the implementation so far.
{% extends "template.tpl" if true %}
{% block title %}
main - {{ default_title }}
{% endblock %}
{% block main %}
{{ super() }}
{% include "literals.tpl" without context %}
{% include "exprs.tpl" with context %}
{% include "stmts.tpl" without context %}
{% include "utf8.tpl" without context %}
{% include "filter.tpl" with context %}
{% include "tests.tpl" without context %}
{% include "macros.tpl" without context %}
{% endblock main %}
{% block custom %}
<div>custom content</div>
{% endblock %}
there's a simple, and built-in support for json which is implemented in the
src/json.cpp. the
json_parse
method, will parse a given json string, and return a Json
structure.
Json
structure can be fed to templ_var
method and get a Templ_Var *
instance in return, which can
be used in templ_render
context.
Json json = json_parse(R"foo([
{
"name": "noob",
"age" : "25",
"address": {
"city": "frankfurt",
"street": "siegerstr. 2"
}
},
{
"name": "reinhold",
"age" : "23",
"address": {
"city": "leipzig",
"street": "mozartstr. 20"
}
}
])foo");
Templ *templ = templ_compile_string("{{ users[0].name }}: {{ users[0].address.city }} -- {{ users[1].name }}: {{ users[1].address.city }}");
Templ_Var *users = templ_var("users", json);
Templ_Vars vars = templ_vars();
templ_vars_add(&vars, users);
char *result = templ_render(templ, &vars);
templ
supports unicode with the utf-8 encoding for string literals as well as names. be aware though that right now only limited amount of transformation in filters is supported.
below is a list of character ranges which have lower-/uppercase conversion support.
ABCDEFGHIJKLMNOPQRSTUVWXYZÄÜÖẞ
АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ
ÆÅÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮİIJĴĶŸ
ŁŃŅŇ
abcdefghijklmnopqrstuvwxyzäüöß
абвгдеёжзийклмнопрстуфхцчшщъыьэюя
æåçèéêëìíîïðñòóôõöøùúûüýþāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıijĵķÿ
łńņň
characters that are not supported will be printed back as they are.
table with all character blocks.
{% set シ个 = "原ラ掘聞" %}
{{ シ个 }}
{% set приветствие = "здравствуйте" %}
{{ приветствие }}
{{ "🤩✨🥰" * 10 }}
原ラ掘聞
здравствуйте
🤩✨🥰🤩✨🥰🤩✨🥰🤩✨🥰🤩✨🥰🤩✨🥰🤩✨🥰🤩✨🥰🤩✨🥰🤩✨🥰
below is a list of expressions that are supported right now
string literals are supported with quotation marks as well as with apostrophe.
"string literal"
'also string literal'
integer and floating point numbers are supported.
42
42.0
list literals start with an opening bracket and contain a comma separated list of elements, which in turn have to be a valid jinja expression.
['europe', 'asia', 'australia']
lists can be assigned as a value to a variable and be used in a for
statement as both, literals and variables.
{% for it in ['europe', 'asia', 'australia'] %}
{{ it }}
{% endfor %}
{% set continents = ['europe', 'asia', 'australia'] %}
{% for it in continents %}
{{ it }}
{% endfor %}
tuple are basically lists with the exception of being read-only.
('x', 'y')
dictionaries are supported as expressions in assignments and in
expression in for
loops.
{% set d = {'name': 'adam', 'age': '30'} %}
{% for it in {'name': 'eve', 'age': '25'} %}
...
{% endfor %}
boolean values
true
false
3+5*7/2
below is a list of supported math operations
+
-
*
**
/
//
%
list of supported statements
blocks are supported with an optional name
in the endblock
statement. as the inheritance of templates is also supported, parent block's content can be overwritten entirely, or be included alongside your own content with the super()
method.
{% block <name> %}
{% endblock <name> %}
{% do <expression> %}
{% extends "<template>" <if expr> %}
{% filter <name1> | <name2> %}
<anweisungen>
{% endfilter %}
for
statement supports multiple return values, else
branch, almost all loop
variables, break
and continue
statements.
{% for <iterator> in <menge> %}
<anweisungen>
{% else %}
<anweisungen>
{% endfor %}
the following loop variables can be used inside a for
loop:
- loop.index
- loop.index0
- loop.revindex
- loop.revindex0
- loop.first
- loop.last
- loop.length
- loop.cycle
- loop.depth
- loop()
flow control statement if
is supported with elif
and else
branches.
{% if <condition> %}
<statements>
{% elif <condition> %}
<statements>
{% else %}
<statements>
{% endif %}
you can use any valid jinja and supported expressions as condition that have a boolean value as result.
true
false
1 < 2
a is eq "foo"
firstname == "arminius" and lastname == "der cherusker"
{% import "<template>" as <sym> %}
{% from "<template>" import <sym1> as <alias1> %}
additional templates can be included into a template. include
statement supports if
expression, and the additional annotations with context
, without context
, ignore missing
.
{% include "<template>" <if ausdruck> %}
{% macro <name>(parameter, ...) %}
{% endmarcro %}
{% raw %}
{% endraw %}
{% set <lvalue expression> = <rvalue expression> %}
{% set <lvalue expression> %}
<statements>
{% endset %}
ongoing process of implementing the vast amount of filters. the following filters are implemented in dev:
- abs
- attr
- batch
- capitalize
- center
- default
- dictsort
- escape
- filesizeformat
- first
- float
- format
- lower
- max
- min
- reject
- rejectattr
- reverse
- select
- selectattr
- slice
- sum
- truncate
- upper
most of the tests present in the jinja2 spec are already implemented in dev.
- callable
- defined
- devisibleby
- equal
- even
- ge
- gt
- in
- iterable
- le
- lt
- mapping
- ne
- none
- number
- odd
- sameas
- sequence
- string
- undefined
lightweight introspection is built in. for it to work you have to provide a meta json file, which describes the data layout of the given raw pointer.
in the example below the User
struct
struct Address {
char *city;
};
struct User {
int age;
char *name;
Address address;
};
is described with the following json
[
{
"name" : "age",
"offset": 0,
"kind" : 1
},
{
"name" : "name",
"offset": 8,
"kind" : 0
},
{
"name" : "address",
"offset": 16,
"kind" : 5,
"format": [{
"name" : "city",
"offset": 0,
"kind" : 0
}]
}
]
to use the c++ data in template you first have to create a Templ_Var *
instance, which can be done as follows:
User user = { 20, "alex", { "paris" } };
Templ_Var *user = templ_var("user", &user, json_parse(json_format_string));
...
the kind
field in the meta json file has to be the int value from the Json_Node_Kind
enum
enum Json_Node_Kind {
JSON_STR,
JSON_INT,
JSON_FLOAT,
JSON_BOOL,
JSON_ARRAY,
JSON_OBJECT,
JSON_NULL,
};
templ supports registering of custom procedures which then can be executed in a template. you can register three types of procedures.
global procs are standalone and not bound to any context. they can be used everywhere you can use builtin procs as well.
PROC_CALLBACK(custom_hello) {
using namespace templ::devapi;
return val_str("hello, world");
}
int main() {
using namespace templ::api;
using namespace templ::devapi;
templ_init(MB(100), MB(100), MB(100));
templ_register_proc("hello", custom_hello, 0, 0, type_str);
...
}
and then later in the template:
{{ hello() }}
{% set var = hello() }}
you can also register procedures that are bound to a certain type. for that the following api calls can be used, that
you can import separately by using namespace templ::devapi
:
templ::templ_register_any_proc; // register procedure for every datatype
templ::templ_register_seq_proc; // register procedure for sequence datatypes only
templ::templ_register_num_proc; // register procedure for numeric datatypes only
templ::templ_register_bool_proc; // register procedure for bool
templ::templ_register_dict_proc; // register procedure for dictionary
templ::templ_register_float_proc; // register procedure for float
templ::templ_register_int_proc; // register procedure for int
templ::templ_register_range_proc; // register procedure for range
templ::templ_register_list_proc; // register procedure for list
templ::templ_register_string_proc; // register procedure for string
note: all type procs can also be used as filters on that type, so {{ "abc".my_custom_proc() }}
is the same as {{ "abc" | my_custom_proc }}
tester are used in the is
expression as a procedure to test against a given value, and have to evaluate to bool.