Capstone: Source to Bytecode to VM
Work through examples, try the reasoning yourself, then put it into practice.
View SlidesBefore you start
- Complete v2 lexer, parser, analyzer, compiler, VM, and BC1 helpers.
What you will practice
- Wire exact helper return types and stop at the earliest failing stage.
- Reconstruct a full source-to-artifact trace with observable runtime checks.
- Use independent boundary evidence instead of treating round trips as sufficient.
In this lesson
Lesson
3 min readCapstone: Source to Bytecode to VM
This is the v2 integration gate. The provided helpers surround a small
CompileAndRun(src) function, but the learning work is to justify every edge:
Lex → Parse → Analyze → Compile → Encode → Decode → Run
Lex/Parse/Decode/Run return errors; Analyze returns a slice of diagnostics; Compile and Encode do not return errors in this API. Do not invent tuple shapes or a typed graph. Analysis validates the existing AST; BC1 is the artifact boundary; there is no linker yet.
Worked reconstruction
Use fn difference(a,b){ return a-b; } let result=difference(9,2);.
Parsing creates a function declaration and a variable declaration. Analysis
resolves the function and two parameters, then accepts the two-argument call.
Compilation puts LOAD a; LOAD b; SUB; RETURN in the function body. Main pushes
9, pushes 2, calls difference with arity 2, then defines result. BC1 encodes
those instructions; Decode reconstructs them before Run is called.
The result binding is 7, but the VM's final operand stack is empty, so the
capstone returns nil. To make that internal value observable through a source
program, append if (result != 7) { 1/0; }. Successful completion now proves
that this assertion path did not run. It does not turn result into a top-level
return value.
Earliest failure table
| Source fragment | First failure |
|---|---|
@ |
Lexing |
let x = ; |
Parsing |
return 1; |
Analysis: return outside function |
fn f(a){} f(); |
Analysis: arity |
let x="a"-1; |
Runtime: numeric operands required |
1/0; |
Runtime: division by zero |
If analysis reports several errors, return its first one and stop. Calling Run anyway would execute a program the front end already rejected. Use a fresh runtime state for each invocation. The same source should encode consistently and produce the same success/error behavior on repeat runs.
Integration gate
Before continuing to v3, retain a valid source, its expected AST outline, selected instructions, and one assertion about execution. Also retain a lexical, syntactic, semantic, and runtime failure. Diagnose each without reading the full reference implementation. These artifacts are your regression baseline when later versions change function representation and runtime values.
Debugging lab
A capstone returns the second analyzer error and calls Run directly on Compile’s object. Both ordinary examples seem to work. Name two contract violations and tests that distinguish them.
Compare your diagnosis after attempting the lab
Return the first analyzer error in encounter order and stop. The required Encode→Decode boundary must also be exercised; malformed-format/round-trip fixtures expose bypassing it. Runtime success alone cannot prove either contract.
Transfer task
Create a program with an undefined name in a branch that would not execute at runtime, and another with a valid branch guarding division by zero. Explain why static name traversal and dynamic branch execution give different failure outcomes.
Carry forward
Carry the v2 regression baseline into v3. The next milestone replaces named-call assumptions with closure values and extends the heap; it does not discard parsing, stack, or phase-boundary discipline.
Build compile-and-run. 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.
- Step 1
Compile and Run (Capstone)
- Step 2
Capstone: Source to Bytecode to VM Checkpoint
Predict fresh artifacts, diagnose a broken invariant, and test a version boundary. Select one answer per multiple-choice question; numeric responses use the stated integer format. Use review links to revisit the relevant representation, then attempt the independent transfer task.