Skip to content

Advanced

Enoch-199811 edited this page Aug 9, 2026 · 3 revisions

Advanced — Smart references, libraries, memory and real projects

English | 中文 | Русский | Español | Português | 繁體中文 | Deutsch

Masterclass: typed smart references, calling native C libraries, memory management, performance, annotations as design tools, and a full hands-on project.

Typed smart references

A smart reference is a typed value pointing at an lvalue. It is declared with permission × follow × base type:

&<permission> <follow> <base type> <name> = &<lvalue>;
  • Permissions (7 stacks): r read, w write, m move (pointer), rw, rm, wm, rwm.
  • Follows (4): u program level (Unistream), f method level, a scope/area level, t thread level.
  • Base type: int / double / float / string / char / arrays / classes — generic.

That is 7 × 4 = 28 reference types.

Reading, writing, moving

int counter = 0;
&w u int wr = &counter;          // writable program-level ref
Ref::write(wr, 10);

&r u int rr = &counter;          // read-only ref
CIO::println(get Ref::read(rr)); // 10

&rw u int rw = &counter;         // read-write
CIO::println(get rw);            // 10
rw = 5;
CIO::println(counter);           // 5

Pointer movement (m permission)

With m, the reference is a moving pointer (like a++ in C):

ALL a = new int[3];
a[0] = 10; a[1] = 20; a[2] = 30;
&m u int mp = &a[1];
CIO::println(get mp);            // 20
mp++;
CIO::println(get mp);            // 30
mp = 99;                         // writes through the pointer
CIO::println(a[2]);              // 99
CIO::println(cause Ref::move(mp));  // refused: pointer moved out of bounds

Refusals as a design tool

References refuse what their permission forbids — and the refusal reason is just a value:

CIO::println(cause Ref::write(rr, 99));
// Ref refused: reference is read-only, cannot write

Annotations: @read, @write, @onlyread, @unfork

Annotations declare contracts on streams and methods:

// A stream that must never be forked again:
Stream Sealed {
    void ping();
} @unfork
Sealed BadFork {}       // startup: refused: stream Sealed is @unfork, cannot fork

// A read-only stream: users may not call write methods:
Stream ReadOnly {
    int count;
    void bump();
    int get();
}
ReadOnly RO {
    void bump() { this::count = count + 1; } @write
    int get() { res count; } @read
} @onlyread
  • @write / @read on a method declare its read/write nature explicitly — they take priority over AST heuristics (a body that looks read-only can be declared a write method, and vice versa).
  • @onlyread refuses any write-method call on the stream.
  • @unfork refuses every fork path, including new on a @unfork class.

Calling native C libraries

A stream can bind to a shared library:

Stream m & "libm.so.6";          // bind libm

Main {
    void exec() {
        CIO::println(get m::sin(0));    // 0
        CIO::println(get m::pow(2, 10)); // 1024
    }
}

Binary functions are called as double(*)(double, ...) (up to 6 arguments); exported symbols become stream methods. bio_dlsym / the platform shim handles dlopen/LoadLibrary differences, so this works on Windows too.

Memory management

  • The interpreter uses a block-based arena — allocations never move.
  • Default limit: 256 MiB in interpreted mode; 0 = unlimited.
  • Limit flags: bio -e 256M script.bio (or -e 1G, -e 0).
  • Compiled products: no limit unless BIO_MEM_LIMIT is set in the environment; bio shell build embeds this behavior in the generated main.
  • When the limit is hit, the program stops with a clear message: memory limit exceeded (limit N bytes).

Performance

  • Caching: streams and methods are cached on the hot path (stream_cache / method_cache) — repeated lookups by the same name are O(1).
  • Incremental compilation: bio build hashes each module's source; only changed modules recompile (N module(s) cached, M recompiled).
  • Compile mode: bio shell build links the interpreter runtime into a standalone executable — startup is instant and the product runs anywhere without bio.

Cross-platform builds

make bin (via tools/make-dist.sh) cross-compiles release trees for all platforms with zig — no foreign SDKs:

make bin
# bin/linux-x86_64/bin/bio + lib/libbio.so
# bin/win64/bin/bio.exe + lib/bio.dll + bio.bat
# bin/macos-arm64/bin/bio + lib/libbio.dylib
# ...

Platforms: linux-x86_64, linux-arm64, win32, win64, win-arm64, macos-x86_64, macos-arm64. Requires zig >= 0.14 on PATH.

Internals: the launcher is dynamically linked against the shared runtime in lib/ (rpath $ORIGIN/../lib); Windows uses bio.bat to set PATH. Threads use ucontext on POSIX and Win32 fibers on Windows; binary libraries use dlopen or LoadLibrary through the platform shim.

Hands-on: a real project

Let's build a word-frequency counter that reads a file, counts words, and prints a sorted report.

1. Skeleton

bio init wordcount

2. src/main.bio

program main;

need function countWords;          // provided by utils/counter.bio

Main {
    void exec() {
        if (CIO::getln() == "") { }   // (placeholder for arg reading)
        FIO::open("input.txt");
        string text = FIO::readFile("input.txt");
        ALL report = countWords(text);
        CIO::println(get report);
    }
}

3. utils/counter.bio

program utils;

function countWords(text string) {
    // SIO lets us scan word by word through a string buffer.
    int words = 0;
    for (int i = 0; i < text::length(); i = i + 1;) {
        char c = text[i];
        if (c == ' ' || c == '\n' || c == '\t') {
            words = words + 1;
        }
    }
    res words + 1;    // last word has no trailing space
}

4. Build and run

bio build . -s                 # standalone executable
bio build . -m                 # packaged .img with the platform runtime
bio run .                      # interpret directly

5. Package for distribution

bio build . -m wordcount.zip   # one distributable file

That's the full loop: source → validated bundle → standalone binary → cross-platform package.

Debugging

  • bio --tokens file.bio — dump the lexer token stream.
  • Refusal messages are first-class: cause expr shows exactly why a request failed.
  • Compile with make PROFILE=sanitize for ASan + UBSan builds.

Further reading

  • Beginner — basics and control flow.
  • Intermediate — streams, classes, projects, threads.
  • Packaging — formats and release trees.
  • Examples — 15 commented programs covering every feature (especially 11-smart-refs.bio, 14-binary-lib.bio, 15-annotations.bio).

Clone this wiki locally