Introduction
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
| Section | What you’ll learn |
|---|---|
| Crush Language | Syntax, types, control flow, functions, capabilities, polyglot embedding |
| CAST | The intermediate AST format that walkers produce |
| CASM | The stack-based bytecode the VM executes |
| Appendix | Glossary, 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
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
| Crate | What it gives you |
|---|---|
crush-lang-sdk | Start here. Ergonomic Runtime + ProgramBuilder over the VM: load, compile, and run Crush programs; register host capabilities. |
crush-frontend | Parser, semantic analyzer, optimizer, and CASM compiler (parse_source). |
crush-vm | The CVM1 runtime: bytecode assembler/disassembler and the sandboxed interpreter with quotas + capability gates. |
crush-cast | The CAST intermediate representation (the stable AST). |
casm | The CASM bytecode format. |
crush-errors | Shared error types. |
tree-sitter-crush | Tree-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 capabilitiesgraphics— graphics host capabilitiesrepl-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/HostCapand the SDK’shost_capsextension point to register your own handlers, plus built-in basics likeio.print; - the
stdlibmodule 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
- Source: Write code in any supported language.
- Walker: A language-specific walker generates a Crush AST (CAST).
- Compiler: The Crush compiler transforms CAST into Crush Assembly (CASM).
- VM: The Crush VM executes CASM, enforcing security and resource limits.
Next Steps
- Syntax & Fundamentals: Deep dive into the Crush language.
- The Capability System: Understand how security works.
- Polyglot Programming: Master cross-language execution.
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:
- Function calls, field access:
f(),obj.field - Unary:
-,not - Multiplicative:
*,/,% - Additive:
+,- - Comparison:
<,>,<=,>= - Equality:
==,!= - Logical AND:
and - 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:
importcreates 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: Learn about Crush’s type system
- Variables: Variable scoping and management
- Control Flow: Detailed control flow guide
- Functions: Function definition and calling
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
| Type | Example | Notes |
|---|---|---|
Int | 42 | 64-bit signed |
Float | 3.14 | 64-bit IEEE 754 |
String | "Hello" | UTF-8 |
Bool | true | |
Bytes | b"data" | raw binary buffer |
Error | Error("msg") | first-class error |
Null | null | absence 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: Variable scoping and management
- Operators: Detailed operator reference
- Functions: Working with functions
- Control Flow: If, while, for loops
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: Learn about operators
- Control Flow: If, while, for loops
- Functions: Function parameters and returns
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:
| Precedence | Operators | Description |
|---|---|---|
| 1 | (), ., [] | Grouping, field access, indexing |
| 2 | -, !, not | Unary negation, logical NOT |
| 3 | *, /, % | Multiplication, division, modulo |
| 4 | +, - | Addition, subtraction |
| 5 | <, >, <=, >= | Comparison |
| 6 | ==, != | Equality |
| 7 | &&, and | Logical AND |
| 8 | ||, or | Logical 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: Use operators in conditions
- Functions: Function definitions
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 tox) - 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: Define and call functions
- Capabilities: Secure I/O operations
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: Secure I/O operations
- Polyglot Programming: Embed multiple languages
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 stdoutio.read- Read from stdinfs.read- Read filesfs.write- Write filesnet.http- Make HTTP requestssys.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, andio.readas 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’scorecaps, 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
| Capability | Description | Arguments | Returns | Scope |
|---|---|---|---|---|
io.print | Print to stdout | message: String | null | N/A |
io.read | Read from stdin | none | String | N/A |
io.eprint | Print to stderr | message: String | null | N/A |
Example:
io.print("Enter your name:");
let name = io.read();
io.print("Hello, " + name);
Filesystem Capabilities
| Capability | Description | Arguments | Returns | Scope |
|---|---|---|---|---|
fs.read | Read file | path: String | String | Path list |
fs.write | Write file | path: String, content: String | null | Path list |
fs.exists | Check if exists | path: String | Bool | Path list |
fs.delete | Delete file | path: String | null | Path list |
fs.list | List directory | path: String | Array<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
| Capability | Description | Arguments | Returns |
|---|---|---|---|
sys.exec | Execute command | cmd: String | String |
sys.env | Get env variable | name: String | String |
sys.args | Get CLI args | none | Array<String> |
sys.exit | Exit program | code: Int | never |
Example:
let args = sys.args();
if args.length < 2 {
io.eprint("Usage: program <file>");
sys.exit(1);
}
Network Capabilities
| Capability | Description | Arguments | Returns |
|---|---|---|---|
net.http | HTTP request | url: String | String |
net.get | HTTP GET | url: String | String |
net.post | HTTP POST | url: String, body: String | String |
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.readvsfs.writevsfs.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
- Polyglot Programming: Embed multiple languages
- Standard Library: Additional capabilities
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
| capability | notes |
|---|---|
io.print | built-in, always available |
str.concat, str.len | built-in |
str.trim, str.to_upper, str.substring | require --stdlib |
conv.to_int | requires --stdlib |
math.* | requires --stdlib |
fs.read, fs.write, fs.exists, fs.list | require --fs |
env.get | requires --env |
time.now | requires --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:
| form | example | what it is |
|---|---|---|
| polyglot block | @python { ... }, @javascript { ... } | embed another language; the @ is required |
| compiler directive | @gpu, @kernel, @target | steer the backend (e.g. the PTX/GPU path) |
| AST / AI annotation | @invariant, @decision, @covers, @writes, @synthesize | typed 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:
| Language | Syntax | Walker status | Supported constructs |
|---|---|---|---|
| JavaScript/TypeScript | @javascript { } | Complete | Dual-backend (swc primary, boa optional). Full JS + TS + JSX/TSX |
| Python | @python { } | Complete | Native frontend via rustpython-parser |
| Rust | @rust { } | Complete | Native frontend via syn |
| Bash | @bash { } | Complete | Full AST parsing via brush-parser |
| C / C++ | @c { } | Mature | Tree-sitter-c and tree-sitter-cpp |
| Go | @go { } | Mature | Tree-sitter-based walker |
| Zig | @zig { } | Mature | Tree-sitter-based walker |
| Wasm | @wasm { } | Mature | Integration 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
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 Type | Python | JavaScript | Bash | Rust | C | Go |
|---|---|---|---|---|---|---|
| Int | int | number | $VAR | i64 | int64_t | int64 |
| Float | float | number | $VAR | f64 | double | float64 |
| String | str | string | $VAR | String | char* | string |
| Bool | bool | boolean | true/false | bool | bool | bool |
| Array | list | Array | array | Vec | array | [] |
| Map | dict | Object | assoc | HashMap | struct | map |
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
- Crush variables are marshaled to language blocks as native types
- Language blocks can read and modify shareable values
- Changes are marshaled back to Crush after block execution
- 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, andsklearnrequire 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 Type | Python | JavaScript | Bash | Rust | C | Go |
|---|---|---|---|---|---|---|
| Int | int | number | $VAR | i64 | int64_t | int64 |
| Float | float | number | $VAR | f64 | double | float64 |
| String | str | string | $VAR | String | char* | string |
| Bool | bool | boolean | true/false | bool | bool | bool |
| Array | list | Array | array | Vec | array | [] |
| Map | dict | Object | assoc array | HashMap | struct | map |
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: Additional capabilities
- CAST Specification: The walker output format
- CASM Overview: The bytecode walkers compile to
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 stdoutio.read()- Read from stdinio.eprint(message)- Print to stderr
Filesystem Module (std.fs)
File and directory operations:
fs.read(path)- Read filefs.write(path, content)- Write filefs.exists(path)- Check if existsfs.delete(path)- Delete filefs.list(path)- List directory
System Module (std.sys)
System-level operations:
sys.exec(command)- Execute commandsys.env(name)- Get environment variablesys.args()- Get command-line argumentssys.exit(code)- Exit program
Network Module (std.net)
Network operations:
net.http(url)- HTTP requestnet.get(url)- HTTP GETnet.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
- Capability System - How capabilities gate stdlib access
- Polyglot Programming - Embed multiple languages
Examples
Real Crush programs drawn from the exosphere workspace and the broader Crush ecosystem. Each demonstrates a distinct pattern or capability domain.
| Example | Pattern |
|---|---|
| Fibonacci & Functions | Recursion, function calls, type hints |
| Arrays & Loops | Array literals, for-in, break/continue |
| Exception Handling | try/catch/throw |
| Concurrency & Structs | Struct instantiation, spawn/yield |
| Lambdas & Pipes | Lambda syntax, pipeline operator |
| Import Styles | Module, MCP, capability, polyglot, external imports |
| System Info | Sys/math capabilities, HTML generation |
| Build Pipeline | Multi-function decomposition, event sourcing, fail-fast |
| Async LLM Dashboard | async/await, DOM API, seahorse LLM integration |
Source locations:
exosphere/crates/core/crush-lang/tests/fixtures/— core language fixtures (exosphere repo)exosphere/tests/language/— integration testsexosphere/examples/crush-pipefish-dashboard/— real appexosphere-apps/crates/apps/super-surfer/apps/— web appsexosphere/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
returnis 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, ...]andlen()built-in arr[i]zero-based integer indexingfor x in iterable { }— iterates arrays and rangesbreakexits the loop immediately;continueskips 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 toethrow expr— throws any value as an exception (string, Int, Map, etc.)- Execution after
throwinside thetryblock is skipped - Execution after the
catchblock 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 taskyield— suspends the current task and switches to another ready taskspawn/yieldimplement 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
| Form | Purpose |
|---|---|
import module | Crush stdlib module (io, fs, net, sys, math, time, …) |
import module as alias | Module with alias |
import module { a, b } | Selective import — only named symbols |
use @mcp "url" { tools } as alias | Wire an MCP server as capabilities |
use @cap "cap.path" { names } as alias | Import specific capability handles |
use @lang python "module" { symbols } | Import a polyglot language module |
import @git "url" as alias | Bind a git repository as a resource |
import @http "url" as alias | Bind 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 formattingstr()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
| Call | Capability |
|---|---|
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.crushDonated fromnixpt/nakshatra— used in production viacrush-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_toolandemitare stubs here; in production they compile tocap_callagainstprocess.spawnandevent.emitcapabilities - Self-contained example — stubs replace real caps so the file runs without host wiring
Grammar note from the source file:
spawnis 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 theseahorseLLM client moduledom.get_element_by_id()/dom.set_text_content()/dom.get_attribute()— DOM capability callsdom.add_event_listener(element, event, handler_name)— string-named event handler wiringasync fn— declares a function that runs asynchronouslyawait expr— suspends until the async value resolves- Fire-and-forget:
generate_async(...)is called withoutawaitso the caller returns immediately try { await ... } catch error { ... }— exception handling around async operations- Module-level mutable state:
is_generatingguards 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 addsTryCatch,Throw,LangBlock,StructDef,Break,Continue,DomMutate,DomEventListener,DomQuery,Spawn,Await,Lambda,Pipeline,Range,Match, and AI-native statement/expression nodes. Walkers in the repo declarecast_versionvalues 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
- Language-Agnostic: CAST nodes should represent common programming constructs, not language-specific syntax
- Explicit Control Flow: Control flow (if/while/for) is represented explicitly, not as jumps
- Metadata Preservation: Each node includes
metafor source language, line numbers, etc. - 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 sourcecolumn: 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 node | CASM instruction |
|---|---|
AI / Query | ai_query |
AI / ToolChain | ai_tool_chain |
AI / AgentDelegation | ai_agent_delegation |
AI / LearningLoop | ai_learning_loop |
AI / ContextAware | ai_context_aware |
AI / GoalDeclaration | ai_goal_decl |
AI / ProgressUpdate | ai_progress_update |
AI / KnowledgeSharing | ai_knowledge_share |
AI / CapabilityDiscovery | ai_capability_discovery |
AI / AdaptationRequest | (maps to ai_context_aware + runtime signal) |
See Also
examples/cast/— canonical CAST example corpus- CAST Base Spec — statement and expression node reference (v0.3 base)
- CASM Instruction Reference — the
ai_*instruction category
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 Reference: Explore the complete set of CASM opcodes.
- Binary Format: Learn how CASM is optimized for distribution.
- Crush Language Guide: See how the high-level language maps to CASM.
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 → resultmeans: popb, popa, pushresultvalue →means: popvalue(nothing pushed)→ valuemeans: pushvalue(nothing popped)
Instruction Categories
- Stack Operations
- Memory Operations
- Arithmetic Operations
- Comparison Operations
- Logical Operations
- Bitwise Operations
- Stack Manipulation
- Control Flow
- Array Operations
- Object Operations
- Type Operations
- Capability Calls
- Concurrency
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):trueorfalse
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 stdoutio.read- Read from stdinfs.read- Read filefs.write- Write filenet.http- HTTP requestsys.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
| Category | Instructions |
|---|---|
| Stack | push_int, push_float, push_str, push_bool, push_null, pop, dup |
| Memory | store, load, export_var, import_var |
| Arithmetic | add, sub, mul, div, mod, neg |
| Comparison | eq, ne, lt, gt, le, ge |
| Logical | and, or, not |
| Bitwise | bit_and, bit_or, bit_xor, bit_not, shl, shr |
| Stack Manip | swap, rot, pick, roll |
| Control Flow | jmp, jmp_if, jmp_if_not, call, ret, break, continue |
| Arrays | new_array, arr_get, arr_set, arr_len, arr_push, arr_pop |
| Objects | new_obj, new_struct, get_field, set_field |
| Ranges | make_range |
| Types | type_of, cast |
| Exceptions | enter_try, exit_try, throw |
| Capabilities | cap_call |
| Concurrency | spawn, yield, await |
| Polyglot | exec_lang |
| DOM | dom_query, dom_mutate, dom_event_listener |
| AI-native | ai_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
| Field | Type | Required | Description |
|---|---|---|---|
version | String | ✓ | CASM format version (currently "0.1") |
functions | Object | ✓ | Map of function name to function definition |
lang | String | ✗ | Source language (e.g., "python", "crush", "rust") |
manifest | Object | ✗ | 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
| Field | Type | Required | Description |
|---|---|---|---|
params | Array<String> | ✗ | Parameter names (default: []) |
locals | Array<String> | ✗ | Local variable names (default: []) |
body | Array<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
| Field | Type | Required | Description |
|---|---|---|---|
op | String | ✓ | Operation name (e.g., "push_int", "add") |
lang | String | ✗ | Source language for this instruction |
meta | Object | ✗ | Metadata (file, line, column, etc.) |
| others | Various | Varies | Operation-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:
| Field | Type | Description |
|---|---|---|
file | String | Source file path |
line | Integer | Line number in source file |
column | Integer | Column number in source file |
lang | String | Source 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
| Field | Type | Required | Description |
|---|---|---|---|
permissions | Array<String> | ✓ | List of required capabilities |
Permission Strings
Permissions follow the format namespace.method:
io.print- Print to stdoutio.read- Read from stdinfs.read- Read filesfs.write- Write filesfs.delete- Delete filesnet.http- Make HTTP requestssys.exec- Execute system commandssys.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
- Instruction Set Reference: Learn about all available operations
- Examples: See complete CASM programs
- Serialization: Learn about JSON and binary formats
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:
| Extension | Format | Auto-detected |
|---|---|---|
.casm | JSON | ✓ |
.casmb | Binary (MessagePack) | ✓ |
| Other | JSON (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:
| Program | JSON (.casm) | Binary (.casmb) | Savings |
|---|---|---|---|
| Hello World | 450 bytes | 180 bytes | 60% |
| Fibonacci | 1.2 KB | 650 bytes | 46% |
| Complex App | 50 KB | 28 KB | 44% |
Binary format typically saves 40-60% of file size.
Performance Comparison
Parsing performance (approximate):
| Format | Parse Time | Serialize Time |
|---|---|---|
| JSON | 100% (baseline) | 100% (baseline) |
| Binary | 40% (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
- Write in JSON during development
- Version control JSON files
- Compile to binary for production
- 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:
| Format | Size | vs JSON |
|---|---|---|
| JSON | 100% | - |
| Binary | 45% | 55% smaller |
| Binary + gzip | 30% | 70% smaller |
| Binary + brotli | 25% | 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
| Aspect | JSON | Binary |
|---|---|---|
| Extension | .casm | .casmb |
| Encoding | JSON | MessagePack |
| Readable | ✓ | ✗ |
| Size | Larger | Smaller (40-60% savings) |
| Speed | Slower | Faster (2-3x) |
| Use Case | Development, debugging | Production, 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
- Program Structure: Understand CASM program anatomy
- Instruction Reference: Complete instruction documentation
- Crush Language: Learn the high-level Crush language
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
| Instruction | Effect | Example |
|---|---|---|
push_int | → int | {"op": "push_int", "value": 42} |
push_str | → str | {"op": "push_str", "value": "hello"} |
pop | value → | {"op": "pop"} |
dup | a → a, a | {"op": "dup"} |
Arithmetic
| Instruction | Effect | Example |
|---|---|---|
add | a, b → result | {"op": "add"} |
sub | a, b → result | {"op": "sub"} |
mul | a, b → result | {"op": "mul"} |
div | a, b → result | {"op": "div"} |
Control Flow
| Instruction | Effect | Example |
|---|---|---|
jmp | Jump | {"op": "jmp", "target": 10} |
jmp_if | cond → jump if true | {"op": "jmp_if", "target": 10} |
call | Call function | {"op": "call", "function": "add"} |
ret | Return | {"op": "ret"} |
Capabilities
| Instruction | Effect | Example |
|---|---|---|
cap_call | Call 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
| Type | Example | Description |
|---|---|---|
Int | 42 | 64-bit integer |
Float | 3.14 | 64-bit float |
String | "hello" | UTF-8 string |
Bool | true | Boolean |
Null | null | Null value |
Array | [1, 2, 3] | Array |
Map | {"key": "value"} | Map/Object |
Capability Reference
| Capability | Description |
|---|---|
io.print | Print to stdout |
io.read | Read from stdin |
fs.read | Read file |
fs.write | Write file |
fs.exists | Check if file exists |
net.http | HTTP request |
sys.exec | Execute command |
sys.args | Get CLI arguments |
Language Comparisons
How Crush compares to other popular languages.
Crush vs Python
Similarities
- Simple, readable syntax
- Dynamic typing
- High-level abstractions
Differences
| Feature | Crush | Python |
|---|---|---|
| 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
| Feature | Crush | JavaScript |
|---|---|---|
| Polyglot | ✓ | ✗ |
| Security | ✓ Capabilities | ✗ Node.js has full access |
| Syntax | Rust-like | C-like |
Crush vs Rust
Similarities
- Similar syntax
- Type hints
- Performance focus
Differences
| Feature | Crush | Rust |
|---|---|---|
| Type System | Dynamic | Static, strict |
| Memory Safety | VM-managed | Borrow checker |
| Polyglot | ✓ | ✗ |
| Compilation | To bytecode + AOT native | To native code |
| AOT Performance | Near-native via crush-aotc | Native |
Crush vs Bash
Similarities
- Scripting focus
- System administration
- Command execution
Differences
| Feature | Crush | Bash |
|---|---|---|
| Type System | ✓ Types | ✗ Everything is string |
| Polyglot | ✓ | ✗ |
| Syntax | Modern | Unix shell |
| Error Handling | Better | Limited |
Performance
Crush has five execution tiers spanning interpreter to native code:
| Tier | Speed vs CVM1 (simple) | Speed vs CVM1 (compute) |
|---|---|---|
| CVM1 (interpreter) | 1.0× | 1.0× |
| FastVM | 0.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
- Add type hints to function signatures
- Replace
print()withio.print() - Add capability permissions to manifest
- Keep Python code in
@python {}blocks during transition
From JavaScript
- Change
functiontofn - Add semicolons
- Replace
console.log()withio.print() - Keep JavaScript in
@javascript {}blocks
From Bash
- Wrap shell commands in
@bash {}blocks - Use Crush for logic and control flow
- Add type safety with Crush variables