Skip to content

Getting Started

XPR is a safe, sandboxed expression language for evaluating user-defined expressions against structured data. It runs identically in JavaScript, Python, and Go.

What is XPR?

XPR lets you evaluate expressions like:

items.filter(x => x.price > threshold).map(x => x.name)

against a context object you provide — safely, with no access to the host environment.

Use cases:

  • Dynamic business rules in data pipelines
  • User-configurable filters and transforms
  • Safe formula evaluation in multi-tenant apps

Install

bash
npm install @xpr-lang/xpr
bash
bun add @xpr-lang/xpr
bash
pip install xpr-lang
bash
go get github.com/xpr-lang/xpr-go

Quick Start

typescript
import { Xpr } from '@xpr-lang/xpr';

const engine = new Xpr();

const result = engine.evaluate(
  'items.filter(x => x.price > threshold).map(x => x.name)',
  {
    items: [
      { name: 'Widget', price: 99 },
      { name: 'Gadget', price: 25 },
    ],
    threshold: 50,
  }
);
// → ['Widget']
python
from xpr import Xpr

engine = Xpr()

result = engine.evaluate(
    'items.filter(x => x.price > threshold).map(x => x.name)',
    {
        'items': [
            {'name': 'Widget', 'price': 99},
            {'name': 'Gadget', 'price': 25},
        ],
        'threshold': 50,
    }
)
# → ['Widget']
go
import xpr "github.com/xpr-lang/xpr-go"

engine := xpr.New()

result, err := engine.Evaluate(
    `items.filter(x => x.price > threshold).map(x => x.name)`,
    map[string]any{
        "items": []any{
            map[string]any{"name": "Widget", "price": 99},
            map[string]any{"name": "Gadget", "price": 25},
        },
        "threshold": 50,
    },
)
// → ["Widget"]

Custom Functions

Register domain-specific functions that expressions can call:

typescript
engine.addFunction('slugify', (s) => s.toLowerCase().replace(/ /g, '-'));
// Now usable: slugify(product.name)
python
engine.add_function('slugify', lambda s: s.lower().replace(' ', '-'))
# Now usable: slugify(product.name)
go
engine.AddFunction("slugify", func(args ...any) (any, error) {
    s := args[0].(string)
    return strings.ReplaceAll(strings.ToLower(s), " ", "-"), nil
})
// Now usable: slugify(product.name)

Security

XPR is designed to be safe by default:

  • No side effects — expressions cannot mutate the context
  • No host access — no filesystem, network, or environment variables
  • Prototype blocking__proto__, constructor, etc. are blocked
  • Depth limit — max AST depth of 50 prevents stack overflow
  • Timeout — expressions are killed after 100ms by default

v0.2 Features

Let Bindings

Chain multiple bindings with semicolons — the final expression is the result:

let items = [1,2,3,4,5]; let big = items.filter(x => x > 2); big.map(x => x * 10)

[30, 40, 50]

let f = (x) => x * 2; f(5)

10

Spread Operator

Merge arrays and objects:

[...[1,2], ...[3,4]]

[1, 2, 3, 4]

{...defaults, ...overrides}

→ merged object (overrides wins on key collision)

New Methods

range(5)                          // → [0,1,2,3,4]
[1,2,3,4,5].chunk(2)              // → [[1,2],[3,4],[5]]
[1,2,1,3].unique()                // → [1,2,3]
["a","b","c"].zip([1,2,3])        // → [["a",1],["b",2],["c",3]]
items.groupBy(x => x.type)        // → {type: [items...]}
{"b":2,"a":1}.entries()           // → [["a",1],["b",2]]
{"a":1}.has("a")                  // → true
"hello".charAt(1)                 // → "e"
"42".padStart(5, "0")             // → "00042"

What's New

v0.3 — Date/time functions (now, parseDate, formatDate, date arithmetic), regex functions (matches, match, replacePattern), negative array indexing, spread in function calls.

v0.4 — Destructuring in let bindings and arrow function parameters (object + array, with defaults and rest). First-class regex literals (/pattern/flags).

v0.5 — Math functions (sqrt, log, pow, random, sign, trunc) and constants (PI, E). Type predicates (isNumber, isString, etc.). 13 new array methods (sortBy, take, drop, sum, avg, compact, partition, keyBy, and more). fromEntries(), str.split(/regex/), rest parameters in arrows.

See the Changelog for the full version history.

Next Steps