Skip to content

Commit bfcee30

Browse files
hyperpolymathclaude
andcommitted
compiler: port Bytecode.res + Codegen.res + VM.res -> .affine
Modules 7-9 (251 + 526 + 727 = 1504 lines), all verified green with affinescript check and the full 9-module harness. Completes the core compiler execution path. Bytecode.affine: 44 opcodes (stack/vars/arith-with-position-metadata/comparison/ control-flow/arrays/haptics/the five Echo ops/print/halt) + Value/Chunk/ BytecodeProgram + value_to_string/opcode_to_string. paradoxType constructors Px-prefixed to avoid colliding with Types' StabilityFactor::NullPropagation. Codegen.affine: AST -> bytecode. Immutable Compiler threaded; compile_expr returns the expression Location (so ExprStmt's trailing OpPop needs no re-traversal of a consumed node); jump backpatching via array rebuild (set_code_at). Echo lowering pushes args left-to-right then emits the dedicated OpEcho* op. VM.affine: stack interpreter. Immutable Vm; stack is a [Value] with an sp mirror; globals an assoc list. Runtime errors are values (Result + ExecResult = ExContinue|ExHalt|ExError), not exceptions -- error strings preserved verbatim. Echo runtime semantics byte-faithful: OpEchoToResidue debits echo_erase_cost (15.0), erases the witness, pushes VResidue (idempotent on a residue); OpEchoInput errors on a residue; OpEchoOutput returns the retained output. Pop-order, OpArray ordering, and OpSub/OpDiv operand order all match VM.res arm-for-arm. Documented faithful deviations: float pow uses repeated multiplication (integer- exact; the stdlib has no ln/log) -- the only numeric divergence; the .res "Not implemented" ops (OpLte/OpGte, OpCall) reproduced exactly; the Console *debug* disassembler omitted (its pure helpers kept); real VM print/IO kept. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0195yA45jSSP7YDPwJSpw4bM
1 parent 17acf9b commit bfcee30

3 files changed

Lines changed: 1504 additions & 0 deletions

File tree

compiler/src/Bytecode.affine

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// Bytecode.affine — bytecode instruction set for the Error-Lang VM
3+
// (ported from compiler/src/Bytecode.res).
4+
//
5+
// Stack-based bytecode VM designed for:
6+
// - Computational haptics integration
7+
// - Positional semantics preservation
8+
// - Paradox visualization
9+
// - Educational transparency
10+
//
11+
// ── AffineScript port conventions ──
12+
// * `open` becomes module imports. Bytecode defines the opcode/value/chunk
13+
// types every sibling compiler back-end module reuses, so Codegen and VM
14+
// `use Bytecode::*;` after `use Types::*;`.
15+
// * AffineScript has no mutable struct fields and no record-spread; the
16+
// types here are pure data (immutable), so the only adaptation is the
17+
// ReScript inline-record variants and inline-record struct fields, which
18+
// become positional / named-immutable respectively.
19+
// * ReScript `value` variant `VFunction({arity, address, name})` -> the
20+
// positional `VFunction(Int, Int, String)` (arity, address, name).
21+
// `VEcho({input, output})` -> `VEcho(Value, Value)`;
22+
// `VResidue({output})` -> `VResidue(Value)`.
23+
// * `positionMetadata.operatorType` field is renamed `opType` to avoid the
24+
// field/enum-name ambiguity the TypeSuperposition port flagged
25+
// (`quantumType` -> `qtype`); purely a local rename, semantics identical.
26+
// * Opcode/value constructor names do not collide with `Types` and are kept
27+
// verbatim (OpPush/OpAdd/…, VInt/VFloat/…).
28+
//
29+
// ── Faithfulness notes: documented gaps / simplifications vs Bytecode.res ──
30+
// * `paradoxType` constructors are PREFIXED `Px` (PxTypeSuperposition …
31+
// PxMemoryPhantom). Reason: the unprefixed `NullPropagation` would COLLIDE
32+
// with the global `StabilityFactor::NullPropagation(Int)` constructor in
33+
// Types.affine (constructor names are global in AffineScript), exactly the
34+
// situation TypeChecker.affine handled by C-prefixing its `ty` ctors. The
35+
// payloads are opaque to every consumer (`OpInjectParadox(_)` discards
36+
// them in Codegen and VM), so the spelling change is invisible to
37+
// behaviour. The 1:1 mapping is: PxTypeSuperposition, PxPositionalSemantics,
38+
// PxScopeLeakage, PxTemporalCorruption, PxArithmeticDrift, PxNullPropagation,
39+
// PxContextCollapse, PxReservedWordRoulette, PxGlobalEntanglement,
40+
// PxMemoryPhantom.
41+
// * `disassemble` (a `Console.log`-based debug pretty-printer; not referenced
42+
// by Codegen or the VM, not part of program behaviour) is OMITTED per the
43+
// "omit Console/test-harness code" rule. The pure helpers it called —
44+
// `value_to_string` and `opcode_to_string` — ARE ported: `value_to_string`
45+
// is needed at runtime by the VM's positional-concatenation path, and
46+
// `opcode_to_string` mirrors the .res disassembly text faithfully.
47+
// * `Int.toString` / `Float.toString` / `String.padStart` -> `int_to_string`
48+
// / `float_to_string` / (padStart only existed inside the omitted
49+
// `disassemble`, so it is not needed). Array.map/joinWith -> map + join.
50+
// * No `runTests` code exists in Bytecode.res, so nothing else is omitted.
51+
52+
module Bytecode;
53+
54+
use prelude::*;
55+
use string::{join};
56+
use Types::{Location};
57+
58+
// ============================================
59+
// Operator position metadata
60+
// ============================================
61+
62+
// Operators whose meaning flips with source position.
63+
pub enum OperatorType {
64+
PlusOp, // Can be addition OR concatenation
65+
StarOp // Can be multiplication OR exponentiation
66+
}
67+
68+
// Position metadata for operators that change behavior based on location.
69+
// (ReScript inline record under `OpAdd`/`OpMul`; here a named struct.)
70+
pub struct PositionMetadata {
71+
line: Int,
72+
column: Int,
73+
opType: OperatorType
74+
}
75+
76+
// Paradox kinds (Px-prefixed; see header faithfulness note re: collision with
77+
// Types::StabilityFactor::NullPropagation).
78+
pub enum ParadoxType {
79+
PxTypeSuperposition,
80+
PxPositionalSemantics,
81+
PxScopeLeakage,
82+
PxTemporalCorruption,
83+
PxArithmeticDrift,
84+
PxNullPropagation,
85+
PxContextCollapse,
86+
PxReservedWordRoulette,
87+
PxGlobalEntanglement,
88+
PxMemoryPhantom
89+
}
90+
91+
// ============================================
92+
// Runtime values
93+
// ============================================
94+
95+
pub enum Value {
96+
VInt(Int),
97+
VFloat(Float),
98+
VString(String),
99+
VBool(Bool),
100+
VNil,
101+
VArray([Value]),
102+
// ReScript VFunction({arity, address, name}) -> positional (arity, address, name).
103+
VFunction(Int, Int, String),
104+
// A single fiber witness: input `x` reached output `y` (one element of
105+
// `Echo<A, B>`). ReScript VEcho({input, output}) -> positional (input, output).
106+
VEcho(Value, Value),
107+
// The residue of an echo after erasure: the input witness is gone
108+
// (non-recoverable), only the reached output is retained. This is
109+
// `EchoR<A, B>` at runtime. ReScript VResidue({output}) -> positional (output).
110+
VResidue(Value)
111+
}
112+
113+
// ============================================
114+
// Bytecode instructions
115+
// ============================================
116+
117+
pub enum Opcode {
118+
// Stack manipulation
119+
OpPush(Value), // Push constant onto stack
120+
OpPop, // Pop top of stack
121+
OpDup, // Duplicate top of stack
122+
123+
// Variables
124+
OpGetLocal(Int), // Get local variable
125+
OpSetLocal(Int), // Set local variable
126+
OpGetGlobal(String), // Get global variable
127+
OpSetGlobal(String), // Set global variable
128+
129+
// Arithmetic (with positional semantics metadata)
130+
OpAdd(PositionMetadata), // Addition (or concatenation based on position!)
131+
OpSub, // Subtraction
132+
OpMul(PositionMetadata), // Multiplication (or exponentiation based on position!)
133+
OpDiv, // Division
134+
OpMod, // Modulo
135+
OpNegate, // Unary negation
136+
137+
// Comparison
138+
OpEq, OpNeq, OpLt, OpGt, OpLte, OpGte,
139+
140+
// Logical
141+
OpAnd, OpOr, OpNot,
142+
143+
// Control flow
144+
OpJump(Int), // Unconditional jump
145+
OpJumpIfFalse(Int), // Conditional jump
146+
OpCall(Int), // Call function (arity)
147+
OpReturn, // Return from function
148+
149+
// Arrays
150+
OpArray(Int), // Create array with N elements
151+
OpIndex, // Array indexing
152+
153+
// Special (computational haptics)
154+
OpTrace(String), // Add trace point
155+
OpCheckpoint(String), // Add checkpoint
156+
OpUpdateStability, // Recalculate stability score
157+
OpInjectParadox(ParadoxType), // Activate a paradox
158+
159+
// Echo types (structured loss)
160+
OpEcho, // Pop output then input, push a fiber witness VEcho(input, output)
161+
OpEchoToResidue, // Pop an echo, push its residue (erases the witness; costs stability)
162+
OpResidueStrictlyLoses, // Pop a value, push VBool(true) iff it is a residue
163+
OpEchoInput, // Pop an echo, push its input witness (runtime error on a residue)
164+
OpEchoOutput, // Pop an echo or residue, push its retained output
165+
166+
// Debug
167+
OpPrint(Bool), // Print (println if true)
168+
OpHalt // Stop execution
169+
}
170+
171+
// ============================================
172+
// Compiled bytecode chunk + program
173+
// ============================================
174+
175+
pub struct Chunk {
176+
code: [Opcode],
177+
constants: [Value],
178+
// Source location mapping for error reporting.
179+
locations: [Location]
180+
}
181+
182+
pub struct BytecodeProgram {
183+
main: Chunk,
184+
functions: [(String, Chunk)]
185+
}
186+
187+
// ============================================
188+
// Utilities
189+
// ============================================
190+
191+
pub fn value_to_string(v: Value) -> String {
192+
match v {
193+
VInt(n) => int_to_string(n),
194+
VFloat(f) => float_to_string(f),
195+
VString(s) => "\"" ++ s ++ "\"",
196+
VBool(b) => if b { "true" } else { "false" },
197+
VNil => "nil",
198+
VArray(arr) => {
199+
let items = join(map(arr, value_to_string), ", ");
200+
"[" ++ items ++ "]"
201+
},
202+
VFunction(_arity, _address, name) => "<function " ++ name ++ ">",
203+
VEcho(input, output) =>
204+
"echo(" ++ value_to_string(input) ++ " ↦ " ++ value_to_string(output) ++ ")",
205+
VResidue(output) => "residue(↦ " ++ value_to_string(output) ++ ")"
206+
}
207+
}
208+
209+
pub fn opcode_to_string(op: Opcode) -> String {
210+
match op {
211+
OpPush(v) => "PUSH " ++ value_to_string(v),
212+
OpPop => "POP",
213+
OpDup => "DUP",
214+
OpGetLocal(i) => "GET_LOCAL " ++ int_to_string(i),
215+
OpSetLocal(i) => "SET_LOCAL " ++ int_to_string(i),
216+
OpGetGlobal(name) => "GET_GLOBAL " ++ name,
217+
OpSetGlobal(name) => "SET_GLOBAL " ++ name,
218+
OpAdd(_p) => "ADD",
219+
OpSub => "SUB",
220+
OpMul(_p) => "MUL",
221+
OpDiv => "DIV",
222+
OpMod => "MOD",
223+
OpNegate => "NEGATE",
224+
OpEq => "EQ",
225+
OpNeq => "NEQ",
226+
OpLt => "LT",
227+
OpGt => "GT",
228+
OpLte => "LTE",
229+
OpGte => "GTE",
230+
OpAnd => "AND",
231+
OpOr => "OR",
232+
OpNot => "NOT",
233+
OpJump(offset) => "JUMP " ++ int_to_string(offset),
234+
OpJumpIfFalse(offset) => "JUMP_IF_FALSE " ++ int_to_string(offset),
235+
OpCall(arity) => "CALL " ++ int_to_string(arity),
236+
OpReturn => "RETURN",
237+
OpArray(size) => "ARRAY " ++ int_to_string(size),
238+
OpIndex => "INDEX",
239+
OpTrace(msg) => "TRACE \"" ++ msg ++ "\"",
240+
OpCheckpoint(name) => "CHECKPOINT \"" ++ name ++ "\"",
241+
OpUpdateStability => "UPDATE_STABILITY",
242+
OpInjectParadox(_p) => "INJECT_PARADOX",
243+
OpEcho => "ECHO",
244+
OpEchoToResidue => "ECHO_TO_RESIDUE",
245+
OpResidueStrictlyLoses => "RESIDUE_STRICTLY_LOSES",
246+
OpEchoInput => "ECHO_INPUT",
247+
OpEchoOutput => "ECHO_OUTPUT",
248+
OpPrint(is_println) => if is_println { "PRINTLN" } else { "PRINT" },
249+
OpHalt => "HALT"
250+
}
251+
}

0 commit comments

Comments
 (0)