sweet to write, fast to run
Scopes own every value, so memory frees on time, every time. No garbage collector, no borrow checker, no cleanup to forget. One rnx binary checks, runs, tests, and ships it. The workbench below is live — type and watch the compiler check it.
+ source — click to edit
+ compiler output — real check, off-thread
Hit Re-JIT to download the compiler engine and check your code.
Follow one file from keystroke to binary — through every tool, pass, and backend.
act 1 - make it right
fn shade(sun: Vec4f): Float {
defer { print("shade done"); }
let acc = Vec4f.splat(0.0);
let i = 0;
while i < 4 {
acc = acc + sun;
i = i + 1;
}
return acc.x();
}01 - write
Keywords, types, and strings light up as you type. Brackets match, blocks fold, docs peek on hover.
fn shade(sun:Vec4f):Float{
let acc=Vec4f.splat(0.0);
let i=0;
while i<4{
acc=acc+sun;i=i+1;}
return acc.x();}fn shade(sun: Vec4f): Float {
defer { print("shade done"); }
let acc = Vec4f.splat(0.0);
let i = 0;
while i < 4 {
acc = acc + sun;
i = i + 1;
}
return acc.x();
}rnx fmt - 0 diffs on second pass
02 - fmt
rnx fmt normalizes spacing and indentation across the whole file. Idempotent: the second pass changes nothing.
fn double(x: Int): Int => x * 2;
fn triple(x: Int): Int => x * 3;
print(double(21) + triple(14));03 - check
The checker reports all syntax errors at once — each with a caret you can click, not one-at-a-time whack-a-mole.
class B {
init() { this.cb = () => this.flush(); }
fn flush() { }
}class B {
init() { this.cb = fn decay(this) { this.flush() } }
fn flush() { }
}W104 - capture with fn decay(this)
04 - lint + fix
A closure stored on its own owner trips W104. Rewrite it with fn decay(this) to capture GenRef(this) instead.
let v = Vec4f.splat(1.0);
print(v.x());hover - splat
fn Vec4f.splat(v: Float): Vec4f
Broadcast one scalar to all four lanes. No allocation, no ARC traffic.
05 - lsp
The language server answers hover, go-to-definition, and completions for any editor speaking the protocol.
act 2 - make it run
let total = (x) => x * 2;06 - parse
The lexer cuts source into tokens; the parser grows them into a typed tree. Desugar lowers the surface syntax first.
07 - tree
Expand the nodes. This is exactly what the later passes walk — nothing hidden, nothing inferred.
fn shade:
v0 = splat 0.0
v1 = call Vec4f.splat
loop:
v2 = add v0, sun
v3 = lt i, 4
br v3, loop, done
done:
ret v2
08 - lower
The checked tree lowers to LIR — simplified below. The interpreter, Cranelift, and LLVM all read this same text.
0
instructions - illustrative example
fn shade:
v0 = splat 0.0
v1 = call Vec4f.splat
v9 = call debug_format
v10 = alloc tmp_buf
loop:
v2 = add v0, sun
v11 = retain v2
v3 = lt i, 4
br v3, loop, done
done:
ret v2
simplified LIR - inline, DCE, TCO, ARC motion
09 - optimize
Inline, dead-code removal, tail calls, and ARC motion run to fixed point. Unused functions simply stop existing.
LIR Cranelift running
JIT compiles each function on first call. Edit, save, new code live in milliseconds.
PLANNED - HOT RELOAD
edit recompile state kept
LIR LLVM O3 strip ship
Full optimization, per-symbol sections, symbols stripped for the binary you ship.
10 - two backends
Flip the switch. Dev compiles in milliseconds; release takes seconds and flies. Same checksum either way.
$ rnx build --release
built target/shade (ok)
$ ./shade
shade done
42
checksum match
11 - run
Dev runs it now through the JIT; release ships a stripped binary. Either way the output agrees bit-for-bit.
Seven workloads, seven toolchains, one log-log plot. Bottom-left wins: fastest execution with the smallest footprint. Turn the dial and check the methodology below.
Every tool charges a tax. Flip through the ones you have paid and see what Rasmalai does instead.
+ chamber 1 - the tax you pay
A thirty-minute borrow-checker negotiation over a circular graph, then a five-minute cold build to find out if you won. Real safety, billed hourly.
+ chamber 2 - the rasmalai bypass
Lexical scopes do the job lifetimes did — no annotations on linear logic, no refactors to satisfy the checker. This loop compiles in milliseconds and frees itself.
fn total(xs: Array<Int>): Int {
let sum = 0;
for x in xs { sum = sum + x; }
return sum;
}One binary does it all. Switch modes and poke at it — the lint fixer and the test runner respond.
$ rnx check src/bad.rnx — demo output loads with site data.
tree-hook diagnostics point at the exact column and suggest the fix. Try it above in the workbench — edit the source until it breaks.
Free, open source (MIT), and built in the open. Hit a rough edge? Open an issue — we read every one.
curl -fsSL https://rnx.dev/install.sh | sh showing Linux instructions — switch above if that is wrong
Made by people who got tired of waiting on compilers. Star the repo, file the bug, shape the language.