v5 Capstone: Full Pipeline

Work through examples, try the reasoning yourself, then put it into practice.

View Slides

Before you start

  • Complete v5 module parsing, dynamic semantics, compilation, and relocation.

What you will practice

  • Trace multi-module initialization through BC2 and a fresh VM.
  • Test relocation with control flow in a later module.
  • Separate name/export validity from dynamic call checks.
In this lesson

Lesson

3 min read

v5 Capstone: Full Pipeline

The integration path now adds real separate compilation and linking:

Lex → Parse → Analyze → CompileModules → Link → Encode → Decode → NewVM().Run

CompileModules and Encode return artifacts directly. Link, Decode, and Run can return errors. Analyze still returns diagnostic strings, not static type annotations; v5 is dynamically typed.

Worked reconstruction

module util { export fn add(a,b){ return a+b; } }
module main {
  import util as u;
  let total=0;
  for (let i=0; i<3; i=i+1) { total=u.add(total,i); }
  if (total!=3) { 1/0; }
}

util's initializer constructs and exports a closure. main's initializer loads that export map, then runs a loop. Both modules started with local function IDs and local Main instruction coordinates. Link shifts main's Main jump operands by util's initializer length, while preserving function-local jumps. Decode reconstructs the merged artifact. The VM initializes util before main, and the assertion proves total reached 3. Normal completion returns nil.

A straight-line main might pass with a broken linker because no Main jump uses the missing offset. This program is deliberately a transfer test combining v4 control flow with v5 separate compilation.

First-failure discipline

Malformed syntax fails before module resolution. Unknown imports, inaccessible exports, invalid loop transfers, and name errors fail analysis. The linker also validates its independent artifact inputs, but source rejected by Analyze should never reach it. Imported-member call arity remains a runtime check in v5; do not replace examples with v6 typed function syntax or promise v6 errors.

Encode and Decode form the BC2 transport boundary. A round trip supplements independent instruction/relocation tests; it cannot prove that both sides did not preserve the same wrong address. Execute only the decoded object with a fresh VM, and return earlier errors without fallback execution.

Integration gate

Keep a library closure, a later initializer branch/loop, an unknown export, and repeated Link on saved artifacts. Add a declaration whose value export can differ from a later mutation of its captured environment. Explain the observed module semantics before moving to static types.

Debugging lab

A v5 program with two modules works until a for loop is added to main. The same loop works in implicit-main mode. Which boundary is the strongest first suspect and which two offsets should be inspected?

Compare your diagnosis after attempting the lab

Inspect Link: multi-module Main concatenation relocates instruction addresses, while implicit main starts at offset zero. Check Main-length relocation separately from function-count relocation; correct closure IDs do not prove correct branches.

Transfer task

Add a second library importing the first, then put a nested closure and a while loop in the final module. Assert a numeric result through a conditional failure and compare repeated runs and repeated linking of the saved module artifacts.

Carry forward

Carry the multi-module BC2 baseline into v6. The runtime and linker remain useful; new static contracts reject additional source programs before those stages run.

Build compile-and-run-v5. Keep a before/after trace of one failing test. Use the checkpoint to diagnose specific skills; passing it does not replace implementing and testing the artifact.


Practice sequence

Use the order below for a guided path, or choose an item to revisit. A pass records a successful attempt; try a fresh example later to check what you can reconstruct.

Join Discord