The Io programming language — a dynamic prototype-based language built on message passing, targeting WASM/WASI.
Requires wasi-sdk and wasmtime.
make # build build/bin/io_static (WASM binary)
make test # build build/bin/test_iterative_eval
make check # run both test suites with wasmtime
make clean # remove build artifacts
make regenerate # regenerate IoVMInit.c from .io filesSet WASI_SDK if wasi-sdk is not at ~/wasi-sdk:
make WASI_SDK=/opt/wasi-sdk# Run a one-liner
wasmtime build/bin/io_static -e '"hello" println'
# Run a file
wasmtime --dir=. build/bin/io_static path/to/script.io
# C test suite (iterative evaluator)
wasmtime build/bin/test_iterative_eval
# Io test suite
wasmtime --dir=. --dir=/tmp build/bin/io_static libs/iovm/tests/correctness/run.io
IO_TEST_VERBOSE=1 wasmtime --dir=. --dir=/tmp build/bin/io_static libs/iovm/tests/correctness/run.iolibs/iovm/source/ — Core VM implementation (C)
libs/iovm/io/ — Io standard library (.io files, compiled to C via io2c)
libs/iovm/tests/ — C test files
tools/source/main.c — REPL / CLI entry point
agents/ — Design docs for the continuations/stackless work
| File | Purpose |
|---|---|
IoState_iterative.c |
Iterative eval loop with frame state machine |
IoState_eval.c |
Entry points (IoState_doCString_, IoState_on_doCString_withLabel_) |
IoMessage.c |
IoMessage_locals_performOn_ — recursive evaluator |
IoBlock.c |
Block activation (currently uses recursive evaluator) |
IoObject_flow.c |
Control flow primitives: if, while, for, loop, break, continue, return |
IoCoroutine.c |
Coroutine implementation (frame-based, no C stack switching) |
IoContinuation.c |
First-class continuations (callcc, capture, invoke) |
IoEvalFrame.h/c |
Frame structure and state machine enums |
IoState_inline.h |
Inline helpers, arg pre-evaluation |
IoState.h |
VM state structure |
Replacing C stack recursion with heap-allocated frames to enable:
- First-class continuations (serializable, network-transmittable)
- Portable coroutines (no platform-specific assembly)
- No setjmp/longjmp, no ucontext, no fibers
See agents/ for detailed design docs:
CONTINUATIONS_TODO.md— Phase tracker and implementation notesC_STACK_ELIMINATION_PLAN.md— Overall architecture planVM_EVAL_LOOP.md— Eval loop design reference
When .io files in libs/iovm/io/ are modified, the generated libs/iovm/source/IoVMInit.c must be regenerated:
make regenerate
make- Iterative (
IoState_iterative.c): Frame state machine. Used for all evaluation including control flow, continuations, coroutine switching. IoMessage_locals_performOn_redirects to the iterative eval loop whencurrentFrameis set. A bootstrap-only recursive fallback exists for VM initialization (before the first eval loop starts).
Messages whose arguments must NOT be pre-evaluated: if, while, loop, for, callcc, method, block, foreach, reverseForeach, foreachLine. Checked in two places in IoState_iterative.c.
- C-level:
IoState_error_setsstate->errorRaised = 1. Eval loop unwinds frames. - Io-level:
Exception raisecallsrawSignalExceptionwhich also setserrorRaised. - Helper functions return early on error (no longjmp). Eval loop handles all unwinding.
DEBUG_EVAL_LOOP— verbose iterative eval loop tracingDEBUG_CORO_EVAL— coroutine operation tracing
Every Io object is a CollectorMarker (typedef struct CollectorMarker IoObject). The marker's object field points to IoObjectData containing: a tag (vtable), slots (PHash cuckoo hash table), protos array, and a data union for primitive values.
All operations are message sends. The parser produces message chains, then IoMessage_opShuffle.c rewrites them by operator precedence. Assignment operators: := → setSlot, = → updateSlot, ::= → newSlot.
Standard library files in libs/iovm/io/ are loaded in the explicit order listed in the Makefile IO_FILES variable: bootstrap files (List_bootstrap.io, Object_bootstrap.io, OperatorTable.io, Object.io, List.io, Exception.io) first, then alphabetical core, then CLI.io, Importer.io last.
- Indentation: Tabs in both C and Io code
- C naming:
IoObjectName_methodName(e.g.,IoList_append_) - C method macro:
IO_METHOD(CLASS, NAME)expands toIoObject *CLASS_NAME(CLASS *self, IoObject *locals, IoMessage *m) - Io naming: camelCase for methods, PascalCase for objects