Why XPR
Existing expression languages fall short for cross-language data pipeline use cases — they're either tied to a single runtime, lack modern collection operations, or carry heavy dependencies. XPR fills that gap.
How it Compares
| JEXL | CEL | JSONata | Starlark | Expr (Go) | XPR | |
|---|---|---|---|---|---|---|
| Lambdas / arrow functions | ❌ | ❌ | Limited | ✅ | ✅ | ✅ |
Collection methods (map, filter, etc.) | ❌ | Limited | ✅ | ✅ | Limited | ✅ |
| Cross-runtime (JS + Python + Go) | Partial | ❌ | ❌ | ❌ | ❌ | ✅ |
| JS/Python-familiar syntax | ✅ | ❌ | ❌ | Partial | ❌ | ✅ |
| Zero runtime dependencies | ✅ | ❌ | ✅ | ❌ | ✅ | ✅ |
| Shared conformance tests | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
Key Properties
- Cross-runtime parity — The same expression produces identical results in JavaScript, Python, and Go, verified by 600+ shared conformance tests across 22 test files.
- Familiar syntax — Arrow functions (
x => x * 2), template literals, optional chaining, pipe operator (|>), destructuring. If you know JS or Python, you already know XPR syntax. - Safe sandbox — No I/O, no side effects, no prototype access. Configurable execution timeout (default 100ms) and AST depth limit (default 50).
- Zero dependencies — Each runtime is a hand-written Pratt parser and tree-walk evaluator. No third-party parsing library, no generated code, no build step.
- Pure expressions — No statements, no mutation, no loops. Iteration is collection methods only.
When to Use XPR
- Dynamic business rules — Evaluate user-defined filter/transform rules against structured data at runtime.
- User-configurable pipelines — Let end-users write expressions that run safely inside your application.
- Multi-tenant formula evaluation — Execute untrusted expressions in a sandbox where host access is impossible.
- Cross-platform portability — Author expressions once and evaluate them identically in a JS frontend, Python backend, and Go service.
When NOT to Use XPR
- General-purpose programming — XPR has no
while, noclass, no I/O, no imports. Use a real scripting language. - Stateful computation —
letbindings are immutable; there is no variable reassignment or persistent state across evaluations. - Native performance requirements — XPR is a tree-walk interpreter. For tight loops over large datasets, use native code.
- When you need host access — Expressions cannot reach the filesystem, network, or environment variables by design.