Control structures enable dynamic content generation in templates by adding logical flow control, such as loops (for
) and conditionals (if
). These structures can be nested to create complex templates.
The for
loop iterates over arrays, maps, or other iterable objects.
{% for key in iterable %}
{{ key }}
{% endfor %}
Template:
{% for key in simple.strmap %}
Key: {{ key }}
{% endfor %}
Context Data:
{
"simple": {
"strmap": {
"key1": "value1",
"key2": "value2"
}
}
}
Output:
Key: key1
Key: key2
Template:
{% for item in products %}
Product: {{ item }}
{% endfor %}
Context Data:
{
"products": ["Coffee Maker", "Toaster"]
}
Output:
Product: Coffee Maker
Product: Toaster
The if
statement conditionally renders content based on data values.
{% if condition %}
Content to render if condition is true
{% endif %}
Template:
{% if simple.float %}
Float value is: {{ simple.float }}
{% endif %}
Context Data:
{
"simple": {
"float": 3.14
}
}
Output:
Float value is: 3.14
Template:
{% if !simple %}
false
{% else %}
!simple
{% endif %}
Context Data:
{
"simple": null
}
Output:
!simple
Control structures can be nested for complex logical flows.
Template:
{% for key in simple.strmap %}
{% if simple.float %}
{{ key }}: {{ simple.float }}
{% endif %}
{% endfor %}
Context Data:
{
"simple": {
"strmap": {
"key1": "value1",
"key2": "value2"
},
"float": 3.14
}
}
Output:
key1: 3.14
key2: 3.14
Template:
{% if simple.float %}
{% for key in simple.strmap %}
{{ key }}
{% endfor %}
{% endif %}
Context Data:
{
"simple": {
"strmap": {
"key1": "value1",
"key2": "value2"
},
"float": 3.14
}
}
Output:
key1
key2
Control structures support rich expression syntax, including:
- Numbers:
123
,3.14
- Strings:
"hello"
,'world'
- Booleans:
true
,false
- Variables:
user.name
,product.price
- Arithmetic:
+
,-
,*
,/
,%
- Comparison:
==
,!=
,<
,>
,<=
,>=
- Logical:
&&
,||
,!
{% if user.age >= 18 && user.verified %}
Adult and verified
{% endif %}
{% if price > 100 || quantity >= 5 %}
Eligible for discount
{% endif %}
{% if !(user.blocked) %}
User is not blocked
{% endif %}
Filters transform values within expressions:
{% if user.name|length > 0 %}
Username is not empty
{% endif %}
The template engine breaks expressions into tokens:
- Identifiers: Variable names, property paths
- Literals: Numbers, strings, booleans
- Operators: Arithmetic, comparison, logical
- Others: Parentheses, pipes, filters
The parser uses recursive descent to process expressions, following precedence rules:
Parse -> parseExpression
parseExpression -> parseLogicalOr
parseLogicalOr -> parseLogicalAnd ('||' parseLogicalAnd)
parseLogicalAnd -> parseComparison ('&&' parseComparison)
parseComparison -> parseAdditive (CompOp parseAdditive)(CompOp = '==' | '!=' | '<' | '>' | '<=' | '>=')
parseAdditive -> parseMultiplicative ([+-] parseMultiplicative)
parseMultiplicative -> parseUnary ([/%] parseUnary)
parseUnary -> ('!') parseUnary | parsePrimary
parsePrimary -> parseBasicPrimary ('|' FilterName)
parseBasicPrimary -> Number | String | Boolean | Variable | '(' parseExpression ')'
-
Expression Evaluation
- Evaluated at runtime with precedence rules.
- Short-circuit evaluation for
&&
and||
.
-
Error Handling
- Syntax errors during parsing.
- Runtime errors (e.g., type mismatches) during execution.
-
Variable Scope
- Outer scope variables are accessible within control structures.
for
loops create new scopes for iteration variables.
-
Type Safety
- Operators require matching operand types.
- No implicit type conversion.
-
Performance
- Expressions are parsed once and cached.
- Minimal memory allocation during evaluation.
{% if (price * quantity > 1000) && (user.level|upper == 'VIP') %}
Apply VIP discount
{% endif %}
{% if !(user.age < 18 || user.restricted) && user.verified %}
Access granted
{% endif %}
{% if product.name|trim|length > 0 %}
Product has valid name
{% endif %}
-
Proper Closing
- Always close control structures with
{% endfor %}
or{% endif %}
.
- Always close control structures with
-
Indentation
- Use consistent indentation for readability in nested structures.
-
Negation
- Use
!
for logical negation.
- Use
-
Variable Access
- Outer scope variables are accessible within loops and conditions.
-
Missing Data
- If a variable or property is missing, the template renders an empty string.