Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Crush Banner

Crush Logo The Crush Language Guide

Crush is a capability-based, polyglot programming language and virtual runtime. It lets you write in multiple languages (Python, Rust, Bash, C, Go) within a single program while enforcing fine-grained security through an explicit capability system.

What this guide covers

SectionWhat you’ll learn
Crush LanguageSyntax, types, control flow, functions, capabilities, polyglot embedding
CASTThe intermediate AST format that walkers produce
CASMThe stack-based bytecode the VM executes
AppendixGlossary, quick reference, language comparisons

The compilation pipeline

Source (.crush / .py / .rs / ...)
         │
    Walker (language-specific)
         │
    CAST  (Crush AST — JSON)
         │
    Crush Compiler
         │
    CASM  (Crush Assembly — JSON / binary .castb)
         │
    crush-vm  (CVM1 — bytecode VM)

Hello, Crush

Run in Codebucket (Codespaces)

fn main() {
    io.print("Hello, Crush!");
}

The @ prefix marks a capability call — a crossing of the VM boundary that requires an explicit permission in the program manifest.

Where the source lives

The language implementation is the standalone crush-ast repository, extracted from the exosphere agent-native OS monorepo on 2026-06-12. It contains the CAST intermediate representation, tree-sitter grammar, polyglot walkers, compiler frontend, VM runtime, package manager, and installer.

The upstream exosphere project retains a subprocess-based walker registry that invokes the crush-ast walker binaries, and its own crush-cast/casm/nanovm crates for the Crush language compiler running inside the agent-native OS.

License

Licensed under either of MIT or Apache License 2.0 at your option.

Getting Started

The Crush toolchain is published on crates.io as a set of small, composable crates (all at 0.2.0). You embed Crush in a Rust application by depending on the SDK; the lower-level crates are available if you need direct access to the IR, bytecode, or VM.

The crates

CrateWhat it gives you
crush-lang-sdkStart here. Ergonomic Runtime + ProgramBuilder over the VM: load, compile, and run Crush programs; register host capabilities.
crush-frontendParser, semantic analyzer, optimizer, and CASM compiler (parse_source).
crush-vmThe CVM1 runtime: bytecode assembler/disassembler and the sandboxed interpreter with quotas + capability gates.
crush-castThe CAST intermediate representation (the stable AST).
casmThe CASM bytecode format.
crush-errorsShared error types.
tree-sitter-crushTree-sitter grammar (editor tooling, syntax highlighting).

Add it to a project

cargo add crush-lang-sdk

or in Cargo.toml:

[dependencies]
crush-lang-sdk = "0.2"

The SDK pulls in crush-vm, crush-frontend, crush-cast, casm, and crush-errors transitively — you usually don’t depend on them directly.

Optional features

crush-lang-sdk keeps its default surface lean; opt into host integrations as needed:

crush-lang-sdk = { version = "0.2", features = ["net", "db", "graphics"] }
  • net — networking host capabilities (@net.*)
  • db — database host capabilities
  • graphics — graphics host capabilities
  • repl-helper — richer REPL line editing

Quick start (embedding)

use crush_lang_sdk::{Runtime, ProgramBuilder};

fn main() -> anyhow::Result<()> {
    let program = ProgramBuilder::new()
        .permission("io.print")
        .line(r#".func main"#)
        .line(r#"PUSH_STR "hello, cvm1""#)
        .line(r#"CAP_CALL "io.print" 1"#)
        .line(r#"HALT"#)
        .build()?;

    let result = Runtime::new().run(&program)?;
    assert_eq!(result.output, "hello, cvm1");
    Ok(())
}

To compile Crush source (rather than hand-written CASM), use crush_frontend::parse_source to produce a CAST Program, then compile and run it through the SDK.

A note on capabilities

Crush is capability-gated: a program can only call host functions (io.print, fs.read, net.get, …) that it declares a permission for and that the host has actually registered. The published crates give you:

  • the capability framework — HostCaps / HostCap and the SDK’s host_caps extension point to register your own handlers, plus built-in basics like io.print;
  • the stdlib module and the feature-gated host capabilities above.

The full batteries-included capability set documented in the Standard Library and Capability System chapters describes the capability interface. A host environment supplies the implementations — the exosphere agent-native OS ships the complete corecaps set; when you embed Crush in your own application you register exactly the capabilities you want to expose.

Crush Overview

Crush is a capability-based, polyglot virtual operating system (vOS) and language. It is designed to provide a secure, high-performance environment where multiple languages can coexist and interact seamlessly.

What is Crush?

Crush is both a programming language and a runtime environment. It features:

  • Everything is a Capsule: High-level isolation for all code units.
  • Capability-Based Security: Fine-grained, explicit permissions for all system interactions.
  • Polyglot Execution: First-class support for Python, Rust, Bash, C, and Go.
  • Exo-Core Architecture: A minimal virtualization layer that manages hardware interactions via a Hardware Abstraction Layer (HAL).

The Crush Language

The Crush language provides a clean, expression-oriented syntax that acts as the glue for the vOS.

fn main() {
    io.print("Hello, Crush!");
}

The @ Operator

In Crush, the @ prefix is used to denote a Capability Call. This visual marker identifies code that crosses the VM boundary to interact with the host or other capsules.

fs.read("config.json");  // Crossing the boundary to the filesystem
let x = 1 + 2;             // Internal VM computation (no @)

Core Philosophy

1. Security by Capability

Unlike traditional operating systems where permissions are tied to the user, Crush ties permissions to the Capsule. A capsule can only perform actions (like reading a file or opening a socket) if it has been explicitly granted that capability in its Capsule.toml.

2. Radical Polyglotism

Crush doesn’t force you to choose one language. You can use the best tool for the job:

  • Rust for performance-critical logic.
  • Python for data processing and AI.
  • Bash for system orchestration.
  • Crush for high-level logic and capability management.

3. Identity and Isolation

Each capsule runs in its own isolated environment. The exo-core ensures that capsules cannot interfere with each other’s memory or resources unless explicitly allowed via shared handles.

Execution Model

  1. Source: Write code in any supported language.
  2. Walker: A language-specific walker generates a Crush AST (CAST).
  3. Compiler: The Crush compiler transforms CAST into Crush Assembly (CASM).
  4. VM: The Crush VM executes CASM, enforcing security and resource limits.

Next Steps

Syntax and Grammar

This chapter covers the fundamental syntax and grammar rules of the Crush language.

Lexical Structure

Comments

// Single-line comment

/*
 * Multi-line comment
 * Can span multiple lines
 */

fn main() {
    // Comments can appear anywhere
    io.print("Hello");  // Including after statements
}

Identifiers

Identifiers (variable names, function names) must:

  • Start with a letter or underscore
  • Contain only letters, digits, and underscores
  • Not be a reserved keyword
// Valid identifiers
let x = 1;
let my_variable = 2;
let _private = 3;
let counter123 = 4;

// Invalid identifiers
// let 123abc = 5;  // Cannot start with digit
// let my-var = 6;  // Hyphens not allowed

Keywords

Reserved keywords in Crush:

fn          let         mut         if          else
while       for         in          return      break
continue    import      export      use         as
true        false       null        spawn       yield
struct      match       try         catch       throw
capability  async       await       lang

Literals

// Integer literals
let dec = 42;
let hex = 0x2A;
let bin = 0b101010;

// Float literals
let pi = 3.14159;
let sci = 1.5e-10;

// String literals
let s1 = "Hello";
let s2 = 'World';
let multiline = """
    This is a
    multi-line string
""";

// Boolean literals
let t = true;
let f = false;

// Null literal
let n = null;

Statements vs Expressions

Statements

Statements perform actions but don’t produce values:

// Variable declaration
let x = 42;

// Function definition
fn greet() {
    io.print("Hello");
}

// Control flow
if x > 0 {
    io.print("Positive");
}

// Expression statement
io.print("Hello");

Expressions

Expressions produce values:

// Arithmetic expressions
let sum = 5 + 3;

// Function calls
let result = calculate(10);

// Conditionals as expressions (future feature)
// let max = if a > b { a } else { b };

Semicolons

Semicolons are required at the end of statements:

let x = 42;
io.print(x);
return x;

Exception: The last expression in a block doesn’t need a semicolon:

fn add(a: Int, b: Int) -> Int {
    return a + b;  // Semicolon required
}

fn add_implicit(a: Int, b: Int) -> Int {
    a + b  // No semicolon (implicit return, future feature)
}

Blocks

Blocks are delimited by curly braces {}:

{
    let x = 10;
    let y = 20;
    io.print(x + y);
}

fn main() {
    // Function body is a block
    let message = "Hello";
    io.print(message);
}

if condition {
    // If body is a block
    io.print("True");
}

Variable Declaration

// Basic declaration
let x = 42;

// With type hint
let name: String = "Alice";

// Multiple declarations
let a = 1;
let b = 2;
let c = 3;

Function Definition

// Basic function
fn greet() {
    io.print("Hello!");
}

// With parameters
fn add(a: Int, b: Int) {
    return a + b;
}

// With return type
fn multiply(x: Int, y: Int) -> Int {
    return x * y;
}

// With type hints
fn process(data: String, count: Int) -> Bool {
    // ...
    return true;
}

Operators

Arithmetic

let sum = a + b;      // Addition
let diff = a - b;     // Subtraction
let prod = a * b;     // Multiplication
let quot = a / b;     // Division
let rem = a % b;      // Modulo
let neg = -a;         // Negation

Comparison

let eq = a == b;      // Equal
let ne = a != b;      // Not equal
let lt = a < b;       // Less than
let gt = a > b;       // Greater than
let le = a <= b;      // Less or equal
let ge = a >= b;      // Greater or equal

Logical

let and_result = a and b;   // Logical AND
let or_result = a or b;     // Logical OR
let not_result = not a;     // Logical NOT

String Concatenation

let greeting = "Hello, " + name + "!";
let message = "Count: " + count;  // Auto-converts to string

Operator Precedence

From highest to lowest:

  1. Function calls, field access: f(), obj.field
  2. Unary: -, not
  3. Multiplicative: *, /, %
  4. Additive: +, -
  5. Comparison: <, >, <=, >=
  6. Equality: ==, !=
  7. Logical AND: and
  8. Logical OR: or

Use parentheses to override precedence:

let result = (a + b) * c;
let condition = (x > 0) and (y < 10);

Control Flow Syntax

If Statement

if condition {
    // then block
}

if condition {
    // then block
} else {
    // else block
}

if condition1 {
    // block 1
} else if condition2 {
    // block 2
} else {
    // block 3
}

While Loop

while condition {
    // loop body
}

while i < 10 {
    io.print(i);
    i = i + 1;
}

For Loop

for item in collection {
    // loop body
}

for i in range(0, 10) {
    io.print(i);
}

Break and Continue

while true {
    if should_exit {
        break;
    }
    if should_skip {
        continue;
    }
    // ...
}

Capability Calls

Capability calls use the @ prefix:

// Basic capability call
io.print("Hello");

// With multiple arguments
fs.write("file.txt", "content");

// Storing result
let content = fs.read("file.txt");

// Chaining (if result is an object)
let data = net.http("https://api.example.com").json();

Language Blocks

Embed other languages with @language { ... }:

@python {
    print("Hello from Python")
    x = 42
}

@javascript {
    console.log("Hello from JavaScript");
    const y = 100;
}

@bash {
    echo "Hello from Bash"
    ls -la
}

Import Statements

// Import module
import std.io;

// Import with alias
import std.fs as filesystem;

// Import specific items (future feature)
// import std.io.{print, read};

Important: import creates aliases for capabilities, not direct access. The @ prefix is still required for all capability calls:

import std.io as console;

console.print("Hello");  // ✓ Correct
// console.print("Hello");  // ✗ Error: missing @

Export Statements

// Export variable for other capsules
export result = calculate();

// Export function
export fn utility() {
    // ...
}

Type Annotations

// Variable type hints
let name: String = "Alice";
let age: Int = 30;
let score: Float = 95.5;
let active: Bool = true;

// Function parameter and return types
fn calculate(x: Int, y: Int) -> Int {
    return x + y;
}

// Array types (future feature)
// let numbers: Array<Int> = [1, 2, 3];

// Map types (future feature)
// let config: Map<String, Int> = {"key": 42};

Code Organization

Single File

// Imports at top
import std.io;
import std.fs;

// Function definitions
fn helper() {
    // ...
}

fn main() {
    // Entry point
}

Multiple Files (future feature)

// lib.crush
export fn utility() {
    // ...
}

// main.crush
import lib;

fn main() {
    lib.utility();
}

Style Guidelines

Naming Conventions

// Functions: snake_case
fn calculate_total() { }

// Variables: snake_case
let user_name = "Alice";

// Constants: SCREAMING_SNAKE_CASE (future feature)
// const MAX_SIZE = 100;

// Types: PascalCase
struct UserData { }

Indentation

Use 4 spaces (not tabs):

fn main() {
    if condition {
        while loop {
            io.print("Nested");
        }
    }
}

Line Length

Keep lines under 100 characters when possible.

Spacing

// Spaces around operators
let sum = a + b;

// Space after commas
fn call(a, b, c) { }

// No space before semicolon
let x = 42;

// Space after keywords
if condition {
while loop {

Grammar Summary

program         = statement*

statement       = var_decl
                | fn_def
                | if_stmt
                | while_stmt
                | for_stmt
                | return_stmt
                | expr_stmt
                | import_stmt
                | export_stmt

var_decl        = "let" IDENT (":" type)? "=" expression ";"

fn_def          = "fn" IDENT "(" params? ")" ("->" type)? block

if_stmt         = "if" expression block ("else" (if_stmt | block))?

while_stmt      = "while" expression block

for_stmt        = "for" IDENT "in" expression block

return_stmt     = "return" expression? ";"

expr_stmt       = expression ";"

expression      = literal
                | IDENT
                | binary_op
                | unary_op
                | call
                | cap_call
                | lang_block

cap_call        = "@" IDENT "." IDENT "(" args? ")"

lang_block      = "@" IDENT "{" ... "}"

block           = "{" statement* "}"

Next Steps

Data Types

Crush has a simple, dynamic type system with optional type hints. This chapter covers all built-in types and their usage.

Primitive Types

Int

Integer numbers (64-bit signed):

let count = 42;
let negative = -100;
let hex = 0xFF;
let binary = 0b1010;

// Type hint
let age: Int = 30;

Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

Float

Floating-point numbers (64-bit IEEE 754):

let pi = 3.14159;
let scientific = 1.5e-10;
let negative = -2.5;

// Type hint
let temperature: Float = 98.6;

String

UTF-8 encoded text:

let name = "Alice";
let greeting = 'Hello';
let multiline = """
    This is a
    multi-line string
""";

// Type hint
let message: String = "Hello, World!";

String Operations:

// Concatenation
let full_name = first + " " + last;

// Length (via capability)
let len = str.len(name);

// Substring (via capability)
let sub = str.substring(text, 0, 5);

Bool

Boolean values:

let is_active = true;
let is_complete = false;

// Type hint
let flag: Bool = true;

// From comparisons
let result = x > 10;  // Bool

Bytes

Raw byte buffers for binary data:

let data = b"hello";

// Type hint
let buf: Bytes = b"data";

Error

First-class error values for exception handling:

let err = Error("file not found");

// Check error
if type.of(result) == "Error" {
    io.print("Failed: " + result.message);
}

Null

Represents absence of a value:

let empty = null;

// Type hint
let optional: String? = null;  // Future feature

// Checking for null
if value == null {
    io.print("No value");
}

Collection Types

Array

Ordered collection of values:

// Array literal
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, "two", 3.0, true];  // Mixed types allowed

// Type hint (future feature)
// let scores: Array<Int> = [90, 85, 95];

// Empty array
let empty = [];

Array Operations:

// Access by index
let first = numbers[0];
let last = numbers[4];

// Length
let len = array.length(numbers);

// Append
array.push(numbers, 6);

// Remove last
let popped = array.pop(numbers);

// Iterate
for item in numbers {
    io.print(item);
}

Map (Object)

Key-value pairs:

// Map literal
let person = {
    "name": "Alice",
    "age": 30,
    "active": true
};

// Type hint (future feature)
// let config: Map<String, Int> = {"max": 100};

// Empty map
let empty = {};

Map Operations:

// Access by key
let name = person["name"];
let age = person.age;  // Dot notation

// Set value
person["email"] = "alice@example.com";
person.phone = "555-1234";

// Check key exists
if map.has_key(person, "email") {
    io.print("Email exists");
}

// Iterate
for key in map.keys(person) {
    let value = person[key];
    io.print(key + ": " + value);
}

Type Conversion

Explicit Conversion

// Int to String
let str = conv.to_string(42);

// String to Int
let num = conv.to_int("42");

// Float to Int
let rounded = conv.to_int(3.14);

// Int to Float
let decimal = conv.to_float(42);

Implicit Conversion

String concatenation auto-converts:

let message = "Count: " + 42;  // "Count: 42"
let result = "Pi is " + 3.14;  // "Pi is 3.14"

Type Checking

Runtime Type Checking

let value = 42;

// Check type
let type_name = type.of(value);  // "Int"

if type_name == "Int" {
    io.print("It's an integer");
}

Type Hints

Type hints are optional annotations:

// Variable type hints
let name: String = "Alice";
let age: Int = 30;
let score: Float = 95.5;
let active: Bool = true;

// Function parameter types
fn greet(name: String, age: Int) {
    io.print("Hello, " + name);
}

// Function return type
fn calculate(x: Int, y: Int) -> Int {
    return x + y;
}

Note: Type hints are currently for documentation only. Runtime type checking is dynamic.

Structs

Custom data structures are implemented (struct keyword, StructDef AST node, NewStruct expression):

struct Point {
    x: Float,
    y: Float
}

fn main() {
    let p = Point { x: 10.0, y: 20.0 };
    io.print(p.x);
}

Function Type

Functions are first-class values. The Function type in hints represents any callable:

fn apply(f: Function, x: Int) -> Int {
    return f(x);
}

// Lambda type hint
let cb: Function = |x| { return x * 2; };

Optional / Nullable Types

The Type? nullable hint syntax is recognized by the parser:

let name: String? = null;

if name != null {
    io.print(name);
}

Note: The ?? null-coalescing operator is not yet implemented; use an explicit if check instead.

Enums (Future Feature)

Enumerated types are not yet in the AST:

// Not yet supported:
// enum Status { Pending, Active, Complete }

Type Aliases (Future Feature)

// Not yet supported:
// type UserId = Int;

Type Inference

Crush infers types from values:

let x = 42;           // Inferred as Int
let pi = 3.14;        // Inferred as Float
let name = "Alice";   // Inferred as String
let flag = true;      // Inferred as Bool
let items = [1, 2];   // Inferred as Array

Type Compatibility

Numeric Types

let i: Int = 42;
let f: Float = 3.14;

// Int can be used where Float expected (auto-promotion)
let sum: Float = i + f;  // 45.14

// Float to Int requires explicit conversion
let rounded: Int = conv.to_int(f);

String Concatenation

Any type can be concatenated with String:

let message = "Count: " + 42;
let info = "Pi is " + 3.14;
let status = "Active: " + true;

Type System Summary

TypeExampleNotes
Int4264-bit signed
Float3.1464-bit IEEE 754
String"Hello"UTF-8
Booltrue
Bytesb"data"raw binary buffer
ErrorError("msg")first-class error
Nullnullabsence of value
Void—function return type only
Any—dynamic/untyped hint
Array[1, 2, 3]
Map{"key": "value"}
Struct(Name)Point { x: 1.0, y: 2.0 }user-defined
Function|x| { ... }first-class callable
Optional(T)T?nullable hint

Best Practices

1. Use Type Hints for Function Signatures

// Good: Clear interface
fn calculate(x: Int, y: Int) -> Int {
    return x + y;
}

// Okay: Less clear
fn calculate(x, y) {
    return x + y;
}

2. Be Consistent with Types

// Good: Consistent types
let numbers = [1, 2, 3, 4, 5];

// Avoid: Mixed types (unless necessary)
let mixed = [1, "two", 3.0];

3. Check for Null

if value != null {
    // Safe to use value
    io.print(value);
}

4. Use Meaningful Type Names

// Good
let user_count: Int = 100;
let temperature: Float = 98.6;

// Less clear
let x: Int = 100;
let y: Float = 98.6;

Type Errors

Common type-related errors:

// Division by zero
let result = 10 / 0;  // Runtime error

// Invalid conversion
let num = conv.to_int("abc");  // Runtime error

// Null access
let value = null;
io.print(value.field);  // Runtime error

Next Steps

Variables and Scoping

This chapter covers variable declaration, assignment, scoping rules, and variable management in Crush.

Variable Declaration

Basic Declaration

let x = 42;
let name = "Alice";
let active = true;

Explicit Mutability

Use mut to mark a variable as explicitly mutable (same behavior as let today, but signals intent):

let mut counter = 0;
counter = counter + 1;

With Type Hints

let age: Int = 30;
let score: Float = 95.5;
let message: String = "Hello";
let flag: Bool = true;

Assignment and Reassignment

Variables declared with let are mutable by default:

let counter = 0;
counter = 1;
counter = counter + 1;  // counter is now 2

Compound Assignment (Future Feature)

counter += 1;   // counter = counter + 1
counter -= 1;   // counter = counter - 1
counter *= 2;   // counter = counter * 2
counter /= 2;   // counter = counter / 2

Scoping Rules

Function Scope

Variables declared in a function are local to that function:

fn example() {
    let x = 10;  // Local to example()
    io.print(x);
}

fn main() {
    example();
    // io.print(x);  // Error: x not in scope
}

Block Scope

Variables declared in a block are local to that block:

fn main() {
    let x = 10;
    
    if true {
        let y = 20;  // Local to if block
        io.print(x);  // Can access outer x
        io.print(y);
    }
    
    io.print(x);  // OK
    // io.print(y);  // Error: y not in scope
}

Global Scope (Module Level)

Variables at module level are accessible throughout the module:

let GLOBAL_CONFIG = "production";

fn get_config() -> String {
    return GLOBAL_CONFIG;
}

fn main() {
    io.print(GLOBAL_CONFIG);
}

Variable Shadowing

Inner scopes can shadow outer variables:

fn main() {
    let x = 10;
    io.print(x);  // 10
    
    {
        let x = 20;  // Shadows outer x
        io.print(x);  // 20
    }
    
    io.print(x);  // 10 (outer x restored)
}

Export and Import

Exporting Variables

// capsule_a.crush
export result = calculate();
export config = {"mode": "production"};

Importing Variables

// capsule_b.crush
let data = import.var("capsule_a", "result");
let cfg = import.var("capsule_a", "config");

Constants (Future Feature)

const PI = 3.14159;
const MAX_SIZE = 100;

// PI = 3.14;  // Error: cannot reassign constant

Variable Lifetime

Variables live until their scope ends:

fn main() {
    let x = allocate_resource();
    // x is alive
    
    if condition {
        let y = another_resource();
        // Both x and y are alive
    }  // y is destroyed here
    
    // Only x is alive
}  // x is destroyed here

Best Practices

1. Declare Variables Close to Use

// Good
fn process() {
    let data = fetch_data();
    transform(data);
}

// Less ideal
fn process() {
    let data;
    // ... many lines ...
    data = fetch_data();
    transform(data);
}

2. Use Meaningful Names

// Good
let user_count = 100;
let temperature_celsius = 25.5;

// Avoid
let x = 100;
let temp = 25.5;

3. Minimize Scope

// Good: Narrow scope
if needs_processing {
    let temp_data = process();
    use(temp_data);
}

// Less ideal: Wider scope
let temp_data;
if needs_processing {
    temp_data = process();
    use(temp_data);
}

4. Initialize Variables

// Good
let counter = 0;

// Avoid (future: may require initialization)
// let counter;
// counter = 0;

Next Steps

Operators

Crush provides a comprehensive set of operators for arithmetic, comparison, logical operations, and more.

Arithmetic Operators

Basic Arithmetic

let sum = 5 + 3;        // 8
let diff = 10 - 4;      // 6
let product = 6 * 7;    // 42
let quotient = 20 / 4;  // 5
let remainder = 17 % 5; // 2

Negation

let x = 42;
let neg = -x;  // -42

String Concatenation

The + operator concatenates strings:

let greeting = "Hello, " + "World!";
let message = "Count: " + 42;  // Auto-converts to string

Comparison Operators

let a = 10;
let b = 20;

let equal = a == b;          // false
let not_equal = a != b;      // true
let less = a < b;            // true
let greater = a > b;         // false
let less_equal = a <= b;     // true
let greater_equal = a >= b;  // false

Logical Operators

Crush supports both keyword and symbolic forms — they compile to the same instructions:

AND

let result = true and false;   // false (keyword form)
let result = true && false;    // false (symbolic form)
let check = (x > 0) && (x < 100);

OR

let result = true or false;    // true (keyword form)
let result = true || false;    // true (symbolic form)
let check = (x < 0) || (x > 100);

NOT

let result = not true;   // false (keyword form)
let result = !true;      // false (symbolic form)
let check = !(x == 0);

Range Operator

.. creates a range value (compiled to make_range):

for i in 0..10 {
    io.print(i);  // 0, 1, ..., 9
}

let r = 1..5;  // range from 1 to 5 (exclusive)

Pipeline Operator

|> passes the left-hand value as the first argument to the right-hand function (lowest precedence):

let result = data |> process |> format;
// Equivalent to: format(process(data))

let cleaned = "  hello  " |> str.trim |> str.to_upper;

Operator Precedence

From highest to lowest:

PrecedenceOperatorsDescription
1(), ., []Grouping, field access, indexing
2-, !, notUnary negation, logical NOT
3*, /, %Multiplication, division, modulo
4+, -Addition, subtraction
5<, >, <=, >=Comparison
6==, !=Equality
7&&, andLogical AND
8||, orLogical OR
9|>Pipeline

Examples

let result = 2 + 3 * 4;           // 14 (not 20)
let result = (2 + 3) * 4;         // 20
let check = x > 0 and x < 100;    // Comparison before AND
let r = fetch() |> parse |> save; // Pipeline is last

Next Steps

Control Flow

Crush provides standard control flow structures: if/else, while loops, for loops, pattern matching, and exception handling.

If Statements

Basic If

if condition {
    io.print("Condition is true");
}

If-Else

if x > 0 {
    io.print("Positive");
} else {
    io.print("Non-positive");
}

If-Else If-Else

if score >= 90 {
    io.print("A");
} else if score >= 80 {
    io.print("B");
} else if score >= 70 {
    io.print("C");
} else {
    io.print("F");
}

While Loops

Basic While

let i = 0;
while i < 5 {
    io.print(i);
    i = i + 1;
}

Infinite Loop with Break

let counter = 0;
while true {
    if counter >= 10 {
        break;
    }
    io.print(counter);
    counter = counter + 1;
}

For Loops

Iterating Over Arrays

let numbers = [1, 2, 3, 4, 5];
for num in numbers {
    io.print(num);
}

Range Iteration

Use the .. range operator (compiles to make_range):

for i in 0..10 {
    io.print(i);  // 0 through 9
}

for i in 1..=5 {
    io.print(i);  // 1 through 5 (inclusive)
}

Break and Continue

Break

Exit the loop immediately:

while true {
    if should_exit {
        break;
    }
    // ...
}

Continue

Skip to next iteration:

for i in numbers {
    if i % 2 == 0 {
        continue;  // Skip even numbers
    }
    io.print(i);
}

Pattern Matching

match is implemented — the keyword, AST node, and CASM codegen all exist:

match value {
    0 => io.print("Zero"),
    1 => io.print("One"),
    _ => io.print("Other")
}

Pattern forms supported:

  • Literal values (0, "str", true)
  • Identifier binding (x => ... binds to x)
  • Struct destructuring
  • Wildcard _

Exception Handling

try/catch/throw are fully implemented:

try {
    let data = fs.read("file.txt");
    io.print(data);
} catch error {
    io.eprint("Failed: " + error);
}

Throwing Exceptions

fn divide(a: Int, b: Int) -> Int {
    if b == 0 {
        throw "division by zero";
    }
    return a / b;
}

The compiler emits enter_try/exit_try/throw CASM instructions for these constructs.

Next Steps

Functions

Functions are first-class values in Crush. This chapter covers function definition, calling, parameters, and return values.

Function Definition

Basic Function

fn greet() {
    io.print("Hello!");
}

With Parameters

fn greet(name: String) {
    io.print("Hello, " + name + "!");
}

With Return Value

fn add(a: Int, b: Int) -> Int {
    return a + b;
}

Multiple Parameters

fn calculate(x: Int, y: Int, operation: String) -> Int {
    if operation == "add" {
        return x + y;
    } else if operation == "multiply" {
        return x * y;
    }
    return 0;
}

Function Calls

// No arguments
greet();

// With arguments
greet("Alice");

// Storing result
let sum = add(5, 3);

// Nested calls
let result = calculate(add(2, 3), 4, "multiply");

Return Values

Explicit Return

fn get_value() -> Int {
    return 42;
}

Multiple Return Points

fn check_sign(x: Int) -> String {
    if x > 0 {
        return "positive";
    } else if x < 0 {
        return "negative";
    }
    return "zero";
}

Void Functions

Functions without return values:

fn log_message(msg: String) {
    io.print("[LOG] " + msg);
    // No return statement
}

Parameters

Positional Parameters

fn create_user(name: String, age: Int, active: Bool) {
    // ...
}

create_user("Alice", 30, true);

Default Parameters (Future Feature)

fn greet(name: String = "Guest") {
    io.print("Hello, " + name);
}

greet();         // "Hello, Guest"
greet("Alice");  // "Hello, Alice"

Recursion

fn factorial(n: Int) -> Int {
    if n <= 1 {
        return 1;
    }
    return n * factorial(n - 1);
}

fn main() {
    let result = factorial(5);  // 120
    io.print(result);
}

Higher-Order Functions

Functions are first-class values and can be passed as arguments:

fn apply(f: Function, x: Int) -> Int {
    return f(x);
}

fn double(n: Int) -> Int {
    return n * 2;
}

fn main() {
    let result = apply(double, 21);  // 42
}

Lambdas and Closures

Anonymous functions use the |params| { body } syntax (the Lambda AST node):

let double = |x| { return x * 2; };
let result = double(21);  // 42

// Single-expression form
let square = |x| => x * x;

Closures capture variables from the enclosing scope:

fn make_adder(x: Int) -> Function {
    return |y| { return x + y; };
}

fn main() {
    let add_five = make_adder(5);
    let result = add_five(10);  // 15
}

Async Functions

Use async/await for asynchronous operations. spawn launches a task concurrently:

async fn fetch_data(url: String) -> String {
    let response = await net.get(url);
    return response;
}

fn main() {
    let task = spawn fetch_data("https://api.example.com/data");
    let result = await task;
    io.print(result);
}

Best Practices

1. Use Type Hints

// Good
fn calculate(x: Int, y: Int) -> Int {
    return x + y;
}

// Less clear
fn calculate(x, y) {
    return x + y;
}

2. Keep Functions Focused

// Good: Single responsibility
fn validate_email(email: String) -> Bool {
    // ...
}

fn send_email(to: String, subject: String, body: String) {
    // ...
}

3. Use Descriptive Names

// Good
fn calculate_total_price(items: Array, tax_rate: Float) -> Float {
    // ...
}

// Avoid
fn calc(x, y) {
    // ...
}

Next Steps

Capability System

The capability system is Crush’s security model. All interactions with the outside world (I/O, filesystem, network) require explicit capability permissions.

What are Capabilities?

Capabilities are permissions that grant access to external resources:

  • io.print - Print to stdout
  • io.read - Read from stdin
  • fs.read - Read files
  • fs.write - Write files
  • net.http - Make HTTP requests
  • sys.exec - Execute commands

Capability Calls

Use the @ prefix to call capabilities:

io.print("Hello, World!");

With Arguments

fs.write("file.txt", "content");
let content = fs.read("file.txt");

Storing Results

let user_input = io.read();
let file_data = fs.read("config.json");

Declaring Permissions

Capabilities must be declared in the program manifest:

{
  "manifest": {
    "permissions": {
      "io.print": true,
      "io.read": true,
      "fs.read": ["./data", "./config"],
      "fs.write": ["./output"],
      "net.connect": ["https://api.example.com"]
    }
  }
}

Permission Scoping:

  • true - Full access to that capability
  • [paths] - Restricted to specific paths or URLs
  • Omitted - Denied (default deny-by-default)

Without the permission, the capability call will fail at runtime.

Standard Capabilities

Host-provided, not bundled. crush-vm registers io.print, io.eprint, and io.read as built-in primitives. All other capabilities listed here (fs.*, net.*, sys.*) are host-provided — they must be registered by the embedding host process. The bare crush-ast crates define the capability interface; the implementation comes from the host (e.g. exosphere’s corecaps, or a custom host registration). If a cap is not registered, the call fails at runtime regardless of what the manifest declares.

I/O Capabilities

CapabilityDescriptionArgumentsReturnsScope
io.printPrint to stdoutmessage: StringnullN/A
io.readRead from stdinnoneStringN/A
io.eprintPrint to stderrmessage: StringnullN/A

Example:

io.print("Enter your name:");
let name = io.read();
io.print("Hello, " + name);

Filesystem Capabilities

CapabilityDescriptionArgumentsReturnsScope
fs.readRead filepath: StringStringPath list
fs.writeWrite filepath: String, content: StringnullPath list
fs.existsCheck if existspath: StringBoolPath list
fs.deleteDelete filepath: StringnullPath list
fs.listList directorypath: StringArray<String>Path list

Example:

if fs.exists("config.json") {
    let config = fs.read("config.json");
    io.print(config);
} else {
    fs.write("config.json", "{}");
}

System Capabilities

CapabilityDescriptionArgumentsReturns
sys.execExecute commandcmd: StringString
sys.envGet env variablename: StringString
sys.argsGet CLI argsnoneArray<String>
sys.exitExit programcode: Intnever

Example:

let args = sys.args();
if args.length < 2 {
    io.eprint("Usage: program <file>");
    sys.exit(1);
}

Network Capabilities

CapabilityDescriptionArgumentsReturns
net.httpHTTP requesturl: StringString
net.getHTTP GETurl: StringString
net.postHTTP POSTurl: String, body: StringString

Example:

let response = net.get("https://api.example.com/data");
io.print(response);

Security Model

Principle of Least Privilege

Only request capabilities you actually use:

// Good: Minimal permissions
{
  "permissions": ["io.print"]
}

// Bad: Excessive permissions
{
  "permissions": ["io.print", "fs.read", "fs.write", "net.http", "sys.exec"]
}

No Ambient Authority

Unlike traditional OS permissions, capabilities are:

  • Explicit: Must be declared in manifest
  • Granular: fs.read vs fs.write vs fs.delete
  • Auditable: Easy to see what a program can do

Capability Denial

If a program calls a capability it doesn’t have:

// Manifest: {"permissions": ["io.print"]}

fn main() {
    io.print("Hello");  // ✓ Allowed
    fs.read("file.txt");  // ✗ Runtime error: Permission denied
}

Best Practices

1. Declare Only What You Need

// Good
{
  "permissions": ["io.print", "fs.read"]
}

// Avoid
{
  "permissions": ["io.*", "fs.*", "net.*"]
}

2. Check Before Using

if fs.exists("config.json") {
    let config = fs.read("config.json");
} else {
    io.print("Config not found");
}

3. Handle Errors

// Future: try-catch
try {
    let data = fs.read("file.txt");
} catch (error) {
    io.eprint("Failed to read file: " + error);
}

Capability Composition

Capabilities can be composed:

fn read_and_print(filename: String) {
    let content = fs.read(filename);
    io.print(content);
}

// Requires both fs.read and io.print

Next Steps

What is actually implemented today

The chapters above describe the capability model. This section records, separately, which capabilities the runtime currently ships — because the two have drifted, and a guide that documents a capability the runtime does not have is worse than one that stays silent.

Verified by running each call against crush-run (built with --features stdlib):

Available

capabilitynotes
io.printbuilt-in, always available
str.concat, str.lenbuilt-in
str.trim, str.to_upper, str.substringrequire --stdlib
conv.to_intrequires --stdlib
math.*requires --stdlib
fs.read, fs.write, fs.exists, fs.listrequire --fs
env.getrequires --env
time.nowrequires --time

Run crush-run caps for the authoritative list on your build — and note that capability groups behind a Cargo feature (--stdlib, --net, --db, --graphics) will warn if that feature was not compiled in. A “not enabled in this build” warning is not the same as “does not exist.”

Not implemented — documented in this guide but NOT in the runtime

io.eprint · io.read · fs.delete · sys.exit · sys.args · sys.env · sys.exec · type.of · console.print · array.push · array.pop · array.length · map.keys · map.has_key

Examples using these will not run. They are retained here as intent, not as documentation of behaviour. If you hit one, that is a gap in the runtime, not a mistake in your code.

A note on syntax: what @ means in Crush

Capability calls are written unprefixed: io.print("hi"), not @io.print("hi"). Earlier revisions of this guide used an @ sigil; the parser has never accepted it in expression position, and every example here has been corrected.

That correction is narrow, and it is important not to over-generalise it. @ is a real and load-bearing sigil in Crush — it just does not introduce a capability call. It is overloaded across several distinct constructs, and stripping it blindly will corrupt them:

formexamplewhat it is
polyglot block@python { ... }, @javascript { ... }embed another language; the @ is required
compiler directive@gpu, @kernel, @targetsteer the backend (e.g. the PTX/GPU path)
AST / AI annotation@invariant, @decision, @covers, @writes, @synthesizetyped metadata attached to CAST nodes
capability call@io.print(...) → io.print(...)no sigil. This is the one that was wrong.

The lexer emits a single AtIdent token for all of these; what a given @name means is decided by the parser from context, not by the sigil.

So: if you are writing a tool that rewrites Crush source, do not treat @ as a single construct. Note in particular that some annotations carry a dot (@wip.started_by), so a “strip @ from anything shaped like @x.y” rule will silently eat them.

Polyglot Programming

Crush’s killer feature: embed multiple programming languages in a single program. Write Python for data science, JavaScript for JSON, Bash for system tasks, and Rust for performance - all seamlessly integrated.

Language Blocks

Use @language { ... } syntax to embed code:

fn main() {
    @python {
        print("Hello from Python!")
    }
    
    @javascript {
        console.log("Hello from JavaScript!");
    }
    
    @bash {
        echo "Hello from Bash!"
    }
}

Walker Implementation Status

Each language needs a walker — a compiler component that parses source code and emits CAST. Walkers vary in completeness:

LanguageSyntaxWalker statusSupported constructs
JavaScript/TypeScript@javascript { }CompleteDual-backend (swc primary, boa optional). Full JS + TS + JSX/TSX
Python@python { }CompleteNative frontend via rustpython-parser
Rust@rust { }CompleteNative frontend via syn
Bash@bash { }CompleteFull AST parsing via brush-parser
C / C++@c { }MatureTree-sitter-c and tree-sitter-cpp
Go@go { }MatureTree-sitter-based walker
Zig@zig { }MatureTree-sitter-based walker
Wasm@wasm { }MatureIntegration tested with .wat and WASI

Polyglot Execution Model

Each polyglot block executes inside a WebAssembly (WASM) sandbox using the WASI capability model:

┌─────────────────────────────────────────────────────┐
│                  Crush Runtime                       │
│  ┌───────────────────────────────────────────────┐  │
│  │           Wasmtime / Wasmer Runtime           │  │
│  │  ┌─────────────────────────────────────────┐  │  │
│  │  │        Language WASM Modules            │  │  │
│  │  │  • Python (RustPython/Pyodide)          │  │  │
│  │  │  • JavaScript (QuickJS-wasm)            │  │  │
│  │  │  • Rust (wasm32-wasi)                   │  │  │
│  │  │  • C/Go (Emscripten/TinyGo)             │  │  │
│  │  └─────────────────────────────────────────┘  │  │
│  │                 WASI ABI                      │  │
│  └───────────────────────────────────────────────┘  │
│                        │                             │
│                        ▼                             │
│            Crush Capability Bridge                   │
│     (Maps WASI pre-opens → Crush capabilities)      │
└─────────────────────────────────────────────────────┘

Why WASI?

  • Cross-platform: Same security on Linux, macOS, Windows
  • Built-in capability model: No custom sandboxing per language
  • Pre-opened directories: WASM modules only access granted paths
  • Industry standard: Used by Cloudflare Workers, Fastly, etc.

Execution Properties

  • Isolated: Each capsule has its own memory space
  • Capability-controlled: Capsules only receive explicitly granted capabilities
  • Type-safe: Values are marshaled through CASM-compatible types
  • Sandboxed: Cannot escape the capsule boundary or access host system directly

Example

Run in Codebucket (Codespaces)

fn main() {
    @python {
        # This capsule has ONLY io.print capability
        # Cannot access fs.write, net.http, etc.
        print("Hello from isolated Python capsule")
    }
}

Variable Sharing

Variables are shared when representable in CASM’s core type system.

Shareable Types

CASM TypePythonJavaScriptBashRustCGo
Intintnumber$VARi64int64_tint64
Floatfloatnumber$VARf64doublefloat64
Stringstrstring$VARStringchar*string
Boolboolbooleantrue/falseboolboolbool
ArraylistArrayarrayVecarray[]
MapdictObjectassocHashMapstructmap

Language-Specific Objects Stay Local

Python classes, JavaScript Promises, Rust structs, and other language-specific objects remain local to their capsule.

fn main() {
    let x = 42;  // ✓ Shareable: Int
    
    @python {
        # x is available as Python int
        result = x * 2  # ✓ Shareable: becomes Crush Int
        
        class MyClass:  # ✗ Not shareable: Python-specific
            pass
        
        obj = MyClass()  # ✗ Not shareable
    }
    
    io.print(result);  // ✓ Works: result is Int
    // io.print(obj);  // ✗ Error: obj not CASM-compatible
}

How It Works

  1. Crush variables are marshaled to language blocks as native types
  2. Language blocks can read and modify shareable values
  3. Changes are marshaled back to Crush after block execution
  4. Non-shareable objects are discarded when the capsule exits

Library Availability

Because polyglot blocks run in WASM, not all libraries are available:

✅ Allowed (Pure Computation)

  • Math, string manipulation, algorithms
  • JSON, YAML, TOML parsing
  • Regex, compression, crypto (pure implementations)
  • Data structures, collections

✅ Allowed (Via Capabilities)

  • File I/O → requires fs.* capability
  • Network → requires net.* capability
  • Stdout/stdin → requires io.* capability

❌ Blocked (By Design)

  • Raw syscalls, FFI to host
  • Process spawning (subprocess, os.system)
  • Arbitrary file access (os.open, std::fs)
  • Dynamic library loading

Key insight: Libraries that perform I/O work only if Crush grants the corresponding capability.

Python Integration

Basic Python

@python {
    import math  # Pure computation - allowed
    result = math.sqrt(16)
    print(f"Square root: {result}")
}

JSON Processing

@python {
    import json  # Pure library - allowed
    
    data = '{"name": "Alice", "age": 30}'
    parsed = json.loads(data)
    print(parsed["name"])
}

Note: Libraries like pandas, numpy, and sklearn require WASM-compatible builds. Pure computation libraries (math, json, re) work out of the box.

JavaScript Integration

JSON Processing

@javascript {
    const data = {
        name: "Alice",
        age: 30,
        scores: [90, 85, 95]
    };
    
    const json = JSON.stringify(data, null, 2);
    console.log(json);
}

Async Operations

@javascript {
    async function fetchData() {
        const response = await fetch('https://api.example.com/data');
        const data = await response.json();
        return data;
    }
    
    const result = await fetchData();
}

Bash Integration

Bash blocks execute in a restricted shell environment with only capability-backed commands:

Using Capabilities

@bash {
    # These map to Crush capabilities
    crush_print "Hello from Bash"
    
    # File operations require fs.* capabilities
    contents=$(crush_read "./data/file.txt")
    crush_print "$contents"
}

Note: Direct system commands (apt-get, systemctl, etc.) are not available. Bash blocks use capability-backed builtins.

Rust Integration

Performance-Critical Code

@rust {
    fn fibonacci(n: u64) -> u64 {
        match n {
            0 => 0,
            1 => 1,
            _ => fibonacci(n - 1) + fibonacci(n - 2)
        }
    }
    
    let result = fibonacci(20);
}

io.print("Fibonacci: " + result);

Capability Calls from Rust

Rust capsules cannot access host std directly. They must use capability calls:

@rust {
    // ✗ Invalid: Direct host access bypasses capabilities
    // use std::fs::File;
    // let file = File::create("output.txt")?;
    
    // ✓ Correct: Use capability calls
    extern "C" {
        fn fs_write(path: *const u8, data: *const u8, len: usize);
    }
    
    let data = b"Hello from Rust!";
    unsafe {
        fs_write(
            b"output.txt\0".as_ptr(),
            data.as_ptr(),
            data.len()
        );
    }
}

Security Note: Rust capsules are sandboxed like all other language capsules. They cannot access the host filesystem, network, or system calls without explicit capability grants.

C Integration

Low-Level Operations

@c {
    #include <stdio.h>
    #include <stdlib.h>
    
    int* allocate_array(int size) {
        return (int*)malloc(size * sizeof(int));
    }
    
    int sum = 0;
    for (int i = 0; i < 10; i++) {
        sum += i;
    }
}

io.print("Sum: " + sum);

Go Integration

Concurrency

@go {
    package main
    
    import (
        "fmt"
        "sync"
    )
    
    func worker(id int, wg *sync.WaitGroup) {
        defer wg.Done()
        fmt.Printf("Worker %d done\n", id)
    }
    
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    wg.Wait()
}

Complete Example: Multi-Language Pipeline

fn main() {
    // Fetch data with JavaScript
    @javascript {
        const fetch = require('node-fetch');
        const response = await fetch('https://api.github.com/repos/rust-lang/rust');
        const data = await response.json();
        repoData = data;
    }
    
    // Process with Python
    @python {
        import json
        
        # Extract relevant fields
        processed = {
            'name': repoData['name'],
            'stars': repoData['stargazers_count'],
            'forks': repoData['forks_count']
        }
        
        # Calculate popularity score
        score = processed['stars'] + processed['forks'] * 2
        processed['score'] = score
    }
    
    // Format with Rust
    @rust {
        let formatted = format!(
            "Repository: {}\nStars: {}\nForks: {}\nScore: {}",
            processed["name"],
            processed["stars"],
            processed["forks"],
            processed["score"]
        );
    }
    
    // Output
    io.print(formatted);
    
    // Save with Bash
    @bash {
        echo "$formatted" > repo_stats.txt
        cat repo_stats.txt
    }
}

Best Practices

1. Use the Right Language for the Job

// Good: Python for data processing
@python {
    import pandas as pd
    df = pd.read_csv("data.csv")
    result = df.groupby("category").sum()
}

// Good: Bash for system tasks
@bash {
    systemctl restart nginx
}

// Good: Rust for performance
@rust {
    fn compute_intensive_task() -> i64 {
        // ...
    }
}

2. Minimize Language Switches

// Less efficient: Multiple switches
@python { x = 1 }
@python { y = 2 }
@python { z = x + y }

// Better: Single block
@python {
    x = 1
    y = 2
    z = x + y
}

3. Handle Language-Specific Errors

@python {
    try:
        data = process_file("input.csv")
    except Exception as e:
        error_msg = str(e)
}

if error_msg != null {
    io.eprint("Python error: " + error_msg);
}

Variable Type Mapping

Crush TypePythonJavaScriptBashRustCGo
Intintnumber$VARi64int64_tint64
Floatfloatnumber$VARf64doublefloat64
Stringstrstring$VARStringchar*string
Boolboolbooleantrue/falseboolboolbool
ArraylistArrayarrayVecarray[]
MapdictObjectassoc arrayHashMapstructmap

Limitations

1. No Direct Function Calls Across Languages

// Not supported:
@python {
    def helper():
        return 42
}

// Can't call Python function from Crush
// let x = helper();  // Error

Workaround: Use variables:

@python {
    def helper():
        return 42
    result = helper()
}

let x = result;  // OK

2. Language Block Isolation

Each language block runs in its own context:

@python {
    x = 42
}

@python {
    # x is not available here
    # Must re-import from Crush
    print(x)  # Error unless x was exported from Crush
}

Next Steps

Standard Library

The Crush standard library provides additional capabilities and utilities beyond the core language.

Host-provided, not bundled. The capability framework and a small set of primitives (io.print, io.eprint, io.read) are built into crush-vm. Everything else — fs.*, net.*, sys.*, and the full corecaps suite — must be registered by the embedding host. The bare crush-ast crates ship the interface, not the implementation. When embedding crush-ast, only the capabilities your host explicitly registers are available at runtime.

Importing Modules

import std.io;
import std.fs;
import std.net;
import std.sys;

With Aliases

import std.fs as filesystem;
import std.io as console;

Available Modules

I/O Module (std.io)

Input/output operations:

  • io.print(message) - Print to stdout
  • io.read() - Read from stdin
  • io.eprint(message) - Print to stderr

Filesystem Module (std.fs)

File and directory operations:

  • fs.read(path) - Read file
  • fs.write(path, content) - Write file
  • fs.exists(path) - Check if exists
  • fs.delete(path) - Delete file
  • fs.list(path) - List directory

System Module (std.sys)

System-level operations:

  • sys.exec(command) - Execute command
  • sys.env(name) - Get environment variable
  • sys.args() - Get command-line arguments
  • sys.exit(code) - Exit program

Network Module (std.net)

Network operations:

  • net.http(url) - HTTP request
  • net.get(url) - HTTP GET
  • net.post(url, body) - HTTP POST

Example Usage

import std.io;
import std.fs;
import std.sys;

fn main() {
    // Get command-line arguments
    let args = sys.args();

    if args.length < 2 {
        io.eprint("Usage: program <filename>");
        sys.exit(1);
    }

    let filename = args[1];

    // Check if file exists
    if fs.exists(filename) {
        let content = fs.read(filename);
        io.print(content);
    } else {
        io.eprint("File not found: " + filename);
        sys.exit(1);
    }
}

Next Steps

Examples

Real Crush programs drawn from the exosphere workspace and the broader Crush ecosystem. Each demonstrates a distinct pattern or capability domain.

ExamplePattern
Fibonacci & FunctionsRecursion, function calls, type hints
Arrays & LoopsArray literals, for-in, break/continue
Exception Handlingtry/catch/throw
Concurrency & StructsStruct instantiation, spawn/yield
Lambdas & PipesLambda syntax, pipeline operator
Import StylesModule, MCP, capability, polyglot, external imports
System InfoSys/math capabilities, HTML generation
Build PipelineMulti-function decomposition, event sourcing, fail-fast
Async LLM Dashboardasync/await, DOM API, seahorse LLM integration

Source locations:

  • exosphere/crates/core/crush-lang/tests/fixtures/ — core language fixtures (exosphere repo)
  • exosphere/tests/language/ — integration tests
  • exosphere/examples/crush-pipefish-dashboard/ — real app
  • exosphere-apps/crates/apps/super-surfer/apps/ — web apps
  • exosphere/crates/core/crush-lang/examples/ — documented examples

Fibonacci & Functions

Source: crates/core/crush-lang/tests/fixtures/fibonacci.crush

The canonical recursion example — also validates type-hinted function signatures.

fn fib(n: Int) -> Int {
    if n <= 1 {
        return n
    }
    return fib(n - 1) + fib(n - 2)
}

fn main() {
    let result = fib(10)
    return result
}

What this shows:

  • fn name(param: Type) -> ReturnType — typed function signature
  • Recursive calls work without any special annotation
  • return is explicit; there is no implicit last-expression return

A simpler function that doubles its argument (from function_call.crush):

fn double(n: Int) {
    return n * 2
}

double(21)

Functions without a -> Type annotation implicitly return Void.

Arrays & Loops

Source: tests/language/arrays_and_loops.crush

Array creation, indexed access, for-in iteration, and loop control.

let arr = [10, 20, 30, 40, 50];
print("Array created: " + arr);

let size = len(arr);
print("Array length: " + size);

// Indexed access
print(arr[0]);   // 10
print(arr[2]);   // 30

// Iterate all elements
for x in arr {
    print("Item: " + x);
}

// break — stop at 30
for x in arr {
    if x == 30 {
        break;
    }
    print("Item: " + x);
}

// continue — skip 30
for x in arr {
    if x == 30 {
        continue;
    }
    print("Item: " + x);
}

What this shows:

  • Array literal [v1, v2, ...] and len() built-in
  • arr[i] zero-based integer indexing
  • for x in iterable { } — iterates arrays and ranges
  • break exits the loop immediately; continue skips to next iteration

String characters are also indexable:

let s = "hello";
print(s[0]);   // "h"
print(s[4]);   // "o"

Range iteration with ..:

for i in 0..10 {
    print(i);   // 0 through 9
}

Exception Handling

Source: tests/language/exception_test.crush

try/catch/throw are fully implemented — not a future feature.

print("Starting Exception Test")

try {
    print("Inside try block")
    throw "Oops"
    print("This should not print")
} catch e {
    print("Caught exception: " + e)
}

print("After catch block")

Output:

Starting Exception Test
Inside try block
Caught exception: Oops
After catch block

What this shows:

  • try { ... } catch e { ... } — the caught value binds to e
  • throw expr — throws any value as an exception (string, Int, Map, etc.)
  • Execution after throw inside the try block is skipped
  • Execution after the catch block continues normally

The compiler emits enter_try / exit_try / throw CASM instructions.

Defensive Pattern

fn safe_divide(a: Int, b: Int) -> Int {
    if b == 0 {
        throw "division by zero";
    }
    return a / b;
}

try {
    let result = safe_divide(10, 0);
    print(result);
} catch e {
    print("Error: " + e);
}

Concurrency & Structs

Source: tests/language/concurrency_structs.crush

Struct instantiation with field access, plus spawn/yield for cooperative multitasking.

// Struct instantiation
let p = new Point();
p.x = 10;
p.y = 20;
print("Point x: " + p.x);
print("Point y: " + p.y);

// Spawn a concurrent task
print("Main starting spawn");
spawn worker();

print("Main yielding 1");
yield;

print("Main yielding 2");
yield;

print("Main resumed");

fn worker() {
    print("Worker running");
    yield;
    print("Worker finishing");
}

What this shows:

  • new StructName() instantiates a struct
  • Field assignment and access via . operator
  • spawn fn() — launches a function as a cooperative task
  • yield — suspends the current task and switches to another ready task
  • spawn/yield implement M:1 cooperative concurrency (no preemption)

The tree-sitter grammar also supports struct definitions with typed fields:

struct Point {
    x: Float,
    y: Float
}

let p = Point { x: 10.0, y: 20.0 };
print(p.x);

Lambdas & the Pipeline Operator

Source: crates/core/crush-lang/walkers/tree-sitter-crush/test_lambda.crush

Two lambda forms and the |> pipeline operator.

// Block-body lambda (multiple statements)
let add = |a, b| {
    return a + b;
};

// Arrow lambda (single expression, no braces or return)
let mul = |x, y| => x * y;

// Call like any function
let sum = add(1, 2);      // 3
let product = mul(4, 5);  // 20

Typed Parameters

let add_typed = |x: Int, y: Int| => x + y;

The Pipeline Operator

|> passes the left value as the first argument to the right function:

let res = add(1, 2) |> mul(3);
// Equivalent to: mul(add(1, 2), 3) = mul(3, 3) = 9

Pipelines chain left-to-right with the lowest operator precedence:

let result = raw_data
    |> parse
    |> validate
    |> format;

Higher-Order Functions

Functions accept and return other functions:

fn apply(f: Function, x: Int) -> Int {
    return f(x);
}

fn double(n: Int) -> Int { return n * 2; }

let r = apply(double, 21);   // 42
let r2 = apply(|x| => x * x, 5);  // 25 — lambda passed inline

Closures

Lambdas capture the enclosing scope:

fn make_adder(x: Int) -> Function {
    return |y| { return x + y; };
}

let add5 = make_adder(5);
let result = add5(10);   // 15

Import Styles

Source: crates/core/crush-lang/walkers/tree-sitter-crush/test_imports.crush

Crush has several import forms. Standard modules use import; external resources and capabilities use use @.

// Standard module import
import io;
import fs as files;                    // aliased
import net { http_get, http_post };    // selective

// MCP server — wire a remote API as typed capabilities
use @mcp "https://api.github.com" { "issues.list", "repos.get" } as github;

// Capability import — grant specific cap handles under an alias
use @cap "fs.read" { "fs.read", "fs.list" } as reader;

// Polyglot import — pull a symbol from a language module
use @lang python "sys" { "version", "path" } as pysys;

// External resources
import @git "https://github.com/nixpt/exosphere.git" as exo;
import @http "https://example.com/data.json" as data;

fn main() {
    io.print("Imports working!");
}

Form Reference

FormPurpose
import moduleCrush stdlib module (io, fs, net, sys, math, time, …)
import module as aliasModule with alias
import module { a, b }Selective import — only named symbols
use @mcp "url" { tools } as aliasWire an MCP server as capabilities
use @cap "cap.path" { names } as aliasImport specific capability handles
use @lang python "module" { symbols }Import a polyglot language module
import @git "url" as aliasBind a git repository as a resource
import @http "url" as aliasBind an HTTP resource

All use @... forms are enforced by the capability system: the runtime only grants the declared access; anything undeclared is denied at execution time.

System Info Dashboard

Source: exosphere-apps/crates/apps/super-surfer/apps/sysinfo.crush

A complete Super-Surfer app that queries system capabilities and renders an HTML dashboard. Shows how Crush acts as a conductor over capability-gated host calls.

capability system readonly

fn main() {
    let hostname = sys.hostname()
    let os       = sys.os_name()
    let cpus     = sys.cpu_count()
    let cpu      = sys.cpu_usage()
    let memory   = sys.memory_info()
    let disk     = sys.disk_usage()
    let uptime   = sys.uptime()
    let processes = sys.process_count()
    let now      = time.now()

    // Round to 2 decimal places
    let mem_used  = math.round(memory.used_gb * 100) / 100
    let mem_total = math.round(memory.total_gb * 100) / 100
    let mem_pct   = math.round(memory.percent)
    let disk_pct  = math.round(disk.percent)
    let cpu_pct   = math.round(cpu)

    // Build an HTML card grid
    let html = "<div style='max-width:800px;margin:0 auto;padding:40px 32px;'>"
    let html = html + "<h1>System Information</h1>"
    let html = html + "<p>Generated at " + now + "</p>"

    let html = html + "<div style='display:grid;grid-template-columns:1fr 1fr;gap:16px;'>"

    // CPU card
    let html = html + "<div><div>CPU</div>"
    let html = html + "<div>" + str(cpu_pct) + "%</div>"
    let html = html + "<div>" + str(cpus) + " cores</div></div>"

    // Memory card
    let html = html + "<div><div>Memory</div>"
    let html = html + "<div>" + str(mem_pct) + "%</div>"
    let html = html + "<div>" + str(mem_used) + " / " + str(mem_total) + " GB</div></div>"

    let html = html + "</div>"
    let html = html + "</div>"

    return html
}

What this shows:

  • capability system readonly — manifest-level capability declaration at the top of the file
  • Struct field access: memory.used_gb, disk.percent
  • math.round() for formatting
  • str() coercion for concatenation
  • Functions that return HTML strings (Super-Surfer renders the return value)
  • The conductor pattern: Crush drives many host calls then assembles their results

Capabilities Used

CallCapability
sys.hostname()sys.hostname
sys.cpu_usage()sys.cpu_usage
sys.memory_info()sys.memory_info
sys.disk_usage()sys.disk_usage
time.now()time.now
math.round()math.round (stdlib, no cap needed)

Build Pipeline

Source: crates/core/crush-lang/examples/build_pipeline.crush Donated from nixpt/nakshatra — used in production via crush-run.

This is Crush in its intended design role: a capability-gated, event-sourced conductor for external toolchains. Crush doesn’t compile anything itself — it orchestrates native tools through capability calls, making the pipeline auditable and policy-checkable instead of an opaque shell script.

// Stub capability-bound host calls (in production these bind to
// process.spawn / event.emit capabilities and compile to cap_call).
fn run_tool(cmd: String) -> Int { return 0 }
fn emit(topic: String, name: String) -> Int { return 0 }

// Run one build step: emit start event, run the tool,
// emit success or failure, propagate the exit code.
fn step(name: String, cmd: String) -> Int {
    emit("build.step.start", name)
    let code = run_tool(cmd)
    if code != 0 {
        emit("build.step.failed", name)
        return code
    }
    emit("build.step.ok", name)
    return 0
}

fn main() -> Int {
    emit("build.start", "kernel")

    // 1. Configure
    let c1 = step("config", "cp config/kernel.config kernel/.config")
    if c1 != 0 { return c1 }

    let c2 = step("olddefconfig", "make -C kernel olddefconfig")
    if c2 != 0 { return c2 }

    // 2. Compile (Crush conducts; gcc/Kbuild does the actual work)
    let c3 = step("kernel", "make -C kernel -j8 bzImage")
    if c3 != 0 { return c3 }

    // 3. Build initramfs
    let c4 = step("initramfs", "mkinitramfs --out build/init.cpio.gz")
    if c4 != 0 { return c4 }

    // 4. Smoke-boot in QEMU
    let c5 = step("boot", "qemu-run --kernel kernel/arch/x86/boot/bzImage --initrd build/init.cpio.gz")
    if c5 != 0 { return c5 }

    emit("build.done", "kernel")
    return 0
}

main()

Patterns demonstrated:

  • Multi-function decomposition — step() encapsulates the emit-run-check pattern
  • Fail-fast via exit code propagation — if code != 0 { return code } short-circuits
  • Event sourcing — every step emits start/ok/failed events; the stream is replayable
  • Capability-bound host calls — run_tool and emit are stubs here; in production they compile to cap_call against process.spawn and event.emit capabilities
  • Self-contained example — stubs replace real caps so the file runs without host wiring

Grammar note from the source file:

spawn is a RESERVED keyword (actor concurrency) — name process-spawning helpers differently (e.g. run_tool). Statements are newline-terminated: no multi-line calls, no list literals across lines.

Async LLM Dashboard

Source: examples/crush-pipefish-dashboard/dashboard.crush

A Super-Surfer web application that integrates with pipefish (the local LLM server) via the seahorse module. Demonstrates async/await, DOM manipulation, event listeners, and try/catch around async calls.

import seahorse

let current_model = "llama3.2";
let is_generating = false;

fn init() {
    dom.set_text_content(dom.get_element_by_id("status"), "Ready");
    dom.set_text_content(dom.get_element_by_id("model-name"), current_model);
    update_model_list();
    setup_event_handlers();
    print("Dashboard initialized");
}

fn setup_event_handlers() {
    dom.add_event_listener(
        dom.get_element_by_id("generate-btn"),
        "click",
        "on_generate_click"
    );
    dom.add_event_listener(
        dom.get_element_by_id("model-select"),
        "change",
        "on_model_change"
    );
}

fn on_generate_click() {
    if is_generating {
        return;
    }

    let prompt_input = dom.get_element_by_id("prompt-input");
    let prompt = dom.get_attribute(prompt_input, "value");

    if prompt == "" {
        show_error("Please enter a prompt");
        return;
    }

    is_generating = true;
    update_ui_for_generation(true);

    let temperature = get_temperature();
    let max_tokens = get_max_tokens();

    // Fire and forget — UI stays responsive while generation runs
    generate_async(current_model, prompt, temperature, max_tokens);
}

// async fn runs in the background; the caller is not blocked
async fn generate_async(model: String, prompt: String, temp: Float, max_tokens: Int) {
    try {
        let result = await seahorse.generate(model, prompt, {
            "temperature": temp,
            "max_tokens": max_tokens
        });
        display_result(result);
    } catch error {
        show_error("Generation failed: " + error);
    }

    is_generating = false;
    update_ui_for_generation(false);
}

What this shows:

  • import module — loads the seahorse LLM client module
  • dom.get_element_by_id() / dom.set_text_content() / dom.get_attribute() — DOM capability calls
  • dom.add_event_listener(element, event, handler_name) — string-named event handler wiring
  • async fn — declares a function that runs asynchronously
  • await expr — suspends until the async value resolves
  • Fire-and-forget: generate_async(...) is called without await so the caller returns immediately
  • try { await ... } catch error { ... } — exception handling around async operations
  • Module-level mutable state: is_generating guards against double-submission

Async/Await Basics

// Simple sequential await
io.print("Test: Sequential awaits")
await async.sleep(50)
io.print("First sleep done")
await async.sleep(50)
io.print("Second sleep done")

Source: tests/language/async_test.crush

Crush AST (CAST) Specification

Version note: This document describes the v0.3 base spec. The Rust struct implementation (crush-cast/src/lib.rs) is more complete: it adds TryCatch, Throw, LangBlock, StructDef, Break, Continue, DomMutate, DomEventListener, DomQuery, Spawn, Await, Lambda, Pipeline, Range, Match, and AI-native statement/expression nodes. Walkers in the repo declare cast_version values of "0.1" (Rust/Bash) or "0.2" (Python/JS).

Overview

CAST is the intermediate representation used by Crush to enable polyglot programming. Language walkers translate source code into CAST, and the Crush compiler translates CAST into CASM bytecode.

Design Principles

  1. Language-Agnostic: CAST nodes should represent common programming constructs, not language-specific syntax
  2. Explicit Control Flow: Control flow (if/while/for) is represented explicitly, not as jumps
  3. Metadata Preservation: Each node includes meta for source language, line numbers, etc.
  4. Type Hints: Optional type annotations for static analysis

Document Structure

Top-Level CAST Document

{
  "version": "0.3",
  "entry": "main",
  "lang": "python",
  "imports": {
    "native": ["os", "sys"],
    "crush": []
  },
  "functions": {
    "main": {
      "params": [],
      "body": [ /* Statement[] */ ],
      "return_type": null,
      "meta": {}
    }
  },
  "structs": {}
}

Statement Nodes

VarDecl - Variable Declaration

{
  "type": "VarDecl",
  "name": "x",
  "value": { /* Expr */ },
  "type_hint": "int",  // Optional
  "meta": { "lang": "python", "line": 5 }
}

Export - Export Variable

{
  "type": "Export",
  "name": "result",
  "value": { /* Expr */ },
  "meta": { "lang": "python" }
}

ExprStmt - Expression Statement

{
  "type": "ExprStmt",
  "expr": { /* Expr */ },
  "meta": { "lang": "python" }
}

If - Conditional Statement

{
  "type": "If",
  "condition": { /* Expr */ },
  "then_body": [ /* Statement[] */ ],
  "else_body": [ /* Statement[] */ ],  // Optional, can be null or []
  "meta": { "lang": "python", "line": 10 }
}

While - While Loop

{
  "type": "While",
  "condition": { /* Expr */ },
  "body": [ /* Statement[] */ ],
  "meta": { "lang": "python", "line": 15 }
}

For - For Loop

{
  "type": "For",
  "iterator": "item",
  "iterable": { /* Expr */ },
  "body": [ /* Statement[] */ ],
  "meta": { "lang": "python", "line": 20 }
}

Return - Function Return

{
  "type": "Return",
  "value": { /* Expr */ },  // Optional, null for void return
  "meta": { "lang": "python" }
}

StructDef - Struct/Class Definition

{
  "type": "StructDef",
  "name": "Point",
  "fields": [
    { "name": "x", "type_hint": "int" },
    { "name": "y", "type_hint": "int" }
  ],
  "methods": [
    {
      "name": "distance",
      "params": ["other"],
      "body": [ /* Statement[] */ ],
      "return_type": "float"
    }
  ],
  "meta": { "lang": "python" }
}

Expression Nodes

Literals

IntLiteral

{
  "type": "IntLiteral",
  "value": 42,
  "meta": { "lang": "python" }
}

StringLiteral

{
  "type": "StringLiteral",
  "value": "Hello, World!",
  "meta": { "lang": "python" }
}

BoolLiteral

{
  "type": "BoolLiteral",
  "value": true,
  "meta": { "lang": "python" }
}

NullLiteral

{
  "type": "NullLiteral",
  "meta": { "lang": "python" }
}

ArrayLiteral

{
  "type": "ArrayLiteral",
  "elements": [ /* Expr[] */ ],
  "meta": { "lang": "python" }
}

MapLiteral

{
  "type": "MapLiteral",
  "entries": [
    { "key": { /* Expr */ }, "value": { /* Expr */ } }
  ],
  "meta": { "lang": "python" }
}

Var - Variable Reference

{
  "type": "Var",
  "name": "x",
  "meta": { "lang": "python" }
}

BinaryOp - Binary Operation

{
  "type": "BinaryOp",
  "operator": "+",  // +, -, *, /, %, ==, !=, <, >, <=, >=, and, or
  "left": { /* Expr */ },
  "right": { /* Expr */ },
  "meta": { "lang": "python" }
}

UnaryOp - Unary Operation

{
  "type": "UnaryOp",
  "operator": "-",  // -, not, ~
  "operand": { /* Expr */ },
  "meta": { "lang": "python" }
}

Call - Function Call

{
  "type": "Call",
  "function": "add",
  "args": [ /* Expr[] */ ],
  "meta": { "lang": "python" }
}

CapabilityCall - Capability Invocation

{
  "type": "CapabilityCall",
  "name": "io.print",
  "args": [ /* Expr[] */ ],
  "meta": {
    "capability": true,
    "namespace": "io",
    "method": "print",
    "lang": "python"
  }
}

FieldAccess - Struct Field Access

{
  "type": "FieldAccess",
  "object": { /* Expr */ },
  "field": "x",
  "meta": { "lang": "python" }
}

Index - Array/Map Indexing

{
  "type": "Index",
  "object": { /* Expr */ },
  "index": { /* Expr */ },
  "meta": { "lang": "python" }
}

Metadata

All nodes should include a meta field with:

  • lang: Source language (e.g., “python”, “rust”, “c”)
  • line: Optional line number in source
  • column: Optional column number in source
  • Additional language-specific metadata as needed

Type Hints

Optional type_hint fields can be:

  • Primitives: "int", "float", "str", "bool"
  • Collections: "array", "map"
  • Custom: "StructName"
  • Generic: "Array<int>", "Map<str, int>"

Compilation Strategy

Control Flow → CASM

If Statement:

compile(condition)
jmp_if_not else_label
compile(then_body)
jmp end_label
else_label:
compile(else_body)
end_label:

While Loop:

loop_start:
compile(condition)
jmp_if_not loop_end
compile(body)
jmp loop_start
loop_end:

For Loop (requires iterator protocol):

compile(iterable)
store __iter
loop_start:
load __iter
cap_call iter.next 1
dup
jmp_if_not loop_end
store iterator
compile(body)
jmp loop_start
loop_end:
pop

Version History

  • v0.3: Added control flow nodes (If, While, For), data structures (StructDef), and formal specification
  • v0.2: Added CapabilityCall, Import, Export
  • v0.1: Initial version with basic expressions and statements

AI-Native CAST

The Doctrine

CAST is JSON. Any agent can emit it directly — no walker, no compiler front-end, no source-to-AST pipeline required. An agent that understands the CAST schema can produce a complete, runnable program as a single JSON document and hand it to crush_lang::compile_cast() to get CASM bytecode.

This is the AI-native doctrine (s107 / EXO-175): the primary authoring surface for AI agents in Exosphere is CAST, not Crush source code.

Agent output (JSON)  →  compile_cast()  →  CASM  →  crush-vm (CVM1)
                ↑
    No lexer, no parser, no walker.
    The agent IS the front-end.

Validation before compilation:

#![allow(unused)]
fn main() {
// standalone crush-ast
crush_cast::validate_json(&cast_json)?;  // schema check
let casm = crush_frontend::compile(&cast_json)?;

// exosphere embedding (crush_lang re-exports the same pipeline)
let casm = crush_lang::compile_cast(&cast_json)?;
}

Program Skeleton

Every CAST document has this top-level shape:

{
  "cast_version": "0.1.0",
  "entry": "main",
  "lang": null,
  "functions": {
    "main": {
      "params": [],
      "body": [ /* Statement[] */ ],
      "meta": {}
    }
  },
  "ai_meta": null
}

Set "lang": "agent" (or any string) to identify the emitting agent in source maps. Set "ai_meta" to attach program-level metadata (see below).


AI Expression Nodes

Five "type": "AI" expression variants. They compile to the ai_* CASM instruction family.

Query

Natural-language query execution. The runtime resolves query against the available LLM/tool context and returns a typed value.

{
  "type": "VarDecl",
  "name": "answer",
  "value": {
    "type": "AI",
    "ai_type": "Query",
    "query": "Answer this question concisely",
    "result_type": "string",
    "context": {
      "question": "What is the capital of France?"
    }
  },
  "type_hint": "String",
  "meta": {}
}

ToolChain

Orchestrate a sequence (or parallel set) of tool calls. result_binding names where each tool’s output is stored for downstream tools.

{
  "type": "AI",
  "ai_type": "ToolChain",
  "tools": [
    {
      "tool_name": "search",
      "parameters": { "query": "Python best practices" },
      "result_binding": "search_results"
    },
    {
      "tool_name": "analyze",
      "parameters": { "text": "search_results" },
      "result_binding": "analysis"
    },
    {
      "tool_name": "summarize",
      "parameters": { "input": "analysis" },
      "result_binding": "summary"
    }
  ],
  "strategy": { "type": "Sequential" },
  "error_handling": {
    "type": "Retry",
    "max_retries": 2,
    "retry_condition": "status != ok"
  }
}

strategy options: Sequential · Parallel · Conditional · Retry

error_handling options: FailFast · ContinueOnError · Retry { max_retries } · Fallback

AgentDelegation

Delegate a task to one or more agents. The delegation_strategy controls how agents are selected and results are combined.

{
  "type": "AI",
  "ai_type": "AgentDelegation",
  "task": "Review the diff on branch agent/castbook/EXO-175 for unsoundness",
  "agents": ["agent://reviewers/*"],
  "delegation_strategy": { "Consensus": { "threshold": 0.66 } },
  "expected_format": "markdown"
}

delegation_strategy options: FirstAvailable · CapabilityMatch · ParallelSplit · Hierarchical · { "Consensus": { "threshold": 0.0–1.0 } } · Broadcast · Best · RoundRobin

LearningLoop

Record patterns from execution and adapt future behavior.

{
  "type": "AI",
  "ai_type": "LearningLoop",
  "learning_target": "ExecutionPatterns",
  "strategy": "PatternRecognition",
  "adaptations": ["OptimizeToolChain", "LearnNewPatterns"]
}

ContextAware

Wrap an expression with explicit context requirements and provisions. The runtime ensures the required context is present before evaluating expression.

{
  "type": "AI",
  "ai_type": "ContextAware",
  "expression": {
    "type": "AI",
    "ai_type": "Query",
    "query": "Summarize the review consensus in two sentences",
    "result_type": "string",
    "context": {}
  },
  "requires_context": ["session.goal", "review.findings"],
  "provides_context": ["review.summary"]
}

AI Statement Nodes

Five coordination statements at the top level of a function body. They do not produce values — they signal intent to the agent runtime.

GoalDeclaration

{
  "type": "AI",
  "ai_type": "GoalDeclaration",
  "goal": "Ship EXO-175 with 80% schema coverage",
  "success_criteria": ["all examples validate", "no FP on real fleet"],
  "deadline": "2026-06-30T00:00:00Z",
  "meta": {}
}

ProgressUpdate

{
  "type": "AI",
  "ai_type": "ProgressUpdate",
  "goal_id": "EXO-175",
  "progress": 0.65,
  "status": "in-progress",
  "notes": "core examples done; AI-native chapter in flight",
  "meta": {}
}

KnowledgeSharing

Share a learned insight with other agents in the fleet.

{
  "type": "AI",
  "ai_type": "KnowledgeSharing",
  "knowledge_type": "Insight",
  "content": { "finding": "review consensus reached", "confidence": 0.9 },
  "recipients": ["agent://reviewers/*", "foreman"],
  "retention_policy": "Session",
  "meta": {}
}

CapabilityDiscovery

Broadcast a request to find agents that can handle a domain.

{
  "type": "AI",
  "ai_type": "CapabilityDiscovery",
  "domain": "code-review",
  "requirements": ["rust", "security-analysis"],
  "discovery_strategy": "Broadcast",
  "meta": {}
}

AdaptationRequest

Request a runtime or coordination change.

{
  "type": "AI",
  "ai_type": "AdaptationRequest",
  "adaptation_type": "Performance",
  "reason": "review latency above target",
  "parameters": { "max_parallel_reviews": 4 },
  "meta": {}
}

Program-Level AI Metadata

The top-level ai_meta field lets an agent describe the whole program:

{
  "ai_meta": {
    "description": "Demonstrates the AI-native orchestration primitives end to end.",
    "ai_tags": ["orchestration", "delegation", "learning"],
    "required_capabilities": ["ai.query", "ai.agent_delegation"],
    "execution_context": {
      "environment": ["exosphere"],
      "resources": [],
      "permissions": ["ai.query"],
      "dependencies": []
    },
    "learning_objectives": ["optimize delegation latency"],
    "collaboration_patterns": ["consensus", "broadcast"]
  }
}

These fields are metadata only — they do not affect CASM compilation — but the Exosphere runtime uses them for scheduling, capability pre-checks, and audit logs.


Complete Example: Agent Orchestration

The following is a real CAST document from examples/cast/ai-orchestration.cast.json. It is the canonical reference for all five AI expression and statement types together.

{
  "cast_version": "0.1.0",
  "entry": "main",
  "lang": null,
  "functions": {
    "main": {
      "params": [],
      "body": [
        {
          "type": "AI",
          "ai_type": "CapabilityDiscovery",
          "domain": "code-review",
          "requirements": ["rust", "security-analysis"],
          "discovery_strategy": "Broadcast",
          "meta": {}
        },
        {
          "type": "VarDecl",
          "name": "review",
          "value": {
            "type": "AI",
            "ai_type": "AgentDelegation",
            "task": "Review the diff on branch agent/castbook/EXO-175 for unsoundness",
            "agents": ["agent://reviewers/*"],
            "delegation_strategy": { "Consensus": { "threshold": 0.66 } },
            "expected_format": "markdown"
          },
          "type_hint": "Any",
          "meta": {}
        },
        {
          "type": "AI",
          "ai_type": "KnowledgeSharing",
          "knowledge_type": "Insight",
          "content": { "finding": "review consensus reached", "confidence": 0.9 },
          "recipients": ["agent://reviewers/*", "foreman"],
          "retention_policy": "Session",
          "meta": {}
        },
        {
          "type": "VarDecl",
          "name": "insight",
          "value": {
            "type": "AI",
            "ai_type": "LearningLoop",
            "learning_target": "ExecutionPatterns",
            "strategy": "PatternRecognition",
            "adaptations": ["OptimizeToolChain", "LearnNewPatterns"]
          },
          "type_hint": "Any",
          "meta": {}
        },
        {
          "type": "AI",
          "ai_type": "AdaptationRequest",
          "adaptation_type": "Performance",
          "reason": "review latency above target",
          "parameters": { "max_parallel_reviews": 4 },
          "meta": {}
        },
        {
          "type": "VarDecl",
          "name": "summary",
          "value": {
            "type": "AI",
            "ai_type": "ContextAware",
            "expression": {
              "type": "AI",
              "ai_type": "Query",
              "query": "Summarize the review consensus in two sentences",
              "result_type": "string",
              "context": {}
            },
            "requires_context": ["session.goal", "review.findings"],
            "provides_context": ["review.summary"]
          },
          "type_hint": "Any",
          "meta": {}
        },
        {
          "type": "Export",
          "name": "summary",
          "value": { "type": "Var", "name": "summary" },
          "meta": {}
        }
      ],
      "meta": {}
    }
  },
  "ai_meta": {
    "description": "Demonstrates the AI-native orchestration primitives end to end.",
    "ai_tags": ["orchestration", "delegation", "learning"],
    "required_capabilities": ["ai.query", "ai.agent_delegation"]
  }
}

CASM Instructions Emitted

CAST nodeCASM instruction
AI / Queryai_query
AI / ToolChainai_tool_chain
AI / AgentDelegationai_agent_delegation
AI / LearningLoopai_learning_loop
AI / ContextAwareai_context_aware
AI / GoalDeclarationai_goal_decl
AI / ProgressUpdateai_progress_update
AI / KnowledgeSharingai_knowledge_share
AI / CapabilityDiscoveryai_capability_discovery
AI / AdaptationRequest(maps to ai_context_aware + runtime signal)

See Also

CASM Overview

CASM (Crush Assembly) is the low-level, target-independent instruction set for the Crush Virtual Machine. It is the final stage of the compilation pipeline before execution.

What is CASM?

CASM is a stack-based assembly language designed for high-performance and secure execution. It serves as the universal “lingua franca” of the Crush ecosystem:

  • Unified Target: Whether source code is written in Python, Rust, or Crush, it eventually becomes CASM.
  • Explicit Effects: Every system interaction is visible as a cap_call (capability call).
  • Rich Metadata: CASM preserves source line and file information for precise error reporting.
  • Portability: Programs are serialized as JSON (for development) or MessagePack (for distribution).

The Execution Model

Stack-Based Logic

Like WebAssembly or JVM bytecode, CASM operates on a stack:

{"op": "push_int", "value": 10}
{"op": "push_int", "value": 20}
{"op": "add"} // Pops 20, Pops 10, Pushes 30

Capability-First Security

CASM does not have instructions for raw syscalls. All host interactions must go through the capability system:

{"op": "push_str", "value": "logs.txt"}
{"op": "cap_call", "name": "fs.read", "argc": 1}

The VM validates this call against the capsule’s manifest at runtime. If the fs.read capability was not granted, the execution is immediately terminated.

Key Features

1. Unified Value System

CASM uses a unified RuntimeValue system that supports:

  • Primitives: Int, Float, Bool, Null.
  • References: String, Array, Map (managed in the VM Arena).
  • Functions: First-class function handles.

2. Arena-Based Memory

All complex objects in CASM are created within the VM’s Arena. This ensures that memory management is deterministic and that capsules remain strictly isolated from each other.

3. Source Mapping

Each instruction in a .casm file can include a meta block:

{
  "op": "add",
  "meta": {
    "file": "main.py",
    "line": 10
  }
}

If an error occurs, the VM uses this metadata to provide a human-readable trace back to the original source code.

File Structure

A .casm capsule is a JSON object containing:

  • manifest: Metadata and capability requirements.
  • functions: A collection of named blocks of code.
  • entry: The name of the function to execute first.

Next Steps

Instruction Set Reference

This is the complete reference for all CASM instructions. Each instruction is documented with its syntax, stack effects, parameters, description, and examples.

Reading Stack Effects

Stack effects show how instructions modify the stack:

before → after

For example:

  • a, b → result means: pop b, pop a, push result
  • value → means: pop value (nothing pushed)
  • → value means: push value (nothing popped)

Instruction Categories


Stack Operations

push_int

Push an integer onto the stack.

Syntax:

{"op": "push_int", "value": 42}

Stack Effect: → int

Parameters:

  • value (Integer): The integer value to push

Example:

{"op": "push_int", "value": 100}
// Stack: [100]

push_float

Push a floating-point number onto the stack.

Syntax:

{"op": "push_float", "value": 3.14}

Stack Effect: → float

Parameters:

  • value (Float): The float value to push

Example:

{"op": "push_float", "value": 2.718}
// Stack: [2.718]

push_str

Push a string onto the stack.

Syntax:

{"op": "push_str", "value": "Hello"}

Stack Effect: → string

Parameters:

  • value (String): The string value to push

Example:

{"op": "push_str", "value": "Hello, World!"}
// Stack: ["Hello, World!"]

push_bool

Push a boolean onto the stack.

Syntax:

{"op": "push_bool", "value": true}

Stack Effect: → bool

Parameters:

  • value (Boolean): true or false

Example:

{"op": "push_bool", "value": false}
// Stack: [false]

push_null

Push a null value onto the stack.

Syntax:

{"op": "push_null"}

Stack Effect: → null

Parameters: None

Example:

{"op": "push_null"}
// Stack: [null]

pop

Remove the top value from the stack.

Syntax:

{"op": "pop"}

Stack Effect: value →

Parameters: None

Example:

{"op": "push_int", "value": 42}
{"op": "pop"}
// Stack: []

dup

Duplicate the top stack value.

Syntax:

{"op": "dup"}

Stack Effect: value → value, value

Parameters: None

Example:

{"op": "push_int", "value": 5}
{"op": "dup"}
// Stack: [5, 5]

Memory Operations

store

Store the top stack value in a variable.

Syntax:

{"op": "store", "name": "variable_name"}

Stack Effect: value →

Parameters:

  • name (String): Variable name

Example:

{"op": "push_int", "value": 42}
{"op": "store", "name": "x"}
// Variable x = 42
// Stack: []

load

Load a variable’s value onto the stack.

Syntax:

{"op": "load", "name": "variable_name"}

Stack Effect: → value

Parameters:

  • name (String): Variable name

Example:

{"op": "load", "name": "x"}
// Stack: [42] (assuming x = 42)

export_var

Export a variable to the capsule’s export table.

Syntax:

{"op": "export_var", "name": "variable_name"}

Stack Effect: value →

Parameters:

  • name (String): Variable name to export

Example:

{"op": "push_int", "value": 100}
{"op": "export_var", "name": "result"}
// Exports result = 100 for other capsules

import_var

Import a variable from another capsule.

Syntax:

{"op": "import_var", "name": "variable_name"}

Stack Effect: → value

Parameters:

  • name (String): Variable name to import

Example:

{"op": "import_var", "name": "config"}
// Stack: [<imported value>]

Arithmetic Operations

add

Add two values.

Syntax:

{"op": "add"}

Stack Effect: a, b → result

Parameters: None

Behavior:

  • Numbers: arithmetic addition
  • Strings: concatenation

Example:

{"op": "push_int", "value": 5}
{"op": "push_int", "value": 3}
{"op": "add"}
// Stack: [8]

sub

Subtract two numbers.

Syntax:

{"op": "sub"}

Stack Effect: a, b → result

Parameters: None

Example:

{"op": "push_int", "value": 10}
{"op": "push_int", "value": 3}
{"op": "sub"}
// Stack: [7]  (10 - 3)

mul

Multiply two numbers.

Syntax:

{"op": "mul"}

Stack Effect: a, b → result

Parameters: None

Example:

{"op": "push_int", "value": 6}
{"op": "push_int", "value": 7}
{"op": "mul"}
// Stack: [42]

div

Divide two numbers.

Syntax:

{"op": "div"}

Stack Effect: a, b → result

Parameters: None

Example:

{"op": "push_int", "value": 20}
{"op": "push_int", "value": 4}
{"op": "div"}
// Stack: [5]  (20 / 4)

mod

Compute modulo (remainder).

Syntax:

{"op": "mod"}

Stack Effect: a, b → result

Parameters: None

Example:

{"op": "push_int", "value": 17}
{"op": "push_int", "value": 5}
{"op": "mod"}
// Stack: [2]  (17 % 5)

neg

Negate a number.

Syntax:

{"op": "neg"}

Stack Effect: value → -value

Parameters: None

Example:

{"op": "push_int", "value": 42}
{"op": "neg"}
// Stack: [-42]

Comparison Operations

eq

Test equality.

Syntax:

{"op": "eq"}

Stack Effect: a, b → bool

Parameters: None

Example:

{"op": "push_int", "value": 5}
{"op": "push_int", "value": 5}
{"op": "eq"}
// Stack: [true]

ne

Test inequality.

Syntax:

{"op": "ne"}

Stack Effect: a, b → bool

Parameters: None

Example:

{"op": "push_int", "value": 5}
{"op": "push_int", "value": 3}
{"op": "ne"}
// Stack: [true]

lt

Test less than.

Syntax:

{"op": "lt"}

Stack Effect: a, b → bool

Parameters: None

Example:

{"op": "push_int", "value": 3}
{"op": "push_int", "value": 5}
{"op": "lt"}
// Stack: [true]  (3 < 5)

gt

Test greater than.

Syntax:

{"op": "gt"}

Stack Effect: a, b → bool

Parameters: None

Example:

{"op": "push_int", "value": 7}
{"op": "push_int", "value": 3}
{"op": "gt"}
// Stack: [true]  (7 > 3)

le

Test less than or equal.

Syntax:

{"op": "le"}

Stack Effect: a, b → bool

Parameters: None

Example:

{"op": "push_int", "value": 5}
{"op": "push_int", "value": 5}
{"op": "le"}
// Stack: [true]  (5 <= 5)

ge

Test greater than or equal.

Syntax:

{"op": "ge"}

Stack Effect: a, b → bool

Parameters: None

Example:

{"op": "push_int", "value": 8}
{"op": "push_int", "value": 3}
{"op": "ge"}
// Stack: [true]  (8 >= 3)

Logical Operations

and

Logical AND.

Syntax:

{"op": "and"}

Stack Effect: a, b → bool

Parameters: None

Example:

{"op": "push_bool", "value": true}
{"op": "push_bool", "value": false}
{"op": "and"}
// Stack: [false]

or

Logical OR.

Syntax:

{"op": "or"}

Stack Effect: a, b → bool

Parameters: None

Example:

{"op": "push_bool", "value": true}
{"op": "push_bool", "value": false}
{"op": "or"}
// Stack: [true]

not

Logical NOT.

Syntax:

{"op": "not"}

Stack Effect: value → bool

Parameters: None

Example:

{"op": "push_bool", "value": true}
{"op": "not"}
// Stack: [false]

Bitwise Operations

bit_and

Bitwise AND.

Syntax:

{"op": "bit_and"}

Stack Effect: a, b → result

Parameters: None

Example:

{"op": "push_int", "value": 12}  // 1100
{"op": "push_int", "value": 10}  // 1010
{"op": "bit_and"}
// Stack: [8]  // 1000

bit_or

Bitwise OR.

Syntax:

{"op": "bit_or"}

Stack Effect: a, b → result

Parameters: None


bit_xor

Bitwise XOR.

Syntax:

{"op": "bit_xor"}

Stack Effect: a, b → result

Parameters: None


bit_not

Bitwise NOT.

Syntax:

{"op": "bit_not"}

Stack Effect: value → result

Parameters: None


shl

Shift left.

Syntax:

{"op": "shl"}

Stack Effect: value, shift → result

Parameters: None

Example:

{"op": "push_int", "value": 5}   // 101
{"op": "push_int", "value": 2}
{"op": "shl"}
// Stack: [20]  // 10100

shr

Shift right.

Syntax:

{"op": "shr"}

Stack Effect: value, shift → result

Parameters: None


Stack Manipulation

swap

Swap the top two stack values.

Syntax:

{"op": "swap"}

Stack Effect: a, b → b, a

Parameters: None

Example:

{"op": "push_int", "value": 1}
{"op": "push_int", "value": 2}
{"op": "swap"}
// Stack: [1, 2] → [2, 1]

rot

Rotate top three values.

Syntax:

{"op": "rot"}

Stack Effect: a, b, c → b, c, a

Parameters: None


pick

Copy nth item from stack top.

Syntax:

{"op": "pick", "n": 2}

Stack Effect: ..., a, b, c → ..., a, b, c, a

Parameters:

  • n (Integer): Depth to pick from (0 = top)

roll

Move nth item to stack top.

Syntax:

{"op": "roll", "n": 2}

Stack Effect: ..., a, b, c → ..., b, c, a

Parameters:

  • n (Integer): Depth to roll from

Control Flow

jmp

Unconditional jump.

Syntax:

{"op": "jmp", "target": 10}

Stack Effect: (none)

Parameters:

  • target (Integer): Instruction index to jump to

Example:

{"op": "jmp", "target": 5}
// Jump to instruction 5

jmp_if

Jump if true.

Syntax:

{"op": "jmp_if", "target": 10}

Stack Effect: condition →

Parameters:

  • target (Integer): Instruction index to jump to if true

Example:

{"op": "push_bool", "value": true}
{"op": "jmp_if", "target": 5}
// Jumps to instruction 5

jmp_if_not

Jump if false.

Syntax:

{"op": "jmp_if_not", "target": 10}

Stack Effect: condition →

Parameters:

  • target (Integer): Instruction index to jump to if false

call

Call a function.

Syntax:

{"op": "call", "function": "function_name"}

Stack Effect: arg1, arg2, ... → return_value

Parameters:

  • function (String): Name of function to call

Example:

{"op": "push_int", "value": 5}
{"op": "push_int", "value": 3}
{"op": "call", "function": "add"}
// Calls add(5, 3), pushes result

ret

Return from function.

Syntax:

{"op": "ret"}

Stack Effect: return_value → (to caller’s stack)

Parameters: None

Example:

{"op": "push_int", "value": 42}
{"op": "ret"}
// Returns 42 to caller

break

Exit innermost loop.

Syntax:

{"op": "break"}

Stack Effect: (none)

Parameters: None


continue

Jump to loop start.

Syntax:

{"op": "continue"}

Stack Effect: (none)

Parameters: None


Array Operations

new_array

Create array from stack values.

Syntax:

{"op": "new_array", "size": 3}

Stack Effect: v1, v2, v3 → array

Parameters:

  • size (Integer): Number of elements to pop

Example:

{"op": "push_int", "value": 1}
{"op": "push_int", "value": 2}
{"op": "push_int", "value": 3}
{"op": "new_array", "size": 3}
// Stack: [[1, 2, 3]]

arr_get

Get array element.

Syntax:

{"op": "arr_get"}

Stack Effect: array, index → value

Parameters: None

Example:

// Assuming array = [10, 20, 30]
{"op": "load", "name": "arr"}
{"op": "push_int", "value": 1}
{"op": "arr_get"}
// Stack: [20]

arr_set

Set array element.

Syntax:

{"op": "arr_set"}

Stack Effect: array, index, value → array

Parameters: None


arr_len

Get array length.

Syntax:

{"op": "arr_len"}

Stack Effect: array → length

Parameters: None


arr_push

Append to array.

Syntax:

{"op": "arr_push"}

Stack Effect: array, value → array

Parameters: None


arr_pop

Remove last element.

Syntax:

{"op": "arr_pop"}

Stack Effect: array → array, value

Parameters: None


Object Operations

new_obj

Create empty object.

Syntax:

{"op": "new_obj"}

Stack Effect: → object

Parameters: None


new_struct

Create named struct.

Syntax:

{"op": "new_struct", "name": "Point"}

Stack Effect: → struct

Parameters:

  • name (String): Struct type name

get_field

Get object field.

Syntax:

{"op": "get_field", "name": "field_name"}

Stack Effect: object → value

Parameters:

  • name (String): Field name

Example:

{"op": "load", "name": "point"}
{"op": "get_field", "name": "x"}
// Stack: [<x value>]

set_field

Set object field.

Syntax:

{"op": "set_field", "name": "field_name"}

Stack Effect: object, value → object

Parameters:

  • name (String): Field name

Type Operations

type_of

Get type of value.

Syntax:

{"op": "type_of"}

Stack Effect: value → type_string

Parameters: None

Example:

{"op": "push_int", "value": 42}
{"op": "type_of"}
// Stack: ["int"]

cast

Cast value to type.

Syntax:

{"op": "cast", "type": "int"}

Stack Effect: value → casted_value

Parameters:

  • type (String): Target type

Capability Calls

cap_call

Invoke a capability.

Syntax:

{"op": "cap_call", "name": "io.print", "argc": 1}

Stack Effect: arg1, arg2, ... → return_value

Parameters:

  • name (String): Capability name (format: namespace.method)
  • argc (Integer): Number of arguments

Example:

{"op": "push_str", "value": "Hello!"}
{"op": "cap_call", "name": "io.print", "argc": 1}
// Prints "Hello!" to stdout

Common Capabilities:

  • io.print - Print to stdout
  • io.read - Read from stdin
  • fs.read - Read file
  • fs.write - Write file
  • net.http - HTTP request
  • sys.exec - Execute command

Concurrency

spawn

Create new task.

Syntax:

{"op": "spawn"}

Stack Effect: function_name, args... → task_id

Parameters: None


yield

Yield execution.

Syntax:

{"op": "yield"}

Stack Effect: (none)

Parameters: None


Quick Reference Table

CategoryInstructions
Stackpush_int, push_float, push_str, push_bool, push_null, pop, dup
Memorystore, load, export_var, import_var
Arithmeticadd, sub, mul, div, mod, neg
Comparisoneq, ne, lt, gt, le, ge
Logicaland, or, not
Bitwisebit_and, bit_or, bit_xor, bit_not, shl, shr
Stack Manipswap, rot, pick, roll
Control Flowjmp, jmp_if, jmp_if_not, call, ret, break, continue
Arraysnew_array, arr_get, arr_set, arr_len, arr_push, arr_pop
Objectsnew_obj, new_struct, get_field, set_field
Rangesmake_range
Typestype_of, cast
Exceptionsenter_try, exit_try, throw
Capabilitiescap_call
Concurrencyspawn, yield, await
Polyglotexec_lang
DOMdom_query, dom_mutate, dom_event_listener
AI-nativeai_query, ai_tool_chain, ai_agent_delegation, ai_learning_loop, ai_context_aware, ai_goal_decl, ai_progress_update, ai_knowledge_share, ai_capability_discovery

Program Structure

A CASM program is a JSON document with a well-defined structure. This chapter explains each component in detail.

Top-Level Structure

{
  "version": "0.1",
  "functions": { /* ... */ },
  "lang": "python",
  "manifest": { /* ... */ }
}

Fields

FieldTypeRequiredDescription
versionString✓CASM format version (currently "0.1")
functionsObject✓Map of function name to function definition
langString✗Source language (e.g., "python", "crush", "rust")
manifestObject✗Capability permissions and metadata

Function Structure

Each function in the functions object has this structure:

{
  "params": ["arg1", "arg2"],
  "locals": ["temp", "counter"],
  "body": [ /* instructions */ ]
}

Function Fields

FieldTypeRequiredDescription
paramsArray<String>✗Parameter names (default: [])
localsArray<String>✗Local variable names (default: [])
bodyArray<Instruction>✓Instruction sequence

Parameters vs Locals

  • Parameters: Function arguments, bound when the function is called
  • Locals: Additional local variables declared in the function

Example:

{
  "functions": {
    "add": {
      "params": ["a", "b"],
      "locals": ["result"],
      "body": [
        {"op": "load", "name": "a"},
        {"op": "load", "name": "b"},
        {"op": "add"},
        {"op": "store", "name": "result"},
        {"op": "load", "name": "result"},
        {"op": "ret"}
      ]
    }
  }
}

Instruction Structure

Each instruction is a JSON object with these fields:

{
  "op": "push_int",
  "value": 42,
  "lang": "python",
  "meta": {
    "file": "script.py",
    "line": 10,
    "column": 5
  }
}

Instruction Fields

FieldTypeRequiredDescription
opString✓Operation name (e.g., "push_int", "add")
langString✗Source language for this instruction
metaObject✗Metadata (file, line, column, etc.)
othersVariousVariesOperation-specific arguments

Operation-Specific Arguments

Different operations require different arguments. These are flattened into the instruction object:

// push_int requires "value"
{"op": "push_int", "value": 42}

// store requires "name"
{"op": "store", "name": "x"}

// cap_call requires "name" and "argc"
{"op": "cap_call", "name": "io.print", "argc": 1}

// jmp requires "target"
{"op": "jmp", "target": 10}

See the Instruction Set Reference for complete details on each operation’s arguments.

Metadata

The meta field can contain arbitrary JSON data, but these fields have special meaning:

FieldTypeDescription
fileStringSource file path
lineIntegerLine number in source file
columnIntegerColumn number in source file
langStringSource language

Example with full metadata:

{
  "op": "push_str",
  "value": "Hello",
  "lang": "crush",
  "meta": {
    "file": "examples/hello.crush",
    "line": 4,
    "column": 15,
    "lang": "crush"
  }
}

This metadata is used by the VM to provide accurate error messages:

Error at examples/hello.crush:4:15
  |
4 |     let msg = "Hello";
  |               ^^^^^^^
  | Type error: expected Int, got String

Manifest

The manifest declares capability permissions:

{
  "manifest": {
    "permissions": [
      "io.print",
      "io.read",
      "fs.read",
      "fs.write",
      "net.http"
    ]
  }
}

Manifest Fields

FieldTypeRequiredDescription
permissionsArray<String>✓List of required capabilities

Permission Strings

Permissions follow the format namespace.method:

  • io.print - Print to stdout
  • io.read - Read from stdin
  • fs.read - Read files
  • fs.write - Write files
  • fs.delete - Delete files
  • net.http - Make HTTP requests
  • sys.exec - Execute system commands
  • sys.env - Access environment variables

The VM will reject any cap_call that isn’t listed in the manifest.

Complete Example

Here’s a complete CASM program with all components:

{
  "version": "0.1",
  "lang": "crush",
  "functions": {
    "main": {
      "params": [],
      "locals": ["name", "greeting"],
      "body": [
        {
          "op": "push_str",
          "value": "What's your name?",
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 2}
        },
        {
          "op": "cap_call",
          "name": "io.print",
          "argc": 1,
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 2}
        },
        {
          "op": "cap_call",
          "name": "io.read",
          "argc": 0,
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 3}
        },
        {
          "op": "store",
          "name": "name",
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 3}
        },
        {
          "op": "push_str",
          "value": "Hello, ",
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 5}
        },
        {
          "op": "load",
          "name": "name",
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 5}
        },
        {
          "op": "add",
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 5}
        },
        {
          "op": "push_str",
          "value": "!",
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 5}
        },
        {
          "op": "add",
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 5}
        },
        {
          "op": "store",
          "name": "greeting",
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 5}
        },
        {
          "op": "load",
          "name": "greeting",
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 6}
        },
        {
          "op": "cap_call",
          "name": "io.print",
          "argc": 1,
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 6}
        },
        {
          "op": "ret",
          "lang": "crush",
          "meta": {"file": "greet.crush", "line": 7}
        }
      ]
    }
  },
  "manifest": {
    "permissions": [
      "io.print",
      "io.read"
    ]
  }
}

This corresponds to the Crush source:

fn main() {
    io.print("What's your name?");
    let name = io.read();
    
    let greeting = "Hello, " + name + "!";
    io.print(greeting);
}

Entry Point

The VM looks for a function named "main" as the entry point. If no main function exists, the program cannot be executed.

Best Practices

1. Always Include Metadata

Include lang, file, line, and column metadata for better error messages:

{
  "op": "add",
  "lang": "python",
  "meta": {
    "file": "script.py",
    "line": 42,
    "column": 10
  }
}

2. Declare All Locals

List all local variables in the locals array for clarity:

{
  "params": ["x", "y"],
  "locals": ["temp", "result", "i"],
  "body": [ /* ... */ ]
}

3. Minimal Permissions

Only request capabilities you actually use:

{
  "manifest": {
    "permissions": ["io.print"]  // Only what's needed
  }
}

4. Use Descriptive Function Names

{
  "functions": {
    "calculate_fibonacci": { /* ... */ },
    "format_output": { /* ... */ }
  }
}

Next Steps

Serialization Formats

CASM programs can be serialized in two formats: JSON (human-readable) and Binary (compact). This chapter explains both formats and when to use each.

JSON Format (.casm)

The JSON format is the primary, human-readable serialization format for CASM programs.

Characteristics

  • Human-readable: Easy to read, write, and debug
  • Text-based: Can be version-controlled with git
  • Portable: Works across all platforms
  • Larger size: More verbose than binary format
  • Slower parsing: JSON parsing has overhead

File Extension

.casm

Example

{
  "version": "0.1",
  "functions": {
    "main": {
      "params": [],
      "locals": [],
      "body": [
        {"op": "push_int", "value": 42},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        {"op": "ret"}
      ]
    }
  },
  "manifest": {
    "permissions": ["io.print"]
  }
}

When to Use

✅ Use JSON when:

  • Developing and debugging programs
  • Hand-writing CASM code
  • Generating CASM from tools
  • Version controlling bytecode
  • Sharing examples and documentation
  • Learning CASM

❌ Avoid JSON when:

  • Deploying to production (use binary)
  • Size matters (embedded systems, network transfer)
  • Performance is critical

Binary Format (.casmb)

The binary format uses MessagePack for compact, efficient serialization.

Characteristics

  • Compact: 30-50% smaller than JSON
  • Fast: Faster to parse and serialize
  • Binary: Not human-readable
  • Portable: MessagePack is cross-platform

File Extension

.casmb

Structure

The binary format uses MessagePack to serialize the same structure as JSON:

┌─────────────────────────────────┐
│ MessagePack Binary Data         │
│                                 │
│ Same structure as JSON:         │
│ - version (string)              │
│ - functions (map)               │
│ - lang (optional string)        │
│ - manifest (optional map)       │
└─────────────────────────────────┘

When to Use

✅ Use Binary when:

  • Deploying to production
  • Distributing compiled programs
  • Minimizing file size
  • Optimizing load time
  • Embedding in other formats

❌ Avoid Binary when:

  • Debugging or development
  • Need to inspect bytecode
  • Version controlling (use JSON)

Format Detection

The Crush VM automatically detects the format based on file extension:

ExtensionFormatAuto-detected
.casmJSON✓
.casmbBinary (MessagePack)✓
OtherJSON (default)✓

Example

# JSON format (auto-detected)
exo run program.casm

# Binary format (auto-detected)
exo run program.casmb

# Explicit format specification
exo run --format=json program.txt
exo run --format=binary program.bin

Converting Between Formats

JSON to Binary

# Using crush-cli
exo compile program.casm --output program.casmb

# Or programmatically in Rust
use casm::{Program, Format};

let program = Program::load("program.casm")?;
program.save("program.casmb")?;  // Auto-detects binary format

Binary to JSON

# Using crush-cli
crush decompile program.casmb --output program.casm

# Or programmatically
let program = Program::load("program.casmb")?;
program.save("program.casm")?;  // Auto-detects JSON format

Size Comparison

Example program sizes:

ProgramJSON (.casm)Binary (.casmb)Savings
Hello World450 bytes180 bytes60%
Fibonacci1.2 KB650 bytes46%
Complex App50 KB28 KB44%

Binary format typically saves 40-60% of file size.

Performance Comparison

Parsing performance (approximate):

FormatParse TimeSerialize Time
JSON100% (baseline)100% (baseline)
Binary40% (2.5x faster)30% (3.3x faster)

Binary format is 2-3x faster to parse and serialize.

Rust API

Loading Programs

#![allow(unused)]
fn main() {
use casm::{Program, Format};
use std::path::Path;

// Auto-detect format from extension
let program = Program::load(Path::new("program.casm"))?;

// Explicit format
let data = std::fs::read("program.casm")?;
let program = Program::deserialize(&data, Format::Json)?;
}

Saving Programs

#![allow(unused)]
fn main() {
// Auto-detect format from extension
program.save(Path::new("output.casmb"))?;

// Explicit format
let data = program.serialize(Format::Binary)?;
std::fs::write("output.casmb", data)?;
}

Format Enum

#![allow(unused)]
fn main() {
pub enum Format {
    Json,   // Human-readable (.casm)
    Binary, // Compact binary (.casmb)
}

impl Format {
    pub fn from_path(path: &Path) -> Self {
        match path.extension().and_then(|e| e.to_str()) {
            Some("casmb") => Format::Binary,
            _ => Format::Json,
        }
    }
}
}

Best Practices

Development Workflow

  1. Write in JSON during development
  2. Version control JSON files
  3. Compile to binary for production
  4. Distribute binary to end users

Example Workflow

# 1. Develop in JSON
vim program.casm

# 2. Test with JSON
exo run program.casm

# 3. Commit JSON to git
git add program.casm
git commit -m "Add new feature"

# 4. Build binary for release
exo compile program.casm --output program.casmb

# 5. Distribute binary
cp program.casmb /usr/local/bin/myapp.casmb

CI/CD Pipeline

# .github/workflows/build.yml
- name: Compile CASM
  run: |
    exo compile src/*.casm --output-dir dist/
    
- name: Upload artifacts
  uses: actions/upload-artifact@v2
  with:
    name: bytecode
    path: dist/*.casmb

Metadata Preservation

Both formats preserve all metadata:

// JSON format
{
  "op": "push_int",
  "value": 42,
  "lang": "python",
  "meta": {
    "file": "script.py",
    "line": 10
  }
}
// Binary format (MessagePack)
// Same data, just binary-encoded

Metadata is fully preserved in both formats, so debugging information is available even in production binaries.

Compression

For even smaller sizes, compress the binary format:

# gzip compression
gzip program.casmb
# Result: program.casmb.gz (typically 60-70% of .casmb size)

# brotli compression (better)
brotli program.casmb
# Result: program.casmb.br (typically 50-60% of .casmb size)

Combined savings:

FormatSizevs JSON
JSON100%-
Binary45%55% smaller
Binary + gzip30%70% smaller
Binary + brotli25%75% smaller

Validation

Both formats are validated on load:

#![allow(unused)]
fn main() {
match Program::load("program.casm") {
    Ok(program) => println!("Valid CASM program"),
    Err(e) => eprintln!("Invalid CASM: {}", e),
}
}

Common validation errors:

  • Missing required fields (version, functions)
  • Invalid instruction opcodes
  • Malformed JSON/MessagePack
  • Type mismatches

Future Formats

Potential future serialization formats:

  • WebAssembly: For browser execution
  • Protobuf: For RPC and network protocols
  • CBOR: Alternative binary format
  • Custom binary: Optimized Crush-specific format

Summary

AspectJSONBinary
Extension.casm.casmb
EncodingJSONMessagePack
Readable✓✗
SizeLargerSmaller (40-60% savings)
SpeedSlowerFaster (2-3x)
Use CaseDevelopment, debuggingProduction, distribution
Version Control✓ Recommended✗ Not recommended
Metadata✓ Preserved✓ Preserved

Recommendation: Use JSON for development, binary for production.

CASM Examples

This chapter provides practical examples of CASM programs, from simple to complex.

Example 1: Hello World

The simplest CASM program:

{
  "version": "0.1",
  "functions": {
    "main": {
      "params": [],
      "locals": [],
      "body": [
        {"op": "push_str", "value": "Hello, World!"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        {"op": "ret"}
      ]
    }
  },
  "manifest": {
    "permissions": ["io.print"]
  }
}

Execution trace:

1. push_str "Hello, World!"  → Stack: ["Hello, World!"]
2. cap_call io.print 1       → Prints "Hello, World!", Stack: []
3. ret                       → Returns from main

Example 2: Variables and Arithmetic

{
  "version": "0.1",
  "functions": {
    "main": {
      "params": [],
      "locals": ["x", "y", "sum"],
      "body": [
        {"op": "push_int", "value": 10},
        {"op": "store", "name": "x"},
        
        {"op": "push_int", "value": 32},
        {"op": "store", "name": "y"},
        
        {"op": "load", "name": "x"},
        {"op": "load", "name": "y"},
        {"op": "add"},
        {"op": "store", "name": "sum"},
        
        {"op": "push_str", "value": "The sum is: "},
        {"op": "load", "name": "sum"},
        {"op": "add"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "ret"}
      ]
    }
  },
  "manifest": {
    "permissions": ["io.print"]
  }
}

Output: The sum is: 42

Example 3: Conditional (If/Else)

{
  "version": "0.1",
  "functions": {
    "main": {
      "params": [],
      "locals": ["age"],
      "body": [
        {"op": "push_int", "value": 18},
        {"op": "store", "name": "age"},
        
        {"op": "load", "name": "age"},
        {"op": "push_int", "value": 18},
        {"op": "ge"},
        {"op": "jmp_if_not", "target": 9},
        
        {"op": "push_str", "value": "You are an adult"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        {"op": "jmp", "target": 11},
        
        {"op": "push_str", "value": "You are a minor"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "ret"}
      ]
    }
  },
  "manifest": {
    "permissions": ["io.print"]
  }
}

Control flow:

if age >= 18:
    print("You are an adult")
else:
    print("You are a minor")

Example 4: While Loop

Count from 1 to 5:

{
  "version": "0.1",
  "functions": {
    "main": {
      "params": [],
      "locals": ["i"],
      "body": [
        {"op": "push_int", "value": 1},
        {"op": "store", "name": "i"},
        
        {"op": "load", "name": "i"},
        {"op": "push_int", "value": 5},
        {"op": "le"},
        {"op": "jmp_if_not", "target": 14},
        
        {"op": "load", "name": "i"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "load", "name": "i"},
        {"op": "push_int", "value": 1},
        {"op": "add"},
        {"op": "store", "name": "i"},
        
        {"op": "jmp", "target": 2},
        
        {"op": "ret"}
      ]
    }
  },
  "manifest": {
    "permissions": ["io.print"]
  }
}

Output:

1
2
3
4
5

Example 5: Function Calls

Factorial function:

{
  "version": "0.1",
  "functions": {
    "main": {
      "params": [],
      "locals": ["result"],
      "body": [
        {"op": "push_int", "value": 5},
        {"op": "call", "function": "factorial"},
        {"op": "store", "name": "result"},
        
        {"op": "push_str", "value": "5! = "},
        {"op": "load", "name": "result"},
        {"op": "add"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "ret"}
      ]
    },
    "factorial": {
      "params": ["n"],
      "locals": ["temp"],
      "body": [
        {"op": "load", "name": "n"},
        {"op": "push_int", "value": 1},
        {"op": "le"},
        {"op": "jmp_if_not", "target": 5},
        
        {"op": "push_int", "value": 1},
        {"op": "ret"},
        
        {"op": "load", "name": "n"},
        {"op": "push_int", "value": 1},
        {"op": "sub"},
        {"op": "call", "function": "factorial"},
        {"op": "store", "name": "temp"},
        
        {"op": "load", "name": "n"},
        {"op": "load", "name": "temp"},
        {"op": "mul"},
        {"op": "ret"}
      ]
    }
  },
  "manifest": {
    "permissions": ["io.print"]
  }
}

Output: 5! = 120

Example 6: Arrays

Working with arrays:

{
  "version": "0.1",
  "functions": {
    "main": {
      "params": [],
      "locals": ["arr", "len", "i"],
      "body": [
        {"op": "push_int", "value": 10},
        {"op": "push_int", "value": 20},
        {"op": "push_int", "value": 30},
        {"op": "new_array", "size": 3},
        {"op": "store", "name": "arr"},
        
        {"op": "load", "name": "arr"},
        {"op": "arr_len"},
        {"op": "store", "name": "len"},
        
        {"op": "push_str", "value": "Array length: "},
        {"op": "load", "name": "len"},
        {"op": "add"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "load", "name": "arr"},
        {"op": "push_int", "value": 1},
        {"op": "arr_get"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "ret"}
      ]
    }
  },
  "manifest": {
    "permissions": ["io.print"]
  }
}

Output:

Array length: 3
20

Example 7: Objects and Structs

{
  "version": "0.1",
  "functions": {
    "main": {
      "params": [],
      "locals": ["point"],
      "body": [
        {"op": "new_struct", "name": "Point"},
        {"op": "store", "name": "point"},
        
        {"op": "load", "name": "point"},
        {"op": "push_int", "value": 10},
        {"op": "set_field", "name": "x"},
        {"op": "store", "name": "point"},
        
        {"op": "load", "name": "point"},
        {"op": "push_int", "value": 20},
        {"op": "set_field", "name": "y"},
        {"op": "store", "name": "point"},
        
        {"op": "load", "name": "point"},
        {"op": "get_field", "name": "x"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "ret"}
      ]
    }
  },
  "manifest": {
    "permissions": ["io.print"]
  }
}

Output: 10

Example 8: File I/O

Reading and writing files:

{
  "version": "0.1",
  "functions": {
    "main": {
      "params": [],
      "locals": ["content"],
      "body": [
        {"op": "push_str", "value": "data.txt"},
        {"op": "push_str", "value": "Hello from CASM!"},
        {"op": "cap_call", "name": "fs.write", "argc": 2},
        
        {"op": "push_str", "value": "File written successfully"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "push_str", "value": "data.txt"},
        {"op": "cap_call", "name": "fs.read", "argc": 1},
        {"op": "store", "name": "content"},
        
        {"op": "push_str", "value": "File content: "},
        {"op": "load", "name": "content"},
        {"op": "add"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "ret"}
      ]
    }
  },
  "manifest": {
    "permissions": ["io.print", "fs.read", "fs.write"]
  }
}

Example 9: Error Handling

Using metadata for error reporting:

{
  "version": "0.1",
  "lang": "python",
  "functions": {
    "main": {
      "params": [],
      "locals": ["x", "y"],
      "body": [
        {
          "op": "push_int",
          "value": 10,
          "lang": "python",
          "meta": {"file": "script.py", "line": 1, "column": 5}
        },
        {
          "op": "store",
          "name": "x",
          "lang": "python",
          "meta": {"file": "script.py", "line": 1}
        },
        {
          "op": "push_int",
          "value": 0,
          "lang": "python",
          "meta": {"file": "script.py", "line": 2, "column": 5}
        },
        {
          "op": "store",
          "name": "y",
          "lang": "python",
          "meta": {"file": "script.py", "line": 2}
        },
        {
          "op": "load",
          "name": "x",
          "lang": "python",
          "meta": {"file": "script.py", "line": 3, "column": 8}
        },
        {
          "op": "load",
          "name": "y",
          "lang": "python",
          "meta": {"file": "script.py", "line": 3, "column": 12}
        },
        {
          "op": "div",
          "lang": "python",
          "meta": {"file": "script.py", "line": 3, "column": 10}
        },
        {
          "op": "ret",
          "lang": "python",
          "meta": {"file": "script.py", "line": 3}
        }
      ]
    }
  }
}

Error output:

Error at script.py:3:10
  |
3 | result = x / y
  |          ^^^^^
  | Division by zero

Example 10: Complete Program

A complete program with multiple functions and capabilities:

{
  "version": "0.1",
  "lang": "crush",
  "functions": {
    "main": {
      "params": [],
      "locals": ["numbers", "sum", "avg"],
      "body": [
        {"op": "push_int", "value": 10},
        {"op": "push_int", "value": 20},
        {"op": "push_int", "value": 30},
        {"op": "push_int", "value": 40},
        {"op": "push_int", "value": 50},
        {"op": "new_array", "size": 5},
        {"op": "store", "name": "numbers"},
        
        {"op": "load", "name": "numbers"},
        {"op": "call", "function": "sum_array"},
        {"op": "store", "name": "sum"},
        
        {"op": "load", "name": "sum"},
        {"op": "push_int", "value": 5},
        {"op": "div"},
        {"op": "store", "name": "avg"},
        
        {"op": "push_str", "value": "Sum: "},
        {"op": "load", "name": "sum"},
        {"op": "add"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "push_str", "value": "Average: "},
        {"op": "load", "name": "avg"},
        {"op": "add"},
        {"op": "cap_call", "name": "io.print", "argc": 1},
        
        {"op": "ret"}
      ]
    },
    "sum_array": {
      "params": ["arr"],
      "locals": ["total", "i", "len"],
      "body": [
        {"op": "push_int", "value": 0},
        {"op": "store", "name": "total"},
        
        {"op": "push_int", "value": 0},
        {"op": "store", "name": "i"},
        
        {"op": "load", "name": "arr"},
        {"op": "arr_len"},
        {"op": "store", "name": "len"},
        
        {"op": "load", "name": "i"},
        {"op": "load", "name": "len"},
        {"op": "lt"},
        {"op": "jmp_if_not", "target": 23},
        
        {"op": "load", "name": "total"},
        {"op": "load", "name": "arr"},
        {"op": "load", "name": "i"},
        {"op": "arr_get"},
        {"op": "add"},
        {"op": "store", "name": "total"},
        
        {"op": "load", "name": "i"},
        {"op": "push_int", "value": 1},
        {"op": "add"},
        {"op": "store", "name": "i"},
        
        {"op": "jmp", "target": 9},
        
        {"op": "load", "name": "total"},
        {"op": "ret"}
      ]
    }
  },
  "manifest": {
    "permissions": ["io.print"]
  }
}

Output:

Sum: 150
Average: 30

Tips for Writing CASM

1. Use Comments in JSON

While CASM itself doesn’t support comments, you can add them in your JSON during development:

{
  "body": [
    {"op": "push_int", "value": 42},
    {"_comment": "Print the value"},
    {"op": "cap_call", "name": "io.print", "argc": 1}
  ]
}

(The VM will ignore unknown fields like _comment)

2. Label Jump Targets

Keep track of instruction indices:

{
  "body": [
    /* 0 */ {"op": "load", "name": "x"},
    /* 1 */ {"op": "push_int", "value": 0},
    /* 2 */ {"op": "gt"},
    /* 3 */ {"op": "jmp_if_not", "target": 6},
    /* 4 */ {"op": "push_str", "value": "Positive"},
    /* 5 */ {"op": "jmp", "target": 7},
    /* 6 */ {"op": "push_str", "value": "Non-positive"},
    /* 7 */ {"op": "cap_call", "name": "io.print", "argc": 1}
  ]
}

3. Always Include Metadata

{
  "op": "add",
  "lang": "crush",
  "meta": {
    "file": "program.crush",
    "line": 10,
    "column": 15
  }
}

4. Validate Your CASM

crush validate program.casm

Next Steps

Glossary

Arena

A centralized memory pool managed by the Crush VM. All complex objects (Strings, Arrays, Maps) reside in the Arena to ensure safety and isolation.

CASM (Crush Assembly)

The low-level, target-independent instruction set executed by the Crush VM.

CAST (Crush AST)

The intermediate JSON representation of a program’s semantic intent, generated by language walkers.

Capsule

The primary unit of isolation and distribution in Crush. A capsule contains a manifest (Capsule.toml) and optionally a payload (CASM or native code).

Capability

A specific, fine-grained permission granted to a capsule (e.g., fs.read). Capabilities are the only way capsules can interact with the host or each other.

Exo-Core

The minimal virtualization layer of the Crush vOS. It manages the HAL, VFS, and capsule loading.

HAL (Hardware Abstraction Layer)

The interface between the vOS and the physical host hardware.

Native Capsule

A capsule that is linked directly into the crush binary for performance or bootstrapping reasons (e.g., core.fs).

Walker

A compiler component that translates source code from a specific language into CAST.

Vortex

The interactive shell and REPL for the Crush environment.

Quick Reference

Quick reference cards for CASM and Crush.

CASM Instructions Quick Reference

Stack Operations

InstructionEffectExample
push_int→ int{"op": "push_int", "value": 42}
push_str→ str{"op": "push_str", "value": "hello"}
popvalue →{"op": "pop"}
dupa → a, a{"op": "dup"}

Arithmetic

InstructionEffectExample
adda, b → result{"op": "add"}
suba, b → result{"op": "sub"}
mula, b → result{"op": "mul"}
diva, b → result{"op": "div"}

Control Flow

InstructionEffectExample
jmpJump{"op": "jmp", "target": 10}
jmp_ifcond → jump if true{"op": "jmp_if", "target": 10}
callCall function{"op": "call", "function": "add"}
retReturn{"op": "ret"}

Capabilities

InstructionEffectExample
cap_callCall capability{"op": "cap_call", "name": "io.print", "argc": 1}

Crush Syntax Quick Reference

Variables

let x = 42;
let name: String = "Alice";

Functions

fn greet(name: String) {
    io.print("Hello, " + name);
}

fn add(a: Int, b: Int) -> Int {
    return a + b;
}

Control Flow

if condition {
    // ...
} else {
    // ...
}

while condition {
    // ...
}

for item in collection {
    // ...
}

Capabilities

io.print("Hello");
fs.read("file.txt");
net.http("https://api.example.com");

Polyglot

@python {
    print("Hello from Python")
}

@javascript {
    console.log("Hello from JS");
}

@bash {
    echo "Hello from Bash"
}

Common Patterns

Read File

if fs.exists("file.txt") {
    let content = fs.read("file.txt");
    io.print(content);
}

HTTP Request

let response = net.get("https://api.example.com/data");
io.print(response);

Command-Line Args

let args = sys.args();
if args.length < 2 {
    io.eprint("Usage: program <arg>");
    sys.exit(1);
}

Type Reference

TypeExampleDescription
Int4264-bit integer
Float3.1464-bit float
String"hello"UTF-8 string
BooltrueBoolean
NullnullNull value
Array[1, 2, 3]Array
Map{"key": "value"}Map/Object

Capability Reference

CapabilityDescription
io.printPrint to stdout
io.readRead from stdin
fs.readRead file
fs.writeWrite file
fs.existsCheck if file exists
net.httpHTTP request
sys.execExecute command
sys.argsGet CLI arguments

Language Comparisons

How Crush compares to other popular languages.

Crush vs Python

Similarities

  • Simple, readable syntax
  • Dynamic typing
  • High-level abstractions

Differences

FeatureCrushPython
Polyglot✓ Embed multiple languages✗ Single language
Security✓ Capability-based✗ Full system access
Compilation✓ Compiles to bytecode✗ Interpreted
Type Hints✓ Optional✓ Optional (PEP 484)

Example Comparison

Python:

def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

Crush:

fn greet(name: String) {
    io.print("Hello, " + name + "!");
}

greet("Alice");

Crush vs JavaScript

Similarities

  • Dynamic typing
  • First-class functions
  • Async support (future)

Differences

FeatureCrushJavaScript
Polyglot✓✗
Security✓ Capabilities✗ Node.js has full access
SyntaxRust-likeC-like

Crush vs Rust

Similarities

  • Similar syntax
  • Type hints
  • Performance focus

Differences

FeatureCrushRust
Type SystemDynamicStatic, strict
Memory SafetyVM-managedBorrow checker
Polyglot✓✗
CompilationTo bytecode + AOT nativeTo native code
AOT PerformanceNear-native via crush-aotcNative

Crush vs Bash

Similarities

  • Scripting focus
  • System administration
  • Command execution

Differences

FeatureCrushBash
Type System✓ Types✗ Everything is string
Polyglot✓✗
SyntaxModernUnix shell
Error HandlingBetterLimited

Performance

Crush has five execution tiers spanning interpreter to native code:

TierSpeed vs CVM1 (simple)Speed vs CVM1 (compute)
CVM1 (interpreter)1.0×1.0×
FastVM0.09×0.5×
AOT Rust (rustc)42×130×
AOT C (gcc -O3)54×317×
AOT C (clang -O3)55×378×

AOT-compiled Crush achieves near-C performance — LLVM and GCC constant-fold the stack machine operations into direct native code. For compute-bound Crush programs, crush-aotc produces .so files that run within 2-5× of hand-written C.

See the crush-ast benchmarks for detailed cross-language comparisons.

When to Use Crush

✅ Use Crush when:

  • You need to combine multiple languages
  • Security and isolation are important
  • You want capability-based permissions
  • You’re building polyglot applications
  • You need native performance with scripting ergonomics (crush-aotc)

❌ Consider alternatives when:

  • You have a large existing codebase in another language
  • You need mature ecosystem (Python/JavaScript)
  • You’re building web frontends (use JavaScript/TypeScript)

Migration Guide

From Python

  1. Add type hints to function signatures
  2. Replace print() with io.print()
  3. Add capability permissions to manifest
  4. Keep Python code in @python {} blocks during transition

From JavaScript

  1. Change function to fn
  2. Add semicolons
  3. Replace console.log() with io.print()
  4. Keep JavaScript in @javascript {} blocks

From Bash

  1. Wrap shell commands in @bash {} blocks
  2. Use Crush for logic and control flow
  3. Add type safety with Crush variables