A pausable bytecode virtual machine with its own language frontend. Write programs that can suspend themselves at any point, save their entire state as a portable snapshot, and resume execution later — on the same machine or a different one.
- Write
.pausource code. - The compiler generates bytecode.
- The VM executes the bytecode.
- At any
yieldpoint, the VM serializes its entire state into a portable snapshot (PAUS format). - Resume reloads the snapshot and the VM picks up right where it left off.
- Snapshot is architecture-independent — pause on x86, resume on ARM.
Pausible does not dump raw memory. It serializes the VM's semantic structures — stack frames, heap objects, I/O handles, and the task tree — into an architecture-independent snapshot. You can pause a program on x86 and resume it on ARM.
- Explicit yield — Programs decide when to pause via the
yieldkeyword, not an external signal. - Portable snapshots — The PAUS binary format is endian-agnostic and cross-platform. No raw memory dumps.
- I/O reconnect strategies — I/O handles carry a strategy annotation (
@replay,@seek,@cached) that controls how connections are rebuilt on resume. - Structured concurrency —
spawn/wait_children()with a strict parent-child task tree. A parent may not yield until all children have completed. - Yield resume branches —
yield resume { ok => ..., partial(report) => ... }lets programs handle reconnect failures explicitly. - Static typing —
Int,Float,Bool,String,List<T>,Null,Handle. Arithmetic and comparison are type-safe at compile time. - Short-circuit evaluation —
&&and||compile to conditional jumps, matching the semantics you expect. - Recursive-descent parser — Pratt-style expression parsing with clear error messages that include source positions.
- Rust nightly (2024 edition)
- Linux (x86_64 or ARM)
git clone https://github.com/your-org/pausible.git
cd pausible
cargo build --release# Compile and run a .pau source file
pausible run examples/hello.pau
# Compile only (type-check + bytecode generation)
pausible compile examples/fib.pau -o fib.bin
# Type-check only (no codegen)
pausible check examples/http_get.pau
# Resume from a snapshot
pausible resume snapshot.pauspausible run prog.pau --dump-ast # Print the parsed AST
pausible run prog.pau --dump-bytecode # Print compiled bytecodecargo test # All tests (unit + integration)
cargo test --lib # Unit tests only
cargo clippy --tests -- -W clippy::pedantic # Lint (must be zero warnings)fn fib(n: Int) -> Int {
if n <= 1 {
return n;
}
return fib(n - 1) + fib(n - 2);
}
fn main() {
let result = fib(20);
stdout::write("Done");
}
Parameters require type annotations. Return types are required for functions that return a value. main() is the entry point.
fn abs(x: Int) -> Int {
if x >= 0 {
return x;
} else {
return -x;
}
}
fn sum_to(n: Int) -> Int {
let total = 0;
let i = 0;
while i <= n {
let total = total + i;
let i = i + 1;
}
return total;
}
Variables are immutable (let bindings). Reassignment uses shadowing (let x = x + 1).
fn fetch_and_save(url: String) {
let resp = http::get(url) @replay;
let f = file::open("output.json", "w") @seek;
file::write(f, resp);
file::close(f);
}
fn main() {
fetch_and_save("https://api.example.com/data");
}
The @strategy annotation on an I/O call tells the VM how to handle the resource when resuming after a snapshot. Built-in I/O modules:
| Module | Functions | Default Strategy |
|---|---|---|
file |
open, read, write, seek, close |
@seek |
http |
get, post |
@replay |
tcp |
connect, read, write, close |
@replay |
stdin |
read |
@cached |
stdout |
write |
@cached |
stderr |
write |
@cached |
timer |
sleep |
@replay |
fn process_batch() {
let data = http::get("https://api.example.com/batch") @replay;
yield resume {
ok => {
stdout::write("Resumed successfully, continuing...");
}
partial(report) => {
stderr::write("Some handles failed to reconnect");
}
}
// Execution continues here after resume
stdout::write("Batch complete");
}
A plain yield; without a resume block is also valid. The program will suspend and write a snapshot; on resume, execution picks up at the next instruction.
fn fetch(url: String) -> String {
return http::get(url) @replay;
}
fn main() {
spawn fetch("https://api.example.com/a");
spawn fetch("https://api.example.com/b");
spawn fetch("https://api.example.com/c");
let results = wait_children(); // Blocks until all children complete
yield; // Safe: all children are done
}
Children must complete before the parent yields. The compiler enforces this statically — if you forget wait_children() before yield, you get a compile-time error.
fn main() {
let xs = [1, 2, 3];
let first = xs[0];
xs[1] = 42;
// xs is now [1, 42, 3]
}
| Strategy | Meaning | Resume Behavior |
|---|---|---|
@replay |
Replayable — the operation can be repeated | Re-executes the original request. If the result differs, fires a DataDiverged event. |
@seek |
Seekable — the resource supports positioning | Reopens the resource and seeks to the recorded offset. If the file is missing or shorter, fires ResourceLost. |
@cached |
Ephemeral — one-shot data | Uses the snapshot-cached value. No reconnect attempt. |
pausible/
src/
main.rs # CLI (clap derive): compile, run, check, resume
lexer.rs # Tokenizer: keywords, literals, operators, comments
parser.rs # Recursive-descent + Pratt expression parser
ast.rs # AST node types: Expr, Stmt, FunctionDef, Program
typeck.rs # Type checker: inference, validation, symbol table
codegen.rs # AST -> bytecode compiler (Chunk + Function)
vm.rs # Stack-based VM: instruction dispatch, call frames
opcode.rs # OpCode enum (45 instructions) + serialization
value.rs # Value enum: Int, Float, Bool, String, List, Null
heap.rs # Arena allocator + tracing GC (mark-sweep, free-list)
function.rs # Function struct: name, arity, chunk, locals
chunk.rs # Chunk: bytecode sequence, constant pool
io.rs # I/O handle management + reconnect strategies
task.rs # Task tree for structured concurrency
serialize.rs # Binary (de)serialization for PAUS format
snapshot.rs # Snapshot: full VM state save/load
lib.rs # Crate root, re-exports
tests/
demo.rs # End-to-end integration tests (13 .pau programs)
io_tests.rs # I/O integration tests (19 snapshot + resume cycles)
docs/
DESIGN.md # Architecture and design decisions
LANGUAGE.md # Full EBNF grammar + language spec
PHASE5.md # Phase 5 implementation roadmap
AGENT.md # Developer guide for contributors
| Phase | Description | Status |
|---|---|---|
| Phase 1 | Core VM (26 opcodes, stack, call frames, GC) | ✅ Complete |
| Phase 2 | Snapshot format (PAUS header, heap/frame/stack serialization) | ✅ Complete |
| Phase 3 | I/O system (file, HTTP, TCP, stdin/stdout/stderr, timer) | ✅ Complete |
| Phase 4 | Structured concurrency (spawn, wait_children, task tree) | ✅ Complete |
| Phase 5 | Language frontend (lexer, parser, typeck, codegen, CLI) | ✅ Complete |
- 506 unit tests, all passing
- 32 integration tests (13 demo + 19 I/O), all passing
- Zero Clippy warnings (pedantic level)
- ~15,700 lines of Rust across 17 source files
MIT