Cheatsheet
Quick reference for writing tape code. Tape is a compiled language with dual targets (VM interpreter + native x86-64), three strictness profiles, and a built-in component model.
Comments
// Line comment
/* Block comment */
/* Block comments nest:
/* inner comment */
still inside outer
*/Profiles
@profile(t0); // freestanding: no GC, no heap, kernel code
@profile(t1); // typed application: no GC, values freed at scope exit, exe/DLL
@profile(t2); // scripting: GC, no DLL (default)- t0 can only import t0
- t1 can import t1, t0, and t2 (compiled into the program)
- t2 can import t2, t1, t0
Variables & Constants
let x: i64 = 42; // immutable
var y: i64 = 0; // mutable
let z = 42; // type inferred
var name: string; // zero-initialized
@uninitialized var tmp: [441]u8; // not zeroed: write it before reading
const MAX: i64 = 1024; // compile-time constantTypes
| Type | Description |
|---|---|
i8 i16 i32 i64 | Signed integers |
u8 u16 u32 u64 | Unsigned integers |
f32 f64 | Floats |
bool | Boolean |
string | UTF-8 owned string (t1/t2) |
[]const u8 | Byte slice (t0 strings) |
*T | Mutable pointer |
*const T | Immutable pointer |
*volatile T | Device memory: every access performed as written |
*!T | Non-null pointer |
?T | Optional |
[]T []const T | Slices |
[N]T | Fixed-size array |
fn(T) -> R | Function pointer |
fn(T) -> R or E | Function pointer that can fail (f(x) or return) |
Functions
fn add(a: i64, b: i64) -> i64 {
return a + b;
}
fn greet(name: string, loud: bool = false) { } // default params
// Named arguments — skip middle defaults:
fn create_window(title: string, width: i64 = 800, height: i64 = 600, vsync: bool = true) -> Window { }
create_window("App", height: 400); // skip width, keep default
create_window("App", vsync: false); // skip width and height
create_window("App", height: 400, width: 1024); // named, any order
pub fn exported() -> i64 { return 42; } // visible to importers
// Methods
struct Vec2 { x: f64; y: f64; }
fn Vec2.length(*const self) -> f64 { // reads only: works on a let
return math.sqrt_f64(self.x * self.x + self.y * self.y);
}
fn Vec2.flip(*self) { self.x = -self.x; } // changes it: needs a var
fn Vec2.scale(self, factor: f64) -> Vec2 { // by value
return Vec2 { x: self.x * factor; y: self.y * factor; };
}Operator Overloading
fn Vec2.op_add(self, other: Vec2) -> Vec2 { ... }
fn Vec2.op_sub(self, other: Vec2) -> Vec2 { ... }
fn Vec2.op_mul(self, other: Vec2) -> Vec2 { ... }
fn Vec2.op_neg(self) -> Vec2 { ... }
fn Vec2.op_eq(self, other: Vec2) -> bool { ... } // != auto-derived
fn Vec2.op_lt(self, other: Vec2) -> bool { ... }Structs
struct Point { x: f64; y: f64; }
let p = Point { x: 1.0; y: 2.0; }; // a literal names every field...
struct Opts { path: string; retries: i64 = 3; verbose: bool = false; }
let o = Opts { path: "/tmp"; }; // ...except those with a default
let z = Point { x: 1.0; .. }; // `..` zeroes the rest (y = 0.0)
@packed
struct Header { magic: u32; len: u16; flags: u8; }
@align(16)
struct SimdVec { data: [4]f32; }
@opaque
struct Handle { id: u64; } // fields hidden outside moduleEnums
enum Direction : u8 { North = 0; East = 1; South = 2; West = 3; }
enum HttpErr : u16 {
NotFound = 404: "not found";
ServerError = 500: "internal server error";
}
let msg = HttpErr.desc(HttpErr.NotFound); // "not found"Tagged Unions
tagged Shape {
Circle { radius: f64 }
Rect { width: f64; height: f64 }
Point;
}
fn area(s: Shape) -> f64 {
match (s) {
Shape.Circle { radius } => { return 3.14159 * radius * radius; }
Shape.Rect { width; height } => { return width * height; }
Shape.Point => { return 0.0; }
}
}Untagged Unions (t0/t1)
union Register { i: i64; f: f64; bytes: [8]u8; }Control Flow
if (x > 0) { } else if (x == 0) { } else { }
while (i < 10) { i = i + 1; }
for (i in 0..10) { } // exclusive range
for (i in 0..=9) { } // inclusive range
for (item in items) { } // slice/array iteration
for (val in iterator) { } // anything with .next() -> ?T
match (value) {
Pattern => { }
_ => { } // wildcard catch-all
}
// Match works with tagged unions, enums, integers and text:
match (dir) {
Direction.North => { }
Direction.East, Direction.West => { } // several in one arm
_ => { }
}
match (c) { @byte("a"), 66 => { } _ => { } } // an integer: `_` required
match (name) { "fill" => { } _ => { } } // string or []const u8
break;
continue;Optionals
let maybe: ?i64 = null;
let found: ?i64 = 42;
if (maybe != null) { io.println("{maybe!}"); } // unwrap with !
let val = maybe or { 0 }; // default value
let node: ?*Node = find(42); // optional pointer
let v = node!.value; // unwrap before `.`, `[]`, calls
let n: *Node = find(42) or return; // in a ?T or void fnError Handling
enum ParseErr : u8 { BadInput = 1: "invalid input"; }
fn parse(s: string) -> i64 or ParseErr {
if (bad) { return ParseErr.BadInput; }
return 42;
}
// Propagate to caller:
let val = parse(input) or return;
// Handle inline:
let val = parse(input) or {
io.println(ParseErr.desc(err));
return 1;
};Defer
let buf = mem.alloc(4096) or return; // in a fn returning `T or mem.MemError`
defer mem.free(buf, 4096); // runs at scope exit
errdefer mem.free(buf, 4096); // runs only if error returnedCoroutines
co fn countdown(n: i64) { // declares the frame type `countdown`
for (i in 0..n) { io.println("{n - i}"); yield; }
}
var c: countdown = countdown.start(3); // nothing runs yet
while (c.resume()) { } // true at each yield, false at the end
c.done(); c.running(); c.reset(); // reset runs its defers
// yield from other.start(args); runs another coroutine to its endImports & Modules
import io from "io"; // stdlib
import utils from "./utils.tape"; // relative
import { println, read_line } from "io"; // selective (no prefix needed)
import { Vec3, make_vec } from "./veclib"; // selective
io.println("qualified"); // module prefix
println("unqualified"); // selective importVisibility
pub fn api() -> i64 { return 42; }
fn internal() { }
pub {
fn increment() { }
fn decrement() { }
var count: i64 = 0;
}Operators
+ - * / % // arithmetic
& | ^ ~ << >> // bitwise; & | ^ also on two bools (both sides run)
== != < <= > >= // comparison
&& || ! // logical (&& || short-circuit)
+= -= *= /= %= &= |= ^= <<= >>= // compound assignment
|> // pipe: a |> f(b) is f(a, b); right side a call or fn name
as // type cast
is // variant check (s is Shape.Circle) or component constraint check
.. // rangePrecedence (high to low): postfix (. [] () !) → cast (as is) → unary (- ! & * ~) → mul → add → shift → bitwise → comparison → logical → pipe → assignment.
Prefix operators bind tighter than as/is:
&x as *u8 // (&x) as *u8 — not &(x as *u8)
*ptr as i64 // (*ptr) as i64A pointer and a number don’t add: @offset(p, n) is the address n
elements past p (*@offset(p, 2) = 0;), (p as i64 + n) as *T n
bytes past it.
Pipe Operator
let result = data |> parse() |> validate() |> transform();
// equivalent to: transform(validate(parse(data)))Vectors
let a: u32x4 = [1, 2, 3, 4]; // 128-bit lanes: u8x16 .. i64x2
let b: u32x4 = @splat(u32x4, 7);
let m: u32x4 = @eq(a, b); // masks; also @lt @gt @min @max
let v: u32x4 = *((&buf[0]) as *u32x4); // through a pointer
if (@cpu_has(aes)) { // aes clmul sha sse41 sse42 avx2 neon crc32 rdrand rdseed
s = @aes_enc(s, k); // u8x16; @aes_enc_last @aes_dec @aes_dec_last
} // @aes_imc @aes_keygen_assist(k, rcon)
let p: u64x2 = @clmul_lo(x, y); // carry-less multiply; @clmul_hi
let av: u8x16 = @avg(a8, b8); // codecs: @add_sat @sub_sat @madd @mul_hi
let lo: u8x16 = @interleave_lo(a8, b8); // @interleave_hi @even_lanes @odd_lanesConstant Time
@constant_time // no secret-dependent branch, loop, address
fn ct_eq(a: []const u8, b: []const u8) -> bool { ... }
@constant_time
fn rot(x: u64, @public n: u64) -> u64 { ... } // @public: not secret
struct St { h: [8]u32; @public used: i64; }
// inside a @constant_time fn:
let r: u64 = @select(c, x, y); // both evaluated, never a branch
@move_bytes(dst, src, n); // memmove at the machine's speed (any profile)
@fill_bytes(dst, 0, n); // memset
let hi: u64 = @mul_hi(a, b); // high 64 bits of the 128-bit a * b
var c: u64 = 0;
let s0: u64 = @add_carry(a0, b0, c, &c); // a0 + b0 + c, the carry out into c
let d0: u64 = @sub_borrow(a0, b0, c, &c); // a0 - b0 - c, the borrow out into c
if (!@declassify(ok)) { return Error.Auth; } // what may be revealedStrings
let msg = "Hello, {name}!"; // interpolation
let escaped = "line1\nline2\ttab"; // escape sequences: \n \t \r \\ \" \0 \xHH
let braces = "\{literal\}"; // \{ and \} for literal braces
let raw: []const u8 = "kernel msg"; // t0: byte slice
// Triple-quoted — multi-line, can contain ", escapes + interpolation active:
let html = """
<div class="main">
<p>{greeting}</p>
</div>
""";
// Raw strings — no escapes, no interpolation:
let json = r"""{"key": "value", "count": 42}""";Components
component Button {
prop label: string; // input from parent
prop size: i64 = 48; // with default
var count: i64 = 0; // mutable reactive state
state hovered: bool = false; // interaction state (for #style)
event on_click(); // event declaration
slot content; // child content slot
fn handle_pointer(ev: *ui.PointerEvent) {
if (ev.kind == ui.PointerKind.Up) {
fire on_click(); // fire event
}
}
view {
widgets.Panel { bg: #336699;
widgets.Label { text: label; }
}
}
}
// Instantiation:
Button { label: "Save"; on_click: save_file; }Events
event on_click(); // zero-arg
event on_value(val: i64); // typed
fire on_click(); // call bound handler
fire on_value(42); // with argument
// Parent binds:
Button { label: "Go"; on_click: my_handler; }Slots
component Card {
slot header;
slot content;
view {
ui.Column { header; content; }
}
}
Card {
slot header { widgets.Label { text: "Title"; } };
slot content { MyForm {} };
}Lists
view {
for (item in items) {
TodoItem { key: item.id; text: item.text; } // key: instance follows its item
}
}Dispatch (vtable methods)
component Panel {
@tape { ui.drawable(); }
fn paint(s: *gfx.Surface, x: i64, y: i64, w: i64, h: i64) { }
}
// Constraint type:
type Drawable = *component[@dispatch fn paint(s: *gfx.Surface, x: i64, y: i64, w: i64, h: i64)];
fn render(widget: Drawable, s: *gfx.Surface) { widget.paint(s, 0, 0, 100, 100); }Regions
#code // default: standard tape logic
#view // component declarations with view trees
#style // component props set by rule (styling)
#shader // vertex, pixel and compute shaders (the gpu module)
#test // inline test cases (only during `tape test`)
#resource // embedded assets (icon, image, font, sound, binary)
#localization // translatable strings (@t("..."))
#bytecode // inline assembly (t0 only)#test
#test
test "math works" {
@assert(2 + 2 == 4);
}
test "function works" {
let result = my_fn(10);
@assert(result == 42);
}Metaprogramming (@tape)
// Compile-time function:
@tape fn gen_vec(T: type) {
@emit struct Vec_${T} { data: *${T}; len: i64; cap: i64; }
@emit fn vec_${T}_push(v: *Vec_${T}, item: ${T}) { }
}
@tape { gen_vec(i64); }
// Compile-time constants:
@tape const TABLE: [256]u32 = { /* computed at compile time */ };
@tape const SHADER: []const u8 = @embed("blur.glsl");
// Reflection:
@tape fn print_info(T: type) {
var info = @typeof(T);
comptime for (field in info.fields) {
@println(" {field.name}: size={field.size}");
}
}
// Comptime match:
comptime match(T) {
i64, i32 => { @emit const DEFAULT = 0; }
f64 => { @emit const DEFAULT = 0.0; }
_ => { @compile_error("unsupported"); }
}
// Intrinsics:
@sizeof(T) @alignof(T) @typeof(T) @assert(cond)
@has_field(T, "name") @has_method(T, "name")
@embed("path") @compile_error("msg")refactor() Blocks
Virtual module boundary — compiler validates, tape refactor materializes:
import io from "io";
import math from "math";
fn main() { io.println("{distance(3.0, 4.0)}"); }
refactor("./geom.tape") {
fn distance(x: f64, y: f64) -> f64 {
return math.sqrt_f64(x*x + y*y);
}
}After tape refactor main.tape:
geom.tapecreated withpub { fn distance(...) { } }+ only needed importsmain.taperewritten withimport { distance } from "./geom.tape";
C FFI
@link("mylib"); // a portable name: mylib.dll, mylib.so, mylib.dylib
extern("actual_name") fn tape_name(x: i64) -> i64;
extern("actual_name") fn tape_name_p(x: *u8) -> *u8; // one symbol, another signature
// a platform library (kernel32, libc.so.6, a .dll, a path) is linked in a @target branch
@tape {
if (@target == "win64") {
@emit @link("kernel32");
@emit extern fn ExitProcess(code: u32);
@emit @link("msvcrt"); // the C runtime
} else if (@target == "linux" || @target == "linux-arm64") {
@emit @link("libc.so.6");
} else {
@emit @link("/usr/lib/libSystem.B.dylib");
}
// after the branches: from whichever C library the branch linked
@emit extern fn puts(s: *const u8) -> i32; // C reads a C string
@emit extern fn printf(fmt: *const u8, ...) -> i32; // variadic
@emit extern fn qsort(base: *u8, n: u64, size: u64,
cmp: extern fn(*const u8, *const u8) -> i32);
}
puts("hi"); // a literal: NUL-terminated
let c: *u8 = cstr.from_view(s); // any other string: a copy
defer cstr.free(c);
puts(c); // puts(s) is an error
struct Rect { x: f64; y: f64; w: f64; h: f64; }
// @emit extern fn rect_inset(r: Rect, d: f64) -> Rect; structs by value
fn by_value(a: *const u8, b: *const u8) -> i32 { return 0; }
// qsort(data, n, 8, by_value); a tape fn named as an extern fn: its C entry
@export
pub fn callable_from_c() -> i32 { return 0; }Platform-Specific Code
@tape {
if (@target == "win64") {
@emit fn platform_init() { /* windows */ }
} else if (@target == "uusi") {
@emit fn platform_init() { /* uusi kernel */ }
}
}
// @target is compared with a target's name only ("win65" is an error);
// a match (@target) covers every target or has _Build Settings
enum Build : u8 { Dev = 1; Beta = 2; Release = 3; }
@setting pub const BUILD: Build = Build.Dev; // the default: what tape run sees
@setting const SERVER: []const u8 = "local";
#resource
@windowed(BUILD != Build.Dev) // no console window in a releasetape build app.tape -o app.exe --set BUILD=Release --set SERVER=chat.example.com
tape run app.tape --set brand.SERVER=prod # another module's: module.NAMEMemory (t0/t1)
import mem from "mem";
let buf: *u8 = mem.alloc_or_trap(4096); // or mem.alloc(n) -> *u8 or MemError
defer mem.free(buf, 4096);
mem.copy(dst, src, count);
mem.zero(ptr, count);
mem.set(ptr, 0xFF, count);Device registers and framebuffers (volatile access):
let uart = 0xFEB0_0000 as *volatile Uart;
while ((uart.status & 0x20) == 0) {} // a 4-byte load on every test
uart.data = c as u32; // exactly one 4-byte store
fb[y * pitch + x] = rgb; // fb: *volatile u32*T converts to *volatile T, never back (not with as either); no
whole-struct copies, slices or method calls through one. Not atomic, not
a CPU barrier.
Atomic operations (memory threads share; p a *i32, *u32, *i64 or *u64):
let v = @atomic_load(p); // also through a *const
@atomic_store(p, v);
let before = @atomic_add(p, 1); // wrapping; the value before
if (@atomic_add(&rc, -1) == 1) { ... } // the count reached zero
let won: bool = @atomic_cas(p, 0, 1); // swapped only when *p was 0Sequentially consistent; never moved or removed by the optimizer; not
through a *volatile T or on a field of an @packed struct.
Ownership rules:
- Managed types (string, components) freed at scope exit
- Assignment is deep copy for managed types
- Function params are borrowed (caller retains ownership)
- Return transfers ownership to caller
- Raw pointers (
*T) are unmanaged — you handle lifetime
Concurrency (threads)
@pool(1, 4) // min 1, max 4 pool threads (default @pool(1))
thread image {
var scratch: i64 = 0; // per pool thread, persists across calls
thread encode { // nested thread
fn png(readonly img: Image) -> []u8 or EncodeError { ... }
}
fn resize(img: Image, w: i64, h: i64) -> Image or ImageError { ... }
fn save(readonly img: Image) -> []u8 or ImageError {
// Inside a thread, a nested thread's call blocks and returns the value:
return encode.png(img) or { return ImageError.EncodeFailed; };
}
}
// From a component or main-thread code: non-blocking, returns a TaskHandle;
// callbacks run on the caller's thread
task = image.resize(photo, 800, 600) { on_done: show, on_error: report };
fn show(result: Image) { ... }
fn report(err: ImageError) { ... }CLI
tape build app.tape -I stdlib -o app.exe # compile to native
tape build app.tape -I stdlib -o app --target uusi # cross-compile
tape run script.tape -I stdlib # interpret via VM
tape check app.tape -I stdlib # typecheck only
tape test # run #test regions
tape refactor app.tape # materialize refactor blocks
tape refactor app.tape --dry-run # preview
tape repl # interactive REPL (planned)Key Design Rules
- No generics — use
@tape fn gen(T: type)+@emitinstead - No closures — use function pointers + typed events
- No exceptions — use
T or Eerror returns - No async/await — use thread declarations
- No inheritance — use composition (slots, events, dispatch constraints)
- No GC in t0/t1 — explicit memory
- No glob imports (
import *) — always list symbols explicitly pub {}groups exports; no “default public” for modules- Every feature must work in both VM and native backends