# Template Engine Tour
This is a comprehensive guide to the Snippy template engine syntax. The template engine provides powerful text generation capabilities with variable substitution, conditionals, loops, expressions, and more.
## Quick Start
Snippy templates are plain text files with special syntax for dynamic content. The engine processes templates and produces final output by evaluating expressions and statements.
### Basic Syntax Overview
The template engine uses three types of delimiters:
- `{{ expression }}` - **Expressions**: Output values, variables, and calculations
- `{% statement %}` - **Statements**: Control flow, variable assignment, and commands
- `{# comment #}` - **Comments**: Documentation that doesn't appear in output
## 1. Plain Text and Comments
Templates can contain regular text that passes through unchanged:
```
Hello World!
This is plain text.
```
Comments are ignored during processing:
```
{# This is a comment #}
Text before comment
{# Another comment
spanning multiple lines #}
Text after comment
```
**Output:**
```
Text before comment
Text after comment
```
## 2. Variables and Expressions
### Variable Output
Use double braces to output variable values:
```
Hello {{ name }}!
Welcome to {{ place }}.
```
With variables `name="Alice"` and `place="Wonderland"`:
```
Hello Alice!
Welcome to Wonderland.
```
### Setting Variables
Use the `{% set %}` statement to define variables:
```
{% set name "John" %}
{% set greeting "Hello" %}
{{ greeting }} {{ name }}!
```
**Output:**
```
Hello John!
```
### Dot Notation for Namespaced Variables
Variables can have hierarchical names using dots:
```
{% set config.database.host "localhost" %}
{% set config.database.port "5432" %}
Database: {{ config.database.host }}:{{ config.database.port }}
```
**Output:**
```
Database: localhost:5432
```
### Clearing Variables
Use the `{% clear %}` statement to remove variables from memory:
```
{% set message "Hello World" %}
{% set items.first "apple" %}
{% set items.second "banana" %}
Before clear: {{ message }}
Items: {{ items.first }}, {{ items.second }}
{% clear message %}
{% clear items %}
After clear: {{ message }}
Items: {{ items.first }}, {{ items.second }}
```
**Output:**
```
Before clear: Hello World
Items: apple, banana
After clear:
Items: ,
```
The clear statement can:
- Clear individual variables: `{% clear variable_name %}`
- Clear entire namespaces: `{% clear namespace %}` removes all variables starting with `namespace.`
- Safely handle non-existent variables (no error if the variable doesn't exist)
### Capture Statement
The `{% capture %}` statement allows you to capture template output within a specific scope and store it in a variable. This is useful for generating complex content that you want to reuse or process further.
```
{% set name "Alice" %}
{% set count 3 %}
{% capture greeting_message %}
Hello {{ name }}! You have {{ count }} new messages.
{% if count > 1 %}
That's quite a few!
{% endif %}
{% endcapture %}
Generated message: {{ greeting_message }}
{% set formatted_greeting "Message: " + greeting_message %}
Final output: {{ formatted_greeting }}
```
**Output:**
```
Generated message: Hello Alice! You have 3 new messages.
That's quite a few!
Final output: Message: Hello Alice! You have 3 new messages.
That's quite a few!
```
The capture statement:
- Captures all output (text, variables, expressions) within its scope
- Stores the captured content in the specified variable
- Can contain complex logic including conditionals, loops, and nested expressions
- Preserves formatting and newlines from the captured content
- Can be nested within other statements for advanced template composition
## 3. Arithmetic and Expressions
### Basic Arithmetic
The template engine supports standard mathematical operations with both integers and floating-point numbers:
```
Result: {{ 5 + 3 }}
Product: {{ 4 * 7 }}
Division: {{ 15 / 3 }}
Subtraction: {{ 10 - 2 }}
```
**Output:**
```
Result: 8
Product: 28
Division: 5
Subtraction: 8
```
### Float Arithmetic
Floating-point numbers are fully supported in calculations:
```
Float result: {{ 5.5 + 2.3 }}
Mixed: {{ 10 + 3.14 }}
Division: {{ 22.0 / 7.0 }}
Precision: {{ 1.5 * 2.0 }}
```
**Output:**
```
Float result: 7.8
Mixed: 13.14
Division: 3.14286
Precision: 3.0
```
### Order of Operations and Parentheses
Mathematical precedence is respected, and parentheses can override it:
```
Order of operations: {{ 2 + 3 * 4 }}
With parentheses: {{ (2 + 3) * 4 }}
Complex: {{ ((1 + 2) * 3) + 4 }}
Mixed operations: {{ (10 / 2) + (3 * 4) }}
```
**Output:**
```
Order of operations: 14
With parentheses: 20
Complex: 13
Mixed operations: 17
```
### Type Casting
Convert between numeric types using `int()`:
```
{% set value 42.7 %}
Float value: {{ value }}
Integer value: {{ int(value) }}
Complex casting: {{ int((arg.a ?? 1.0) * 255) }}
```
**Output:**
```
Float value: 42.7
Integer value: 42
Complex casting: 255
```
## 4. String Operations
### String Concatenation
Use the `~` operator for string concatenation (different from arithmetic `+`):
```
{% set first "Hello" %}
{% set second "World" %}
{{ first ~ " " ~ second }}
{{ "Literal" ~ " " ~ "concatenation" }}
```
**Output:**
```
Hello World
Literal concatenation
```
**String Concatenation vs Addition:**
```
{% set str1 "Hello" %}
{% set str2 "World" %}
Concatenation: {{ str1 ~ str2 }}
Addition: {{ str1 + str2 }}
```
**Output:**
```
Concatenation: HelloWorld
Addition: HelloWorld
```
Both `~` and `+` work for string concatenation, but `~` is explicitly for strings while `+` also handles arithmetic. Use `~` when you want to be explicit about string concatenation.
**Complex Concatenation:**
```
{{ arg0 ~ " " ~ arg1 ~ " " ~ arg2 }}
{{ "Result: " ~ (5 + 3) ~ " items" }}
```
### String Literals
Strings can be defined with double quotes:
```
{{ "This is a string literal" }}
{{ "Strings can contain spaces and symbols!" }}
```
**Output:**
```
This is a string literal
Strings can contain spaces and symbols!
```
## 5. Null Coalescing
Use `??` to provide fallback values for undefined variables:
```
Default: {{ missing ?? "fallback value" }}
Found: {{ name ?? "no name" }}
```
With variable `name="Alice"` (and `missing` undefined):
```
Default: fallback value
Found: Alice
```
Complex null coalescing with expressions:
```
Complex: {{ (arg.a ?? 1.0) * 255 }}
```
## 6. Ternary Operator
Use the ternary operator `condition ? true_value : false_value` for conditional expressions:
```
{% set status "active" %}
{% set name "World" %}
{% set priority "" %}
Status check: {{ status ? "enabled" : "disabled" }}
Greeting: {{ name ? "Hello " ~ name : "Anonymous user" }}
Priority level: {{ priority ? priority : "normal" }}
```
**Output:**
```
Status check: enabled
Greeting: Hello World
Priority level: normal
```
### Ternary with Filters
Combine ternary operators with filters for powerful conditional transformations:
```
{% set name "world" %}
{% set status "active" %}
{% set missing "" %}
With filters on true branch: {{ name ? name|upper : "unknown" }}
With filters on false branch: {{ missing ? "found" : "not found"|upper }}
With filters on both branches: {{ status ? "active"|upper : "inactive"|lower }}
```
**Output:**
```
With filters on true branch: WORLD
With filters on false branch: NOT FOUND
With filters on both branches: ACTIVE
```
### Nested Ternary Expressions
Ternary operators can be nested for complex conditional logic:
```
{% set priority "high" %}
{% set level "medium" %}
Nested ternary: {{ priority ? priority|upper : level ? level|lower : "none" }}
```
**Output:**
```
Nested ternary: HIGH
```
## 7. Filters and Pipes
Apply transformations to values using the pipe operator `|`:
```
{% set name "hello world" %}
Uppercase: {{ name | upper }}
Lowercase: {{ name | lower }}
Chain filters: {{ "MIXED Case" | lower | upper }}
```
**Output:**
```
Uppercase: HELLO WORLD
Lowercase: hello world
Chain filters: MIXED CASE
```
Available filters:
- `upper` - Convert to uppercase
- `lower` - Convert to lowercase
- `escape` - Convert special characters to escaped quoted string
- `quote` - Wrap in quotes and escape internal quotes/backslashes
- `join` - Join list elements with a separator
### Escape Filter
The `escape` filter converts strings with special characters into quoted strings with proper escape sequences:
```
{% set message "Line 1
Line 2" %}
{% set tab_text "before after" %}
{% set quote_text "She said \"hello\"" %}
Escaped newline: {{ message | escape }}
Escaped tab: {{ tab_text | escape }}
Escaped quotes: {{ quote_text | escape }}
```
**Output:**
```
Escaped newline: "Line 1\nLine 2"
Escaped tab: "before\tafter"
Escaped quotes: "She said \"hello\""
```
The escape filter handles:
- `\n` - Newline characters
- `\t` - Tab characters
- `\r` - Carriage return characters
- `\"` - Double quote characters
- `\\` - Backslash characters
- `\0` - Null characters
The output is always wrapped in double quotes, making it suitable for generating code or configuration files.
### Quote Filter
The `quote` filter wraps strings in double quotes and escapes internal quotes and backslashes:
```
{% set simple "test" %}
{% set with_quote "test\"test" %}
{% set with_backslash "test\\test" %}
Simple: {{ simple | quote }}
With quote: {{ with_quote | quote }}
With backslash: {{ with_backslash | quote }}
Literal: {{ "hello" | quote }}
```
**Output:**
```
Simple: "test"
With quote: "test\"test"
With backslash: "test\\test"
Literal: "hello"
```
### Join Filter
The `join` filter combines list elements into a single string with a specified separator:
```
{% set keys ["KEY_A", "KEY_B", "KEY_X"] %}
Combined: {{ keys | join(" | ") }}
Comma separated: {{ keys | join(", ") }}
No separator: {{ keys | join("") }}
```
**Output:**
```
Combined: KEY_A | KEY_B | KEY_X
Comma separated: KEY_A, KEY_B, KEY_X
No separator: KEY_AKEY_BKEY_X
```
**Filter Chaining:**
Filters can be chained and work with string literals, variables, or complex expressions:
```
{% set name "John" %}
{% set greeting "Hello" %}
Basic variable: {{ name | upper }}
Chained filters: {{ "MiXeD" | lower | upper }}
Expression with filter: {{ (greeting ~ " World") | lower }}
Multiple chains: {{ "test" | upper | lower | upper }}
```
**Output:**
```
Basic variable: JOHN
Chained filters: MIXED
Expression with filter: hello world
Multiple chains: TEST
```
## 8. Conditional Logic
### Basic Conditionals
Use `{% if %}` statements for conditional content:
```
{% if enabled %}
Feature is enabled!
{% endif %}
{% if disabled %}
This won't show
{% endif %}
```
With variable `enabled="true"` (and `disabled` undefined):
```
Feature is enabled!
```
### If-Elif-Else Chains
Complex conditional logic with multiple branches:
```
{% if var1 %}
Variable 1 is set
{% elif var2 %}
Variable 2 is set
{% else %}
No variables set
{% end %}
```
Variables are considered "truthy" if they exist and have a non-empty value.
### Comparison Operators
Use comparison operators to create more sophisticated conditions:
```
{% if status == "active" %}
User is active
{% endif %}
{% if count != 0 %}
Has items
{% endif %}
{% if priority > 5 %}
High priority
{% endif %}
{% if score >= 90 %}
Excellent score
{% endif %}
{% if temperature < 32 %}
Below freezing
{% endif %}
{% if age <= 18 %}
Minor
{% endif %}
```
Available comparison operators:
- `==` - Equal to
- `!=` - Not equal to
- `>` - Greater than
- `>=` - Greater than or equal to
- `<` - Less than
- `<=` - Less than or equal to
Comparisons work with both numbers and strings:
```
{% if version == "2.0" %}
Version 2.0
{% endif %}
{% if count > 10 %}
Many items
{% endif %}
```
Numeric comparisons are performed when both sides are numbers, otherwise string comparison is used.
### Logical Operators
Combine multiple conditions using logical operators:
#### AND Operator
Use `and` to require all conditions to be true:
```
{% set enabled "true" %}
{% set valid "true" %}
{% set status "active" %}
{% if enabled and valid %}
Both enabled and valid
{% endif %}
{% if enabled and valid and status %}
All three conditions are true
{% endif %}
```
**Output:**
```
Both enabled and valid
All three conditions are true
```
#### OR Operator
Use `or` when any condition being true is sufficient:
```
{% set admin "false" %}
{% set moderator "true" %}
{% set owner "" %}
{% if admin or moderator %}
User has privileges
{% endif %}
{% if admin or moderator or owner %}
User has some level of access
{% endif %}
```
**Output:**
```
User has privileges
User has some level of access
```
#### Combining AND and OR
Use parentheses to group logical operations:
```
{% set user_active "true" %}
{% set is_admin "false" %}
{% set is_moderator "true" %}
{% set has_permission "true" %}
{% if user_active and (is_admin or is_moderator) %}
Active user with elevated privileges
{% endif %}
{% if (is_admin or is_moderator) and has_permission %}
Privileged user with permissions
{% endif %}
```
**Output:**
```
Active user with elevated privileges
Privileged user with permissions
```
#### Complex Logical Expressions
Logical operators work with any expressions, including comparisons:
```
{% set score 85 %}
{% set bonus_points 10 %}
{% set is_member "true" %}
{% if (score > 80) and (bonus_points > 5) %}
High score with bonus
{% endif %}
{% if (score < 60) or (is_member and bonus_points > 0) %}
Either low score or member with bonus
{% endif %}
```
**Output:**
```
High score with bonus
Either low score or member with bonus
```
Logical operators follow standard precedence:
- `and` has higher precedence than `or`
- Use parentheses to override precedence when needed
- Both operators are left-associative (evaluated left to right)
### Comparison with Expressions
Comparison operators can be combined with arithmetic and other expressions:
```
{% if (count + 1) == 6 %}
Count plus one equals six
{% endif %}
{% if items.length > 0 %}
Has items
{% endif %}
{% if (value * 2) <= 100 %}
Doubled value is 100 or less
{% endif %}
```
### Conditional Syntax Variations
Both `{% endif %}` and `{% end %}` can close conditional blocks:
```
{% if condition %}
Content here
{% end %}
{% if another_condition %}
More content
{% endif %}
```
## 9. Loops
### Basic Loops
Repeat content a specific number of times:
```
{% loop 3 %}
Hello World!
{% end %}
```
**Output:**
```
Hello World!
Hello World!
Hello World!
```
### Variable-Driven Loops
Use variables to control loop count:
```
{% set iterations "5" %}
{% loop iterations %}
Iteration
{% end %}
```
### Foreach Loops
Iterate over namespaced variables:
```
{% set items.one "first" %}
{% set items.two "second" %}
{% set items.three "third" %}
{% foreach items %}
Key: {{ key }}, Value: {{ value }}
{% end %}
```
**Output:**
```
Key: one, Value: first
Key: two, Value: second
Key: three, Value: third
```
In `{% foreach %}` loops:
- `{{ key }}` contains the variable suffix (after the dot)
- `{{ value }}` contains the variable's value
### Nested Foreach
Handle multiple namespaced variable sets:
```
{% set users.alice "admin" %}
{% set users.bob "user" %}
{% set products.laptop "$999" %}
{% set products.mouse "$25" %}
Users:
{% foreach users %}
{{ key }}: {{ value }}
{% end %}
Products:
{% foreach products %}
{{ key }}: ${{ value }}
{% end %}
```
## 10. Lists
### Basic List Creation
Create ordered lists using square bracket syntax:
```
{% set fruits ["apple", "banana", "cherry"] %}
First fruit: {{ fruits.0 }}
Second fruit: {{ fruits.1 }}
Third fruit: {{ fruits.2 }}
```
**Output:**
```
First fruit: apple
Second fruit: banana
Third fruit: cherry
```
Lists are zero-indexed, so the first element is accessed with `.0`, second with `.1`, etc.
### Mixed Data Types in Lists
Lists can contain different types of values including variables, numbers, and strings:
```
{% set mixed [var0, 1, 2, "string"] %}
Index 0: {{ mixed.0 }}
Index 1: {{ mixed.1 }}
Index 2: {{ mixed.2 }}
Index 3: {{ mixed.3 }}
```
With variable `var0="default_value"`:
```
Index 0: default_value
Index 1: 1
Index 2: 2
Index 3: string
```
### Lists with Expressions
List elements can be complex expressions:
```
{% set numbers [1, 2, 3, 4, 5] %}
{% set expressions [1 + 2, 4 * 2, 10 / 2] %}
First number: {{ numbers.0 }}
Calculated result: {{ expressions.1 }}
```
**Output:**
```
First number: 1
Calculated result: 8
```
### Iterating Over Lists with Foreach
Lists work seamlessly with `{% foreach %}` loops:
```
{% set items ["first", "second", "third"] %}
{% foreach items %}
Item {{ key }}: {{ value }}
{% end %}
```
**Output:**
```
Item 0: first
Item 1: second
Item 2: third
```
In list iteration:
- `{{ key }}` contains the numeric index (0, 1, 2, ...)
- `{{ value }}` contains the list element value
### Empty Lists
Empty lists can be created and handled gracefully:
```
{% set empty_list [] %}
Before foreach
{% foreach empty_list %}
This will not appear
{% end %}
After foreach
```
**Output:**
```
Before foreach
After foreach
```
### Comprehensive List Example
Combining multiple list features:
```
{% set numbers [1, 2, 3, 4, 5] %}
{% set expressions [1 + 2, 4 * 2, 10 / 2] %}
Numbers:
{% foreach numbers %}
{{ key }}: {{ value }}
{% end %}
Expressions:
{% foreach expressions %}
{{ key }}: {{ value }}
{% end %}
Direct access: {{ numbers.0 }}, {{ expressions.1 }}
```
**Output:**
```
Numbers:
0: 1
1: 2
2: 3
3: 4
4: 5
Expressions:
0: 3
1: 8
2: 5
Direct access: 1, 8
```
## 11. Dictionaries and Objects
Create key-value structures using dictionary syntax:
```
{% set config { host: "localhost", port: 8080, name: "MyApp" } %}
Server: {{ config.host }}:{{ config.port }}
Application: {{ config.name }}
```
**Output:**
```
Server: localhost:8080
Application: MyApp
```
Dictionary values can be strings, numbers, or variables:
```
{% set debug true %}
{% set settings {
environment: "development",
debug_mode: debug,
max_users: 100,
version: 1.2
} %}
Environment: {{ settings.environment }}
Debug: {{ settings.debug_mode }}
Max users: {{ settings.max_users }}
```
## 12. List Operations
### Append List Statement
Add items to existing lists using `{% append_list %}`:
```
{% set keys [] %}
{% if arg.a %}{% append_list keys "KEY_A" %}{% endif %}
{% if arg.b %}{% append_list keys "KEY_B" %}{% endif %}
{% if arg.x %}{% append_list keys "KEY_X" %}{% endif %}
{% if keys.0 %}
Keys found: {{ keys | join(", ") }}
{% endif %}
```
With variables `arg.a=true`, `arg.b=false`, `arg.x=true`:
```
Keys found: KEY_A, KEY_X
```
The `append_list` statement dynamically adds elements to lists, making it useful for conditional list building.
### Append Unique Sub Statement
Add content to a variable only if it doesn't already exist using `{% append_unique_sub %}`:
```
{% set list "item1" %}
{% append_unique_sub list "item2" %}
{% append_unique_sub list "item1" %}
{% append_unique_sub list "item3" %}
List: {{ list }}
```
**Output:**
```
List: item1item2item3
```
The `append_unique_sub` statement checks if the content already exists in the variable before appending it, preventing duplicates. This is useful for building strings where repeated content should be avoided.
### List with Join Filter in Practice
Combining list operations with filters for real-world use cases:
```
{% set buttons [] %}
{% if show_save %}{% append_list buttons "SAVE" %}{% endif %}
{% if show_cancel %}{% append_list buttons "CANCEL" %}{% endif %}
{% if show_help %}{% append_list buttons "HELP" %}{% endif %}
{% if buttons.0 %}
if (userInput & ({{ buttons | join(" | ") }})) {
handleButtonPress();
}
{% endif %}
```
## 13. Unique Line Processing
Remove duplicate lines from content blocks:
```
{% unique_line %}
line1
line2
line1
line3
line2
{% end %}
```
**Output:**
```
line1
line2
line3
```
This is useful for generating lists where duplicates should be eliminated.
## 14. Attachments
Load external content into variables using the attachment system:
```
Before attachment
{% attachment "test_attachment" content %}
{{ content }}
After attachment
```
The attachment identifier (`"test_attachment"`) is resolved by the application's attachment resolver, which loads the content into the specified variable (`content`).
## 15. Macros
The macro system allows you to define reusable template fragments with parameters. Macros help reduce duplication and create modular, maintainable templates.
### Basic Macro Definition
Define a macro using `{% macro %}` and `{% endmacro %}`:
```
{% macro banner() -%}
banner motd ^
===========================================
| This device is property of BigCorpCo |
| Unauthorized access is unauthorized |
| Unless you're authorized to access it |
| In which case play nice and stay safe |
===========================================
^
{% endmacro -%}
{{ banner() }}
```
**Output:**
```
banner motd ^
===========================================
| This device is property of BigCorpCo |
| Unauthorized access is unauthorized |
| Unless you're authorized to access it |
| In which case play nice and stay safe |
===========================================
^
```
### Macros with Parameters
Macros can accept parameters to make them more flexible:
```
{% macro def_if_desc(if_role) -%}
Unused port, dedicated to {{ if_role }} devices
{%- endmacro -%}
{{ def_if_desc("Home Computer") }}
{{ def_if_desc("Server") }}
```
**Output:**
```
Unused port, dedicated to Home Computer devices
Unused port, dedicated to Server devices
```
### Multiple Parameters
Macros support multiple parameters separated by commas:
```
{% macro user_info(username, role, department) -%}
User: {{ username }}
Role: {{ role }}
Department: {{ department }}
{%- endmacro -%}
{{ user_info("jdoe", "Senior Developer", "Engineering") }}
```
**Output:**
```
User: jdoe
Role: Senior Developer
Department: Engineering
```
### Macros with Logic
Macros can contain any template constructs including conditionals and expressions:
```
{% macro format_status(status, priority) -%}
{% if priority == "high" -%}
🔴 HIGH: {{ status }}
{%- elif priority == "medium" -%}
🟡 MEDIUM: {{ status }}
{%- else -%}
🟢 {{ status }}
{%- endif -%}
{%- endmacro -%}
{{ format_status("Server Down", "high") }}
{{ format_status("Maintenance", "medium") }}
{{ format_status("All Good", "low") }}
```
**Output:**
```
🔴 HIGH: Server Down
🟡 MEDIUM: Maintenance
🟢 All Good
```
### Nested Macro Calls
Macros can call other macros:
```
{% macro format_name(first, last) -%}
{{ first }} {{ last }}
{%- endmacro -%}
{% macro employee_badge(emp_id, first_name, last_name, dept) -%}
ID: {{ emp_id }}
Name: {{ format_name(first_name, last_name) }}
Department: {{ dept }}
{%- endmacro -%}
{{ employee_badge("E001", "John", "Doe", "IT") }}
```
**Output:**
```
ID: E001
Name: John Doe
Department: IT
```
### Network Configuration Example
```
{% macro vlan_config(vlan_id, vlan_name, subnet, gateway) -%}
vlan {{ vlan_id }}
name {{ vlan_name }}
!
interface vlan{{ vlan_id }}
ip address {{ gateway }} {{ subnet }}
no shutdown
{%- endmacro -%}
{{ vlan_config("100", "USERS", "255.255.255.0", "192.168.100.1") }}
{{ vlan_config("200", "SERVERS", "255.255.255.0", "192.168.200.1") }}
```
**Output:**
```
vlan 100
name USERS
!
interface vlan100
ip address 192.168.100.1 255.255.255.0
no shutdown
vlan 200
name SERVERS
!
interface vlan200
ip address 192.168.200.1 255.255.255.0
no shutdown
```
### Whitespace Control
Use `{%-` and `-%}` to control whitespace in macro output:
```
{% macro compact_list(item1, item2, item3) -%}
{{ item1 }}, {{ item2 }}, {{ item3 }}
{%- endmacro -%}
Items: {{ compact_list("apple", "banana", "cherry") }}
```
**Output:**
```
Items: apple, banana, cherry
```
### Macro Best Practices
1. **Use descriptive names**: `create_user_card()` instead of `card()`
2. **Keep macros focused**: One responsibility per macro
3. **Document parameters**: Use clear parameter names
4. **Handle edge cases**: Check for empty or missing parameters
5. **Use whitespace control**: Manage output formatting with `{%-` and `-%}`
## 16. Advanced Expression Examples
### Complex Expressions
Combine multiple features in sophisticated expressions:
```
Basic parentheses: {{ (2 + 3) * 4 }}
Nested parentheses: {{ ((1 + 2) * 3) + 4 }}
Multiple parentheses levels: {{ ((10 + 5) * 2) + ((8 - 3) * 4) }}
Complex null coalescing: {{ (missing.value ?? 2.5) * (factor ?? 10) }}
Type casting: {{ int((arg.a ?? 1.0) * 255) }}
Mixed operators with casting: {{ int((value ?? 3.14) * 100 + (offset ?? 0)) }}
Deeply nested: {{ (((a ?? 1) + (b ?? 2)) * ((c ?? 3) + (d ?? 4))) / 2 }}
Division with parentheses: {{ (numerator ?? 100) / (denominator ?? 4) }}
Null coalescing with zero: {{ (zero_value ?? 42) + 10 }}
```
**Output (with sample variables):**
```
Basic parentheses: 20
Nested parentheses: 13
Multiple parentheses levels: 50
Complex null coalescing: 25.0
Type casting: 255
Mixed operators with casting: 314
Deeply nested: 12
Division with parentheses: 25
Null coalescing with zero: 52
```
### String and Arithmetic Mixing
Combine string concatenation with arithmetic:
```
{% set a "10" %}
{% set b "3" %}
String concat: {{ "Value: " ~ (a + b) }}
Mixed operations: {{ "Result: " ~ ((a * 2) + b) ~ " units" }}
```
## 17. Error Handling and Edge Cases
### Empty Templates
Empty templates produce empty output:
```
(empty file)
```
**Output:**
```
(empty output)
```
### Undefined Variables
Undefined variables in expressions evaluate to empty strings or zero in arithmetic contexts:
```
Undefined in text: {{ undefined_var }}
Undefined in arithmetic: {{ undefined_var + 5 }}
With null coalescing: {{ undefined_var ?? "default" }}
```
**Output:**
```
Undefined in text:
Undefined in arithmetic: 5
With null coalescing: default
```
### Conditional Edge Cases
Variables are considered false if:
- They are undefined
- They are empty strings
- They contain only whitespace
```
{% if "" %}
Empty string is false
{% endif %}
{% if "any_value" %}
Non-empty string is true
{% endif %}
```
## 18. Best Practices
### Variable Naming
- Use descriptive names: `config.database.host` instead of `h`
- Use dots for hierarchical organization: `user.profile.name`
- Keep names readable: `max_items` instead of `mi`
### Template Organization
- Use comments to document complex logic
- Break complex expressions into multiple variables
- Group related variable assignments
### Performance Considerations
- Minimize deep nesting in expressions
- Use null coalescing for optional variables
- Prefer simple conditionals over complex boolean logic
- Use ternary operators for simple conditional values
### Example: Well-Structured Template
```
{# Configuration template for web server #}
{% set config.server.host "0.0.0.0" %}
{% set config.server.port "8080" %}
{% set config.database.enabled "true" %}
{% set config.database.host "localhost" %}
{% set config.database.port "5432" %}
# Server Configuration
Host: {{ config.server.host }}
Port: {{ config.server.port }}
{% if config.database.enabled %}
# Database Configuration
Database Host: {{ config.database.host }}
Database Port: {{ config.database.port }}
Connection String: {{ config.database.host ~ ":" ~ config.database.port }}
{% endif %}
{# Generate additional config based on environment #}
{% if env ?? "development" %}
Debug Mode: Enabled
Log Level: Debug
{% else %}
Debug Mode: Disabled
Log Level: Info
{% endif %}
```