Regions Overview

What are regions?

A .tape file can contain multiple regions, each with its own parser and purpose. The region splitter divides the file at #region markers, then each region is processed separately.

tape
#code
import ui from "ui";
import widgets from "widgets";

var count: i64 = 0;
fn increment() { count = count + 1; }

#view
component Counter {
    view {
        widgets.Label { text: "{count}"; }
        widgets.Button { label: "+"; on_click: increment; }
    }
}

#test
test "increment works" {
    count = 0;
    increment();
    @assert(count == 1);
}

Available regions

RegionPurposeStatus
#codeStandard tape logic (default)Implemented
#viewComponent declarationsImplemented
#testInline test casesImplemented
#styleComponent styling: props set by ruleImplemented
#shaderVertex, pixel and compute shaders in tape, for the gpu moduleImplemented (Metal on macOS, OpenGL 4.3 on Windows and Linux)
#bytecodex86-64 instructions in functions (t0, native)Implemented
#localizationTranslatable strings for @t("...")Implemented
#resourceEmbedded assetsPlanned (splitter recognizes, not processed)

Default region

If no region marker appears, the entire file is #code. Most programs don’t need explicit region markers.

Region ordering

Regions must appear in a fixed order. The splitter rejects out-of-order or duplicate regions:

plaintext
#code → #view → #style → #shader → #localization → #resource → #test → #bytecode

Not all regions need to be present — only the ordering of those that appear is enforced.

Preamble

Content before the first explicit region marker (if any) is treated as preamble and prepended to #code. This allows imports and declarations at the top of a multi-region file to be visible in #code:

tape
import io from "io";
import str from "str";

#code
fn greet(name: string) { io.println("hello " + name); }

#view
component App { ... }

How each region is processed

#code

Parsed by the standard tape parser. Produces the module AST (functions, structs, enums, components, constants, imports).

#view

Parsed by tape_parse_view — a component-focused parser. Components declared in #view are merged into the module’s component list alongside any components in #code.

#test

Parsed by tape_parse_tests. Each test "name" { ... } block becomes a function. The test region supports:

  • test "description" { ... } — test cases, and table-driven ones (with [...] as (...))
  • setup { ... } / teardown { ... } — run before / after each test
  • @assert, @assert_err, @assert_trap, @fail and @skip, and @error_of(expr), an expression’s error as a ?E
  • helper fns, consts, module variables (var/let) and fixture types, compiled only for tape test

See #test.

Tests are compiled and run via tape test <file>.

#style

Rules that set component props by component name, nested for the instances inside another component, with constants and other modules’ styles imported as "module#style". The compiler checks every rule against the props’ types and compiles a styled prop as if it were written at the instantiation. See #style.

#shader

Vertex, pixel and compute shaders written in tape: each stage is checked with the program and compiled to the GPU’s languages (GLSL, Metal) when the program is, and is a gpu.Shader value in tape code that the gpu module makes pipelines from. See #shader.

#localization

Translations of the strings marked with @t("...") in #code and #view, checked at compile time and built into the program; the locale module chooses the language. See #localization.

#bytecode

Functions whose bodies are x86-64 instructions, for t0 kernel code (port I/O, control registers, descriptor tables). Native only. See #bytecode.

#resource

PlannedThis region is recognized by the splitter but not processed. It is reserved for future use.

Cross-region references

  • #view components can reference functions and variables from #code
  • #test can call functions from #code and instantiate #view components
  • #code can define and use components directly (no #view required)

Last modified: