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 (planned)
#test — inline test cases
#code
import ui from "ui";
import str from "str";
import widgets from "widgets";
pub fn main() -> i32 {
let app = Counter {};
ui.run(app as *u8, 400, 200, "Counter");
return 0;
}
#view
component Counter {
var count: i64 = 0;
var count_text: *u8 = "0";
fn increment() {
count = count + 1;
count_text = str.i64_to_cstr(count);
}
view {
ui.Column {
widgets.Label { text: count_text; size: 24; }
ui.Row {
widgets.Button { label: "+"; on_click: increment; }
}
}
}
} 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
struct Event {
id: i64;
name: [16]u8;
}
@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 {
@tape { ui.drawable(); }
@tape { ui.interactive(); }
prop label: *u8;
event on_click();
state hovered: bool = false;
state active: bool = false;
fn handle_pointer(ev: *ui.PointerEvent) {
if (ev.kind == ui.PointerKind.Enter) { hovered = true; }
if (ev.kind == ui.PointerKind.Leave) { hovered = false; }
if (ev.kind == ui.PointerKind.Down) { active = true; }
if (ev.kind == ui.PointerKind.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 are declared for styling — the #style region that consumes them is specified, not yet implemented.
event — typed callback slot
fire — invoke bound handler
state — bool flags for :hovered, :active
community
Join us on Discord
Follow the development, ask questions, share ideas, and help shape the language.
Join the Discord
What's in the box
Dual backend
VM interpreter for dev, native machine code 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, #view, #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 and ARM64 directly. PE, ELF, or Mach-O. No LLVM, no Cranelift, no external toolchain.
C without bindings
extern fn calls into C, @export makes tape callable from it. Every @link symbol is verified at build time.