v2 · Now available

A bounded expression language

Evaluate dynamic logic at runtime with guaranteed termination and an empty sandbox by default. Familiar syntax with variables, functions, and |> pipe operators.

go get github.com/deepnoodle-ai/risor/v2

let users = [    { name: "Alice", role: "admin", active: true },    { name: "Bob",   role: "viewer", active: false },    { name: "Carol", role: "admin", active: true },]​let admins = users    .filter(u => u.active)    .filter(u => u.role == "admin")    .map(u => u.name)    |> sorted​admins  // ["Alice", "Carol"]

What does bounded mean?

Always terminates

The virtual machine enforces restrictions that bound execution. Scripts can't hang your application.

Empty sandbox

Scripts start with zero capabilities. No I/O, no network, no filesystem. The host application decides what to expose.

Adjustable complexity

Three built-in presets: expression only, basic scripting, or full language. Expose exactly the complexity each use case needs.


Choose your sophistication level

Risor ships with three syntax presets, and individual features can be toggled on or off for more granular control. Each context gets exactly the language surface it needs.

01

Expression Only

Literals, operators, property access, and function calls. No variables, no control flow.

Features

Literals
Operators
Property access
Function calls
price * quantity * (1 - discount)

02

Basic Scripting

Adds variables, conditionals, pipes, destructuring, and spread. No function definitions.

+ Added in this tier

Variables
If/else
Try/catch
Pipes
Destructuring
Spread
Templates
let total = price * quantity
let rate = if (total > 100) { 0.15 } else { 0.05 }
let final = total * (1 - rate)

final |> round

03

Full Language

Adds functions, closures, pattern matching, and error handling. All features enabled.

+ Added in this tier

Functions
Closures
Pattern matching
Return statements
function calculate(items) {
    let subtotal = items
        .map(i => i.price * i.qty)
        |> math.sum

    let rate = if (subtotal > 100) { 0.15 } else { 0.05 }
    subtotal * (1 - rate)
}

let result = try { calculate(order.items) } catch e { 0 }

Language features

Reads like TypeScript, with pipe operators, match expressions, and richer methods on lists and maps. Language changes are made conservatively, only when there is clear need.

Arrow functions & closures

let greet = name => "hello " + name
let add = (a, b) => a + b
let counter = () => {
    let n = 0
    return () => { n = n + 1; n }
}

Nullish coalescing

let name = user.name ?? "anonymous"
let port = config.port ?? 8080
let deep = a?.b?.c ?? fallback
let list = data ?? []
let timeout = opts.timeout ?? 30

Destructuring

let {name, role} = user
let [first, ...rest] = items
let {host, port: p} = config
let [a, b] = [1, 2]
let {x, y, ...other} = point

Pipe operator

let result = data
    |> transform
    |> validate
    |> serialize
    |> compress

Match expressions

let label = match status {
    "active" => "ON"
    "paused" => "WAIT"
    "error"  => "ERR"
    _        => "OFF"
}

Try/catch expressions

let val = try {
    parse(raw)
} catch e {
    defaults
}
let ok = try { connect(url) } catch e { nil }

Object & map literals

let config = {
    timeout: 30,
    retries: 3,
    tags: ["prod", "us-east"],
    nested: { enabled: true },
}

Collection methods

items.filter(x => x.active)
     .map(x => x.name)
     .sorted()
     .join(", ")
// ["Alice", "Carol"]

The Risor virtual machine

Risor compiles to bytecode and runs on a lightweight virtual machine built for speed and safety. The Go implementation is pure Go with no CGo and no reflection.

Bytecode compiled

Source is compiled to bytecode before execution. The compilation step catches errors early and keeps evaluation fast.

Stack based VM

A purpose built stack machine executes bytecode with predictable performance. No tree-walking interpreter overhead.

Resource bounded

The VM enforces instruction count limits and recursion depth. Runaway scripts are stopped before they cause problems.

Lightweight instantiation

Each compiled instance uses 2-8 KB of heap. Spin up thousands of isolated evaluations without pooling — where other runtimes need 100 KB to 2 MB per instance.


One language, multiple runtimes

Risor is a language specification, not a library for one host. The Go runtime is production-ready. Native Rust and TypeScript runtimes are in active development, sharing a conformance test suite.

Go

Production

The primary runtime. Pure Go, no CGo, single binary. Production-ready today.

Full language specification

Bytecode-compiled VM

Native Go type interop

One import, one function call

Rust

In Progress

A native Rust runtime is in active development, targeting WASM and edge environments.

TypeScript

In Progress

A native TypeScript runtime is in active development for Node.js and browser environments.


Use cases

Dynamic Rules

Pricing, eligibility, validation. Update logic faster than deploys.

User Scripting

Let users extend your app. The sandbox ensures their code can't break yours.

Data Pipelines

Filter, transform, reshape. Pipe operators chain operations without loops.

Policy Evaluation

Authorization rules in imperative syntax. No new paradigm to learn.


Embed Risor in 5 minutes

One import. One function call. Full sandboxed scripting in your Go application.


Risor © 2026 Deep Noodle