| 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.0list 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
false3+5*7/2below 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 stringnote: 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.
