// features
What tape does differently
Every decision is a tradeoff made deliberately. Here's what we chose and why.
dual-target execution
tape run interprets via the VM.
tape build emits native x86-64.
Same IR in, same result out. The test suite enforces parity — a feature isn't done until both backends pass.
three-address code IR
Tape 1.0 used a stack machine as its IR. That made native codegen a nightmare — you can't allocate registers when values are implicit stack positions. TAC gives every value a name. The VM interprets it linearly. The native backend maps it to registers. No reconstruction, no guessing.
@tape compile-time execution
Instead of generics, tape runs code at compile time.
@tape fn gen_queue(T: type) generates specialized structs and functions.
Same language, same debugger, no inference puzzles, no monomorphization explosion.
Zig proved this works. We took the idea and ran with it.
component model
Not a library. Not a framework. Components are in the type system:
prop, slot,
event, fire.
The compiler generates vtables, allocates instances, and dispatches methods.
No runtime reflection, no virtual DOM.
source regions
One file, multiple sub-languages: #code for logic,
#view for declarative component trees,
#style for styling,
#test for inline tests.
Each has its own parser, all emit the same IR. Like Vue SFCs but every section speaks tape.
three profiles (t0, t1, t2)
t0 is freestanding — no GC, no heap, for kernels. t1 is typed applications — explicit memory, compiles to exe/DLL. t2 is scripting — optional types, GC available, for prototyping. You pick per-module. A t1 app can import t0 libraries and run t2 scripts.
error returns, not exceptions
T or E is a tagged union where E is your own enum.
or return propagates.
or {} handles inline.
Every error path is visible at the call site. Zero cost on success.
No string-based errors — enums carry both code and description.
no generics. no closures. ever.
These are locked decisions, not missing features. Generics add enormous compiler complexity
for ergonomics that @tape provides more explicitly.
Closures require capture analysis and heap allocation — typed event parameters cover
every callback use case without hidden cost.
direct native codegen
We emit x86-64 machine code ourselves. ELF for our kernel, PE for Windows. No LLVM (too complex, too slow to compile). No Cranelift. No external linker. The tape compiler is one binary that produces executables.