Profiles

Tape programs declare a profile that controls which language features are available.

The three profiles

ProfileUse caseTypesMemoryTargets
t0Kernel, freestandingRequiredManual onlyNative (VM for testing)
t1ApplicationsRequiredAuto-freed at scope exit, no GCVM + Native
t2Scripts, prototypesRequired (inferred from initializers)Arena, GC for long runsVM + Native

A t0 program ships as native code, but tape run and tape test also run it in the VM, so its logic can be tested on the development machine. Calling a #bytecode function or using @asm there is a runtime error.

Declaring a profile

A file begins with a profile declaration; without one it is t2:

tape
@profile(t1);

Import restrictions

  • t2 can import: t2, t1, t0
  • t1 can import: t1, t0 (can import t2 source — compiles it in)
  • t0 can import: t0 only

What changes per profile

t0 — freestanding

No GC, no heap unless you provide an allocator. string is another name for []const u8, a view that never allocates. Can use @asm, volatile, atomics. Kernel can launch t1/t2 programs but cannot call into them.

t1 — typed application

Static types required (inferred from initializer or annotated). Compiles to native exe or DLL. No GC: string and other owned values are freed automatically when they go out of scope, and pointers are managed by hand. This is where most programs live.

t2 — scripting

Statically typed like t1: a local’s type is inferred from its initializer, and parameters and return types are always written. There is no dynamic type in tape (any is a reserved word); values of several kinds are a tagged union. Values live in an arena, with a simple GC for long-running scripts, so there is no ownership to think about. Cannot compile to DLL. Think: shell scripts, tools and UI prototypes in the language the finished application is written in.

Last modified: