BRAIDGROUP
RESEARCH & DEV
23. Documentation

Metaprogramming

Runtime Compilation with compiler.eval()

The std.compiler module provides eval() for compiling and executing Braid source code at runtime. This enables dynamic code generation, plugin systems, and just-in-time compilation of domain-specific expressions.

import std.compiler

fn dynamic_calc(expr: string) -> int {
    let source = "fn dyn_fn() -> int { return " + expr + " }"
    let result = compiler.eval(source)
    return result
}

fn main() {
    let r1 = dynamic_calc("2 + 2")           // 4
    let r2 = dynamic_calc("10 * 5")           // 50
    let r3 = dynamic_calc("42")               // 42
    print_int(r1)
    print_int(r2)
    print_int(r3)
}

compiler.compile() for Bytecode Generation

The compiler.compile() function compiles Braid source to bytecode (.bx format) without executing. This is useful for precompiling hot paths or generating modules on the fly.

import std.compiler

fn generate_kernel(kernel_name: string, size: int) {
    let src = "fn " + kernel_name + "(x: int) -> int { return x * " + size + " }"
    let bc = compiler.compile(src)
    // bc is a bytecode module ready for execution
    return bc
}

fn main() {
    let mul_by_5 = generate_kernel("mul5", 5)
    let result = compiler.exec(mul_by_5, [10])
    print_int(result)     // 50
}

Supercompilation

Braid's compiler includes a supercompilation pass (run_supercompiler) that performs compile-time evaluation and specialization. The supercompiler analyzes the AST, evaluates constant expressions, unfolds recursive calls when profitable, and specializes generic code for concrete types. It runs during the optimization phase (Phase 5) of the C pipeline.

// Supercompiler evaluates this at compile time:
fn factorial(n: int) -> int {
    if n <= 1 {
        return 1
    }
    return n * factorial(n - 1)
}

let result = factorial(5)
// Supercompiler unrolls: 5 * 4 * 3 * 2 * 1 = 120
// Compiled output: result = 120  (no runtime call)

Autocatalytic Recursion

Braid supports autocatalytic recursion, where a function can generate and execute new code that recursively calls itself with transformed logic. This pattern enables self-optimizing programs that adapt their behavior based on runtime feedback.

import std.compiler

fn self_optimizing(n: int) -> int {
    if n <= 1 {
        return 1
    }
    // Generate specialized code for this n
    let src = "fn fast_path(x: int) -> int { return x * " + n + " }"
    let specialized = compiler.eval(src)
    return specialized(n)
}

Compile-Time Code Generation

The std.dts module can be combined with std.compiler to generate code from data structures. This is useful for serialization, protocol buffers, and type-safe database queries.

import std.compiler
import std.dts

fn make_getter(field: string) {
    let src = "fn get_" + field + "(obj) { return obj." + field + " }"
    return compiler.eval(src)
}

fn main() {
    let get_name = make_getter("name")
    let user = User { name: "Alice", age: 30 }
    let name = get_name(user)
    print_string(name)    // "Alice"
}

Macro-Style Code Templates

By combining string building with compiler.eval(), Braid supports macro-like code generation patterns, enabling metaprogramming without a separate macro expansion phase.

fn make_comparator(field_name: string) {
    let src = "fn compare(a, b) -> int { if a." + field_name 
            + " < b." + field_name + " { return -1 } "
            + " else if a." + field_name + " > b." + field_name + " { return 1 } "
            + " else { return 0 } }"
    return compiler.eval(src)
}

fn main() {
    let by_age = make_comparator("age")
    let alice = Person { name: "Alice", age: 30 }
    let bob = Person { name: "Bob", age: 25 }
    print_int(by_age(alice, bob))    // 1 (Alice older)
}