Skip to content

Latest commit

 

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pausible

Rust Status

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.

Core Idea

  • Write .pau source code.
  • The compiler generates bytecode.
  • The VM executes the bytecode.
  • At any yield point, 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.

Key Features

  • Explicit yield — Programs decide when to pause via the yield keyword, 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.

Quick Start

Prerequisites

  • Rust nightly (2024 edition)
  • Linux (x86_64 or ARM)

Build

git clone https://github.com/your-org/pausible.git
cd pausible
cargo build --release

Run a Program

# 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.paus

Debug Flags

pausible run prog.pau --dump-ast        # Print the parsed AST
pausible run prog.pau --dump-bytecode   # Print compiled bytecode

Run Tests

cargo test                # All tests (unit + integration)
cargo test --lib           # Unit tests only
cargo clippy --tests -- -W clippy::pedantic   # Lint (must be zero warnings)

Language Overview

Functions

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.

Variables and Control Flow

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).

I/O with Strategy Annotations

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

Yield and Resume

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.

Structured Concurrency

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.

Lists

fn main() {
    let xs = [1, 2, 3];
    let first = xs[0];
    xs[1] = 42;
    // xs is now [1, 42, 3]
}

I/O Strategy Reference

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.

Project Structure

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

Current Status

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

License

MIT


中文版 (Chinese)

About

Pausible language.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages