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

tape
// Line comment

/* Block comment */

/* Block comments nest:
   /* inner comment */
   still inside outer
*/

Profiles

tape
@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

tape
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 constant

Types

TypeDescription
i8 i16 i32 i64Signed integers
u8 u16 u32 u64Unsigned integers
f32 f64Floats
boolBoolean
stringUTF-8 owned string (t1/t2)
[]const u8Byte slice (t0 strings)
*TMutable pointer
*const TImmutable pointer
*volatile TDevice memory: every access performed as written
*!TNon-null pointer
?TOptional
[]T []const TSlices
[N]TFixed-size array
fn(T) -> RFunction pointer
fn(T) -> R or EFunction pointer that can fail (f(x) or return)

Functions

tape
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

tape
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

tape
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 module

Enums

tape
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

tape
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)

tape
union Register { i: i64; f: f64; bytes: [8]u8; }

Control Flow

tape
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

tape
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 fn

Error Handling

tape
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

tape
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 returned

Coroutines

tape
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 end

Imports & Modules

tape
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 import

Visibility

tape
pub fn api() -> i64 { return 42; }
fn internal() { }

pub {
    fn increment() { }
    fn decrement() { }
    var count: i64 = 0;
}

Operators

tape
+ - * / %                    // 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
..                          // range

Precedence (high to low): postfix (. [] () !) → cast (as is) → unary (- ! & * ~) → mul → add → shift → bitwise → comparison → logical → pipe → assignment.

Prefix operators bind tighter than as/is:

tape
&x as *u8      // (&x) as *u8 — not &(x as *u8)
*ptr as i64    // (*ptr) as i64

A 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

tape
let result = data |> parse() |> validate() |> transform();
// equivalent to: transform(validate(parse(data)))

Vectors

tape
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_lanes

Constant Time

tape
@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 revealed

Strings

tape
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

tape
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

tape
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

tape
component Card {
    slot header;
    slot content;
    view {
        ui.Column { header; content; }
    }
}

Card {
    slot header { widgets.Label { text: "Title"; } };
    slot content { MyForm {} };
}

Lists

tape
view {
    for (item in items) {
        TodoItem { key: item.id; text: item.text; }   // key: instance follows its item
    }
}

Dispatch (vtable methods)

tape
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

tape
#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

tape
#test
test "math works" {
    @assert(2 + 2 == 4);
}

test "function works" {
    let result = my_fn(10);
    @assert(result == 42);
}

Metaprogramming (@tape)

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:

tape
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.tape created with pub { fn distance(...) { } } + only needed imports
  • main.tape rewritten with import { distance } from "./geom.tape";

C FFI

tape
@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
@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

tape
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 release
bash
tape 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.NAME

Memory (t0/t1)

tape
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):

tape
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):

tape
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 0

Sequentially 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)

tape
@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

bash
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) + @emit instead
  • No closures — use function pointers + typed events
  • No exceptions — use T or E error 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