a programming language
One language,
kernel* to UI.
Freestanding kernel code, typed applications, quick scripts — same syntax, same toolchain, same mental model.
Explicit over implicit. Predictable over clever.
What it looks like
Single-file components
Regions in one file
Logic, components, and styles share a file. Each region has its own parser but compiles to the same IR. No context-switching between languages.
#code — functions, variables, logic
#view — declarative component trees
#style — property templates
#test — inline test cases
// #code
import ui from "ui";
import widgets from "widgets";
var count: i64 = 0;
fn increment() { count = count + 1; }
fn decrement() { count = count - 1; }
// #view
component Counter {
prop initial: i64 = 0;
view {
ui.Column {
Display { value: count; }
ui.Row {
widgets.Button { label: "+"; on_click: increment; }
widgets.Button { label: "-"; on_click: decrement; }
}
}
}
} enum IoError : u8 {
NotFound = 1: "file not found";
ReadFailed = 2: "read failed";
}
fn open_file(path: string) -> File or IoError {
let fd: i64 = sys_open(path);
if (fd < 0) {
return IoError.NotFound;
}
return File { fd: fd; };
}
fn load_config() -> Config or IoError {
let f: File = open_file("config.ini") or return;
let data: string = read_all(f) or return;
return parse_config(data);
} No exceptions
Errors are values
Functions return T or E where E is your own enum. No stack unwinding, no hidden control flow. Every error propagation is visible at the call site.
or return — propagate to caller
or { } — handle inline
enum descriptions — error IS the message
No generics needed
Compile-time execution
@tape {} runs tape code at compile time.
Generate type-specialized structs and functions — same language, same debugger, no inference puzzles.
@emit — inject declarations
$T — splice type names into identifiers
@sizeof, @typeof — reflect on types
@tape fn gen_queue(T: type) {
@emit struct Queue_$T {
items: *T; count: i64; cap: i64;
}
@emit fn queue_$T_push(q: *Queue_$T, item: T) {
// grow + append
}
@emit fn queue_$T_pop(q: *Queue_$T) -> ?T {
// remove + return
}
}
@tape { gen_queue(i64); }
@tape { gen_queue(Event); } component Button {
prop label: string;
event on_click();
state hovered: bool = false;
state active: bool = false;
fn on_pointer_enter() { hovered = true; }
fn on_pointer_leave() { hovered = false; }
fn on_pointer_up() {
active = false;
fire on_click();
}
view {
widgets.Panel {
widgets.Label { text: label; }
}
}
} No closures needed
Events and state
Components declare typed events. Parents bind handlers — just function pointers, no hidden allocations. Interaction state flags drive styling through #style pseudo-selectors.
event — typed callback slot
fire — invoke bound handler
state — bool flags for :hover, :active
What's in the box
Dual backend
VM interpreter for dev, native x86-64 for prod. Same TAC IR, same semantics.
Three-address IR
Explicit data flow. Values have names, not stack positions. Trivial to optimize.
@tape {} comptime
Run code at compile time. Generate types, compute tables, replace generics entirely.
Components
First-class props, slots, events, vtables. Built into the type system, not bolted on.
Source regions
#code, #components, #style, #test — sub-languages in one file, one IR out.
Three profiles
t0 kernel, t1 application, t2 scripting. Pick your strictness per module.
Error returns
T or Error. Propagate with `or return`, handle with `or {}`. No exceptions.
Direct codegen
Emits x86-64 directly. ELF or PE. No LLVM, no Cranelift, no external toolchain.
C at the edges
Fast internal ABI. Thin trampolines at library boundaries. extern fn for C interop.