@xpr-lang/xpr
v0.5.1
Published
Sandboxed cross-language expression language for data pipeline transforms
Maintainers
Readme
@xpr-lang/xpr
XPR is a sandboxed cross-language expression language for data pipeline transforms. This is the TypeScript/JavaScript reference runtime.
Try it live: xpr-playground.pages.dev — run XPR expressions in JS, Python, and Go side-by-side in your browser.
Install
bun add @xpr-lang/xpr
# or
npm install @xpr-lang/xprQuick Start
import { Xpr } from '@xpr-lang/xpr';
const engine = new Xpr();
engine.evaluate('items.filter(x => x.price > 50).map(x => x.name)', {
items: [
{ name: 'Widget', price: 25 },
{ name: 'Gadget', price: 75 },
{ name: 'Doohickey', price: 100 },
]
});
// → ["Gadget", "Doohickey"]API
evaluate(expression, context?)
Evaluates an XPR expression against an optional context object.
const engine = new Xpr();
engine.evaluate('1 + 2'); // → 3
engine.evaluate('user.name', { user: { name: 'Alice' } }); // → "Alice"
engine.evaluate('items.length', { items: [1, 2, 3] }); // → 3Returns the result as unknown. Throws XprError on parse or evaluation errors.
addFunction(name, fn)
Register a custom function callable from expressions:
const engine = new Xpr();
engine.addFunction('double', (x) => (x as number) * 2);
engine.addFunction('greet', (name) => `Hello, ${name}!`);
engine.evaluate('double(21)'); // → 42
engine.evaluate('greet("World")'); // → "Hello, World!"
engine.evaluate('items.map(x => double(x))', { items: [1, 2, 3] }); // → [2, 4, 6]Built-in Functions
Math: round, floor, ceil, abs, min, max
Type: type, int, float, string, bool
String methods: .len(), .upper(), .lower(), .trim(), .startsWith(), .endsWith(), .contains(), .split(), .replace(), .slice(), .indexOf(), .repeat(), .trimStart(), .trimEnd(), .charAt(), .padStart(), .padEnd()
Array methods: .map(), .filter(), .reduce(), .find(), .some(), .every(), .flatMap(), .sort(), .reverse(), .length, .includes(), .indexOf(), .slice(), .join(), .concat(), .flat(), .unique(), .zip(), .chunk(), .groupBy()
Object methods: .keys(), .values(), .entries(), .has()
Utility: range()
v0.2 Features
Let Bindings: Immutable scoped bindings allow you to define and reuse values within expressions:
engine.evaluate('let x = 1; let y = x + 1; y'); // → 2
engine.evaluate('let items = [1, 2, 3]; items.map(x => x * 2)', {}); // → [2, 4, 6]Spread Operator: Spread syntax for arrays and objects enables composition and merging:
engine.evaluate('[1, 2, ...[3, 4]]'); // → [1, 2, 3, 4]
engine.evaluate('{...{a: 1}, b: 2}'); // → {a: 1, b: 2}v0.3 Features
Date/Time
Dates are epoch milliseconds (UTC only). ICU format tokens: yyyy, MM, dd, HH, mm, ss.
engine.evaluate('formatDate(now(), "yyyy-MM-dd")')
// → "2026-03-15"
engine.evaluate('dateDiff(parseDate("2024-01-01T00:00:00Z"), now(), "days")')
// → 439
engine.evaluate('dateAdd(parseDate("2024-01-31T00:00:00Z"), 1, "months")')
// → 1709337600000Regex
Function-based regex (RE2 flavor). Double-escape backslashes in string literals.
engine.evaluate('matches("hello 42", "\\\\d+")') // → true
engine.evaluate('match("order-123", "\\\\d+")') // → "123"
engine.evaluate('matchAll("a1b2c3", "\\\\d")') // → ["1", "2", "3"]
engine.evaluate('replacePattern("hello world", "o", "0")') // → "hell0 w0rld"Negative Indexing and Spread in Calls
engine.evaluate('[1,2,3][-1]') // → 3 (last element)
engine.evaluate('max(...[1, 5, 3, 2])') // → 5v0.4 Features
Destructuring
Object and array destructuring in let bindings and arrow function parameters.
engine.evaluate('let {name, age} = user; name', {user: {name: 'Alice', age: 30}})
// → 'Alice'
engine.evaluate('users.map(({name, age}) => `${name}: ${age}`)',
{users: [{name: 'Alice', age: 30}]})
// → ['Alice: 30']
engine.evaluate('let [head, ...tail] = items; tail', {items: [1, 2, 3]})
// → [2, 3]Regex Literals
First-class regex type with /pattern/flags literal syntax.
engine.evaluate('/\\\\d+/.test("order-123")') // → true
engine.evaluate('"2024-01-15".match(/\\\\d{4}/)') // → "2024"
engine.evaluate('"hello world".replace(/o/, "0")') // → "hell0 w0rld"v0.5 Features
Math
engine.evaluate('sqrt(16)') // → 4
engine.evaluate('log(E)') // → 1
engine.evaluate('PI * pow(5, 2)') // → 78.53981633974483
engine.evaluate('sign(-7)') // → -1
engine.evaluate('trunc(3.9)') // → 3Type Predicates
engine.evaluate('isNumber(42)') // → true
engine.evaluate('isString("x")') // → true
engine.evaluate('isObject([1,2])') // → false (arrays are "array" type)
engine.evaluate('isNull(null)') // → trueNew Array Methods
engine.evaluate('[3,null,1,null,5].compact().sortBy(x => x)') // → [1, 3, 5]
engine.evaluate('[1,2,3,4,5].take(3)') // → [1, 2, 3]
engine.evaluate('[1,2,3,4].sum()') // → 10
engine.evaluate('[1,2,3,4].avg()') // → 2.5
engine.evaluate('[1,2,3,4].partition(x => x > 2)') // → [[3, 4], [1, 2]]
engine.evaluate('[3,1,2].first()') // → 3
engine.evaluate('[3,1,2].last()') // → 2Rest Parameters
Arrow functions support rest parameters that collect remaining arguments into an array:
engine.evaluate('let total = (...xs) => xs.sum(); total(1, 2, 3, 4)') // → 10
engine.evaluate('let head = (first, ...rest) => rest; head(1, 2, 3, 4)') // → [2, 3, 4]Other
engine.evaluate('fromEntries([["a", 1], ["b", 2]])') // → { a: 1, b: 2 }
engine.evaluate('"a1b2c3".split(/\\\\d+/)') // → ["a", "b", "c"]Conformance
This runtime supports Level 1–3 (all conformance levels):
- Level 1: Literals, arithmetic, comparison, logic, ternary, property access, function calls
- Level 2: Arrow functions, collection methods, string methods, template literals
- Level 3: Pipe operator (
|>), optional chaining (?.), nullish coalescing (??)
v0.2 additions: Let bindings, spread operator, 20 new built-in methods (10 array, 7 string, 2 object, 1 global)
v0.3 additions: Date/time (12 fns), regex functions (4 fns), negative indexing, spread in calls
v0.4 additions: Destructuring (let + arrow params), regex literals, regex type
v0.5 additions: 6 math fns + PI/E, 6 type predicates, 13 new array methods, fromEntries(), rest params
Specification
See the XPR Language Specification for the full EBNF grammar, type system, operator precedence, and conformance test suite.
License
MIT
