# Probe Expression Engine Syntax for Templating: A Complete Guide

> Learn Probe's expression engine syntax for templating. Discover how to use double curly braces {{ }} for dynamic values and string concatenation with Expr-Lang.

- Repository: [Tomohisa Oda/probe](https://github.com/linyows/probe)
- Tags: deep-dive
- Published: 2026-03-06

---

**Probe uses the Expr-Lang expression engine with double-curly-brace syntax `{{ <expression> }}` to evaluate dynamic values, supporting type preservation for single expressions and string concatenation for mixed content.**

The **Probe** monitoring tool (linyows/probe) leverages a powerful expression engine to enable dynamic templating within configuration files, step definitions, and action payloads. Understanding the Probe expression engine syntax for templating is essential for writing flexible, data-driven health checks and automation workflows.

## Core Templating Syntax in Probe

### The Double-Curly-Brace Pattern

All template expressions in Probe follow a consistent delimiter pattern defined in [`expr.go`](https://github.com/linyows/probe/blob/main/expr.go):

```text
{{ <expression> }}

```

This syntax wraps valid Expr-Lang expressions that the engine evaluates against a provided environment map. The implementation in [`expr.go`](https://github.com/linyows/probe/blob/main/expr.go) registers safe functions and validates expressions before execution to prevent security vulnerabilities.

### Type Preservation vs String Concatenation

Probe distinguishes between two evaluation modes based on template structure:

- **Single expression templates**: When the entire string consists of one `{{ … }}` block, `EvalTemplateWithTypePreservation` returns the raw evaluated value (e.g., integers remain `int`, booleans remain `bool`).
- **Mixed content templates**: When text contains one or more `{{ … }}` fragments, the engine evaluates each fragment and concatenates results into a **string**.

This behavior is extensively tested in [`expr_test.go`](https://github.com/linyows/probe/blob/main/expr_test.go), which defines the supported syntax and type-handling expectations.

## Supported Operators and Functions

### Standard Operators and Variables

Probe's expression engine supports Go-like operators and variable references from the evaluation environment. Expressions can access nested maps using dot notation:

```go
env := map[string]any{
    "vars": map[string]any{"count": 42},
}
// Access via: {{ vars.count }}

```

### Null-Coalescing and Ternary Operators

The engine implements advanced control flow operators demonstrated in [`expr_test.go`](https://github.com/linyows/probe/blob/main/expr_test.go):

- **Null-coalescing** (`??`): Returns the right-hand operand if the left is null or empty
- **Ternary** (`cond ? a : b`): Evaluates conditions and returns the appropriate branch

```go
tmpl := "{{ env.URL ?? 'http://localhost' }} is {{ status == 200 ? 'OK' : 'FAIL' }}"

```

### Built-in Custom Functions

[`expr.go`](https://github.com/linyows/probe/blob/main/expr.go) registers several custom functions for dynamic data generation:

- `random_int(max)` – Returns random integer (0 ≤ n < max)
- `random_str(length)` – Generates random alphanumeric string
- `unixtime()` – Returns current Unix timestamp
- `encode_base64(string)` / `decode_base64(string)` – Base64 encoding operations

## Implementation in expr.go

The core templating logic resides in **[`expr.go`](https://github.com/linyows/probe/blob/main/expr.go)**, which provides the primary API methods:

- `Eval(expression, env)` – Evaluates raw expressions without templating
- `EvalTemplate(template, env)` – Processes mixed-content templates with string output
- `EvalTemplateWithTypePreservation(template, env)` – Preserves types for single-expression templates
- `EvalTemplateMap(input, env)` – Recursively evaluates templates within map structures

Security validation occurs within these methods to prevent code injection, as verified by the comprehensive test suite in **[`expr_test.go`](https://github.com/linyows/probe/blob/main/expr_test.go)**.

## Practical Code Examples

### Simple Variable Substitution

```go
env := map[string]any{
    "name": "Alice",
}
result, _ := (&probe.Expr{}).EvalTemplate("Hello {{ name }}!", env)
// → "Hello Alice!"

```

### Type-Preserved Evaluation

```go
env := map[string]any{
    "vars": map[string]any{"count": 42},
}
val, _ := (&probe.Expr{}).EvalTemplateWithTypePreservation("{{ vars.count }}", env)
// → 42 (int, not "42")

```

### Using Advanced Operators

```go
env := map[string]any{
    "status": 200,
    "env":    map[string]any{"URL": ""},
}
tmpl := "{{ env.URL ?? 'http://localhost' }} is {{ status == 200 ? 'OK' : 'FAIL' }}"
out, _ := (&probe.Expr{}).EvalTemplate(tmpl, env)
// → "http://localhost is OK"

```

### Built-in Function Usage

```go
env := map[string]any{}
expr := &probe.Expr{}

// Random integer (0 ≤ n < 100)
randInt, _ := expr.Eval("random_int(100)", env)

// Random alphanumeric string of length 16
randStr, _ := expr.Eval("random_str(16)", env)

// Current Unix timestamp
ts, _ := expr.Eval("unixtime()", env)

// Base64 encode/decode
enc, _ := expr.Eval("encode_base64('user:pass')", env) // → "dXNlcjpwYXNz"
dec, _ := expr.Eval("decode_base64(enc)", map[string]any{"enc": enc}) // → "user:pass"

```

### Evaluating Nested Map Templates

```go
input := map[string]any{
    "url":       "{{ env.URL }}",
    "auth":      "Bearer {{ env.TOKEN }}",
    "nested": map[string]any{
        "msg": "Hello {{ name }}",
    },
}
env := map[string]any{
    "env":  map[string]any{"URL": "https://api.example.com", "TOKEN": "s3cr3t"},
    "name": "Bob",
}
out := (&probe.Expr{}).EvalTemplateMap(input, env)
// out["url"]        → "https://api.example.com"
// out["auth"]       → "Bearer s3cr3t"
// out["nested"].(map[string]any)["msg"] → "Hello Bob"

```

## Summary

- Probe uses **double-curly-brace syntax** (`{{ expression }}`) for all templating operations via the Expr-Lang engine.
- **Type preservation** occurs when templates contain only a single expression block, while mixed content produces **string concatenation**.
- The engine supports **Go-like operators**, **null-coalescing (`??`)**, **ternary conditionals (`? :`)**, and **custom functions** like `random_int` and `encode_base64`.
- Core implementation resides in **[`expr.go`](https://github.com/linyows/probe/blob/main/expr.go)** with comprehensive validation and security checks defined in **[`expr_test.go`](https://github.com/linyows/probe/blob/main/expr_test.go)**.

## Frequently Asked Questions

### What is the basic syntax for Probe's expression templates?

Probe templates use double-curly-brace delimiters: `{{ <expression> }}`. Any valid Expr-Lang expression can be placed inside the braces, including variable references, arithmetic operations, and function calls. This syntax is processed by the `EvalTemplate` method in [`expr.go`](https://github.com/linyows/probe/blob/main/expr.go).

### How does Probe handle type conversion in templates?

Probe distinguishes between single-expression and mixed-content templates. When a template consists solely of one `{{ … }}` block, `EvalTemplateWithTypePreservation` returns the raw evaluated type (integer, boolean, etc.). When text contains multiple expressions or literal strings, the engine concatenates everything into a final string result.

### What built-in functions are available in Probe's expression engine?

Probe registers several custom functions in [`expr.go`](https://github.com/linyows/probe/blob/main/expr.go) for dynamic data generation: `random_int(max)` for random integers, `random_str(length)` for alphanumeric strings, `unixtime()` for timestamps, and `encode_base64`/`decode_base64` for Base64 operations. These functions are validated and sandboxed to prevent security vulnerabilities.

### Can Probe templates reference nested variables and use advanced operators?

Yes, Probe supports dot-notation for nested map access (e.g., `{{ vars.count }}`) and implements advanced operators including null-coalescing (`??`) for default values and ternary conditionals (`cond ? a : b`) for inline branching. These features are extensively tested in [`expr_test.go`](https://github.com/linyows/probe/blob/main/expr_test.go) to ensure consistent evaluation behavior.