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.
forloops 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.