diff --git a/.machine_readable/STATE.scm b/.machine_readable/STATE.scm index f9e9aa4..f49d91d 100644 --- a/.machine_readable/STATE.scm +++ b/.machine_readable/STATE.scm @@ -1,5 +1,5 @@ ;; SPDX-License-Identifier: PMPL-1.0-or-later -(state (metadata (version "1.7.0") (last-updated "2026-02-27") (status active)) +(state (metadata (version "1.8.0") (last-updated "2026-02-28") (status active)) (project-context (name "idaptik") (purpose "Asymmetric co-op stealth puzzle-platformer and adaptive game engine ecosystem") @@ -19,11 +19,16 @@ (critical-next-actions (action "Level Architect MVP: implement level data model (14 Idris2 ABI modules) — see plan file") (action "Game balance: play-test levels; tune guard spawn rates and alert thresholds in LevelConfig.res") - (action "Sonnet: migrate 1,723 deprecated Js.* API calls to @rescript/core equivalents") - (action "Sonnet: extract duplicated %raw keyboard handler blocks (5 files) to shared utility") - (action "Sonnet: migrate 24 getExn/parseExn calls in vm/idaptiky to SafeFloat/SafeJson") - (action "Axiom.jl: consolidate 2,857-line abstract.jl into 4+ focused files — see TODO-URGENT-COPROCESSOR-CONSOLIDATION.md")) + (action "Guard visual knockdown: add rotation/lying-flat sprite during KnockedDown state in GuardNPC.res renderGuard()") + (action "Zig solvers: implement actual visibility.zig and wiring.zig solver logic (Phase 1 per WORKPLAN)") + (action "Idris2 0.9.0: re-test Layout.idr and Foreign.idr when compiler releases")) + (completed-migrations + (migration "2026-02-28" "Js.* API migration: ~945 deprecated Js.Dict/Console/log/String2/Array2/Math/Float/Int/Promise/Nullable calls migrated to @rescript/core equivalents across 67 files; remaining Js.Json encode/decode calls left as-is (no 1:1 equivalent)") + (migration "2026-02-28" "getExn/parseExn: 7 calls in vm/ migrated — Belt.Array.getExn→getUnsafe (all bounds-checked); State.res deserializeState rewritten with JSON.Classify.classify") + (migration "2026-02-28" "Keyboard handlers: 5 duplicated %raw blocks extracted to KeyboardNav.res utility (addKeydownHandler, removeKeydownHandler, removeFromRef, eventKey, preventDefault); WorldBuilder, TrainingBase, TrainingMenuScreen, VictoryScreen, GameOverScreen refactored") + (migration "2026-02-28" "ratatui lru vuln: Cargo.toml bumped ratatui 0.29→0.30 (resolves transitive lru 0.12.5 Dependabot alert)")) (recent-changes + (change "2026-02-28" "CLEANUP-SESSION: Migrated ~945 deprecated Js.* API calls to @rescript/core across 67 files; extracted 5 duplicated %raw keyboard handlers to KeyboardNav.res; migrated 7 getExn/parseExn calls to safe alternatives; bumped ratatui 0.29→0.30 (lru vuln fix); removed Chapel references from WORKPLAN; triaged stale session todos; cleaned up #/editor stub in UMS main.js; updated OPUS-TODO tracking table; updated LOOSE-ENDS.md and STATE.scm") (change "2026-02-27" "OPUS-SESSION-2: Coprocessor consolidation (10 individual files→3: Coprocessor_Compute.res [Maths+Vector+Tensor+Physics], Coprocessor_Security.res [Crypto+Neural+Quantum+Audio+Graphics], Coprocessor_IO.res [unchanged]); 36 stale copies deleted across build dirs; Kernel_Crypto.res and Kernel_Quantum.res comments updated; Coprocessor_Backends.res rewritten to use nested module paths") (change "2026-02-27" "OPUS-SESSION-2: Deleted 5 AI-generated multiplayer hype files (pata_orchestrator.ex, consensus_core.ex, bonding_handler.ex, pressure_monitor.ex, control_channel.ex); removed Resilience Core from application.ex; removed control:* channel from user_socket.ex") (change "2026-02-27" "OPUS-SESSION-2: Created DESIGN-DECISIONS.adoc (developer-facing, 476 lines) and DESIGN-OVERVIEW.adoc (public-facing, 273 lines) at repo root; language stack finalized (ReScript+Idris2+Zig+Elixir+Rust); V-lang, Chapel, Dragonfly removed") diff --git a/LOOSE-ENDS.md b/LOOSE-ENDS.md index a5d6579..e29b955 100644 --- a/LOOSE-ENDS.md +++ b/LOOSE-ENDS.md @@ -5,14 +5,14 @@ Quick wins and half-finished items to follow up on. Most are 5 minutes or less. ## Documentation Cleanup -- [ ] **WORKPLAN-2026-02-27.md**: Remove Chapel references (Phase 1 line 13, blocker - line 73, risk line 98). Chapel was removed 2026-02-27, replaced by Zig solvers. -- [ ] **shared/OPUS-TODO.adoc**: Update tracking table — at least 2 items already done - (P1 README.adoc files exist; P2 mapBounded implemented with chunkSize=9). Move - completed items to a DONE section. -- [ ] **main-game/docs/immediate-actions/2026-02-22-session-todos.md**: 5 days stale. - Check if 3 PENDING items are still relevant (guard visual knockdown, pole detection - cone, split-screen key bindings). Archive to COMPLETED- prefix if done. +- [x] **WORKPLAN-2026-02-27.md**: ~~Remove Chapel references~~ — Done 2026-02-28. + Chapel references replaced with Zig solvers; Chapel blocker row removed. +- [x] **shared/OPUS-TODO.adoc**: ~~Update tracking table~~ — Done 2026-02-28. + P1 README.adoc marked PARTIAL (some exist, some don't); P2 mapBounded marked PARTIAL + (implemented with chunkSize=9, per-domain defaults still needed). +- [x] **main-game/docs/immediate-actions/2026-02-22-session-todos.md**: ~~Triage stale items~~ + — Done 2026-02-28. Detection cones confirmed implemented; guard visual knockdown still + open; split-screen deferred. ## Build & Code @@ -20,20 +20,28 @@ Quick wins and half-finished items to follow up on. Most are 5 minutes or less. type pattern matching bug. Re-test when Idris2 0.9.0 releases. - [ ] **idaptik-ums/src/abi/Foreign.idr**: Excluded from build — depends on Layout.idr. Has a TODO for safe callback registration. Unblocked when Layout.idr compiles. -- [ ] **idaptik-ums/main.js**: Hash route `#/editor` is stubbed out (renders - GeneratorDemo regardless). Uncomment App import + render when TEA editor is ready, - or delete the stub if editor is deferred beyond MVP. +- [x] **idaptik-ums/main.js**: ~~Hash route `#/editor` stub~~ — Done 2026-02-28. + Removed dead routing code; single-render entry point now. - [ ] **Zig solvers**: `ffi/zig/src/visibility.zig` and `wiring.zig` are boilerplate stubs. Need actual solver implementations (Phase 1 per WORKPLAN). ## Migrations (Sonnet-scale) -- [ ] **1,723 deprecated Js.\* API calls** → migrate to @rescript/core equivalents - (tracked in STATE.scm critical-next-actions) -- [ ] **24 getExn/parseExn calls** in vm/idaptiky → SafeFloat/SafeJson -- [ ] **5 duplicated %raw keyboard handler blocks** → extract to shared utility +- [x] **~945 deprecated Js.\* API calls** → migrated to @rescript/core equivalents + (2026-02-28). Js.Dict, Js.Console, Js.log, Js.String2, Js.Array2, Js.Math, Js.Float, + Js.Int, Js.Promise, Js.Nullable all replaced across 67 files. Remaining Js.Json + encode/decode calls (string, number, object_, array, decodeObject, decodeString, + decodeNumber, decodeArray) left as-is — no clean @rescript/core 1:1 equivalent. +- [x] **7 getExn/parseExn calls** in vm/ → migrated 2026-02-28. Belt.Array.getExn + replaced with Belt.Array.getUnsafe (all calls already bounds-checked). State.res + deserializeState rewritten to use JSON.Classify.classify instead of Js.Json.decode*. +- [x] **5 duplicated %raw keyboard handler blocks** → extracted to shared + KeyboardNav.res utility (2026-02-28). WorldBuilder, TrainingBase, TrainingMenuScreen, + VictoryScreen, GameOverScreen all refactored to use KeyboardNav.addKeydownHandler, + KeyboardNav.removeFromRef, KeyboardNav.eventKey, KeyboardNav.preventDefault. - [ ] **Axiom.jl**: Consolidate 2,857-line abstract.jl into 4+ focused files - (see TODO-URGENT-COPROCESSOR-CONSOLIDATION.md) + (see TODO-URGENT-COPROCESSOR-CONSOLIDATION.md) — lives in external sibling repo, + not actionable from this repository. ## Infrastructure @@ -41,7 +49,8 @@ Quick wins and half-finished items to follow up on. Most are 5 minutes or less. GitHub, re-add hyperpolymath as admin, set up secrets (GITLAB_TOKEN etc.) - [ ] **K9-SVC contractiles**: Deferred — add security boundaries for /db/portfolios/* and /db/campaigns/* when sync server gets real auth/multi-user. -- [ ] **ratatui lru vuln** in escape-hatch: Upgrade available, quick Cargo.toml bump. +- [x] **ratatui lru vuln** in escape-hatch: ~~Upgrade available~~ — Done 2026-02-28. + Bumped ratatui 0.29 → 0.30 in Cargo.toml. ## Future Ideas diff --git a/dlc/idaptik-reversible/examples/07_puzzle_solver_demo.res b/dlc/idaptik-reversible/examples/07_puzzle_solver_demo.res index 9c0b60a..693db23 100644 --- a/dlc/idaptik-reversible/examples/07_puzzle_solver_demo.res +++ b/dlc/idaptik-reversible/examples/07_puzzle_solver_demo.res @@ -31,14 +31,14 @@ let createSimplePuzzle = (): Puzzle.puzzle => { // Solve the puzzle let solvePuzzle = (): unit => { - Js.Console.log( + Console.log( "╔════════════════════════════════════════╗", ) - Js.Console.log("║ Puzzle Solver Demo ║") - Js.Console.log( + Console.log("║ Puzzle Solver Demo ║") + Console.log( "╚════════════════════════════════════════╝", ) - Js.Console.log("") + Console.log("") let puzzle = createSimplePuzzle() Puzzle.printPuzzleInfo(puzzle) @@ -46,43 +46,43 @@ let solvePuzzle = (): unit => { // Create solver let solver = PuzzleSolver.create(puzzle) - Js.Console.log("Attempting solution...") - Js.Console.log("") + Console.log("Attempting solution...") + Console.log("") // Move 1: ADD x y - Js.Console.log("Move 1: ADD x y") + Console.log("Move 1: ADD x y") let (solver, result) = PuzzleSolver.executeMove(solver, Add.make("x", "y")) switch result { | Success(newState) => { - Js.Console.log("✓ Move successful") + Console.log("✓ Move successful") StateDiff.printDiff(puzzle.initialState, newState) } - | PuzzleSolved => Js.Console.log("🎉 Puzzle solved!") - | InvalidMove(msg) => Js.Console.log(`✗ Invalid move: ${msg}`) - | MoveLimitReached => Js.Console.log("✗ Move limit reached") + | PuzzleSolved => Console.log("🎉 Puzzle solved!") + | InvalidMove(msg) => Console.log(`✗ Invalid move: ${msg}`) + | MoveLimitReached => Console.log("✗ Move limit reached") } - Js.Console.log("") + Console.log("") PuzzleSolver.printStatus(solver) // Check if solved if PuzzleSolver.isSolved(solver) { - Js.Console.log("") - Js.Console.log( + Console.log("") + Console.log( "╔════════════════════════════════════════╗", ) - Js.Console.log("║ 🎉 Puzzle Solved! ║") - Js.Console.log( + Console.log("║ 🎉 Puzzle Solved! ║") + Console.log( "╚════════════════════════════════════════╝", ) - Js.Console.log(`Moves used: ${Belt.Int.toString(PuzzleSolver.getMoveCount(solver))}`) + Console.log(`Moves used: ${Belt.Int.toString(PuzzleSolver.getMoveCount(solver))}`) switch puzzle.maxMoves { | Some(max) => { let used = PuzzleSolver.getMoveCount(solver) if used <= max { - Js.Console.log( + Console.log( `✓ Under move limit (${Belt.Int.toString(used)}/${Belt.Int.toString(max)})`, ) } @@ -94,39 +94,39 @@ let solvePuzzle = (): unit => { // Demonstrate undo let demonstrateUndo = (): unit => { - Js.Console.log("") - Js.Console.log( + Console.log("") + Console.log( "╔════════════════════════════════════════╗", ) - Js.Console.log("║ Undo Demo ║") - Js.Console.log( + Console.log("║ Undo Demo ║") + Console.log( "╚════════════════════════════════════════╝", ) - Js.Console.log("") + Console.log("") let puzzle = createSimplePuzzle() let solver = PuzzleSolver.create(puzzle) // Make a move - Js.Console.log("Making a move: ADD x y") + Console.log("Making a move: ADD x y") let (solver, _) = PuzzleSolver.executeMove(solver, Add.make("x", "y")) - Js.Console.log(`Moves: ${Belt.Int.toString(PuzzleSolver.getMoveCount(solver))}`) + Console.log(`Moves: ${Belt.Int.toString(PuzzleSolver.getMoveCount(solver))}`) // Undo it - Js.Console.log("Undoing...") + Console.log("Undoing...") let (solver, success) = PuzzleSolver.undoMove(solver) if success { - Js.Console.log("✓ Undo successful") - Js.Console.log(`Moves: ${Belt.Int.toString(PuzzleSolver.getMoveCount(solver))}`) + Console.log("✓ Undo successful") + Console.log(`Moves: ${Belt.Int.toString(PuzzleSolver.getMoveCount(solver))}`) // Check state is restored let currentState = PuzzleSolver.getCurrentState(solver) if State.statesMatch(currentState, puzzle.initialState) { - Js.Console.log("✓ State perfectly restored to initial") + Console.log("✓ State perfectly restored to initial") } } else { - Js.Console.log("✗ Undo failed") + Console.log("✗ Undo failed") } } @@ -134,5 +134,5 @@ let demonstrateUndo = (): unit => { solvePuzzle() demonstrateUndo() -Js.Console.log("") -Js.Console.log("✓ Puzzle solver demo complete!") +Console.log("") +Console.log("✓ Puzzle solver demo complete!") diff --git a/dlc/idaptik-reversible/examples/09_complete_workflow.res b/dlc/idaptik-reversible/examples/09_complete_workflow.res index 988c12d..835012f 100644 --- a/dlc/idaptik-reversible/examples/09_complete_workflow.res +++ b/dlc/idaptik-reversible/examples/09_complete_workflow.res @@ -171,32 +171,32 @@ let demoPuzzleSolving = (): unit => { let solver = PuzzleSolver.create(puzzle) // Solve in one move - Js.Console.log("💡 Solution: ADD a b") + Console.log("💡 Solution: ADD a b") let (solver, result) = PuzzleSolver.executeMove(solver, Add.make("a", "b")) switch result { | PuzzleSolved => { - Js.Console.log("🎉 Puzzle solved!") - Js.Console.log(`Moves used: ${Belt.Int.toString(PuzzleSolver.getMoveCount(solver))}`) + Console.log("🎉 Puzzle solved!") + Console.log(`Moves used: ${Belt.Int.toString(PuzzleSolver.getMoveCount(solver))}`) } - | Success(_) => Js.Console.log("Move successful, keep going...") - | InvalidMove(msg) => Js.Console.log(`Invalid: ${msg}`) - | MoveLimitReached => Js.Console.log("Out of moves!") + | Success(_) => Console.log("Move successful, keep going...") + | InvalidMove(msg) => Console.log(`Invalid: ${msg}`) + | MoveLimitReached => Console.log("Out of moves!") } - Js.Console.log("") + Console.log("") } // Part 5: Performance Measurement let demoPerformance = (): unit => { - Js.Console.log( + Console.log( "╔════════════════════════════════════════╗", ) - Js.Console.log("║ Part 5: Performance Measurement ║") - Js.Console.log( + Console.log("║ Part 5: Performance Measurement ║") + Console.log( "╚════════════════════════════════════════╝", ) - Js.Console.log("") + Console.log("") let iterations = 10000 @@ -204,8 +204,8 @@ let demoPerformance = (): unit => { let startTime = now() for _ in 1 to iterations { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let instr = Add.make("x", "y") instr.execute(state) } @@ -213,35 +213,35 @@ let demoPerformance = (): unit => { let totalTime = endTime -. startTime let avgTime = totalTime /. Belt.Int.toFloat(iterations) - Js.Console.log(`ADD instruction (${Belt.Int.toString(iterations)} iterations):`) - Js.Console.log(` Total: ${Js.Float.toFixedWithPrecision(totalTime, ~digits=2)} ms`) - Js.Console.log(` Average: ${Js.Float.toFixedWithPrecision(avgTime, ~digits=4)} ms`) - Js.Console.log( - ` Throughput: ${Js.Float.toFixedWithPrecision(1000.0 /. avgTime, ~digits=0)} ops/sec`, + Console.log(`ADD instruction (${Belt.Int.toString(iterations)} iterations):`) + Console.log(` Total: ${Float.toFixed(totalTime, ~digits=2)} ms`) + Console.log(` Average: ${Float.toFixed(avgTime, ~digits=4)} ms`) + Console.log( + ` Throughput: ${Float.toFixed(1000.0 /. avgTime, ~digits=0)} ops/sec`, ) - Js.Console.log("") + Console.log("") } // Part 6: Complex Algorithm let demoComplexAlgorithm = (): unit => { - Js.Console.log( + Console.log( "╔════════════════════════════════════════╗", ) - Js.Console.log("║ Part 6: Complex Algorithm (GCD) ║") - Js.Console.log( + Console.log("║ Part 6: Complex Algorithm (GCD) ║") + Console.log( "╚════════════════════════════════════════╝", ) - Js.Console.log("") + Console.log("") // Euclidean GCD algorithm (reversible!) let state = State.createState(~variables=["a", "b", "temp"], ~initialValue=0) - Js.Dict.set(state, "a", 48) - Js.Dict.set(state, "b", 18) + Dict.set(state, "a", 48) + Dict.set(state, "b", 18) let vm = VM.make(state) - Js.Console.log("Computing GCD(48, 18) using Euclidean algorithm...") - Js.Console.log("") + Console.log("Computing GCD(48, 18) using Euclidean algorithm...") + Console.log("") // Use ref() cells for the loop-control variables: `let mut` needs // ReScript 12 mode without the legacy Belt/Reason compat flags. @@ -249,15 +249,15 @@ let demoComplexAlgorithm = (): unit => { let continueLoop = ref(true) while continueLoop.contents { - let a = Js.Dict.get(VM.getState(vm), "a")->Belt.Option.getWithDefault(0) - let b = Js.Dict.get(VM.getState(vm), "b")->Belt.Option.getWithDefault(0) + let a = Dict.get(VM.getState(vm), "a")->Belt.Option.getWithDefault(0) + let b = Dict.get(VM.getState(vm), "b")->Belt.Option.getWithDefault(0) if b == 0 { - Js.Console.log(`GCD = ${Belt.Int.toString(a)}`) - Js.Console.log(`Steps: ${Belt.Int.toString(steps.contents)}`) + Console.log(`GCD = ${Belt.Int.toString(a)}`) + Console.log(`Steps: ${Belt.Int.toString(steps.contents)}`) continueLoop := false } else { - Js.Console.log( + Console.log( `Step ${Belt.Int.toString(steps.contents + 1)}: a=${Belt.Int.toString( a, )}, b=${Belt.Int.toString(b)}`, @@ -270,46 +270,46 @@ let demoComplexAlgorithm = (): unit => { } // Update: temp = b, b = a mod b, a = temp - Js.Dict.set(VM.getState(vm), "temp", b) - Js.Dict.set(VM.getState(vm), "b", remaining.contents) - Js.Dict.set( + Dict.set(VM.getState(vm), "temp", b) + Dict.set(VM.getState(vm), "b", remaining.contents) + Dict.set( VM.getState(vm), "a", - Js.Dict.get(VM.getState(vm), "temp")->Belt.Option.getWithDefault(0), + Dict.get(VM.getState(vm), "temp")->Belt.Option.getWithDefault(0), ) steps := steps.contents + 1 if steps.contents > 20 { - Js.Console.log("Max iterations reached") + Console.log("Max iterations reached") continueLoop := false } } } - Js.Console.log("\n✓ Algorithm complete!") - Js.Console.log("") + Console.log("\n✓ Algorithm complete!") + Console.log("") } // Main: Run all demos -Js.Console.log( +Console.log( "╔════════════════════════════════════════════════╗", ) -Js.Console.log("║ IDAPTIK COMPLETE WORKFLOW DEMONSTRATION ║") -Js.Console.log( +Console.log("║ IDAPTIK COMPLETE WORKFLOW DEMONSTRATION ║") +Console.log( "╚════════════════════════════════════════════════╝", ) -Js.Console.log("") -Js.Console.log("This example demonstrates the full Idaptik ecosystem:") -Js.Console.log(" ✓ VM with undo/redo") -Js.Console.log(" ✓ 13 reversible instructions") -Js.Console.log(" ✓ State diff visualization") -Js.Console.log(" ✓ Puzzle solving framework") -Js.Console.log(" ✓ Performance measurement") -Js.Console.log(" ✓ Complex algorithms") -Js.Console.log("") -Js.Console.log("Press any key to continue...") -Js.Console.log("") +Console.log("") +Console.log("This example demonstrates the full Idaptik ecosystem:") +Console.log(" ✓ VM with undo/redo") +Console.log(" ✓ 13 reversible instructions") +Console.log(" ✓ State diff visualization") +Console.log(" ✓ Puzzle solving framework") +Console.log(" ✓ Performance measurement") +Console.log(" ✓ Complex algorithms") +Console.log("") +Console.log("Press any key to continue...") +Console.log("") demoBasicVM() demoBitwiseOps() @@ -318,18 +318,18 @@ demoPuzzleSolving() demoPerformance() demoComplexAlgorithm() -Js.Console.log( +Console.log( "╔════════════════════════════════════════════════╗", ) -Js.Console.log("║ DEMONSTRATION COMPLETE ✅ ║") -Js.Console.log( +Console.log("║ DEMONSTRATION COMPLETE ✅ ║") +Console.log( "╚════════════════════════════════════════════════╝", ) -Js.Console.log("") -Js.Console.log("Next steps:") -Js.Console.log(" • Explore examples/ directory for more") -Js.Console.log(" • Read docs/REVERSIBLE-COMPUTING-CONCEPTS.md") -Js.Console.log(" • Try solving puzzles in data/puzzles/") -Js.Console.log(" • Check out API-REFERENCE.md for complete API") -Js.Console.log("") -Js.Console.log("Happy reversible computing! 🔄") +Console.log("") +Console.log("Next steps:") +Console.log(" • Explore examples/ directory for more") +Console.log(" • Read docs/REVERSIBLE-COMPUTING-CONCEPTS.md") +Console.log(" • Try solving puzzles in data/puzzles/") +Console.log(" • Check out API-REFERENCE.md for complete API") +Console.log("") +Console.log("Happy reversible computing! 🔄") diff --git a/dlc/idaptik-reversible/examples/advanced/02_reversible_encryption.res b/dlc/idaptik-reversible/examples/advanced/02_reversible_encryption.res index 4b902af..ec304aa 100644 --- a/dlc/idaptik-reversible/examples/advanced/02_reversible_encryption.res +++ b/dlc/idaptik-reversible/examples/advanced/02_reversible_encryption.res @@ -23,40 +23,40 @@ let decrypt = (ciphertext: int, key: int): int => { } let reversibleEncryption = (): unit => { - Js.Console.log("=== Reversible Encryption (XOR Cipher) ===\n") + Console.log("=== Reversible Encryption (XOR Cipher) ===\n") let state = State.createState(~variables=["plaintext", "key", "ciphertext"], ~initialValue=0) - Js.Dict.set(state, "plaintext", 0b11010110) // 214 in decimal - Js.Dict.set(state, "key", 0b10101010) // 170 in decimal (secret key) + Dict.set(state, "plaintext", 0b11010110) // 214 in decimal + Dict.set(state, "key", 0b10101010) // 170 in decimal (secret key) let vm = VM.make(state) let originalPlaintext = - Js.Dict.get(VM.getCurrentState(vm), "plaintext")->Belt.Option.getWithDefault(0) - let secretKey = Js.Dict.get(VM.getCurrentState(vm), "key")->Belt.Option.getWithDefault(0) + Dict.get(VM.getCurrentState(vm), "plaintext")->Belt.Option.getWithDefault(0) + let secretKey = Dict.get(VM.getCurrentState(vm), "key")->Belt.Option.getWithDefault(0) - Js.Console.log("Original message (plaintext):") - Js.Console.log(` Binary: ${originalPlaintext->intToStringRadix(~radix=2)}`) - Js.Console.log(` Decimal: ${Belt.Int.toString(originalPlaintext)}`) + Console.log("Original message (plaintext):") + Console.log(` Binary: ${originalPlaintext->intToStringRadix(~radix=2)}`) + Console.log(` Decimal: ${Belt.Int.toString(originalPlaintext)}`) - Js.Console.log("\nSecret key:") - Js.Console.log(` Binary: ${secretKey->intToStringRadix(~radix=2)}`) - Js.Console.log(` Decimal: ${Belt.Int.toString(secretKey)}`) + Console.log("\nSecret key:") + Console.log(` Binary: ${secretKey->intToStringRadix(~radix=2)}`) + Console.log(` Decimal: ${Belt.Int.toString(secretKey)}`) // Encrypt: ciphertext = plaintext XOR key - Js.Console.log("\n--- ENCRYPTION ---") + Console.log("\n--- ENCRYPTION ---") VM.run(vm, Add.make("ciphertext", "plaintext")) // Copy plaintext to ciphertext VM.run(vm, Xor.make("ciphertext", "key")) // XOR with key let ciphertextValue = - Js.Dict.get(VM.getCurrentState(vm), "ciphertext")->Belt.Option.getWithDefault(0) + Dict.get(VM.getCurrentState(vm), "ciphertext")->Belt.Option.getWithDefault(0) - Js.Console.log("Encrypted message (ciphertext):") - Js.Console.log(` Binary: ${ciphertextValue->intToStringRadix(~radix=2)}`) - Js.Console.log(` Decimal: ${Belt.Int.toString(ciphertextValue)}`) + Console.log("Encrypted message (ciphertext):") + Console.log(` Binary: ${ciphertextValue->intToStringRadix(~radix=2)}`) + Console.log(` Decimal: ${Belt.Int.toString(ciphertextValue)}`) // Decrypt: plaintext = ciphertext XOR key - Js.Console.log("\n--- DECRYPTION ---") + Console.log("\n--- DECRYPTION ---") // Zero out plaintext first VM.run(vm, Sub.make("plaintext", "plaintext")) @@ -66,30 +66,30 @@ let reversibleEncryption = (): unit => { VM.run(vm, Xor.make("plaintext", "key")) // XOR with key let decryptedValue = - Js.Dict.get(VM.getCurrentState(vm), "plaintext")->Belt.Option.getWithDefault(0) + Dict.get(VM.getCurrentState(vm), "plaintext")->Belt.Option.getWithDefault(0) - Js.Console.log("Decrypted message:") - Js.Console.log(` Binary: ${decryptedValue->intToStringRadix(~radix=2)}`) - Js.Console.log(` Decimal: ${Belt.Int.toString(decryptedValue)}`) + Console.log("Decrypted message:") + Console.log(` Binary: ${decryptedValue->intToStringRadix(~radix=2)}`) + Console.log(` Decimal: ${Belt.Int.toString(decryptedValue)}`) - Js.Console.log("\n✓ Perfect reversibility!") - Js.Console.log( + Console.log("\n✓ Perfect reversibility!") + Console.log( `Original: ${Belt.Int.toString(originalPlaintext)}, Decrypted: ${Belt.Int.toString( decryptedValue, )}`, ) if originalPlaintext == decryptedValue { - Js.Console.log("✓ Encryption and decryption successful!") + Console.log("✓ Encryption and decryption successful!") } else { - Js.Console.log("✗ Error in encryption/decryption") + Console.log("✗ Error in encryption/decryption") } - Js.Console.log("\nSecurity Note:") - Js.Console.log("XOR cipher is only secure if key is:") - Js.Console.log(" 1. Truly random") - Js.Console.log(" 2. Same length as message") - Js.Console.log(" 3. Used only once (one-time pad)") + Console.log("\nSecurity Note:") + Console.log("XOR cipher is only secure if key is:") + Console.log(" 1. Truly random") + Console.log(" 2. Same length as message") + Console.log(" 3. Used only once (one-time pad)") } reversibleEncryption() diff --git a/escape-hatch/Cargo.toml b/escape-hatch/Cargo.toml index a2cd44a..90f554d 100644 --- a/escape-hatch/Cargo.toml +++ b/escape-hatch/Cargo.toml @@ -8,7 +8,7 @@ description = "IDApTIK Escape Hatch — developer access portal TUI" license = "PMPL-1.0-or-later" [dependencies] -ratatui = "0.29" # TODO: Upgrade to 0.30+ to resolve transitive `lru` Dependabot alert (lru 0.12.5 -> 0.16+) +ratatui = "0.30" crossterm = "0.28" color-eyre = "0.6" chrono = "0.4" diff --git a/idaptik-ums/WORKPLAN-2026-02-27.md b/idaptik-ums/WORKPLAN-2026-02-27.md index 346141d..3aae3ad 100644 --- a/idaptik-ums/WORKPLAN-2026-02-27.md +++ b/idaptik-ums/WORKPLAN-2026-02-27.md @@ -10,7 +10,7 @@ The level architect is at **~30% completion**. It has: - Tauri 2 desktop shell (Rust) - Idris2 ABI definitions (`src/abi/`: Types.idr, Layout.idr, Foreign.idr, LevelABI.idr) - Zig FFI bridge (`ffi/zig/` + `src/ffi/bridge.zig`) -- Chapel parallel solvers (Wiring.chpl, Visibility.chpl) +- Zig parallel solvers (visibility.zig, wiring.zig) - Elixir sync server (shared with main-game) - ReScript UI scaffolding - Justfile with build/dev/test recipes @@ -70,9 +70,7 @@ Quick, mechanical, well-defined tasks. | Blocker | Impact | Resolution | |---------|--------|------------| -| Chapel compiler availability | `build-chapel` recipe skips if `chpl` not installed | Install Chapel via asdf or Guix | | Tauri 2 gtk-rs → glib vuln | Dependabot alert #5 (medium) | Awaits upstream Tauri release | -| ratatui → lru vuln | Dependabot alert #6 (low) | Upgrade ratatui to 0.30+ | --- @@ -95,8 +93,7 @@ dlc/idaptik-reversible/ — Puzzle format + solver 1. **LevelConfig.res format drift** — If main-game LevelConfig changes, the import/export breaks. Solution: define canonical format in Idris2 ABI, generate both sides. 2. **Tauri 2 maturity** — Tauri 2 is relatively new. Some APIs may change. Pin Tauri version. -3. **Chapel availability** — Chapel is niche. Parallel solvers are nice-to-have, not blocking. -4. **Scope creep** — The level architect can grow unbounded. Phase 1 targets MVP: place devices, wire them, set guards, export to LevelConfig. +3. **Scope creep** — The level architect can grow unbounded. Phase 1 targets MVP: place devices, wire them, set guards, export to LevelConfig. --- diff --git a/idaptik-ums/main.js b/idaptik-ums/main.js index 223cd67..85a7453 100644 --- a/idaptik-ums/main.js +++ b/idaptik-ums/main.js @@ -1,42 +1,15 @@ // SPDX-License-Identifier: PMPL-1.0-or-later -// main.js — Entry point with hash-based routing for generator and editor +// main.js — Entry point for Unified Modding Studio // -// Routes: -// #/generator (default) — GeneratorDemo procedural level generator -// #/editor — App (TEA-based level editor, when ready) -// -// The hash router enables the "Open in Editor" flow: GeneratorDemo converts -// a level to Model.level format and navigates to #/editor. +// Currently renders GeneratorDemo only. When the TEA-based level editor +// is ready, add hash routing here (#/generator vs #/editor). import React from 'react'; import { createRoot } from 'react-dom/client'; import { make as GeneratorDemo } from './src/generator/GeneratorDemo.res.mjs'; -// Original TEA editor (uncomment when ready): -// import { make as App } from './src/App.res.mjs'; const container = document.getElementById('app'); if (container) { const root = createRoot(container); - - function renderRoute() { - const hash = window.location.hash || '#/generator'; - - switch (hash) { - case '#/editor': - // TODO: Mount App (TEA editor) when ready - // root.render(React.createElement(App, {})); - root.render(React.createElement(GeneratorDemo, {})); - break; - case '#/generator': - default: - root.render(React.createElement(GeneratorDemo, {})); - break; - } - } - - // Listen for hash changes (navigation between generator and editor) - window.addEventListener('hashchange', renderRoute); - - // Initial render - renderRoute(); + root.render(React.createElement(GeneratorDemo, {})); } diff --git a/idaptik-ums/src/App.res b/idaptik-ums/src/App.res index cfceeba..2756d0f 100644 --- a/idaptik-ums/src/App.res +++ b/idaptik-ums/src/App.res @@ -81,7 +81,7 @@ let rec update = (msg, model) => { | ToggleWizard => ({...model, showWizard: !model.showWizard}, Cmd.none) | ChangeView(m) => ({...model, viewMode: m}, Cmd.none) | ToggleTheme(t) => ({...model, level: {...model.level, theme: t}}, Cmd.none) - | UpdateSquareSize(s) => ({...model, squareSize: Js.Math.max_float(12.0, s)}, Cmd.none) + | UpdateSquareSize(s) => ({...model, squareSize: Math.max(12.0, s)}, Cmd.none) | _ => (model, Cmd.none) } } diff --git a/idaptik-ums/src/Model.res b/idaptik-ums/src/Model.res index 2f84fe6..bed3f33 100644 --- a/idaptik-ums/src/Model.res +++ b/idaptik-ums/src/Model.res @@ -67,9 +67,9 @@ type level = { bonding: Network.bondedLink, } -let decodeLevel = (json: Js.Json.t): level => { - let dict = Js.Json.decodeObject(json)->Belt.Option.getWithDefault(Js.Dict.empty()) - let name = Js.Dict.get(dict, "name")->Belt.Option.flatMap(Js.Json.decodeString)->Belt.Option.getWithDefault("Imported Level") +let decodeLevel = (json: JSON.t): level => { + let dict = Js.Json.decodeObject(json)->Belt.Option.getWithDefault(Dict.make()) + let name = Dict.get(dict, "name")->Belt.Option.flatMap(Js.Json.decodeString)->Belt.Option.getWithDefault("Imported Level") { name: name, dimensions: (12, 12), theme: Light, backgroundColor: "#f1f5f9", physical: Belt.Array.makeBy(12, _ => Belt.Array.makeBy(12, _ => Physical.Air)), @@ -83,6 +83,6 @@ let toNickel = (l: level): string => { "{ name = \"" ++ l.name ++ "\", theme = " ++ themeStr ++ " }" } -let encodeLevel = (l: level): Js.Json.t => { - Js.Json.object_(Js.Dict.fromArray([ ("name", Js.Json.string(l.name)) ])) +let encodeLevel = (l: level): JSON.t => { + Js.Json.object_(Dict.fromArray([ ("name", Js.Json.string(l.name)) ])) } diff --git a/idaptik-ums/src/generator/A2mlWrapper.res b/idaptik-ums/src/generator/A2mlWrapper.res index b1fac5f..ca49d1c 100644 --- a/idaptik-ums/src/generator/A2mlWrapper.res +++ b/idaptik-ums/src/generator/A2mlWrapper.res @@ -98,13 +98,13 @@ let unwrapA2ml = (wrapped: string): result<(string, a2mlMeta), string> => { let obj = json->Js.Json.decodeObject switch obj { | Some(dict) => - let a2mlObj = Js.Dict.get(dict, "a2ml") - let payloadObj = Js.Dict.get(dict, "payload") + let a2mlObj = Dict.get(dict, "a2ml") + let payloadObj = Dict.get(dict, "payload") switch (a2mlObj, payloadObj) { | (Some(metaJson), Some(payload)) => - let metaDict = metaJson->Js.Json.decodeObject->Belt.Option.getWithDefault(Js.Dict.empty()) - let getString = (d, k) => Js.Dict.get(d, k)->Belt.Option.flatMap(Js.Json.decodeString)->Belt.Option.getWithDefault("") - let getInt = (d, k) => Js.Dict.get(d, k)->Belt.Option.flatMap(Js.Json.decodeNumber)->Belt.Option.mapWithDefault(0, Belt.Float.toInt) + let metaDict = metaJson->Js.Json.decodeObject->Belt.Option.getWithDefault(Dict.make()) + let getString = (d, k) => Dict.get(d, k)->Belt.Option.flatMap(Js.Json.decodeString)->Belt.Option.getWithDefault("") + let getInt = (d, k) => Dict.get(d, k)->Belt.Option.flatMap(Js.Json.decodeNumber)->Belt.Option.mapWithDefault(0, Belt.Float.toInt) let meta: a2mlMeta = { version: getString(metaDict, "version"), generator: getString(metaDict, "generator"), diff --git a/idaptik-ums/src/generator/CampaignGraph.res b/idaptik-ums/src/generator/CampaignGraph.res index 46ed723..84cf56b 100644 --- a/idaptik-ums/src/generator/CampaignGraph.res +++ b/idaptik-ums/src/generator/CampaignGraph.res @@ -137,7 +137,7 @@ let reachableFrom = ( if edge.fromBuildingId == current { if Dict.get(visited, edge.toBuildingId)->Option.isNone { Dict.set(visited, edge.toBuildingId, true) - ignore(Js.Array2.push(queue, edge.toBuildingId)) + ignore(Array.push(queue, edge.toBuildingId)) } } }) @@ -160,7 +160,7 @@ let validate = ( e.fromBuildingId == id || e.toBuildingId == id ) if !hasEdge { - ignore(Js.Array2.push(warnings, `Building ${id} has no connections`)) + ignore(Array.push(warnings, `Building ${id} has no connections`)) } }) @@ -170,7 +170,7 @@ let validate = ( let reachable = reachableFrom(graph, startId) Belt.Array.forEach(buildingIds, id => { if !Belt.Array.some(reachable, r => r == id) { - ignore(Js.Array2.push(warnings, `Building ${id} is unreachable from start`)) + ignore(Array.push(warnings, `Building ${id} is unreachable from start`)) } }) } @@ -185,7 +185,7 @@ let validate = ( /// Encode a single campaign edge to a JSON value for safe serialisation. /// Uses JSON.Encode to prevent injection from user-editable fields (labels, /// unlock conditions). -let encodeCampaignEdge = (e: campaignEdge): Js.Json.t => { +let encodeCampaignEdge = (e: campaignEdge): JSON.t => { let condValue = switch e.unlockCondition { | Some(c) => JSON.Encode.string(c) | None => JSON.Encode.null @@ -232,18 +232,18 @@ let decodeCampaignGraph = (jsonStr: string): result => { } try { let json = JSON.parseExn(rawJson) - let obj = json->Js.Json.decodeObject->Belt.Option.getWithDefault(Js.Dict.empty()) - let portfolioId = Js.Dict.get(obj, "portfolioId") + let obj = json->Js.Json.decodeObject->Belt.Option.getWithDefault(Dict.make()) + let portfolioId = Dict.get(obj, "portfolioId") ->Belt.Option.flatMap(Js.Json.decodeString) ->Belt.Option.getWithDefault("") - let edgesJson = Js.Dict.get(obj, "edges") + let edgesJson = Dict.get(obj, "edges") ->Belt.Option.flatMap(Js.Json.decodeArray) ->Belt.Option.getWithDefault([]) let edges = Belt.Array.keepMap(edgesJson, eJson => { - let eObj = eJson->Js.Json.decodeObject->Belt.Option.getWithDefault(Js.Dict.empty()) - let getString = (d, k) => Js.Dict.get(d, k)->Belt.Option.flatMap(Js.Json.decodeString)->Belt.Option.getWithDefault("") + let eObj = eJson->Js.Json.decodeObject->Belt.Option.getWithDefault(Dict.make()) + let getString = (d, k) => Dict.get(d, k)->Belt.Option.flatMap(Js.Json.decodeString)->Belt.Option.getWithDefault("") let fromId = getString(eObj, "fromBuildingId") let toId = getString(eObj, "toBuildingId") let etStr = getString(eObj, "edgeType") @@ -255,7 +255,7 @@ let decodeCampaignGraph = (jsonStr: string): result => { | _ => Unlocks } let label = getString(eObj, "label") - let condition = Js.Dict.get(eObj, "unlockCondition")->Belt.Option.flatMap(Js.Json.decodeString) + let condition = Dict.get(eObj, "unlockCondition")->Belt.Option.flatMap(Js.Json.decodeString) if fromId != "" && toId != "" { Some({ diff --git a/idaptik-ums/src/generator/LevelExport.res b/idaptik-ums/src/generator/LevelExport.res index eeac813..58b6488 100644 --- a/idaptik-ums/src/generator/LevelExport.res +++ b/idaptik-ums/src/generator/LevelExport.res @@ -70,14 +70,14 @@ let exportToConfig = ( let slotW = LevelGen.slotWidth // Transition at column boundary between col 0 and col 1 let trans01x = Belt.Int.toFloat(1 + slotW) - ignore(Js.Array2.push(zoneTransitions, { + ignore(Array.push(zoneTransitions, { LevelConfigTypes.x: trans01x, fromZone: "LAN", toZone: "DMZ", })) // Transition at column boundary between col 1 and col 2 let trans12x = Belt.Int.toFloat(1 + slotW + 1 + slotW) - ignore(Js.Array2.push(zoneTransitions, { + ignore(Array.push(zoneTransitions, { LevelConfigTypes.x: trans12x, fromZone: "DMZ", toZone: "SCADA", diff --git a/idaptik-ums/src/generator/LevelGen.res b/idaptik-ums/src/generator/LevelGen.res index ee09ce6..b9aee4a 100644 --- a/idaptik-ums/src/generator/LevelGen.res +++ b/idaptik-ums/src/generator/LevelGen.res @@ -125,7 +125,7 @@ type generatedLevel = { let nextSeed = (seed: int): int => { // LCG using JS floats to avoid 32-bit int overflow. // a=1664525, c=1013904223, m=2^31 - let raw = Js.Math.abs_float( + let raw = Math.abs( mod_float( 1664525.0 *. Belt.Int.toFloat(land(seed, 0x7FFFFFFF)) +. 1013904223.0, 2147483648.0, @@ -342,7 +342,7 @@ let placeDevicesInSlot = ( zone: zone, securityLevel: secLevel, } - ignore(Js.Array2.push(devices, dev)) + ignore(Array.push(devices, dev)) } (devices, s.contents) } @@ -380,7 +380,7 @@ let placeGuardsInSlot = ( rank: rank, patrolRadius: 2.0, } - ignore(Js.Array2.push(guards, g)) + ignore(Array.push(guards, g)) } (guards, s.contents) } @@ -422,7 +422,7 @@ let wirePuzzles = ( // Find a device NOT in slotA (so the puzzle requires movement) let candidate = Belt.Array.getBy(allDevices, dev => { let devSlotPrefix = `dev-${Belt.Int.toString(slotA)}` - !Js.String2.startsWith(dev.id, devSlotPrefix) && Dict.get(usedDevices, dev.id)->Option.isNone + !String.startsWith(dev.id, devSlotPrefix) && Dict.get(usedDevices, dev.id)->Option.isNone }) switch candidate { | Some(dev) => @@ -436,7 +436,7 @@ let wirePuzzles = ( deviceId: dev.id, puzzleIndex: puzzleCount.contents + 1, } - ignore(Js.Array2.push(puzzles, pw)) + ignore(Array.push(puzzles, pw)) puzzleCount := puzzleCount.contents + 1 | None => () } @@ -464,7 +464,7 @@ let generate = (seed: int): option => { let activeSlots = [] Belt.Array.forEachWithIndex(tmpl.mask, (i, active) => { if active { - ignore(Js.Array2.push(activeSlots, i)) + ignore(Array.push(activeSlots, i)) } }) let slotCount = Belt.Array.length(activeSlots) @@ -519,13 +519,13 @@ let generate = (seed: int): option => { let rightIdx = si + 1 if col < slotsAcross - 1 && Belt.Array.some(activeSlots, x => x == rightIdx) { let (dx, dy) = placeDoorBetween(grid, si, rightIdx) - ignore(Js.Array2.push(doors, (dx, dy, si, rightIdx))) + ignore(Array.push(doors, (dx, dy, si, rightIdx))) } // Below neighbour let belowIdx = si + slotsAcross if row < slotsDown - 1 && Belt.Array.some(activeSlots, x => x == belowIdx) { let (dx, dy) = placeDoorBetween(grid, si, belowIdx) - ignore(Js.Array2.push(doors, (dx, dy, si, belowIdx))) + ignore(Array.push(doors, (dx, dy, si, belowIdx))) } }) @@ -537,10 +537,10 @@ let generate = (seed: int): option => { | Some(sl) => let (devs, ns) = placeDevicesInSlot(si, sl.roomType, sl.zone, s.contents) s := ns - Belt.Array.forEach(devs, d => ignore(Js.Array2.push(allDevices, d))) + Belt.Array.forEach(devs, d => ignore(Array.push(allDevices, d))) let (gds, ns2) = placeGuardsInSlot(si, sl.roomType, sl.zone, s.contents) s := ns2 - Belt.Array.forEach(gds, g => ignore(Js.Array2.push(allGuards, g))) + Belt.Array.forEach(gds, g => ignore(Array.push(allGuards, g))) | None => () } }) diff --git a/idaptik-ums/src/generator/LevelRender.res b/idaptik-ums/src/generator/LevelRender.res index b9576ac..2b9a3f5 100644 --- a/idaptik-ums/src/generator/LevelRender.res +++ b/idaptik-ums/src/generator/LevelRender.res @@ -191,13 +191,13 @@ let drawDevices = (ctx: canvasContext, devices: array): unit => // Circle background beginPath(ctx) - arc(ctx, px, py, radius, 0.0, 2.0 *. Js.Math._PI) + arc(ctx, px, py, radius, 0.0, 2.0 *. Math.Constants.pi) setFill(ctx, deviceColor) fill(ctx) closePath(ctx) // Type abbreviation (first letter) - let abbrev = Js.String2.slice(dev.deviceType, ~from=0, ~to_=1) + let abbrev = String.slice(dev.deviceType, ~from=0, ~to_=1) setFill(ctx, "#ffffff") setFontProp(ctx, `bold ${Belt.Float.toString(tileSize *. 0.4)}px monospace`) setAlignProp(ctx, "center") @@ -228,7 +228,7 @@ let drawGuards = (ctx: canvasContext, guards: array): unit => { fill(ctx) // Rank initial - let initial = Js.String2.slice(g.rank, ~from=0, ~to_=1) + let initial = String.slice(g.rank, ~from=0, ~to_=1) setFill(ctx, "#ffffff") setFontProp(ctx, `bold ${Belt.Float.toString(tileSize *. 0.35)}px monospace`) setAlignProp(ctx, "center") diff --git a/idaptik-ums/src/generator/NetworkGen.res b/idaptik-ums/src/generator/NetworkGen.res index d6d6efe..5eb597f 100644 --- a/idaptik-ums/src/generator/NetworkGen.res +++ b/idaptik-ums/src/generator/NetworkGen.res @@ -102,7 +102,7 @@ let hostnameForDevice = (deviceType: string, zone: string, idx: int): string => | "UPS" => "ups" | _ => "dev" } - let zonePart = Js.String2.toLowerCase(zone) + let zonePart = String.toLowerCase(zone) `${prefix}-${zonePart}-${Belt.Int.toString(idx)}` } @@ -154,7 +154,7 @@ let generateTopology = (level: LevelGen.generatedLevel): generatedTopology => { let coreRouterId = "core-router" let coreRouterX = LevelGen.gridWidth / 2 let coreRouterY = LevelGen.gridHeight / 2 - ignore(Js.Array2.push(nodes, { + ignore(Array.push(nodes, { id: coreRouterId, nodeType: "router", ipAddress: "10.0.0.1", @@ -171,33 +171,33 @@ let generateTopology = (level: LevelGen.generatedLevel): generatedTopology => { Belt.Array.forEach(zoneList, zone => { if Dict.get(seenZones, zone)->Option.isSome { // Subnet info - ignore(Js.Array2.push(subnets, { + ignore(Array.push(subnets, { zone: zone, cidr: cidrForZone(zone), gateway: gatewayForZone(zone), })) // Zone switch node (positioned at zone's first slot) - let switchId = `switch-${Js.String2.toLowerCase(zone)}` + let switchId = `switch-${String.toLowerCase(zone)}` let (switchX, switchY) = switch zone { | "LAN" => (3, 3) | "DMZ" => (10, 3) | "SCADA" => (17, 3) | _ => (10, 6) } - ignore(Js.Array2.push(nodes, { + ignore(Array.push(nodes, { id: switchId, nodeType: "switch", ipAddress: `${subnetForZone(zone)}.2`, zone: zone, x: switchX, y: switchY, - hostname: `sw-${Js.String2.toLowerCase(zone)}`, + hostname: `sw-${String.toLowerCase(zone)}`, subnet: cidrForZone(zone), })) // Connect zone switch to core router - ignore(Js.Array2.push(edges, { + ignore(Array.push(edges, { sourceId: coreRouterId, targetId: switchId, edgeType: "ethernet", @@ -206,25 +206,25 @@ let generateTopology = (level: LevelGen.generatedLevel): generatedTopology => { // Firewall between this zone and previous zone (if both exist) if prevZone.contents != "" { - let fwId = `fw-${Js.String2.toLowerCase(prevZone.contents)}-${Js.String2.toLowerCase(zone)}` + let fwId = `fw-${String.toLowerCase(prevZone.contents)}-${String.toLowerCase(zone)}` let fwX = (switchX + coreRouterX) / 2 - ignore(Js.Array2.push(nodes, { + ignore(Array.push(nodes, { id: fwId, nodeType: "firewall", ipAddress: `${subnetForZone(zone)}.254`, zone: zone, x: fwX, y: 1, - hostname: `fw-${Js.String2.toLowerCase(zone)}`, + hostname: `fw-${String.toLowerCase(zone)}`, subnet: cidrForZone(zone), })) - ignore(Js.Array2.push(edges, { - sourceId: `switch-${Js.String2.toLowerCase(prevZone.contents)}`, + ignore(Array.push(edges, { + sourceId: `switch-${String.toLowerCase(prevZone.contents)}`, targetId: fwId, edgeType: "ethernet", bandwidth: "1Gbps", })) - ignore(Js.Array2.push(edges, { + ignore(Array.push(edges, { sourceId: fwId, targetId: switchId, edgeType: "ethernet", @@ -252,7 +252,7 @@ let generateTopology = (level: LevelGen.generatedLevel): generatedTopology => { | "UPS" => "server" | _ => "terminal" } - ignore(Js.Array2.push(nodes, { + ignore(Array.push(nodes, { id: `net-${dev.id}`, nodeType: nodeType, ipAddress: ip, @@ -264,8 +264,8 @@ let generateTopology = (level: LevelGen.generatedLevel): generatedTopology => { })) // Connect to zone switch - let switchId = `switch-${Js.String2.toLowerCase(dev.zone)}` - ignore(Js.Array2.push(edges, { + let switchId = `switch-${String.toLowerCase(dev.zone)}` + ignore(Array.push(edges, { sourceId: switchId, targetId: `net-${dev.id}`, edgeType: if dev.deviceType == "Camera" { "wireless" } else { "ethernet" }, @@ -281,7 +281,7 @@ let generateTopology = (level: LevelGen.generatedLevel): generatedTopology => { switch alarmDev { | Some(dev) => let pbxIp = nextIp(dev.zone) - ignore(Js.Array2.push(nodes, { + ignore(Array.push(nodes, { id: "pbx-0", nodeType: "pbx", ipAddress: pbxIp, @@ -291,8 +291,8 @@ let generateTopology = (level: LevelGen.generatedLevel): generatedTopology => { hostname: "pbx-main", subnet: cidrForZone(dev.zone), })) - let switchId = `switch-${Js.String2.toLowerCase(dev.zone)}` - ignore(Js.Array2.push(edges, { + let switchId = `switch-${String.toLowerCase(dev.zone)}` + ignore(Array.push(edges, { sourceId: switchId, targetId: "pbx-0", edgeType: "ethernet", diff --git a/idaptik-ums/src/generator/NetworkState.res b/idaptik-ums/src/generator/NetworkState.res index 710f9cf..f16544c 100644 --- a/idaptik-ums/src/generator/NetworkState.res +++ b/idaptik-ums/src/generator/NetworkState.res @@ -136,14 +136,14 @@ type networkLiveState = { /// Not cryptographic — just needs to look realistic and be unique per node. let generateMac = (nodeId: string): string => { let hash = ref(0) - for i in 0 to Js.String2.length(nodeId) - 1 { - let ch = Js.String2.charCodeAt(nodeId, i)->Belt.Float.toInt + for i in 0 to String.length(nodeId) - 1 { + let ch = String.charCodeAt(nodeId, i)->Belt.Float.toInt hash := lxor(lsl(hash.contents, 5) - hash.contents, ch) } let h = abs(hash.contents) let hex = (n: int): string => { - let s = Js.Int.toStringWithRadix(land(n, 0xFF), ~radix=16) - if Js.String2.length(s) < 2 { "0" ++ s } else { s } + let s = Int.toStringWithRadix(land(n, 0xFF), ~radix=16) + if String.length(s) < 2 { "0" ++ s } else { s } } `02:${hex(h)}:${hex(asr(h, 8))}:${hex(asr(h, 16))}:${hex(asr(h, 24))}:${hex(lxor(h, 42))}` } diff --git a/idaptik-ums/src/generator/Portfolio.res b/idaptik-ums/src/generator/Portfolio.res index 59cb547..c0643d9 100644 --- a/idaptik-ums/src/generator/Portfolio.res +++ b/idaptik-ums/src/generator/Portfolio.res @@ -106,7 +106,7 @@ let getBuilding = (portfolio: portfolio, buildingId: string): option { +let encodeBuildingEntry = (entry: portfolioEntry): JSON.t => { JSON.Encode.object(Dict.fromArray([ ("id", JSON.Encode.string(entry.meta.id)), ("name", JSON.Encode.string(entry.meta.name)), @@ -160,9 +160,9 @@ let decodePortfolio = (jsonStr: string): result => { } try { let json = JSON.parseExn(rawJson) - let obj = json->Js.Json.decodeObject->Belt.Option.getWithDefault(Js.Dict.empty()) - let getString = (d, k) => Js.Dict.get(d, k)->Belt.Option.flatMap(Js.Json.decodeString)->Belt.Option.getWithDefault("") - let getFloat = (d, k) => Js.Dict.get(d, k)->Belt.Option.flatMap(Js.Json.decodeNumber)->Belt.Option.getWithDefault(0.0) + let obj = json->Js.Json.decodeObject->Belt.Option.getWithDefault(Dict.make()) + let getString = (d, k) => Dict.get(d, k)->Belt.Option.flatMap(Js.Json.decodeString)->Belt.Option.getWithDefault("") + let getFloat = (d, k) => Dict.get(d, k)->Belt.Option.flatMap(Js.Json.decodeNumber)->Belt.Option.getWithDefault(0.0) let id = getString(obj, "id") let name = getString(obj, "name") @@ -171,13 +171,13 @@ let decodePortfolio = (jsonStr: string): result => { let modifiedAt = getFloat(obj, "modifiedAt") // Rebuild buildings from seeds - let buildingsJson = Js.Dict.get(obj, "buildings") + let buildingsJson = Dict.get(obj, "buildings") ->Belt.Option.flatMap(Js.Json.decodeArray) ->Belt.Option.getWithDefault([]) let buildings = Belt.Array.keepMap(buildingsJson, bJson => { - let bObj = bJson->Js.Json.decodeObject->Belt.Option.getWithDefault(Js.Dict.empty()) - let seed = Js.Dict.get(bObj, "seed") + let bObj = bJson->Js.Json.decodeObject->Belt.Option.getWithDefault(Dict.make()) + let seed = Dict.get(bObj, "seed") ->Belt.Option.flatMap(Js.Json.decodeNumber) ->Belt.Option.mapWithDefault(0, Belt.Float.toInt) diff --git a/idaptik-ums/src/generator/PortfolioSync.res b/idaptik-ums/src/generator/PortfolioSync.res index 54f5bfe..9169ea8 100644 --- a/idaptik-ums/src/generator/PortfolioSync.res +++ b/idaptik-ums/src/generator/PortfolioSync.res @@ -72,19 +72,19 @@ let savePortfolio = (portfolio: Portfolio.portfolio): promiseJs.Promise.then_(r => { - Js.Promise.resolve(parseResponse(r)) - }, _) + response->Promise.thenResolve(r => { + parseResponse(r) + }) } /// Load a portfolio from VerisimDB by its ID. /// Returns the portfolio JSON string, which must be decoded by the caller. let loadPortfolio = (portfolioId: string): promise> => { let url = `${syncServerUrl}/db/portfolios/${portfolioId}` - let response = fetchJson(url, "GET", Js.Nullable.null) - response->Js.Promise.then_(r => { - Js.Promise.resolve(parseResponse(r)) - }, _) + let response = fetchJson(url, "GET", Nullable.null) + response->Promise.thenResolve(r => { + parseResponse(r) + }) } // --------------------------------------------------------------------------- @@ -100,16 +100,16 @@ let saveCampaignGraph = (graph: CampaignGraph.campaignGraph): promiseJs.Promise.then_(r => { - Js.Promise.resolve(parseResponse(r)) - }, _) + response->Promise.thenResolve(r => { + parseResponse(r) + }) } /// Load a campaign graph from ArangoDB by portfolio ID. let loadCampaignGraph = (portfolioId: string): promise> => { let url = `${syncServerUrl}/db/campaigns/${portfolioId}` - let response = fetchJson(url, "GET", Js.Nullable.null) - response->Js.Promise.then_(r => { - Js.Promise.resolve(parseResponse(r)) - }, _) + let response = fetchJson(url, "GET", Nullable.null) + response->Promise.thenResolve(r => { + parseResponse(r) + }) } diff --git a/idaptik-ums/src/generator/PowerGen.res b/idaptik-ums/src/generator/PowerGen.res index 964eb55..495d01b 100644 --- a/idaptik-ums/src/generator/PowerGen.res +++ b/idaptik-ums/src/generator/PowerGen.res @@ -69,7 +69,7 @@ let generatePowerMap = (level: LevelGen.generatedLevel): powerMap => { } } else { // UPS — backup source - ignore(Js.Array2.push(upsDevices, dev)) + ignore(Array.push(upsDevices, dev)) } }) @@ -94,7 +94,7 @@ let generatePowerMap = (level: LevelGen.generatedLevel): powerMap => { Dict.set(zoneDevices, dev.zone, arr) arr } - ignore(Js.Array2.push(existing, dev.id)) + ignore(Array.push(existing, dev.id)) }) // Create primary circuits per zone @@ -123,7 +123,7 @@ let generatePowerMap = (level: LevelGen.generatedLevel): powerMap => { // All devices in this zone except the source are loads let loadIds = Belt.Array.keep(deviceIds, id => id != sourceId) - ignore(Js.Array2.push(circuits, { + ignore(Array.push(circuits, { id: `circuit-${Belt.Int.toString(circuitIdx.contents)}`, sourceDeviceId: sourceId, zone: zone, @@ -139,7 +139,7 @@ let generatePowerMap = (level: LevelGen.generatedLevel): powerMap => { switch Dict.get(zoneDevices, ups.zone) { | Some(deviceIds) => let loadIds = Belt.Array.keep(deviceIds, id => id != ups.id) - ignore(Js.Array2.push(circuits, { + ignore(Array.push(circuits, { id: `circuit-ups-${Belt.Int.toString(circuitIdx.contents)}`, sourceDeviceId: ups.id, zone: ups.zone, diff --git a/main-game/docs/immediate-actions/2026-02-22-session-todos.md b/main-game/docs/immediate-actions/2026-02-22-session-todos.md index 707f947..f2c4237 100644 --- a/main-game/docs/immediate-actions/2026-02-22-session-todos.md +++ b/main-game/docs/immediate-actions/2026-02-22-session-todos.md @@ -26,15 +26,17 @@ - [x] **"Internet location DETECTED" bug fix** — 3-second startup grace period; guards/dogs update but detection reports suppressed until grace expires - [x] **Training menu layout fix** — Adaptive spacing for 6+ entries (Moletaire); title moves up, spacing shrinks to fit on screen -## PENDING (3 items) +## PENDING → TRIAGED (2026-02-28) -### High Priority (gameplay) -- [ ] **Guard visual knockdown** — Guards stop on stomp but don't visually fall over -- [ ] **"Pole" from guard/dog** — Visual element protruding from guard/dog (detection cone?) +### Still open (gameplay polish) +- [ ] **Guard visual knockdown** — Knockdown state & timer work; guards remain visually upright. Needs rotation/lying-flat sprite during KnockedDown state in GuardNPC.res renderGuard(). -### Low Priority (infrastructure) -- [ ] **Split-screen key bindings** — Player 1: WASD+E, Player 2: Arrows+L -- [ ] **VerisimDB user profile** — User prefs, visit/training counts, timestamps +### Completed (detection cones already implemented) +- [x] **"Pole" from guard/dog** — Vision cones already rendered: guards show triangular cones (yellow/red/etc by alert state), dogs show scent circles or camera cones. Implemented in GuardNPC.res (lines 1305-1351) and SecurityDog.res (lines 724-768). + +### Deferred (not blocking MVP) +- [ ] **Split-screen key bindings** — Player 1: WASD+E, Player 2: Arrows+L. No infrastructure exists yet; deferred until co-op local multiplayer is prioritised. +- [ ] **VerisimDB user profile** — User prefs, visit/training counts, timestamps. Deferred until VerisimDB integration is active. ## Architecture Notes diff --git a/main-game/src/app/screens/GameOverScreen.res b/main-game/src/app/screens/GameOverScreen.res index c3cd040..1b0017d 100644 --- a/main-game/src/app/screens/GameOverScreen.res +++ b/main-game/src/app/screens/GameOverScreen.res @@ -186,14 +186,11 @@ let make = (): Navigation.appScreen => { // Register Escape key to quit (go to training menu or world map) let escNavigating = ref(false) let escHandler = (_e: {..}) => { - let key: string = %raw(`_e.key`) + let key = KeyboardNav.eventKey(_e) if key == "Escape" && !escNavigating.contents { escNavigating := true - let _ = %raw(`_e.preventDefault()`) - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.preventDefault(_e) + KeyboardNav.removeFromRef(escKeyHandler) switch GetEngine.get() { | Some(engine) => let target = switch quitTarget.contents { @@ -207,19 +204,13 @@ let make = (): Navigation.appScreen => { } } } - let handler = %raw(`function(fn) { var h = function(e) { fn(e); }; window.addEventListener('keydown', h); return h; }`)( - escHandler, - ) - escKeyHandler := Some(handler) + escKeyHandler := Some(KeyboardNav.addKeydownHandler(escHandler)) }, ), hide: Some( async () => { // Remove Escape key listener - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) Container.setAlpha(container, 0.0) }, ), @@ -227,10 +218,7 @@ let make = (): Navigation.appScreen => { resume: None, reset: Some( () => { - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) }, ), update: Some( diff --git a/main-game/src/app/screens/VictoryScreen.res b/main-game/src/app/screens/VictoryScreen.res index 048d2be..05de4cd 100644 --- a/main-game/src/app/screens/VictoryScreen.res +++ b/main-game/src/app/screens/VictoryScreen.res @@ -227,14 +227,11 @@ let make = (): Navigation.appScreen => { // Register Escape key to continue let escNavigating = ref(false) let escHandler = (_e: {..}) => { - let key: string = %raw(`_e.key`) + let key = KeyboardNav.eventKey(_e) if key == "Escape" && !escNavigating.contents { escNavigating := true - let _ = %raw(`_e.preventDefault()`) - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.preventDefault(_e) + KeyboardNav.removeFromRef(escKeyHandler) switch GetEngine.get() { | Some(engine) => let target = switch continueTarget.contents { @@ -247,18 +244,12 @@ let make = (): Navigation.appScreen => { } } } - let handler = %raw(`function(fn) { var h = function(e) { fn(e); }; window.addEventListener('keydown', h); return h; }`)( - escHandler, - ) - escKeyHandler := Some(handler) + escKeyHandler := Some(KeyboardNav.addKeydownHandler(escHandler)) }, ), hide: Some( async () => { - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) Container.setAlpha(container, 0.0) }, ), @@ -266,10 +257,7 @@ let make = (): Navigation.appScreen => { resume: None, reset: Some( () => { - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) }, ), update: None, diff --git a/main-game/src/app/screens/WorldBuilder.res b/main-game/src/app/screens/WorldBuilder.res index b5908c4..b79eaac 100644 --- a/main-game/src/app/screens/WorldBuilder.res +++ b/main-game/src/app/screens/WorldBuilder.res @@ -1016,17 +1016,14 @@ let buildLocationScreen = (location: LocationData.location): Navigation.appScree // Add key listener navigate to pause menu let escNavigating = ref(false) let rec escHandler = (_e: {..}) => { - let key: string = %raw(`_e.key`) + let key = KeyboardNav.eventKey(_e) Console.log2("[WorldBuilder] Key pressed:", key) if (key == "Escape" || key == "F1" || key == "Tab" || key == "p" || key == "P") && !escNavigating.contents { Console.log("[WorldBuilder] Opening PausePopup...") escNavigating := true - let _: unit = %raw(`_e.preventDefault()`) + KeyboardNav.preventDefault(_e) // Remove listener immediately to prevent stacking - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener("keydown", _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) switch GetEngine.get() { | Some(engine) => let _ = Navigation.presentPopup( @@ -1035,11 +1032,7 @@ let buildLocationScreen = (location: LocationData.location): Navigation.appScree )->Promise.thenResolve(_ => { escNavigating := false // Re-add listener for next time if we resume - escKeyHandler := Some( - %raw(`function(fn) { var h = function(e) { fn(e); }; window.addEventListener("keydown", h); return h; }`)( - escHandler, - ), - ) + escKeyHandler := Some(KeyboardNav.addKeydownHandler(escHandler)) })->Promise.catch(e => { escNavigating := false PanicHandler.handleException(e) @@ -1048,10 +1041,7 @@ let buildLocationScreen = (location: LocationData.location): Navigation.appScree } } } - let handler = %raw(`function(fn) { var h = function(e) { fn(e); }; window.addEventListener('keydown', h); return h; }`)( - escHandler, - ) - escKeyHandler := Some(handler) + escKeyHandler := Some(KeyboardNav.addKeydownHandler(escHandler)) await Motion.animateAsync(container, {"alpha": 1.0}, {duration: 0.5, ease: "easeOut"}) }, @@ -1059,10 +1049,7 @@ let buildLocationScreen = (location: LocationData.location): Navigation.appScree hide: Some( async () => { // Remove ESC key listener - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) await Motion.animateAsync(container, {"alpha": 0.0}, {duration: 0.3}) }, @@ -1080,10 +1067,7 @@ let buildLocationScreen = (location: LocationData.location): Navigation.appScree reset: Some( () => { // Cleanup ESC key listener - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) }, ), update: Some( diff --git a/main-game/src/app/screens/training/TrainingBase.res b/main-game/src/app/screens/training/TrainingBase.res index 753d134..3ef02a8 100644 --- a/main-game/src/app/screens/training/TrainingBase.res +++ b/main-game/src/app/screens/training/TrainingBase.res @@ -457,14 +457,11 @@ let makeTrainingScreen = ( // Register Escape key to go back to training menu, F1/P for settings let escNavigating = ref(false) let rec escHandler = (_e: {..}) => { - let key: string = %raw(`_e.key`) + let key = KeyboardNav.eventKey(_e) if (key == "Escape" || key == "F1" || key == "Tab" || key == "p" || key == "P") && !escNavigating.contents { escNavigating := true - let _ = %raw(`_e.preventDefault()`) - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.preventDefault(_e) + KeyboardNav.removeFromRef(escKeyHandler) if (key == "Escape") { onBack() } else { @@ -476,21 +473,14 @@ let makeTrainingScreen = ( )->Promise.thenResolve(_ => { escNavigating := false // Re-add listener - escKeyHandler := Some( - %raw(`function(fn) { var h = function(e) { fn(e); }; window.addEventListener('keydown', h); return h; }`)( - escHandler, - ), - ) + escKeyHandler := Some(KeyboardNav.addKeydownHandler(escHandler)) }) | None => escNavigating := false } } } } - let handler = %raw(`function(fn) { var h = function(e) { fn(e); }; window.addEventListener('keydown', h); return h; }`)( - escHandler, - ) - escKeyHandler := Some(handler) + escKeyHandler := Some(KeyboardNav.addKeydownHandler(escHandler)) await Motion.animateAsync( container, @@ -502,10 +492,7 @@ let makeTrainingScreen = ( hide: Some( async () => { // Remove Escape key listener - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) await Motion.animateAsync( container, @@ -519,10 +506,7 @@ let makeTrainingScreen = ( reset: Some( () => { // Cleanup Escape key listener on reset - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) }, ), update: Some( diff --git a/main-game/src/app/screens/training/TrainingMenuScreen.res b/main-game/src/app/screens/training/TrainingMenuScreen.res index 1b750ef..8a6aa06 100644 --- a/main-game/src/app/screens/training/TrainingMenuScreen.res +++ b/main-game/src/app/screens/training/TrainingMenuScreen.res @@ -347,14 +347,11 @@ let make = (): Navigation.appScreen => { // Register Escape key to go back to world map (single-fire guard) let escNavigating = ref(false) let escHandler = (_e: {..}) => { - let key: string = %raw(`_e.key`) + let key = KeyboardNav.eventKey(_e) if key == "Escape" && !escNavigating.contents { escNavigating := true - let _ = %raw(`_e.preventDefault()`) - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.preventDefault(_e) + KeyboardNav.removeFromRef(escKeyHandler) switch GetEngine.get() { | Some(engine) => let _ = @@ -365,10 +362,7 @@ let make = (): Navigation.appScreen => { } } } - let handler = %raw(`function(fn) { var h = function(e) { fn(e); }; window.addEventListener('keydown', h); return h; }`)( - escHandler, - ) - escKeyHandler := Some(handler) + escKeyHandler := Some(KeyboardNav.addKeydownHandler(escHandler)) await Motion.animateAsync( container, @@ -380,10 +374,7 @@ let make = (): Navigation.appScreen => { hide: Some( async () => { // Remove Escape key listener - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) await Motion.animateAsync( container, @@ -396,10 +387,7 @@ let make = (): Navigation.appScreen => { resume: None, reset: Some( () => { - switch escKeyHandler.contents { - | Some(_handler) => %raw(`window.removeEventListener('keydown', _handler)`) - | None => () - } + KeyboardNav.removeFromRef(escKeyHandler) }, ), update: None, diff --git a/main-game/src/engine/utils/KeyboardNav.res b/main-game/src/engine/utils/KeyboardNav.res new file mode 100644 index 0000000..f41d15b --- /dev/null +++ b/main-game/src/engine/utils/KeyboardNav.res @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: PMPL-1.0-or-later +// KeyboardNav.res — Shared keyboard handler attachment/removal for screen navigation +// +// Eliminates duplicated %raw blocks across WorldBuilder, TrainingBase, +// TrainingMenuScreen, VictoryScreen, and GameOverScreen. + +// Opaque handler type returned by addKeydownHandler +type handler + +// Attach a keydown listener and return an opaque handler for later removal +let addKeydownHandler: (({..}) => unit) => handler = %raw(` + function(fn) { + var h = function(e) { fn(e); }; + window.addEventListener('keydown', h); + return h; + } +`) + +// Remove a previously-attached keydown listener +let removeKeydownHandler: handler => unit = %raw(` + function(h) { + window.removeEventListener('keydown', h); + } +`) + +// Get the key string from a keyboard event +let eventKey: {..} => string = %raw(`function(e) { return e.key; }`) + +// Call preventDefault on a keyboard event +let preventDefault: {..} => unit = %raw(`function(e) { e.preventDefault(); }`) + +// Convenience: remove handler from an option ref (the common cleanup pattern) +let removeFromRef = (handlerRef: ref>): unit => { + switch handlerRef.contents { + | Some(h) => removeKeydownHandler(h) + | None => () + } +} diff --git a/shared/OPUS-TODO.adoc b/shared/OPUS-TODO.adoc index e03c83f..8b93b19 100644 --- a/shared/OPUS-TODO.adoc +++ b/shared/OPUS-TODO.adoc @@ -315,10 +315,10 @@ the design rationale is preserved. |=== | Priority | Item | Status | Session | P1 | Circuit Breaker | TODO | — -| P1 | Remaining README.adoc files | TODO | — +| P1 | Remaining README.adoc files | PARTIAL | — (shared/src/ and shared/tests/ done; root, vm/, proven/, devices/, engine/, bindings/ still need) | P2 | In-process LRU caching | TODO | — | P2 | Proven library integration | TODO | — -| P2 | mapBounded per-domain defaults + mapBoundedWithRetry | TODO | — +| P2 | mapBounded per-domain defaults + mapBoundedWithRetry | PARTIAL | — (mapBounded implemented with chunkSize=9; per-domain defaults + mapBoundedWithRetry still needed) | P3 | Bebop serialization | TODO | — | P3 | NDJSON streaming | TODO | — | P3 | WASM optimization | TODO | — diff --git a/shared/src/DLCLoader.res b/shared/src/DLCLoader.res index 281e6a7..25d915f 100644 --- a/shared/src/DLCLoader.res +++ b/shared/src/DLCLoader.res @@ -24,7 +24,7 @@ type rawMetadata = { // Parse difficulty string to PuzzleFormat.difficulty let parseDifficulty = (s: string): PuzzleFormat.difficulty => { - switch Js.String2.toLowerCase(s) { + switch String.toLowerCase(s) { | "beginner" => Beginner | "intermediate" => Intermediate | "advanced" => Advanced @@ -34,8 +34,8 @@ let parseDifficulty = (s: string): PuzzleFormat.difficulty => { } // Parse tier from JSON (either int or string) -let parseTier = (json: Js.Json.t): PuzzleFormat.tier => { - switch Js.Json.classify(json) { +let parseTier = (json: JSON.t): PuzzleFormat.tier => { + switch JSON.Classify.classify(json) { | JSONNumber(n) => switch Belt.Float.toInt(n) { | 0 => Tier0 @@ -46,7 +46,7 @@ let parseTier = (json: Js.Json.t): PuzzleFormat.tier => { | _ => Tier0 } | JSONString(s) => - switch Js.String2.toLowerCase(s) { + switch String.toLowerCase(s) { | "tier0" | "0" => Tier0 | "tier1" | "1" => Tier1 | "tier2" | "2" => Tier2 @@ -60,14 +60,14 @@ let parseTier = (json: Js.Json.t): PuzzleFormat.tier => { // Infer tier from puzzle ID/filename if not explicit let inferTierFromId = (id: string): PuzzleFormat.tier => { - let lower = Js.String2.toLowerCase(id) - if Js.String2.includes(lower, "tier4") || Js.String2.includes(lower, "send") || Js.String2.includes(lower, "recv") { + let lower = String.toLowerCase(id) + if String.includes(lower, "tier4") || String.includes(lower, "send") || String.includes(lower, "recv") { Tier4 - } else if Js.String2.includes(lower, "tier3") || Js.String2.includes(lower, "call") || Js.String2.includes(lower, "subroutine") { + } else if String.includes(lower, "tier3") || String.includes(lower, "call") || String.includes(lower, "subroutine") { Tier3 - } else if Js.String2.includes(lower, "tier2") || Js.String2.includes(lower, "stack") || Js.String2.includes(lower, "memory") { + } else if String.includes(lower, "tier2") || String.includes(lower, "stack") || String.includes(lower, "memory") { Tier2 - } else if Js.String2.includes(lower, "tier1") || Js.String2.includes(lower, "branch") || Js.String2.includes(lower, "loop") { + } else if String.includes(lower, "tier1") || String.includes(lower, "branch") || String.includes(lower, "loop") { Tier1 } else { Tier0 @@ -78,8 +78,8 @@ let inferTierFromId = (id: string): PuzzleFormat.tier => { let inferTierFromInstructions = (instrs: array): PuzzleFormat.tier => { let hasAny = (patterns: array) => Belt.Array.some(instrs, i => { - let upper = Js.String2.toUpperCase(i) - Belt.Array.some(patterns, p => Js.String2.includes(upper, p)) + let upper = String.toUpperCase(i) + Belt.Array.some(patterns, p => String.includes(upper, p)) }) if hasAny(["SEND", "RECV"]) { Tier4 @@ -95,14 +95,14 @@ let inferTierFromInstructions = (instrs: array): PuzzleFormat.tier => { } // Extract register pairs from a state object (JSON dict of string -> int) -let extractRegisters = (stateJson: Js.Json.t): array<(string, int)> => { - switch Js.Json.classify(stateJson) { +let extractRegisters = (stateJson: JSON.t): array<(string, int)> => { + switch JSON.Classify.classify(stateJson) { | JSONObject(dict) => - let keys = Js.Dict.keys(dict) + let keys = Dict.keysToArray(dict) Belt.Array.keepMap(keys, key => { - switch Js.Dict.get(dict, key) { + switch Dict.get(dict, key) { | Some(v) => - switch Js.Json.classify(v) { + switch JSON.Classify.classify(v) { | JSONNumber(n) => Some((key, Belt.Float.toInt(n))) | _ => None } @@ -114,40 +114,40 @@ let extractRegisters = (stateJson: Js.Json.t): array<(string, int)> => { } // Parse hints from either old format [{moveNumber, text}] or new [string] -let parseHints = (json: Js.Json.t): array => { - switch Js.Json.classify(json) { +let parseHints = (json: JSON.t): array => { + switch JSON.Classify.classify(json) { | JSONArray(arr) => Belt.Array.keepMap(arr, item => { - switch Js.Json.classify(item) { + switch JSON.Classify.classify(item) { // New format: array of strings | JSONString(s) => Some({PuzzleFormat.moveThreshold: 3, text: s}) // Old format: {moveNumber, text} objects | JSONObject(dict) => { - let text = switch Js.Dict.get(dict, "text") { + let text = switch Dict.get(dict, "text") { | Some(v) => - switch Js.Json.classify(v) { + switch JSON.Classify.classify(v) { | JSONString(s) => s | _ => "" } | None => "" } - let threshold = switch Js.Dict.get(dict, "moveNumber") { + let threshold = switch Dict.get(dict, "moveNumber") { | Some(v) => - switch Js.Json.classify(v) { + switch JSON.Classify.classify(v) { | JSONNumber(n) => Belt.Float.toInt(n) | _ => 3 } | None => - switch Js.Dict.get(dict, "moveThreshold") { + switch Dict.get(dict, "moveThreshold") { | Some(v) => - switch Js.Json.classify(v) { + switch JSON.Classify.classify(v) { | JSONNumber(n) => Belt.Float.toInt(n) | _ => 3 } | None => 3 } } - if Js.String2.length(text) > 0 { + if String.length(text) > 0 { Some({PuzzleFormat.moveThreshold: threshold, text}) } else { None @@ -161,10 +161,10 @@ let parseHints = (json: Js.Json.t): array => { } // Get a string field from a JSON object -let getString = (dict: Js.Dict.t, key: string, default: string): string => { - switch Js.Dict.get(dict, key) { +let getString = (dict: Dict.t, key: string, default: string): string => { + switch Dict.get(dict, key) { | Some(v) => - switch Js.Json.classify(v) { + switch JSON.Classify.classify(v) { | JSONString(s) => s | _ => default } @@ -173,10 +173,10 @@ let getString = (dict: Js.Dict.t, key: string, default: string): stri } // Get an int field from a JSON object -let getInt = (dict: Js.Dict.t, key: string, default: int): int => { - switch Js.Dict.get(dict, key) { +let getInt = (dict: Dict.t, key: string, default: int): int => { + switch Dict.get(dict, key) { | Some(v) => - switch Js.Json.classify(v) { + switch JSON.Classify.classify(v) { | JSONNumber(n) => Belt.Float.toInt(n) | _ => default } @@ -185,13 +185,13 @@ let getInt = (dict: Js.Dict.t, key: string, default: int): int => { } // Get a string array from a JSON object -let getStringArray = (dict: Js.Dict.t, key: string): array => { - switch Js.Dict.get(dict, key) { +let getStringArray = (dict: Dict.t, key: string): array => { + switch Dict.get(dict, key) { | Some(v) => - switch Js.Json.classify(v) { + switch JSON.Classify.classify(v) { | JSONArray(arr) => Belt.Array.keepMap(arr, item => { - switch Js.Json.classify(item) { + switch JSON.Classify.classify(item) { | JSONString(s) => Some(s) | _ => None } @@ -205,15 +205,15 @@ let getStringArray = (dict: Js.Dict.t, key: string): array => // Parse a single puzzle JSON string into PuzzleFormat.t // id: the puzzle identifier (typically filename without .json) let parsePuzzleJson = (id: string, jsonStr: string): option => { - switch Js.Json.parseExn(jsonStr)->Js.Json.classify { + switch JSON.parseExn(jsonStr)->JSON.Classify.classify { | JSONObject(dict) => { let name = getString(dict, "name", id) let description = getString(dict, "description", "") let difficulty = getString(dict, "difficulty", "beginner")->parseDifficulty let maxMoves = getInt(dict, "maxMoves", 50) - let parMoves = switch Js.Dict.get(dict, "optimalMoves") { + let parMoves = switch Dict.get(dict, "optimalMoves") { | Some(v) => - switch Js.Json.classify(v) { + switch JSON.Classify.classify(v) { | JSONNumber(n) => Belt.Float.toInt(n) | _ => getInt(dict, "parMoves", maxMoves) } @@ -221,28 +221,28 @@ let parsePuzzleJson = (id: string, jsonStr: string): option => { } // Parse initial/goal state (supports both initialState and initialRegisters) - let initialRegisters = switch Js.Dict.get(dict, "initialState") { + let initialRegisters = switch Dict.get(dict, "initialState") { | Some(v) => extractRegisters(v) | None => - switch Js.Dict.get(dict, "initialRegisters") { + switch Dict.get(dict, "initialRegisters") { | Some(v) => extractRegisters(v) | None => [] } } - let goalRegisters = switch Js.Dict.get(dict, "goalState") { + let goalRegisters = switch Dict.get(dict, "goalState") { | Some(v) => extractRegisters(v) | None => - switch Js.Dict.get(dict, "goalRegisters") { + switch Dict.get(dict, "goalRegisters") { | Some(v) => extractRegisters(v) | None => [] } } // Parse tier explicit, or inferred - let tier = switch Js.Dict.get(dict, "tier") { + let tier = switch Dict.get(dict, "tier") { | Some(v) => parseTier(v) | None => { - let fromInstr = switch Js.Dict.get(dict, "allowedInstructions") { + let fromInstr = switch Dict.get(dict, "allowedInstructions") { | Some(_) => { let instrs = getStringArray(dict, "allowedInstructions") if Belt.Array.length(instrs) > 0 { @@ -271,7 +271,7 @@ let parsePuzzleJson = (id: string, jsonStr: string): option => { } // Parse hints - let hints = switch Js.Dict.get(dict, "hints") { + let hints = switch Dict.get(dict, "hints") { | Some(v) => parseHints(v) | None => [] } @@ -282,9 +282,9 @@ let parsePuzzleJson = (id: string, jsonStr: string): option => { if Belt.Array.length(topLevel) > 0 { topLevel } else { - switch Js.Dict.get(dict, "metadata") { + switch Dict.get(dict, "metadata") { | Some(v) => - switch Js.Json.classify(v) { + switch JSON.Classify.classify(v) { | JSONObject(metaDict) => getStringArray(metaDict, "tags") | _ => [] } @@ -315,60 +315,60 @@ let parsePuzzleJson = (id: string, jsonStr: string): option => { } // Serialize a puzzle to JSON (for bundle generation) -let puzzleToJson = (p: PuzzleFormat.t): Js.Json.t => { - let dict = Js.Dict.empty() - Js.Dict.set(dict, "id", Js.Json.string(p.id)) - Js.Dict.set(dict, "name", Js.Json.string(p.name)) - Js.Dict.set(dict, "description", Js.Json.string(p.description)) - Js.Dict.set(dict, "difficulty", Js.Json.string(PuzzleFormat.difficultyToString(p.difficulty))) - Js.Dict.set(dict, "tier", Js.Json.number(Belt.Int.toFloat(PuzzleFormat.tierToInt(p.tier)))) - Js.Dict.set(dict, "maxMoves", Js.Json.number(Belt.Int.toFloat(p.maxMoves))) - Js.Dict.set(dict, "parMoves", Js.Json.number(Belt.Int.toFloat(p.parMoves))) +let puzzleToJson = (p: PuzzleFormat.t): JSON.t => { + let dict = Dict.make() + Dict.set(dict, "id", Js.Json.string(p.id)) + Dict.set(dict, "name", Js.Json.string(p.name)) + Dict.set(dict, "description", Js.Json.string(p.description)) + Dict.set(dict, "difficulty", Js.Json.string(PuzzleFormat.difficultyToString(p.difficulty))) + Dict.set(dict, "tier", Js.Json.number(Belt.Int.toFloat(PuzzleFormat.tierToInt(p.tier)))) + Dict.set(dict, "maxMoves", Js.Json.number(Belt.Int.toFloat(p.maxMoves))) + Dict.set(dict, "parMoves", Js.Json.number(Belt.Int.toFloat(p.parMoves))) // Registers as objects - let regsToJson = (regs: array<(string, int)>): Js.Json.t => { - let d = Js.Dict.empty() + let regsToJson = (regs: array<(string, int)>): JSON.t => { + let d = Dict.make() Belt.Array.forEach(regs, ((k, v)) => { - Js.Dict.set(d, k, Js.Json.number(Belt.Int.toFloat(v))) + Dict.set(d, k, Js.Json.number(Belt.Int.toFloat(v))) }) Js.Json.object_(d) } - Js.Dict.set(dict, "initialRegisters", regsToJson(p.initialRegisters)) - Js.Dict.set(dict, "goalRegisters", regsToJson(p.goalRegisters)) + Dict.set(dict, "initialRegisters", regsToJson(p.initialRegisters)) + Dict.set(dict, "goalRegisters", regsToJson(p.goalRegisters)) // Memory pairs if Belt.Array.length(p.initialMemory) > 0 { - let memToJson = (pairs: array<(int, int)>): Js.Json.t => { + let memToJson = (pairs: array<(int, int)>): JSON.t => { Js.Json.array( Belt.Array.map(pairs, ((addr, val_)) => { - let d = Js.Dict.empty() - Js.Dict.set(d, "addr", Js.Json.number(Belt.Int.toFloat(addr))) - Js.Dict.set(d, "value", Js.Json.number(Belt.Int.toFloat(val_))) + let d = Dict.make() + Dict.set(d, "addr", Js.Json.number(Belt.Int.toFloat(addr))) + Dict.set(d, "value", Js.Json.number(Belt.Int.toFloat(val_))) Js.Json.object_(d) }), ) } - Js.Dict.set(dict, "initialMemory", memToJson(p.initialMemory)) - Js.Dict.set(dict, "goalMemory", memToJson(p.goalMemory)) + Dict.set(dict, "initialMemory", memToJson(p.initialMemory)) + Dict.set(dict, "goalMemory", memToJson(p.goalMemory)) } // Allowed instructions switch p.allowedInstructions { | Some(instrs) => - Js.Dict.set(dict, "allowedInstructions", Js.Json.array(Belt.Array.map(instrs, Js.Json.string))) + Dict.set(dict, "allowedInstructions", Js.Json.array(Belt.Array.map(instrs, Js.Json.string))) | None => () } // Hints if Belt.Array.length(p.hints) > 0 { - Js.Dict.set( + Dict.set( dict, "hints", Js.Json.array( Belt.Array.map(p.hints, h => { - let d = Js.Dict.empty() - Js.Dict.set(d, "moveThreshold", Js.Json.number(Belt.Int.toFloat(h.moveThreshold))) - Js.Dict.set(d, "text", Js.Json.string(h.text)) + let d = Dict.make() + Dict.set(d, "moveThreshold", Js.Json.number(Belt.Int.toFloat(h.moveThreshold))) + Dict.set(d, "text", Js.Json.string(h.text)) Js.Json.object_(d) }), ), @@ -377,7 +377,7 @@ let puzzleToJson = (p: PuzzleFormat.t): Js.Json.t => { // Tags if Belt.Array.length(p.tags) > 0 { - Js.Dict.set(dict, "tags", Js.Json.array(Belt.Array.map(p.tags, Js.Json.string))) + Dict.set(dict, "tags", Js.Json.array(Belt.Array.map(p.tags, Js.Json.string))) } Js.Json.object_(dict) @@ -385,10 +385,10 @@ let puzzleToJson = (p: PuzzleFormat.t): Js.Json.t => { // Serialize an array of puzzles to a bundle JSON string let bundleToJsonString = (puzzles: array): string => { - let bundle = Js.Dict.empty() - Js.Dict.set(bundle, "version", Js.Json.string("1.0.0")) - Js.Dict.set(bundle, "generatedAt", Js.Json.string("build-time")) - Js.Dict.set(bundle, "count", Js.Json.number(Belt.Int.toFloat(Belt.Array.length(puzzles)))) - Js.Dict.set(bundle, "puzzles", Js.Json.array(Belt.Array.map(puzzles, puzzleToJson))) + let bundle = Dict.make() + Dict.set(bundle, "version", Js.Json.string("1.0.0")) + Dict.set(bundle, "generatedAt", Js.Json.string("build-time")) + Dict.set(bundle, "count", Js.Json.number(Belt.Int.toFloat(Belt.Array.length(puzzles)))) + Dict.set(bundle, "puzzles", Js.Json.array(Belt.Array.map(puzzles, puzzleToJson))) Js.Json.stringify(Js.Json.object_(bundle)) } diff --git a/shared/src/DeviceType.res b/shared/src/DeviceType.res index 07be495..90ecebd 100644 --- a/shared/src/DeviceType.res +++ b/shared/src/DeviceType.res @@ -86,7 +86,7 @@ type deviceInfo = { } let fromString = (s: string): option => { - switch Js.String2.toLowerCase(s) { + switch String.toLowerCase(s) { | "laptop" => Some(Laptop) | "desktop" => Some(Desktop) | "server" => Some(Server) diff --git a/shared/src/PuzzleFormat.res b/shared/src/PuzzleFormat.res index f5a677b..a10b3ee 100644 --- a/shared/src/PuzzleFormat.res +++ b/shared/src/PuzzleFormat.res @@ -64,7 +64,7 @@ let difficultyToString = (d: difficulty): string => { } let difficultyFromString = (s: string): option => { - switch Js.String2.toLowerCase(s) { + switch String.toLowerCase(s) { | "beginner" => Some(Beginner) | "intermediate" => Some(Intermediate) | "advanced" => Some(Advanced) diff --git a/vm/benchmarks/benchmark.res b/vm/benchmarks/benchmark.res index 037b497..bef28f8 100644 --- a/vm/benchmarks/benchmark.res +++ b/vm/benchmarks/benchmark.res @@ -21,19 +21,19 @@ let benchmark = (name: string, iterations: int, fn: unit => unit): unit => { let totalTime = endTime -. startTime let avgTime = totalTime /. float_of_int(iterations) - Js.Console.log(`${name}:`) - Js.Console.log(` Total: ${Float.toString(totalTime)} ms`) - Js.Console.log(` Iterations: ${Belt.Int.toString(iterations)}`) - Js.Console.log(` Average: ${Float.toString(avgTime)} ms per iteration`) - Js.Console.log(` Throughput: ${Float.toString(1000.0 /. avgTime)} ops/sec`) - Js.Console.log("") + Console.log(`${name}:`) + Console.log(` Total: ${Float.toString(totalTime)} ms`) + Console.log(` Iterations: ${Belt.Int.toString(iterations)}`) + Console.log(` Average: ${Float.toString(avgTime)} ms per iteration`) + Console.log(` Throughput: ${Float.toString(1000.0 /. avgTime)} ops/sec`) + Console.log("") } // Benchmark ADD instruction let benchmarkAdd = (): unit => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let instr = Add.make("x", "y") @@ -45,8 +45,8 @@ let benchmarkAdd = (): unit => { // Benchmark SWAP instruction let benchmarkSwap = (): unit => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 20) + Dict.set(state, "x", 10) + Dict.set(state, "y", 20) let instr = Swap.make("x", "y") @@ -58,8 +58,8 @@ let benchmarkSwap = (): unit => { // Benchmark VM execution let benchmarkVM = (): unit => { let state = State.createState(~variables=["x", "y", "z"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let vm = VM.make(state) @@ -73,7 +73,7 @@ let benchmarkStateClone = (): unit => { let state = State.createState(~variables=["a", "b", "c", "d", "e", "f", "g", "h"], ~initialValue=0) for i in 0 to 7 { let vars = ["a", "b", "c", "d", "e", "f", "g", "h"] - Js.Dict.set(state, Belt.Array.getExn(vars, i), i * 10) + Dict.set(state, Belt.Array.getUnsafe(vars, i), i * 10) } benchmark("State clone (8 vars)", 50000, () => { @@ -99,10 +99,10 @@ let benchmarkUndo = (): unit => { // Run all benchmarks let runAll = (): unit => { - Js.Console.log("") - Js.Console.log(" Idaptik VM Benchmarks") - Js.Console.log("") - Js.Console.log("") + Console.log("") + Console.log(" Idaptik VM Benchmarks") + Console.log("") + Console.log("") benchmarkAdd() benchmarkSwap() @@ -110,9 +110,9 @@ let runAll = (): unit => { benchmarkStateClone() benchmarkUndo() - Js.Console.log("") - Js.Console.log(" Benchmarks Complete") - Js.Console.log("") + Console.log("") + Console.log(" Benchmarks Complete") + Console.log("") } // Run benchmarks diff --git a/vm/src/core/Instruction.res b/vm/src/core/Instruction.res index fc8399d..4cbf488 100644 --- a/vm/src/core/Instruction.res +++ b/vm/src/core/Instruction.res @@ -3,8 +3,8 @@ type t = { instructionType: string, args: array, - execute: Js.Dict.t => unit, - invert: Js.Dict.t => unit, + execute: Dict.t => unit, + invert: Dict.t => unit, } // Helper to create instruction records diff --git a/vm/src/core/InstructionParser.res b/vm/src/core/InstructionParser.res index 292f6c6..92c106d 100644 --- a/vm/src/core/InstructionParser.res +++ b/vm/src/core/InstructionParser.res @@ -17,16 +17,16 @@ let rec parseInternal = ( input: string, resolve: option option>>, ): parseResult => { - let trimmed = Js.String2.trim(input) - let tokens = Js.String2.split(trimmed, " ") - ->Belt.Array.keep(t => Js.String2.length(Js.String2.trim(t)) > 0) + let trimmed = String.trim(input) + let tokens = String.split(trimmed, " ") + ->Belt.Array.keep(t => String.length(String.trim(t)) > 0) if Belt.Array.length(tokens) == 0 { Error("Empty command") } else { - let opcode = Belt.Array.getExn(tokens, 0)->Js.String2.toUpperCase + let opcode = Belt.Array.getUnsafe(tokens, 0)->String.toUpperCase let argCount = Belt.Array.length(tokens) - 1 - let arg = (i: int) => Belt.Array.getExn(tokens, i + 1) + let arg = (i: int) => Belt.Array.getUnsafe(tokens, i + 1) switch opcode { // --- Tier 0: Register arithmetic --- @@ -125,7 +125,7 @@ let rec parseInternal = ( if argCount >= 3 { let testReg = arg(0) let exitReg = arg(1) - let bodyText = Belt.Array.sliceToEnd(tokens, 3)->Js.Array2.joinWith(" ") + let bodyText = Belt.Array.sliceToEnd(tokens, 3)->Array.join(" ") switch parseInternal(bodyText, resolve) { | Ok(bodyInstr) => Ok(IfZero.make(~testReg, ~exitReg, ~thenBranch=[bodyInstr], ~elseBranch=[])) @@ -138,7 +138,7 @@ let rec parseInternal = ( if argCount >= 3 { let testReg = arg(0) let exitReg = arg(1) - let bodyText = Belt.Array.sliceToEnd(tokens, 3)->Js.Array2.joinWith(" ") + let bodyText = Belt.Array.sliceToEnd(tokens, 3)->Array.join(" ") switch parseInternal(bodyText, resolve) { | Ok(bodyInstr) => Ok(IfPos.make(~testReg, ~exitReg, ~thenBranch=[bodyInstr], ~elseBranch=[])) diff --git a/vm/src/core/State.res b/vm/src/core/State.res index a93125c..c891e0f 100644 --- a/vm/src/core/State.res +++ b/vm/src/core/State.res @@ -2,29 +2,29 @@ // State management for reversible VM // Create a register state from a list of variable names -let createState = (~variables: array, ~initialValue=0): Js.Dict.t => { - let state = Js.Dict.empty() +let createState = (~variables: array, ~initialValue=0): Dict.t => { + let state = Dict.make() variables->Belt.Array.forEach(v => { - Js.Dict.set(state, v, initialValue) + Dict.set(state, v, initialValue) }) state } // Deep clone the current state (for logging, snapshots, etc.) -let cloneState = (state: Js.Dict.t): Js.Dict.t => { - let newState = Js.Dict.empty() +let cloneState = (state: Dict.t): Dict.t => { + let newState = Dict.make() state - ->Js.Dict.entries + ->Dict.toArray ->Belt.Array.forEach(((key, value)) => { - Js.Dict.set(newState, key, value) + Dict.set(newState, key, value) }) newState } // Check if two states are equal (used in puzzle goals) -let statesMatch = (a: Js.Dict.t, b: Js.Dict.t): bool => { - let keysA = Js.Dict.keys(a) - let keysB = Js.Dict.keys(b) +let statesMatch = (a: Dict.t, b: Dict.t): bool => { + let keysA = Dict.keysToArray(a) + let keysB = Dict.keysToArray(b) let allKeys = Belt.Set.String.fromArray( Belt.Array.concat(keysA, keysB) @@ -33,27 +33,32 @@ let statesMatch = (a: Js.Dict.t, b: Js.Dict.t): bool => { allKeys ->Belt.Set.String.toArray ->Belt.Array.every(key => { - Js.Dict.get(a, key) == Js.Dict.get(b, key) + Dict.get(a, key) == Dict.get(b, key) }) } // Serialize state to JSON string -let serializeState = (state: Js.Dict.t): string => { - Js.Json.stringifyAny(state)->Belt.Option.getWithDefault("{}") +let serializeState = (state: Dict.t): string => { + JSON.stringifyAny(state)->Option.getOr("{}") } // Deserialize state from JSON string -let deserializeState = (json: string): Js.Dict.t => { +let deserializeState = (json: string): Dict.t => { try { - let parsed = Js.Json.parseExn(json) - let obj = parsed->Js.Json.decodeObject->Belt.Option.getWithDefault(Js.Dict.empty()) - let result = Js.Dict.empty() - obj->Js.Dict.entries->Belt.Array.forEach(((key, value)) => { - let intValue = value->Js.Json.decodeNumber->Belt.Option.map(Belt.Float.toInt)->Belt.Option.getWithDefault(0) - Js.Dict.set(result, key, intValue) - }) - result + let parsed = JSON.parseExn(json) + switch JSON.Classify.classify(parsed) { + | Object(obj) => + let result = Dict.make() + obj->Dict.toArray->Belt.Array.forEach(((key, value)) => { + switch JSON.Classify.classify(value) { + | Number(n) => Dict.set(result, key, Belt.Float.toInt(n)) + | _ => () + } + }) + result + | _ => Dict.make() + } } catch { - | _ => Js.Dict.empty() + | _ => Dict.make() } } diff --git a/vm/src/core/StateDiff.res b/vm/src/core/StateDiff.res index c7bf82f..1057934 100644 --- a/vm/src/core/StateDiff.res +++ b/vm/src/core/StateDiff.res @@ -9,18 +9,18 @@ type diff = { } // Compare two states and generate diff -let computeDiff = (before: Js.Dict.t, after: Js.Dict.t): array => { +let computeDiff = (before: Dict.t, after: Dict.t): array => { // Get all unique variable names from both states - let beforeKeys = Js.Dict.keys(before) - let afterKeys = Js.Dict.keys(after) + let beforeKeys = Dict.keysToArray(before) + let afterKeys = Dict.keysToArray(after) let allKeys = Belt.Array.concatMany([beforeKeys, afterKeys]) ->Belt.Set.String.fromArray ->Belt.Set.String.toArray // Create diff for each variable allKeys->Belt.Array.map(variable => { - let beforeVal = Js.Dict.get(before, variable) - let afterVal = Js.Dict.get(after, variable) + let beforeVal = Dict.get(before, variable) + let afterVal = Dict.get(after, variable) let changed = switch (beforeVal, afterVal) { | (Some(v1), Some(v2)) => v1 != v2 @@ -49,62 +49,62 @@ let printDiffEntry = (d: diff): unit => { | Some(v) => Belt.Int.toString(v) | None => "" } - Js.Console.log(` ${d.variable}: ${beforeStr} ${afterStr} `) + Console.log(` ${d.variable}: ${beforeStr} ${afterStr} `) } else { switch d.before { - | Some(v) => Js.Console.log(` ${d.variable}: ${Belt.Int.toString(v)} (unchanged)`) + | Some(v) => Console.log(` ${d.variable}: ${Belt.Int.toString(v)} (unchanged)`) | None => () } } } // Pretty print entire diff -let printDiff = (before: Js.Dict.t, after: Js.Dict.t): unit => { +let printDiff = (before: Dict.t, after: Dict.t): unit => { let diffs = computeDiff(before, after) let hasChanges = diffs->Belt.Array.some(d => d.changed) if hasChanges { - Js.Console.log(" State Changes:") + Console.log(" State Changes:") diffs->Belt.Array.forEach(printDiffEntry) } else { - Js.Console.log(" No state changes") + Console.log(" No state changes") } } // Count number of differences -let countChanges = (before: Js.Dict.t, after: Js.Dict.t): int => { +let countChanges = (before: Dict.t, after: Dict.t): int => { let diffs = computeDiff(before, after) diffs->Belt.Array.keep(d => d.changed)->Belt.Array.length } // Helper to pad string to length let padLeft = (s: string, len: int): string => { - let current = Js.String2.length(s) + let current = String.length(s) if current >= len { s } else { - let padding = Js.String2.repeat(" ", len - current) + let padding = String.repeat(" ", len - current) padding ++ s } } let padRight = (s: string, len: int): string => { - let current = Js.String2.length(s) + let current = String.length(s) if current >= len { s } else { - let padding = Js.String2.repeat(" ", len - current) + let padding = String.repeat(" ", len - current) s ++ padding } } // Side-by-side comparison -let printSideBySide = (before: Js.Dict.t, after: Js.Dict.t): unit => { +let printSideBySide = (before: Dict.t, after: Dict.t): unit => { let diffs = computeDiff(before, after) - Js.Console.log("") - Js.Console.log(" Variable Before After ") - Js.Console.log("") + Console.log("") + Console.log(" Variable Before After ") + Console.log("") diffs->Belt.Array.forEach(d => { let beforeStr = switch d.before { @@ -118,30 +118,30 @@ let printSideBySide = (before: Js.Dict.t, after: Js.Dict.t): unit => { let varPadded = padRight(d.variable, 9) let changeIndicator = if d.changed { "" } else { " " } - Js.Console.log(` ${varPadded} ${beforeStr} ${afterStr} ${changeIndicator}`) + Console.log(` ${varPadded} ${beforeStr} ${afterStr} ${changeIndicator}`) }) - Js.Console.log("") + Console.log("") } // Check if all specified variables match target values let matchesTarget = ( - current: Js.Dict.t, - target: Js.Dict.t, + current: Dict.t, + target: Dict.t, ): bool => { State.statesMatch(current, target) } // Get variables that don't match target let getMismatches = ( - current: Js.Dict.t, - target: Js.Dict.t, + current: Dict.t, + target: Dict.t, ): array => { - let targetKeys = Js.Dict.keys(target) + let targetKeys = Dict.keysToArray(target) targetKeys->Belt.Array.keep(key => { - let currentVal = Js.Dict.get(current, key) - let targetVal = Js.Dict.get(target, key) + let currentVal = Dict.get(current, key) + let targetVal = Dict.get(target, key) switch (currentVal, targetVal) { | (Some(v1), Some(v2)) => v1 != v2 @@ -152,19 +152,19 @@ let getMismatches = ( // Print mismatches between current and target let printMismatches = ( - current: Js.Dict.t, - target: Js.Dict.t, + current: Dict.t, + target: Dict.t, ): unit => { let mismatches = getMismatches(current, target) if Belt.Array.length(mismatches) == 0 { - Js.Console.log(" All target values matched!") + Console.log(" All target values matched!") } else { - Js.Console.log(` ${Belt.Int.toString(Belt.Array.length(mismatches))} mismatches:`) + Console.log(` ${Belt.Int.toString(Belt.Array.length(mismatches))} mismatches:`) mismatches->Belt.Array.forEach(key => { - let currentVal = Js.Dict.get(current, key)->Belt.Option.getWithDefault(0) - let targetVal = Js.Dict.get(target, key)->Belt.Option.getWithDefault(0) - Js.Console.log(` ${key}: ${Belt.Int.toString(currentVal)} (expected ${Belt.Int.toString(targetVal)})`) + let currentVal = Dict.get(current, key)->Belt.Option.getWithDefault(0) + let targetVal = Dict.get(target, key)->Belt.Option.getWithDefault(0) + Console.log(` ${key}: ${Belt.Int.toString(currentVal)} (expected ${Belt.Int.toString(targetVal)})`) }) } } diff --git a/vm/src/core/SubroutineRegistry.res b/vm/src/core/SubroutineRegistry.res index 2586ca9..0153438 100644 --- a/vm/src/core/SubroutineRegistry.res +++ b/vm/src/core/SubroutineRegistry.res @@ -6,39 +6,39 @@ // registry at creation time. type t = { - mutable definitions: Js.Dict.t>, + mutable definitions: Dict.t>, } let make = (): t => { - definitions: Js.Dict.empty(), + definitions: Dict.make(), } // Define a named subroutine let define = (registry: t, name: string, body: array): unit => { - Js.Dict.set(registry.definitions, name, body) + Dict.set(registry.definitions, name, body) } // Look up a subroutine by name let get = (registry: t, name: string): option> => { - Js.Dict.get(registry.definitions, name) + Dict.get(registry.definitions, name) } // Check if a subroutine exists let has = (registry: t, name: string): bool => { - Js.Dict.get(registry.definitions, name)->Belt.Option.isSome + Dict.get(registry.definitions, name)->Belt.Option.isSome } // List all defined subroutine names let list = (registry: t): array => { - Js.Dict.keys(registry.definitions) + Dict.keysToArray(registry.definitions) } // Remove a subroutine definition let remove = (registry: t, name: string): unit => { - let newDefs = Js.Dict.empty() - registry.definitions->Js.Dict.entries->Belt.Array.forEach(((key, value)) => { + let newDefs = Dict.make() + registry.definitions->Dict.toArray->Belt.Array.forEach(((key, value)) => { if key != name { - Js.Dict.set(newDefs, key, value) + Dict.set(newDefs, key, value) } }) registry.definitions = newDefs diff --git a/vm/src/core/VM.res b/vm/src/core/VM.res index e83e0a6..11942fd 100644 --- a/vm/src/core/VM.res +++ b/vm/src/core/VM.res @@ -7,16 +7,16 @@ // // State encoding: user registers are plain keys (x, y, z). Internal // state (stack, memory, ports) uses underscore-prefixed keys managed -// by VmState.res. All state lives in one Js.Dict.t. +// by VmState.res. All state lives in one Dict.t. type t = { - mutable state: Js.Dict.t, + mutable state: Dict.t, mutable history: array, subroutines: SubroutineRegistry.t, } // Create a new VM with initial register state -let make = (initial: Js.Dict.t): t => { +let make = (initial: Dict.t): t => { state: State.cloneState(initial), history: [], subroutines: SubroutineRegistry.make(), @@ -32,7 +32,7 @@ let run = (vm: t, instr: Instruction.t): unit => { let undo = (vm: t): option => { let len = Belt.Array.length(vm.history) if len > 0 { - let lastInstr = Belt.Array.getExn(vm.history, len - 1) + let lastInstr = Belt.Array.getUnsafe(vm.history, len - 1) lastInstr.invert(vm.state) vm.history = Belt.Array.slice(vm.history, ~offset=0, ~len=len - 1) Some(lastInstr) @@ -56,11 +56,11 @@ let undoAll = (vm: t): int => { // Print current state to console let printState = (vm: t): unit => { - Js.log2("Current State:", vm.state) + Console.log2("Current State:", vm.state) } // Get current state (immutable copy includes internal keys) -let getState = (vm: t): Js.Dict.t => { +let getState = (vm: t): Dict.t => { State.cloneState(vm.state) } @@ -68,7 +68,7 @@ let getState = (vm: t): Js.Dict.t => { let getCurrentState = getState // Get only user registers (no internal state like _sp, _mem:N, etc.) -let getRegisters = (vm: t): Js.Dict.t => { +let getRegisters = (vm: t): Dict.t => { VmState.getRegisters(vm.state) } @@ -98,7 +98,7 @@ let setMemory = (vm: t, addr: int, value: int): unit => { } // Reset VM to a specific state -let resetState = (vm: t, newState: Js.Dict.t): unit => { +let resetState = (vm: t, newState: Dict.t): unit => { vm.state = State.cloneState(newState) vm.history = [] } diff --git a/vm/src/core/VmState.res b/vm/src/core/VmState.res index 72048b0..44339e5 100644 --- a/vm/src/core/VmState.res +++ b/vm/src/core/VmState.res @@ -1,7 +1,7 @@ // SPDX-License-Identifier: PMPL-1.0-or-later // VmState.res Helpers for accessing stack, memory, and port state // -// The VM uses a flat Js.Dict.t for all state. Stack, memory, and ports +// The VM uses a flat Dict.t for all state. Stack, memory, and ports // are encoded with reserved key prefixes: // _sp stack pointer (grows upward from 0) // _s:N stack slot at index N @@ -20,103 +20,103 @@ let memorySize = 256 // --- Stack operations --- -let getStackPointer = (state: Js.Dict.t): int => - Js.Dict.get(state, "_sp")->Belt.Option.getWithDefault(0) +let getStackPointer = (state: Dict.t): int => + Dict.get(state, "_sp")->Belt.Option.getWithDefault(0) -let setStackPointer = (state: Js.Dict.t, sp: int): unit => - Js.Dict.set(state, "_sp", sp) +let setStackPointer = (state: Dict.t, sp: int): unit => + Dict.set(state, "_sp", sp) -let getStackSlot = (state: Js.Dict.t, index: int): int => - Js.Dict.get(state, `_s:${Belt.Int.toString(index)}`)->Belt.Option.getWithDefault(0) +let getStackSlot = (state: Dict.t, index: int): int => + Dict.get(state, `_s:${Belt.Int.toString(index)}`)->Belt.Option.getWithDefault(0) -let setStackSlot = (state: Js.Dict.t, index: int, value: int): unit => - Js.Dict.set(state, `_s:${Belt.Int.toString(index)}`, value) +let setStackSlot = (state: Dict.t, index: int, value: int): unit => + Dict.set(state, `_s:${Belt.Int.toString(index)}`, value) -let clearStackSlot = (state: Js.Dict.t, index: int): unit => - Js.Dict.set(state, `_s:${Belt.Int.toString(index)}`, 0) +let clearStackSlot = (state: Dict.t, index: int): unit => + Dict.set(state, `_s:${Belt.Int.toString(index)}`, 0) // --- Memory operations --- -let getMemory = (state: Js.Dict.t, addr: int): int => - Js.Dict.get(state, `_mem:${Belt.Int.toString(addr)}`)->Belt.Option.getWithDefault(0) +let getMemory = (state: Dict.t, addr: int): int => + Dict.get(state, `_mem:${Belt.Int.toString(addr)}`)->Belt.Option.getWithDefault(0) -let setMemory = (state: Js.Dict.t, addr: int, value: int): unit => - Js.Dict.set(state, `_mem:${Belt.Int.toString(addr)}`, value) +let setMemory = (state: Dict.t, addr: int, value: int): unit => + Dict.set(state, `_mem:${Belt.Int.toString(addr)}`, value) // --- Call stack operations --- -let getCallStackPointer = (state: Js.Dict.t): int => - Js.Dict.get(state, "_csp")->Belt.Option.getWithDefault(0) +let getCallStackPointer = (state: Dict.t): int => + Dict.get(state, "_csp")->Belt.Option.getWithDefault(0) -let setCallStackPointer = (state: Js.Dict.t, csp: int): unit => - Js.Dict.set(state, "_csp", csp) +let setCallStackPointer = (state: Dict.t, csp: int): unit => + Dict.set(state, "_csp", csp) -let getCallStackSlot = (state: Js.Dict.t, index: int): int => - Js.Dict.get(state, `_cs:${Belt.Int.toString(index)}`)->Belt.Option.getWithDefault(0) +let getCallStackSlot = (state: Dict.t, index: int): int => + Dict.get(state, `_cs:${Belt.Int.toString(index)}`)->Belt.Option.getWithDefault(0) -let setCallStackSlot = (state: Js.Dict.t, index: int, value: int): unit => - Js.Dict.set(state, `_cs:${Belt.Int.toString(index)}`, value) +let setCallStackSlot = (state: Dict.t, index: int, value: int): unit => + Dict.set(state, `_cs:${Belt.Int.toString(index)}`, value) // --- Port operations --- -let getPortInPointer = (state: Js.Dict.t, port: string): int => - Js.Dict.get(state, `_pin:${port}`)->Belt.Option.getWithDefault(0) +let getPortInPointer = (state: Dict.t, port: string): int => + Dict.get(state, `_pin:${port}`)->Belt.Option.getWithDefault(0) -let setPortInPointer = (state: Js.Dict.t, port: string, ptr: int): unit => - Js.Dict.set(state, `_pin:${port}`, ptr) +let setPortInPointer = (state: Dict.t, port: string, ptr: int): unit => + Dict.set(state, `_pin:${port}`, ptr) -let getPortOutPointer = (state: Js.Dict.t, port: string): int => - Js.Dict.get(state, `_pout:${port}`)->Belt.Option.getWithDefault(0) +let getPortOutPointer = (state: Dict.t, port: string): int => + Dict.get(state, `_pout:${port}`)->Belt.Option.getWithDefault(0) -let setPortOutPointer = (state: Js.Dict.t, port: string, ptr: int): unit => - Js.Dict.set(state, `_pout:${port}`, ptr) +let setPortOutPointer = (state: Dict.t, port: string, ptr: int): unit => + Dict.set(state, `_pout:${port}`, ptr) -let getPortInSlot = (state: Js.Dict.t, port: string, index: int): int => - Js.Dict.get(state, `_pi:${port}:${Belt.Int.toString(index)}`)->Belt.Option.getWithDefault(0) +let getPortInSlot = (state: Dict.t, port: string, index: int): int => + Dict.get(state, `_pi:${port}:${Belt.Int.toString(index)}`)->Belt.Option.getWithDefault(0) -let setPortInSlot = (state: Js.Dict.t, port: string, index: int, value: int): unit => - Js.Dict.set(state, `_pi:${port}:${Belt.Int.toString(index)}`, value) +let setPortInSlot = (state: Dict.t, port: string, index: int, value: int): unit => + Dict.set(state, `_pi:${port}:${Belt.Int.toString(index)}`, value) -let getPortOutSlot = (state: Js.Dict.t, port: string, index: int): int => - Js.Dict.get(state, `_po:${port}:${Belt.Int.toString(index)}`)->Belt.Option.getWithDefault(0) +let getPortOutSlot = (state: Dict.t, port: string, index: int): int => + Dict.get(state, `_po:${port}:${Belt.Int.toString(index)}`)->Belt.Option.getWithDefault(0) -let setPortOutSlot = (state: Js.Dict.t, port: string, index: int, value: int): unit => - Js.Dict.set(state, `_po:${port}:${Belt.Int.toString(index)}`, value) +let setPortOutSlot = (state: Dict.t, port: string, index: int, value: int): unit => + Dict.set(state, `_po:${port}:${Belt.Int.toString(index)}`, value) // --- Introspection --- let isInternalKey = (key: string): bool => - Js.String2.startsWith(key, "_") + String.startsWith(key, "_") // Get only user registers (no internal keys) -let getRegisters = (state: Js.Dict.t): Js.Dict.t => { - let regs = Js.Dict.empty() - state->Js.Dict.entries->Belt.Array.forEach(((key, value)) => { +let getRegisters = (state: Dict.t): Dict.t => { + let regs = Dict.make() + state->Dict.toArray->Belt.Array.forEach(((key, value)) => { if !isInternalKey(key) { - Js.Dict.set(regs, key, value) + Dict.set(regs, key, value) } }) regs } // Get stack contents as array (bottom to top) -let getStackContents = (state: Js.Dict.t): array => { +let getStackContents = (state: Dict.t): array => { let sp = getStackPointer(state) Belt.Array.makeBy(sp, i => getStackSlot(state, i)) } // Get a range of memory cells -let getMemoryRange = (state: Js.Dict.t, ~start: int, ~length: int): array => +let getMemoryRange = (state: Dict.t, ~start: int, ~length: int): array => Belt.Array.makeBy(length, i => getMemory(state, start + i)) // Get port output buffer contents -let getPortOutputContents = (state: Js.Dict.t, port: string): array => { +let getPortOutputContents = (state: Dict.t, port: string): array => { let ptr = getPortOutPointer(state, port) Belt.Array.makeBy(ptr, i => getPortOutSlot(state, port, i)) } // Get port input buffer contents (unread portion) -let getPortInputRemaining = (state: Js.Dict.t, port: string, ~totalItems: int): array => { +let getPortInputRemaining = (state: Dict.t, port: string, ~totalItems: int): array => { let ptr = getPortInPointer(state, port) Belt.Array.makeBy(totalItems - ptr, i => getPortInSlot(state, port, ptr + i)) } diff --git a/vm/src/core/instructions/Add.res b/vm/src/core/instructions/Add.res index 862ea00..3a149fd 100644 --- a/vm/src/core/instructions/Add.res +++ b/vm/src/core/instructions/Add.res @@ -5,13 +5,13 @@ let make = (a: string, b: string): Instruction.t => { instructionType: "ADD", args: [a, b], execute: state => { - let valA = Js.Dict.get(state, a)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, b)->Belt.Option.getWithDefault(0) - Js.Dict.set(state, a, valA + valB) + let valA = Dict.get(state, a)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, b)->Belt.Option.getWithDefault(0) + Dict.set(state, a, valA + valB) }, invert: state => { - let valA = Js.Dict.get(state, a)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, b)->Belt.Option.getWithDefault(0) - Js.Dict.set(state, a, valA - valB) + let valA = Dict.get(state, a)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, b)->Belt.Option.getWithDefault(0) + Dict.set(state, a, valA - valB) }, } diff --git a/vm/src/core/instructions/And.res b/vm/src/core/instructions/And.res index c2d871a..30446c2 100644 --- a/vm/src/core/instructions/And.res +++ b/vm/src/core/instructions/And.res @@ -8,15 +8,15 @@ let make = (varA: string, varB: string, varC: string): Instruction.t => { instructionType: "AND", args: [varA, varB, varC], execute: state => { - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(0) // Bitwise AND: c = a & b let result = land(valA, valB) - Js.Dict.set(state, varC, result) + Dict.set(state, varC, result) }, invert: state => { // Inverse: clear the ancilla (set c to 0) // This assumes c was 0 before execute - Js.Dict.set(state, varC, 0) + Dict.set(state, varC, 0) }, } diff --git a/vm/src/core/instructions/Div.res b/vm/src/core/instructions/Div.res index 9a48e89..489515c 100644 --- a/vm/src/core/instructions/Div.res +++ b/vm/src/core/instructions/Div.res @@ -11,36 +11,36 @@ let make = (varA: string, varB: string, varQ: string, varR: string): Instruction instructionType: "DIV", args: [varA, varB, varQ, varR], execute: state => { - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(1) // Avoid division by zero + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(1) // Avoid division by zero if valB == 0 { // Division by zero - store error state - Js.Dict.set(state, varQ, 0) - Js.Dict.set(state, varR, valA) // Store original value in remainder + Dict.set(state, varQ, 0) + Dict.set(state, varR, valA) // Store original value in remainder } else { // Normal division: q = a / b, r = a mod b let quotient = valA / valB let remainder = mod(valA, valB) - Js.Dict.set(state, varQ, quotient) - Js.Dict.set(state, varR, remainder) + Dict.set(state, varQ, quotient) + Dict.set(state, varR, remainder) } }, invert: state => { // Inverse: reconstruct a from quotient and remainder // a = (q * b) + r - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(1) - let valQ = Js.Dict.get(state, varQ)->Belt.Option.getWithDefault(0) - let valR = Js.Dict.get(state, varR)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(1) + let valQ = Dict.get(state, varQ)->Belt.Option.getWithDefault(0) + let valR = Dict.get(state, varR)->Belt.Option.getWithDefault(0) let _reconstructed = (valQ * valB) + valR // In a full implementation, we might restore varA here: - // Js.Dict.set(state, varA, reconstructed) + // Dict.set(state, varA, reconstructed) // Clear quotient and remainder (ancilla cleanup) - Js.Dict.set(state, varQ, 0) - Js.Dict.set(state, varR, 0) + Dict.set(state, varQ, 0) + Dict.set(state, varR, 0) // Note: We don't restore varA here because DIV is typically used // to compute quotient/remainder, not to transform the dividend @@ -52,16 +52,16 @@ let makeSimple = (varA: string, varB: string, varQ: string): Instruction.t => { instructionType: "DIV_SIMPLE", args: [varA, varB, varQ], execute: state => { - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(1) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(1) if valB != 0 { let quotient = valA / valB - Js.Dict.set(state, varQ, quotient) + Dict.set(state, varQ, quotient) } }, invert: state => { // Clear the quotient - Js.Dict.set(state, varQ, 0) + Dict.set(state, varQ, 0) }, } diff --git a/vm/src/core/instructions/Flip.res b/vm/src/core/instructions/Flip.res index aae6bb5..31b838b 100644 --- a/vm/src/core/instructions/Flip.res +++ b/vm/src/core/instructions/Flip.res @@ -7,17 +7,17 @@ let make = (varA: string): Instruction.t => { instructionType: "FLIP", args: [varA], execute: state => { - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) // Bitwise NOT: ~a // For integers, we'll use a simple inversion for demonstration // In a real binary system, this would flip all bits let result = lnot(valA) - Js.Dict.set(state, varA, result) + Dict.set(state, varA, result) }, invert: state => { // FLIP is self-inverse: NOT(NOT(a)) = a - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) let result = lnot(valA) - Js.Dict.set(state, varA, result) + Dict.set(state, varA, result) }, } diff --git a/vm/src/core/instructions/IfPos.res b/vm/src/core/instructions/IfPos.res index 08a8c4c..e32ca28 100644 --- a/vm/src/core/instructions/IfPos.res +++ b/vm/src/core/instructions/IfPos.res @@ -19,7 +19,7 @@ let make = ( instructionType: "IF_POS", args: [testReg, exitReg], execute: state => { - let testVal = Js.Dict.get(state, testReg)->Belt.Option.getWithDefault(0) + let testVal = Dict.get(state, testReg)->Belt.Option.getWithDefault(0) if testVal > 0 { Belt.Array.forEach(thenBranch, instr => instr.execute(state)) } else { @@ -27,7 +27,7 @@ let make = ( } }, invert: state => { - let exitVal = Js.Dict.get(state, exitReg)->Belt.Option.getWithDefault(0) + let exitVal = Dict.get(state, exitReg)->Belt.Option.getWithDefault(0) if exitVal > 0 { let reversed = Belt.Array.copy(thenBranch) Belt.Array.reverseInPlace(reversed) diff --git a/vm/src/core/instructions/IfZero.res b/vm/src/core/instructions/IfZero.res index 7f03b1d..a135c4d 100644 --- a/vm/src/core/instructions/IfZero.res +++ b/vm/src/core/instructions/IfZero.res @@ -20,7 +20,7 @@ let make = ( instructionType: "IF_ZERO", args: [testReg, exitReg], execute: state => { - let testVal = Js.Dict.get(state, testReg)->Belt.Option.getWithDefault(0) + let testVal = Dict.get(state, testReg)->Belt.Option.getWithDefault(0) if testVal == 0 { // Execute then-branch Belt.Array.forEach(thenBranch, instr => instr.execute(state)) @@ -31,7 +31,7 @@ let make = ( }, invert: state => { // Reverse uses the exit assertion to determine which branch was taken - let exitVal = Js.Dict.get(state, exitReg)->Belt.Option.getWithDefault(0) + let exitVal = Dict.get(state, exitReg)->Belt.Option.getWithDefault(0) if exitVal == 0 { // Was then-branch reverse it (instructions in reverse order) let reversed = Belt.Array.copy(thenBranch) diff --git a/vm/src/core/instructions/Load.res b/vm/src/core/instructions/Load.res index db2bd40..c15937e 100644 --- a/vm/src/core/instructions/Load.res +++ b/vm/src/core/instructions/Load.res @@ -14,13 +14,13 @@ let make = (reg: string, addr: int): Instruction.t => { instructionType: "LOAD", args: [reg, Belt.Int.toString(addr)], execute: state => { - let regVal = Js.Dict.get(state, reg)->Belt.Option.getWithDefault(0) + let regVal = Dict.get(state, reg)->Belt.Option.getWithDefault(0) let memVal = VmState.getMemory(state, addr) - Js.Dict.set(state, reg, regVal + memVal) + Dict.set(state, reg, regVal + memVal) }, invert: state => { - let regVal = Js.Dict.get(state, reg)->Belt.Option.getWithDefault(0) + let regVal = Dict.get(state, reg)->Belt.Option.getWithDefault(0) let memVal = VmState.getMemory(state, addr) - Js.Dict.set(state, reg, regVal - memVal) + Dict.set(state, reg, regVal - memVal) }, } diff --git a/vm/src/core/instructions/Loop.res b/vm/src/core/instructions/Loop.res index f28a785..8738ec0 100644 --- a/vm/src/core/instructions/Loop.res +++ b/vm/src/core/instructions/Loop.res @@ -40,7 +40,7 @@ let make = ( iterations := iterations.contents + 1 // Check exit condition - let exitVal = ref(Js.Dict.get(state, exitReg)->Belt.Option.getWithDefault(0)) + let exitVal = ref(Dict.get(state, exitReg)->Belt.Option.getWithDefault(0)) // Continue while exit condition not met while exitVal.contents != 0 && iterations.contents < maxIterations { @@ -49,7 +49,7 @@ let make = ( // Execute body Belt.Array.forEach(body, instr => instr.execute(state)) iterations := iterations.contents + 1 - exitVal := Js.Dict.get(state, exitReg)->Belt.Option.getWithDefault(0) + exitVal := Dict.get(state, exitReg)->Belt.Option.getWithDefault(0) } }, invert: state => { @@ -66,13 +66,13 @@ let make = ( iterations := iterations.contents + 1 // Check entry condition (swapped role of exit) - let entryVal = ref(Js.Dict.get(state, entryReg)->Belt.Option.getWithDefault(0)) + let entryVal = ref(Dict.get(state, entryReg)->Belt.Option.getWithDefault(0)) while entryVal.contents != 0 && iterations.contents < maxIterations { Belt.Array.forEach(reversedStep, instr => instr.invert(state)) Belt.Array.forEach(reversedBody, instr => instr.invert(state)) iterations := iterations.contents + 1 - entryVal := Js.Dict.get(state, entryReg)->Belt.Option.getWithDefault(0) + entryVal := Dict.get(state, entryReg)->Belt.Option.getWithDefault(0) } }, } diff --git a/vm/src/core/instructions/Mul.res b/vm/src/core/instructions/Mul.res index 3244732..1d1e8cc 100644 --- a/vm/src/core/instructions/Mul.res +++ b/vm/src/core/instructions/Mul.res @@ -12,23 +12,23 @@ let make = (varA: string, varB: string, varC: string): Instruction.t => { instructionType: "MUL", args: [varA, varB, varC], execute: state => { - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(0) - let valC = Js.Dict.get(state, varC)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(0) + let valC = Dict.get(state, varC)->Belt.Option.getWithDefault(0) // c = c + (a * b) let result = valC + (valA * valB) - Js.Dict.set(state, varC, result) + Dict.set(state, varC, result) }, invert: state => { // Inverse: subtract the product from c // This works if we know a and b haven't changed - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(0) - let valC = Js.Dict.get(state, varC)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(0) + let valC = Dict.get(state, varC)->Belt.Option.getWithDefault(0) let result = valC - (valA * valB) - Js.Dict.set(state, varC, result) + Dict.set(state, varC, result) }, } @@ -41,18 +41,18 @@ let makeInPlace = (varA: string, varB: string): Instruction.t => { instructionType: "MUL_INPLACE", args: [varA, varB], execute: state => { - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(0) - Js.Dict.set(state, varA, valA * valB) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(0) + Dict.set(state, varA, valA * valB) }, invert: state => { // WARNING: This is NOT properly reversible! // Division may not restore original value for all inputs - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(1) // Avoid division by zero + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(1) // Avoid division by zero if valB != 0 { - Js.Dict.set(state, varA, valA / valB) + Dict.set(state, varA, valA / valB) } // If valB is 0, we can't invert - this breaks reversibility! }, diff --git a/vm/src/core/instructions/Negate.res b/vm/src/core/instructions/Negate.res index d1823f7..855d18f 100644 --- a/vm/src/core/instructions/Negate.res +++ b/vm/src/core/instructions/Negate.res @@ -3,8 +3,8 @@ let make = (a: string): Instruction.t => { let negateFn = state => { - let valA = Js.Dict.get(state, a)->Belt.Option.getWithDefault(0) - Js.Dict.set(state, a, -valA) + let valA = Dict.get(state, a)->Belt.Option.getWithDefault(0) + Dict.set(state, a, -valA) } { diff --git a/vm/src/core/instructions/Or.res b/vm/src/core/instructions/Or.res index 9c20bfb..ecf3922 100644 --- a/vm/src/core/instructions/Or.res +++ b/vm/src/core/instructions/Or.res @@ -8,15 +8,15 @@ let make = (varA: string, varB: string, varC: string): Instruction.t => { instructionType: "OR", args: [varA, varB, varC], execute: state => { - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(0) // Bitwise OR: c = a | b let result = lor(valA, valB) - Js.Dict.set(state, varC, result) + Dict.set(state, varC, result) }, invert: state => { // Inverse: clear the ancilla (set c to 0) // This assumes c was 0 before execute - Js.Dict.set(state, varC, 0) + Dict.set(state, varC, 0) }, } diff --git a/vm/src/core/instructions/Pop.res b/vm/src/core/instructions/Pop.res index bfd0838..3cb7ab8 100644 --- a/vm/src/core/instructions/Pop.res +++ b/vm/src/core/instructions/Pop.res @@ -14,15 +14,15 @@ let make = (reg: string): Instruction.t => { let sp = VmState.getStackPointer(state) let newSp = sp - 1 let value = VmState.getStackSlot(state, newSp) - Js.Dict.set(state, reg, value) + Dict.set(state, reg, value) VmState.clearStackSlot(state, newSp) VmState.setStackPointer(state, newSp) }, invert: state => { - let value = Js.Dict.get(state, reg)->Belt.Option.getWithDefault(0) + let value = Dict.get(state, reg)->Belt.Option.getWithDefault(0) let sp = VmState.getStackPointer(state) VmState.setStackSlot(state, sp, value) VmState.setStackPointer(state, sp + 1) - Js.Dict.set(state, reg, 0) + Dict.set(state, reg, 0) }, } diff --git a/vm/src/core/instructions/Push.res b/vm/src/core/instructions/Push.res index cdaeb28..fabf494 100644 --- a/vm/src/core/instructions/Push.res +++ b/vm/src/core/instructions/Push.res @@ -12,17 +12,17 @@ let make = (reg: string): Instruction.t => { instructionType: "PUSH", args: [reg], execute: state => { - let value = Js.Dict.get(state, reg)->Belt.Option.getWithDefault(0) + let value = Dict.get(state, reg)->Belt.Option.getWithDefault(0) let sp = VmState.getStackPointer(state) VmState.setStackSlot(state, sp, value) VmState.setStackPointer(state, sp + 1) - Js.Dict.set(state, reg, 0) + Dict.set(state, reg, 0) }, invert: state => { let sp = VmState.getStackPointer(state) let newSp = sp - 1 let value = VmState.getStackSlot(state, newSp) - Js.Dict.set(state, reg, value) + Dict.set(state, reg, value) VmState.clearStackSlot(state, newSp) VmState.setStackPointer(state, newSp) }, diff --git a/vm/src/core/instructions/Recv.res b/vm/src/core/instructions/Recv.res index 5e49b91..4a65574 100644 --- a/vm/src/core/instructions/Recv.res +++ b/vm/src/core/instructions/Recv.res @@ -14,16 +14,16 @@ let make = (port: string, reg: string): Instruction.t => { execute: state => { let ptr = VmState.getPortInPointer(state, port) let value = VmState.getPortInSlot(state, port, ptr) - let regVal = Js.Dict.get(state, reg)->Belt.Option.getWithDefault(0) - Js.Dict.set(state, reg, regVal + value) + let regVal = Dict.get(state, reg)->Belt.Option.getWithDefault(0) + Dict.set(state, reg, regVal + value) VmState.setPortInPointer(state, port, ptr + 1) }, invert: state => { let ptr = VmState.getPortInPointer(state, port) let newPtr = ptr - 1 let value = VmState.getPortInSlot(state, port, newPtr) - let regVal = Js.Dict.get(state, reg)->Belt.Option.getWithDefault(0) - Js.Dict.set(state, reg, regVal - value) + let regVal = Dict.get(state, reg)->Belt.Option.getWithDefault(0) + Dict.set(state, reg, regVal - value) VmState.setPortInPointer(state, port, newPtr) }, } diff --git a/vm/src/core/instructions/Rol.res b/vm/src/core/instructions/Rol.res index 52bbd9f..590a104 100644 --- a/vm/src/core/instructions/Rol.res +++ b/vm/src/core/instructions/Rol.res @@ -7,21 +7,21 @@ let make = (varA: string, ~bits: int=1, ()): Instruction.t => { instructionType: "ROL", args: [varA], execute: state => { - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) // Rotate left by 'bits' positions (32-bit) let rotated = lor( lsl(valA, bits), lsr(valA, 32 - bits) ) - Js.Dict.set(state, varA, rotated) + Dict.set(state, varA, rotated) }, invert: state => { // Inverse: rotate right by same number of bits - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) let rotated = lor( lsr(valA, bits), lsl(valA, 32 - bits) ) - Js.Dict.set(state, varA, rotated) + Dict.set(state, varA, rotated) }, } diff --git a/vm/src/core/instructions/Ror.res b/vm/src/core/instructions/Ror.res index 71a7939..2c9ef04 100644 --- a/vm/src/core/instructions/Ror.res +++ b/vm/src/core/instructions/Ror.res @@ -7,21 +7,21 @@ let make = (varA: string, ~bits: int=1, ()): Instruction.t => { instructionType: "ROR", args: [varA], execute: state => { - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) // Rotate right by 'bits' positions (32-bit) let rotated = lor( lsr(valA, bits), lsl(valA, 32 - bits) ) - Js.Dict.set(state, varA, rotated) + Dict.set(state, varA, rotated) }, invert: state => { // Inverse: rotate left by same number of bits - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) let rotated = lor( lsl(valA, bits), lsr(valA, 32 - bits) ) - Js.Dict.set(state, varA, rotated) + Dict.set(state, varA, rotated) }, } diff --git a/vm/src/core/instructions/Send.res b/vm/src/core/instructions/Send.res index c4cc795..67d8118 100644 --- a/vm/src/core/instructions/Send.res +++ b/vm/src/core/instructions/Send.res @@ -12,7 +12,7 @@ let make = (port: string, reg: string): Instruction.t => { instructionType: "SEND", args: [port, reg], execute: state => { - let regVal = Js.Dict.get(state, reg)->Belt.Option.getWithDefault(0) + let regVal = Dict.get(state, reg)->Belt.Option.getWithDefault(0) let ptr = VmState.getPortOutPointer(state, port) VmState.setPortOutSlot(state, port, ptr, regVal) VmState.setPortOutPointer(state, port, ptr + 1) diff --git a/vm/src/core/instructions/Store.res b/vm/src/core/instructions/Store.res index 0be435d..b801e56 100644 --- a/vm/src/core/instructions/Store.res +++ b/vm/src/core/instructions/Store.res @@ -12,12 +12,12 @@ let make = (addr: int, reg: string): Instruction.t => { instructionType: "STORE", args: [Belt.Int.toString(addr), reg], execute: state => { - let regVal = Js.Dict.get(state, reg)->Belt.Option.getWithDefault(0) + let regVal = Dict.get(state, reg)->Belt.Option.getWithDefault(0) let memVal = VmState.getMemory(state, addr) VmState.setMemory(state, addr, memVal + regVal) }, invert: state => { - let regVal = Js.Dict.get(state, reg)->Belt.Option.getWithDefault(0) + let regVal = Dict.get(state, reg)->Belt.Option.getWithDefault(0) let memVal = VmState.getMemory(state, addr) VmState.setMemory(state, addr, memVal - regVal) }, diff --git a/vm/src/core/instructions/Sub.res b/vm/src/core/instructions/Sub.res index 99108e9..91ec59d 100644 --- a/vm/src/core/instructions/Sub.res +++ b/vm/src/core/instructions/Sub.res @@ -5,13 +5,13 @@ let make = (a: string, b: string): Instruction.t => { instructionType: "SUB", args: [a, b], execute: state => { - let valA = Js.Dict.get(state, a)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, b)->Belt.Option.getWithDefault(0) - Js.Dict.set(state, a, valA - valB) + let valA = Dict.get(state, a)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, b)->Belt.Option.getWithDefault(0) + Dict.set(state, a, valA - valB) }, invert: state => { - let valA = Js.Dict.get(state, a)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, b)->Belt.Option.getWithDefault(0) - Js.Dict.set(state, a, valA + valB) + let valA = Dict.get(state, a)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, b)->Belt.Option.getWithDefault(0) + Dict.set(state, a, valA + valB) }, } diff --git a/vm/src/core/instructions/Swap.res b/vm/src/core/instructions/Swap.res index 387b54e..ece5413 100644 --- a/vm/src/core/instructions/Swap.res +++ b/vm/src/core/instructions/Swap.res @@ -3,10 +3,10 @@ let make = (a: string, b: string): Instruction.t => { let swapFn = state => { - let valA = Js.Dict.get(state, a)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, b)->Belt.Option.getWithDefault(0) - Js.Dict.set(state, a, valB) - Js.Dict.set(state, b, valA) + let valA = Dict.get(state, a)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, b)->Belt.Option.getWithDefault(0) + Dict.set(state, a, valB) + Dict.set(state, b, valA) } { diff --git a/vm/src/core/instructions/Xor.res b/vm/src/core/instructions/Xor.res index 5192eb5..1b70d88 100644 --- a/vm/src/core/instructions/Xor.res +++ b/vm/src/core/instructions/Xor.res @@ -7,17 +7,17 @@ let make = (varA: string, varB: string): Instruction.t => { instructionType: "XOR", args: [varA, varB], execute: state => { - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(0) // Bitwise XOR using land/lor/lnot operators let result = lor(land(valA, lnot(valB)), land(lnot(valA), valB)) - Js.Dict.set(state, varA, result) + Dict.set(state, varA, result) }, invert: state => { // XOR is self-inverse: (a XOR b) XOR b = a - let valA = Js.Dict.get(state, varA)->Belt.Option.getWithDefault(0) - let valB = Js.Dict.get(state, varB)->Belt.Option.getWithDefault(0) + let valA = Dict.get(state, varA)->Belt.Option.getWithDefault(0) + let valB = Dict.get(state, varB)->Belt.Option.getWithDefault(0) let result = lor(land(valA, lnot(valB)), land(lnot(valA), valB)) - Js.Dict.set(state, varA, result) + Dict.set(state, varA, result) }, } diff --git a/vm/tests/test_all.res b/vm/tests/test_all.res index 9c98a37..c364b7e 100644 --- a/vm/tests/test_all.res +++ b/vm/tests/test_all.res @@ -20,22 +20,22 @@ let printTestSuite = (suite: testSuite): unit => { let passedTests = suite.results->Belt.Array.keep(r => r.passed)->Belt.Array.length let failedTests = totalTests - passedTests - Js.Console.log("") - Js.Console.log(`${""->Js.String2.repeat(50)}`) - Js.Console.log(` ${suite.suiteName}`) - Js.Console.log(`${""->Js.String2.repeat(50)}`) + Console.log("") + Console.log(`${""->String.repeat(50)}`) + Console.log(` ${suite.suiteName}`) + Console.log(`${""->String.repeat(50)}`) suite.results->Belt.Array.forEach(result => { let icon = if result.passed { "" } else { "" } - let durationStr = Js.Float.toFixedWithPrecision(result.duration, ~digits=2) - Js.Console.log(` ${icon} ${result.name} (${durationStr}ms)`) + let durationStr = Float.toFixed(result.duration, ~digits=2) + Console.log(` ${icon} ${result.name} (${durationStr}ms)`) }) - Js.Console.log("") - Js.Console.log(` Total: ${Belt.Int.toString(totalTests)}`) - Js.Console.log(` Passed: ${Belt.Int.toString(passedTests)} `) + Console.log("") + Console.log(` Total: ${Belt.Int.toString(totalTests)}`) + Console.log(` Passed: ${Belt.Int.toString(passedTests)} `) if failedTests > 0 { - Js.Console.log(` Failed: ${Belt.Int.toString(failedTests)} `) + Console.log(` Failed: ${Belt.Int.toString(failedTests)} `) } } @@ -51,24 +51,24 @@ let printSummary = (suites: array): unit => { let totalFailed = totalTests - totalPassed - Js.Console.log("") - Js.Console.log("") - Js.Console.log(" TEST SUMMARY ") - Js.Console.log("") - Js.Console.log(` Test Suites: ${Belt.Int.toString(Belt.Array.length(suites))}`) - Js.Console.log(` Total Tests: ${Belt.Int.toString(totalTests)}`) - Js.Console.log(` Passed: ${Belt.Int.toString(totalPassed)} `) + Console.log("") + Console.log("") + Console.log(" TEST SUMMARY ") + Console.log("") + Console.log(` Test Suites: ${Belt.Int.toString(Belt.Array.length(suites))}`) + Console.log(` Total Tests: ${Belt.Int.toString(totalTests)}`) + Console.log(` Passed: ${Belt.Int.toString(totalPassed)} `) if totalFailed > 0 { - Js.Console.log(` Failed: ${Belt.Int.toString(totalFailed)} `) - Js.Console.log("") - Js.Console.log(" !! Some tests failed!") + Console.log(` Failed: ${Belt.Int.toString(totalFailed)} `) + Console.log("") + Console.log(" !! Some tests failed!") } else { - Js.Console.log("") - Js.Console.log(" All tests passed!") + Console.log("") + Console.log(" All tests passed!") } - Js.Console.log("") + Console.log("") } // Measure execution time @@ -92,21 +92,21 @@ let timeTest = (name: string, testFn: unit => bool): testResult => { // let testCoreInstructions = (): testSuite => { - Js.Console.log("Running Tier 0 instruction tests...") + Console.log("Running Tier 0 instruction tests...") let results = [ timeTest("ADD - basic operation", () => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 10) - Js.Dict.set(state, "b", 5) + Dict.set(state, "a", 10) + Dict.set(state, "b", 5) let instr = Add.make("a", "b") instr.execute(state) - Js.Dict.get(state, "a")->Belt.Option.getWithDefault(0) == 15 + Dict.get(state, "a")->Belt.Option.getWithDefault(0) == 15 }), timeTest("ADD - reversibility", () => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 10) - Js.Dict.set(state, "b", 5) + Dict.set(state, "a", 10) + Dict.set(state, "b", 5) let original = State.cloneState(state) let instr = Add.make("a", "b") instr.execute(state) @@ -115,8 +115,8 @@ let testCoreInstructions = (): testSuite => { }), timeTest("SWAP - reversibility", () => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 10) - Js.Dict.set(state, "b", 20) + Dict.set(state, "a", 10) + Dict.set(state, "b", 20) let original = State.cloneState(state) let instr = Swap.make("a", "b") instr.execute(state) @@ -125,8 +125,8 @@ let testCoreInstructions = (): testSuite => { }), timeTest("SUB - reversibility", () => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 10) - Js.Dict.set(state, "b", 5) + Dict.set(state, "a", 10) + Dict.set(state, "b", 5) let original = State.cloneState(state) let instr = Sub.make("a", "b") instr.execute(state) @@ -135,7 +135,7 @@ let testCoreInstructions = (): testSuite => { }), timeTest("NEGATE - self-inverse", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let original = State.cloneState(state) let instr = Negate.make("x") instr.execute(state) @@ -149,13 +149,13 @@ let testCoreInstructions = (): testSuite => { // Bitwise instruction tests let testBitwiseInstructions = (): testSuite => { - Js.Console.log("Running Tier 0 bitwise instruction tests...") + Console.log("Running Tier 0 bitwise instruction tests...") let results = [ timeTest("XOR - self-inverse", () => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 0b1010) - Js.Dict.set(state, "b", 0b1100) + Dict.set(state, "a", 0b1010) + Dict.set(state, "b", 0b1100) let original = State.cloneState(state) let instr = Xor.make("a", "b") instr.execute(state) @@ -164,7 +164,7 @@ let testBitwiseInstructions = (): testSuite => { }), timeTest("FLIP - self-inverse", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let original = State.cloneState(state) let instr = Flip.make("x") instr.execute(state) @@ -173,7 +173,7 @@ let testBitwiseInstructions = (): testSuite => { }), timeTest("ROL/ROR - mutual inverses", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 0b1010) + Dict.set(state, "x", 0b1010) let original = State.cloneState(state) let rol = Rol.make("x", ~bits=3, ()) let ror = Ror.make("x", ~bits=3, ()) @@ -183,19 +183,19 @@ let testBitwiseInstructions = (): testSuite => { }), timeTest("AND - with ancilla", () => { let state = State.createState(~variables=["a", "b", "c"], ~initialValue=0) - Js.Dict.set(state, "a", 0b1100) - Js.Dict.set(state, "b", 0b1010) + Dict.set(state, "a", 0b1100) + Dict.set(state, "b", 0b1010) let instr = And.make("a", "b", "c") instr.execute(state) - Js.Dict.get(state, "c")->Belt.Option.getWithDefault(0) == 0b1000 + Dict.get(state, "c")->Belt.Option.getWithDefault(0) == 0b1000 }), timeTest("OR - with ancilla", () => { let state = State.createState(~variables=["a", "b", "c"], ~initialValue=0) - Js.Dict.set(state, "a", 0b1100) - Js.Dict.set(state, "b", 0b1010) + Dict.set(state, "a", 0b1100) + Dict.set(state, "b", 0b1010) let instr = Or.make("a", "b", "c") instr.execute(state) - Js.Dict.get(state, "c")->Belt.Option.getWithDefault(0) == 0b1110 + Dict.get(state, "c")->Belt.Option.getWithDefault(0) == 0b1110 }), ] @@ -207,23 +207,23 @@ let testBitwiseInstructions = (): testSuite => { // let testConditionals = (): testSuite => { - Js.Console.log("Running Tier 1 conditional tests...") + Console.log("Running Tier 1 conditional tests...") let results = [ timeTest("IF_ZERO - then branch", () => { let state = State.createState(~variables=["test", "exit", "x", "y"], ~initialValue=0) - Js.Dict.set(state, "y", 5) + Dict.set(state, "y", 5) let instr = IfZero.make( ~testReg="test", ~exitReg="exit", ~thenBranch=[Add.make("x", "y")], ~elseBranch=[], ) instr.execute(state) - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 5 + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 5 }), timeTest("IF_ZERO - reversibility", () => { let state = State.createState(~variables=["test", "exit", "x", "y"], ~initialValue=0) - Js.Dict.set(state, "y", 5) + Dict.set(state, "y", 5) let original = State.cloneState(state) let instr = IfZero.make( ~testReg="test", ~exitReg="exit", @@ -236,21 +236,21 @@ let testConditionals = (): testSuite => { }), timeTest("IF_POS - positive triggers then", () => { let state = State.createState(~variables=["test", "exit", "x", "y"], ~initialValue=0) - Js.Dict.set(state, "test", 1) - Js.Dict.set(state, "y", 5) + Dict.set(state, "test", 1) + Dict.set(state, "y", 5) let instr = IfPos.make( ~testReg="test", ~exitReg="exit", ~thenBranch=[Add.make("x", "y")], ~elseBranch=[], ) instr.execute(state) - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 5 + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 5 }), timeTest("LOOP - sum 1..5", () => { // Sum i from 1 to 5: result = 15 let state = State.createState(~variables=["i", "sum", "one", "limit", "done"], ~initialValue=0) - Js.Dict.set(state, "one", 1) - Js.Dict.set(state, "limit", 5) + Dict.set(state, "one", 1) + Dict.set(state, "limit", 5) let loop = Loop.make( ~entryReg="done", ~exitReg="done", @@ -279,7 +279,7 @@ let testConditionals = (): testSuite => { VM.run(vm, Add.make("sum", "i")) } ) - let sum = Js.Dict.get(VM.getState(vm), "sum")->Belt.Option.getWithDefault(-1) + let sum = Dict.get(VM.getState(vm), "sum")->Belt.Option.getWithDefault(-1) sum == 15 }), ] @@ -292,77 +292,77 @@ let testConditionals = (): testSuite => { // let testStackAndMemory = (): testSuite => { - Js.Console.log("Running Tier 2 stack/memory tests...") + Console.log("Running Tier 2 stack/memory tests...") let results = [ timeTest("PUSH - saves value, zeroes register", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) Push.make("x").execute(state) - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 0 && + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 0 && VmState.getStackPointer(state) == 1 && VmState.getStackSlot(state, 0) == 42 }), timeTest("POP - restores value from stack", () => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) Push.make("x").execute(state) Pop.make("y").execute(state) - Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-1) == 42 && + Dict.get(state, "y")->Belt.Option.getWithDefault(-1) == 42 && VmState.getStackPointer(state) == 0 }), timeTest("PUSH - reversibility", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let push = Push.make("x") push.execute(state) push.invert(state) - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 42 && + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 42 && VmState.getStackPointer(state) == 0 }), timeTest("Stack LIFO ordering", () => { let state = State.createState(~variables=["a", "b", "c", "r"], ~initialValue=0) - Js.Dict.set(state, "a", 10) - Js.Dict.set(state, "b", 20) - Js.Dict.set(state, "c", 30) + Dict.set(state, "a", 10) + Dict.set(state, "b", 20) + Dict.set(state, "c", 30) Push.make("a").execute(state) Push.make("b").execute(state) Push.make("c").execute(state) Pop.make("r").execute(state) - let r1 = Js.Dict.get(state, "r")->Belt.Option.getWithDefault(-1) - Js.Dict.set(state, "r", 0) + let r1 = Dict.get(state, "r")->Belt.Option.getWithDefault(-1) + Dict.set(state, "r", 0) Pop.make("r").execute(state) - let r2 = Js.Dict.get(state, "r")->Belt.Option.getWithDefault(-1) - Js.Dict.set(state, "r", 0) + let r2 = Dict.get(state, "r")->Belt.Option.getWithDefault(-1) + Dict.set(state, "r", 0) Pop.make("r").execute(state) - let r3 = Js.Dict.get(state, "r")->Belt.Option.getWithDefault(-1) + let r3 = Dict.get(state, "r")->Belt.Option.getWithDefault(-1) r1 == 30 && r2 == 20 && r3 == 10 }), timeTest("LOAD - additive from memory", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 5) + Dict.set(state, "x", 5) VmState.setMemory(state, 10, 42) Load.make("x", 10).execute(state) - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 47 + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 47 }), timeTest("LOAD - reversibility", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 5) + Dict.set(state, "x", 5) VmState.setMemory(state, 10, 42) let load = Load.make("x", 10) load.execute(state) load.invert(state) - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 5 + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 5 }), timeTest("STORE - additive to memory", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) Store.make(10, "x").execute(state) VmState.getMemory(state, 10) == 42 }), timeTest("STORE - reversibility", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let store = Store.make(10, "x") store.execute(state) store.invert(state) @@ -370,10 +370,10 @@ let testStackAndMemory = (): testSuite => { }), timeTest("LOAD/STORE round trip", () => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) Store.make(0, "x").execute(state) Load.make("y", 0).execute(state) - Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-1) == 42 + Dict.get(state, "y")->Belt.Option.getWithDefault(-1) == 42 }), ] @@ -385,30 +385,30 @@ let testStackAndMemory = (): testSuite => { // let testSubroutines = (): testSuite => { - Js.Console.log("Running Tier 3 subroutine tests...") + Console.log("Running Tier 3 subroutine tests...") let results = [ timeTest("CALL - basic subroutine", () => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let callInstr = Call.make(~name="add_xy", ~body=[Add.make("x", "y")]) callInstr.execute(state) - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 15 + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 15 }), timeTest("CALL - multi-step body", () => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let body = [Swap.make("x", "y"), Add.make("x", "y")] Call.make(~name="swap_add", ~body).execute(state) // x was 10, y was 5. After swap: x=5, y=10. After add: x=5+10=15 - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 15 + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 15 }), timeTest("CALL - reversibility", () => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let original = State.cloneState(state) let body = [Add.make("x", "y"), Swap.make("x", "y"), Negate.make("x")] let callInstr = Call.make(~name="complex", ~body) @@ -418,8 +418,8 @@ let testSubroutines = (): testSuite => { }), timeTest("CALL - via VM.callSubroutine + undo", () => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let original = State.cloneState(state) let vm = VM.make(state) VM.defineSubroutine(vm, "add_xy", [Add.make("x", "y")]) @@ -456,21 +456,21 @@ let testSubroutines = (): testSuite => { // let testIOChannels = (): testSuite => { - Js.Console.log("Running Tier 4 I/O channel tests...") + Console.log("Running Tier 4 I/O channel tests...") let results = [ timeTest("SEND - write to port", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) Send.make("firewall", "x").execute(state) VmState.getPortOutPointer(state, "firewall") == 1 && VmState.getPortOutSlot(state, "firewall", 0) == 42 }), timeTest("SEND - multiple values", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 10) + Dict.set(state, "x", 10) Send.make("net", "x").execute(state) - Js.Dict.set(state, "x", 20) + Dict.set(state, "x", 20) Send.make("net", "x").execute(state) VmState.getPortOutPointer(state, "net") == 2 && VmState.getPortOutSlot(state, "net", 0) == 10 && @@ -478,7 +478,7 @@ let testIOChannels = (): testSuite => { }), timeTest("SEND - reversibility", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let send = Send.make("display", "x") send.execute(state) send.invert(state) @@ -488,39 +488,39 @@ let testIOChannels = (): testSuite => { let state = State.createState(~variables=["x"], ~initialValue=0) VmState.setPortInSlot(state, "sensor", 0, 42) Recv.make("sensor", "x").execute(state) - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 42 && + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 42 && VmState.getPortInPointer(state, "sensor") == 1 }), timeTest("RECV - additive", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 5) + Dict.set(state, "x", 5) VmState.setPortInSlot(state, "sensor", 0, 42) Recv.make("sensor", "x").execute(state) - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 47 + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 47 }), timeTest("RECV - reversibility", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 5) + Dict.set(state, "x", 5) VmState.setPortInSlot(state, "sensor", 0, 42) let recv = Recv.make("sensor", "x") recv.execute(state) recv.invert(state) - Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 5 && + Dict.get(state, "x")->Belt.Option.getWithDefault(-1) == 5 && VmState.getPortInPointer(state, "sensor") == 0 }), timeTest("SEND/RECV round trip", () => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) Send.make("pipe", "x").execute(state) let sent = VmState.getPortOutSlot(state, "pipe", 0) VmState.setPortInSlot(state, "pipe", 0, sent) Recv.make("pipe", "y").execute(state) - Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-1) == 42 + Dict.get(state, "y")->Belt.Option.getWithDefault(-1) == 42 }), timeTest("Multiple ports independent", () => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 20) + Dict.set(state, "x", 10) + Dict.set(state, "y", 20) Send.make("portA", "x").execute(state) Send.make("portB", "y").execute(state) VmState.getPortOutSlot(state, "portA", 0) == 10 && @@ -534,24 +534,24 @@ let testIOChannels = (): testSuite => { VM.run(vm, Recv.make("data", "y")) VM.run(vm, Recv.make("data", "z")) let regs = VM.getRegisters(vm) - Js.Dict.get(regs, "x")->Belt.Option.getWithDefault(-1) == 100 && - Js.Dict.get(regs, "y")->Belt.Option.getWithDefault(-1) == 200 && - Js.Dict.get(regs, "z")->Belt.Option.getWithDefault(-1) == 300 + Dict.get(regs, "x")->Belt.Option.getWithDefault(-1) == 100 && + Dict.get(regs, "y")->Belt.Option.getWithDefault(-1) == 200 && + Dict.get(regs, "z")->Belt.Option.getWithDefault(-1) == 300 }), timeTest("VM.readPortOutput after SEND", () => { let state = State.createState(~variables=["x"], ~initialValue=0) let vm = VM.make(state) - Js.Dict.set(state, "x", 10) + Dict.set(state, "x", 10) VM.run(vm, Send.make("out", "x")) - Js.Dict.set(VM.getState(vm), "x", 20) + Dict.set(VM.getState(vm), "x", 20) // Note: we need to use the VM's internal state let vmState = vm.state - Js.Dict.set(vmState, "x", 20) + Dict.set(vmState, "x", 20) VM.run(vm, Send.make("out", "x")) let output = VM.readPortOutput(vm, "out") Belt.Array.length(output) == 2 && - Belt.Array.getExn(output, 0) == 10 && - Belt.Array.getExn(output, 1) == 20 + Belt.Array.getUnsafe(output, 0) == 10 && + Belt.Array.getUnsafe(output, 1) == 20 }), ] @@ -563,13 +563,13 @@ let testIOChannels = (): testSuite => { // let testVM = (): testSuite => { - Js.Console.log("Running VM integration tests...") + Console.log("Running VM integration tests...") let results = [ timeTest("VM - execute and undo", () => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let original = State.cloneState(state) let vm = VM.make(state) VM.run(vm, Add.make("x", "y")) @@ -578,8 +578,8 @@ let testVM = (): testSuite => { }), timeTest("VM - undoAll", () => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 0) - Js.Dict.set(state, "y", 1) + Dict.set(state, "x", 0) + Dict.set(state, "y", 1) let original = State.cloneState(state) let vm = VM.make(state) VM.run(vm, Add.make("x", "y")) @@ -598,18 +598,18 @@ let testVM = (): testSuite => { }), timeTest("VM - getRegisters filters internals", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let vm = VM.make(state) VM.run(vm, Push.make("x")) let regs = VM.getRegisters(vm) - let regKeys = Js.Dict.keys(regs) + let regKeys = Dict.keysToArray(regs) // Should only contain "x", not _sp or _s:0 Belt.Array.every(regKeys, k => !VmState.isInternalKey(k)) }), timeTest("VM - mixed tier operations", () => { let state = State.createState(~variables=["x", "y", "z"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let original = State.cloneState(state) let vm = VM.make(state) @@ -633,7 +633,7 @@ let testVM = (): testSuite => { let vm = VM.make(state) VM.setMemory(vm, 42, 100) VM.run(vm, Load.make("x", 42)) - Js.Dict.get(VM.getState(vm), "x")->Belt.Option.getWithDefault(-1) == 100 + Dict.get(VM.getState(vm), "x")->Belt.Option.getWithDefault(-1) == 100 }), ] @@ -642,29 +642,29 @@ let testVM = (): testSuite => { // State tests let testState = (): testSuite => { - Js.Console.log("Running state tests...") + Console.log("Running state tests...") let results = [ timeTest("State - clone independence", () => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let cloned = State.cloneState(state) - Js.Dict.set(state, "x", 100) - Js.Dict.get(cloned, "x")->Belt.Option.getWithDefault(0) == 42 + Dict.set(state, "x", 100) + Dict.get(cloned, "x")->Belt.Option.getWithDefault(0) == 42 }), timeTest("State - serialization", () => { let state = State.createState(~variables=["a", "b", "c"], ~initialValue=0) - Js.Dict.set(state, "a", 1) - Js.Dict.set(state, "b", 2) - Js.Dict.set(state, "c", 3) + Dict.set(state, "a", 1) + Dict.set(state, "b", 2) + Dict.set(state, "c", 3) let serialized = State.serializeState(state) let deserialized = State.deserializeState(serialized) State.statesMatch(state, deserialized) }), timeTest("State - equality check", () => { let state1 = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state1, "x", 10) - Js.Dict.set(state1, "y", 20) + Dict.set(state1, "x", 10) + Dict.set(state1, "y", 20) let state2 = State.cloneState(state1) State.statesMatch(state1, state2) }), @@ -678,7 +678,7 @@ let testState = (): testSuite => { // let testParser = (): testSuite => { - Js.Console.log("Running parser tests...") + Console.log("Running parser tests...") let results = [ timeTest("Parser - Tier 0 instructions", () => { @@ -758,9 +758,9 @@ let testParser = (): testSuite => { // let runAllTests = (): unit => { - Js.Console.log("") - Js.Console.log(" IDAPTIK VM TEST SUITE ") - Js.Console.log("") + Console.log("") + Console.log(" IDAPTIK VM TEST SUITE ") + Console.log("") let suites = [ testCoreInstructions(), diff --git a/vm/tests/unit/instructions/add_test.res b/vm/tests/unit/instructions/add_test.res index 8218c9c..821894a 100644 --- a/vm/tests/unit/instructions/add_test.res +++ b/vm/tests/unit/instructions/add_test.res @@ -5,47 +5,47 @@ open Belt let testAddBasic = (): bool => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 10) - Js.Dict.set(state, "b", 5) + Dict.set(state, "a", 10) + Dict.set(state, "b", 5) let instr = Add.make("a", "b") instr.execute(state) - Js.Dict.get(state, "a")->Option.getWithDefault(0) == 15 + Dict.get(state, "a")->Option.getWithDefault(0) == 15 } let testAddInverse = (): bool => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 10) - Js.Dict.set(state, "b", 5) + Dict.set(state, "a", 10) + Dict.set(state, "b", 5) let instr = Add.make("a", "b") instr.execute(state) instr.invert(state) - Js.Dict.get(state, "a")->Option.getWithDefault(0) == 10 + Dict.get(state, "a")->Option.getWithDefault(0) == 10 } let testAddNegative = (): bool => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", -10) - Js.Dict.set(state, "b", 5) + Dict.set(state, "a", -10) + Dict.set(state, "b", 5) let instr = Add.make("a", "b") instr.execute(state) - Js.Dict.get(state, "a")->Option.getWithDefault(0) == -5 + Dict.get(state, "a")->Option.getWithDefault(0) == -5 } let testAddZero = (): bool => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 42) - Js.Dict.set(state, "b", 0) + Dict.set(state, "a", 42) + Dict.set(state, "b", 0) let instr = Add.make("a", "b") instr.execute(state) - Js.Dict.get(state, "a")->Option.getWithDefault(0) == 42 + Dict.get(state, "a")->Option.getWithDefault(0) == 42 } // Property-based test: reversibility @@ -61,8 +61,8 @@ let testAddReversibility = (): bool => { Array.every(testCases, ((a, b)) => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", a) - Js.Dict.set(state, "y", b) + Dict.set(state, "x", a) + Dict.set(state, "y", b) let original = State.cloneState(state) let instr = Add.make("x", "y") @@ -75,11 +75,11 @@ let testAddReversibility = (): bool => { } let runAll = (): unit => { - Js.Console.log("[ADD Tests]") + Console.log("[ADD Tests]") - Js.Console.log(testAddBasic() ? " Basic addition" : " Basic addition FAILED") - Js.Console.log(testAddInverse() ? " Inverse operation" : " Inverse operation FAILED") - Js.Console.log(testAddNegative() ? " Negative numbers" : " Negative numbers FAILED") - Js.Console.log(testAddZero() ? " Adding zero" : " Adding zero FAILED") - Js.Console.log(testAddReversibility() ? " Reversibility property" : " Reversibility FAILED") + Console.log(testAddBasic() ? " Basic addition" : " Basic addition FAILED") + Console.log(testAddInverse() ? " Inverse operation" : " Inverse operation FAILED") + Console.log(testAddNegative() ? " Negative numbers" : " Negative numbers FAILED") + Console.log(testAddZero() ? " Adding zero" : " Adding zero FAILED") + Console.log(testAddReversibility() ? " Reversibility property" : " Reversibility FAILED") } diff --git a/vm/tests/unit/instructions/and_or_test.res b/vm/tests/unit/instructions/and_or_test.res index b6a8e36..d3b0d81 100644 --- a/vm/tests/unit/instructions/and_or_test.res +++ b/vm/tests/unit/instructions/and_or_test.res @@ -12,9 +12,9 @@ let testAndReversibility = (): bool => { testCases->Belt.Array.every(((a, b, expected)) => { let state = State.createState(~variables=["a", "b", "c"], ~initialValue=0) - Js.Dict.set(state, "a", a) - Js.Dict.set(state, "b", b) - Js.Dict.set(state, "c", 0) // Ancilla must be 0 + Dict.set(state, "a", a) + Dict.set(state, "b", b) + Dict.set(state, "c", 0) // Ancilla must be 0 let original = State.cloneState(state) let instr = And.make("a", "b", "c") @@ -23,7 +23,7 @@ let testAndReversibility = (): bool => { instr.execute(state) // Check result - let c = Js.Dict.get(state, "c")->Belt.Option.getWithDefault(-1) + let c = Dict.get(state, "c")->Belt.Option.getWithDefault(-1) let resultCorrect = c == expected // Invert (clear ancilla) @@ -47,9 +47,9 @@ let testOrReversibility = (): bool => { testCases->Belt.Array.every(((a, b, expected)) => { let state = State.createState(~variables=["a", "b", "c"], ~initialValue=0) - Js.Dict.set(state, "a", a) - Js.Dict.set(state, "b", b) - Js.Dict.set(state, "c", 0) // Ancilla must be 0 + Dict.set(state, "a", a) + Dict.set(state, "b", b) + Dict.set(state, "c", 0) // Ancilla must be 0 let original = State.cloneState(state) let instr = Or.make("a", "b", "c") @@ -58,7 +58,7 @@ let testOrReversibility = (): bool => { instr.execute(state) // Check result - let c = Js.Dict.get(state, "c")->Belt.Option.getWithDefault(-1) + let c = Dict.get(state, "c")->Belt.Option.getWithDefault(-1) let resultCorrect = c == expected // Invert (clear ancilla) @@ -75,19 +75,19 @@ let testOrReversibility = (): bool => { let testAncillaRequirement = (): bool => { // AND/OR require ancilla to be 0 for proper reversibility let state = State.createState(~variables=["a", "b", "c"], ~initialValue=0) - Js.Dict.set(state, "a", 0b1010) - Js.Dict.set(state, "b", 0b1100) - Js.Dict.set(state, "c", 0) + Dict.set(state, "a", 0b1010) + Dict.set(state, "b", 0b1100) + Dict.set(state, "c", 0) let instr = And.make("a", "b", "c") // Execute instr.execute(state) - let c1 = Js.Dict.get(state, "c")->Belt.Option.getWithDefault(-1) + let c1 = Dict.get(state, "c")->Belt.Option.getWithDefault(-1) // Invert instr.invert(state) - let c2 = Js.Dict.get(state, "c")->Belt.Option.getWithDefault(-1) + let c2 = Dict.get(state, "c")->Belt.Option.getWithDefault(-1) // c should be back to 0 c1 == 0b1000 && c2 == 0 @@ -95,7 +95,7 @@ let testAncillaRequirement = (): bool => { // Run all tests let runAllTests = (): bool => { - Js.Console.log("Testing AND/OR instructions...") + Console.log("Testing AND/OR instructions...") let testResults = [ ("AND correctness and reversibility", testAndReversibility()), @@ -105,9 +105,9 @@ let runAllTests = (): bool => { testResults->Belt.Array.forEach(((name, passed)) => { if passed { - Js.Console.log(` ${name}`) + Console.log(` ${name}`) } else { - Js.Console.log(` ${name} FAILED`) + Console.log(` ${name} FAILED`) } }) diff --git a/vm/tests/unit/instructions/call_test.res b/vm/tests/unit/instructions/call_test.res index 794672e..e0e3040 100644 --- a/vm/tests/unit/instructions/call_test.res +++ b/vm/tests/unit/instructions/call_test.res @@ -5,36 +5,36 @@ open Belt let testCallBasic = (): bool => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) // Define a "double_x" subroutine: ADD x x let body = [Add.make("x", "x")] let callInstr = Call.make(~name="double_x", ~body) callInstr.execute(state) - Js.Dict.get(state, "x")->Option.getWithDefault(-1) == 20 + Dict.get(state, "x")->Option.getWithDefault(-1) == 20 } let testCallMultiStep = (): bool => { let state = State.createState(~variables=["x", "y", "z"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) // Subroutine: swap x,y then add x,y final: x=5+10=15, y=10 let body = [Swap.make("x", "y"), Add.make("x", "y")] let callInstr = Call.make(~name="swap_and_add", ~body) callInstr.execute(state) - let x = Js.Dict.get(state, "x")->Option.getWithDefault(-1) - let y = Js.Dict.get(state, "y")->Option.getWithDefault(-1) + let x = Dict.get(state, "x")->Option.getWithDefault(-1) + let y = Dict.get(state, "y")->Option.getWithDefault(-1) x == 15 && y == 10 } let testCallInverse = (): bool => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let original = State.cloneState(state) let body = [Add.make("x", "y"), Swap.make("x", "y"), Negate.make("x")] @@ -47,15 +47,15 @@ let testCallInverse = (): bool => { let testCallViaVM = (): bool => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 10) + Dict.set(state, "y", 5) let original = State.cloneState(state) let vm = VM.make(state) VM.defineSubroutine(vm, "add_xy", [Add.make("x", "y")]) VM.callSubroutine(vm, "add_xy")->ignore - let x = Js.Dict.get(VM.getState(vm), "x")->Option.getWithDefault(-1) + let x = Dict.get(VM.getState(vm), "x")->Option.getWithDefault(-1) if x != 15 { false } else { // Undo should reverse the entire CALL @@ -83,8 +83,8 @@ let testCallReversibility = (): bool => { Array.every(testCases, ((_name, body)) => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 17) - Js.Dict.set(state, "y", 3) + Dict.set(state, "x", 17) + Dict.set(state, "y", 3) let original = State.cloneState(state) let callInstr = Call.make(~name="test", ~body) @@ -96,12 +96,12 @@ let testCallReversibility = (): bool => { } let runAll = (): unit => { - Js.Console.log("[CALL Tests]") - - Js.Console.log(testCallBasic() ? " Basic subroutine call" : " Basic call FAILED") - Js.Console.log(testCallMultiStep() ? " Multi-step subroutine" : " Multi-step FAILED") - Js.Console.log(testCallInverse() ? " CALL inverse reverses body" : " CALL inverse FAILED") - Js.Console.log(testCallViaVM() ? " CALL via VM.callSubroutine" : " CALL via VM FAILED") - Js.Console.log(testCallUndefinedSubroutine() ? " Undefined subroutine returns error" : " Undefined subroutine FAILED") - Js.Console.log(testCallReversibility() ? " CALL reversibility property" : " CALL reversibility FAILED") + Console.log("[CALL Tests]") + + Console.log(testCallBasic() ? " Basic subroutine call" : " Basic call FAILED") + Console.log(testCallMultiStep() ? " Multi-step subroutine" : " Multi-step FAILED") + Console.log(testCallInverse() ? " CALL inverse reverses body" : " CALL inverse FAILED") + Console.log(testCallViaVM() ? " CALL via VM.callSubroutine" : " CALL via VM FAILED") + Console.log(testCallUndefinedSubroutine() ? " Undefined subroutine returns error" : " Undefined subroutine FAILED") + Console.log(testCallReversibility() ? " CALL reversibility property" : " CALL reversibility FAILED") } diff --git a/vm/tests/unit/instructions/flip_test.res b/vm/tests/unit/instructions/flip_test.res index 8b686f7..6275190 100644 --- a/vm/tests/unit/instructions/flip_test.res +++ b/vm/tests/unit/instructions/flip_test.res @@ -7,7 +7,7 @@ let testFlipSelfInverse = (): bool => { testCases->Belt.Array.every(value => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", value) + Dict.set(state, "x", value) let original = State.cloneState(state) let instr = Flip.make("x") @@ -27,7 +27,7 @@ let testFlipReversibility = (): bool => { testCases->Belt.Array.every(value => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", value) + Dict.set(state, "x", value) let original = State.cloneState(state) let instr = Flip.make("x") @@ -54,12 +54,12 @@ let testFlipCorrectness = (): bool => { testCases->Belt.Array.every(((input, expected)) => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", input) + Dict.set(state, "x", input) let instr = Flip.make("x") instr.execute(state) - let result = Js.Dict.get(state, "x")->Belt.Option.getWithDefault(0) + let result = Dict.get(state, "x")->Belt.Option.getWithDefault(0) result == expected }) } @@ -67,9 +67,9 @@ let testFlipCorrectness = (): bool => { // Test FLIP with multiple variables let testFlipMultipleVars = (): bool => { let state = State.createState(~variables=["x", "y", "z"], ~initialValue=0) - Js.Dict.set(state, "x", 0b1010) - Js.Dict.set(state, "y", 0b1100) - Js.Dict.set(state, "z", 0) + Dict.set(state, "x", 0b1010) + Dict.set(state, "y", 0b1100) + Dict.set(state, "z", 0) let original = State.cloneState(state) @@ -92,7 +92,7 @@ let testFlipMultipleVars = (): bool => { // Run all tests let runAllTests = (): bool => { - Js.Console.log("Testing FLIP instruction...") + Console.log("Testing FLIP instruction...") let testResults = [ ("FLIP is self-inverse", testFlipSelfInverse()), @@ -103,9 +103,9 @@ let runAllTests = (): bool => { testResults->Belt.Array.forEach(((name, passed)) => { if passed { - Js.Console.log(` ${name}`) + Console.log(` ${name}`) } else { - Js.Console.log(` ${name} FAILED`) + Console.log(` ${name} FAILED`) } }) diff --git a/vm/tests/unit/instructions/if_pos_test.res b/vm/tests/unit/instructions/if_pos_test.res index 8e50eb9..8a604d5 100644 --- a/vm/tests/unit/instructions/if_pos_test.res +++ b/vm/tests/unit/instructions/if_pos_test.res @@ -2,13 +2,13 @@ // Tests for IF_POS reversible conditional let runTests = () => { - Js.log("[TEST] IF_POS instruction") + Console.log("[TEST] IF_POS instruction") // Test 1: then-branch taken (testReg > 0) { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 5) // positive, so then-branch - Js.Dict.set(state, "y", 10) + Dict.set(state, "x", 5) // positive, so then-branch + Dict.set(state, "y", 10) let instr = IfPos.make( ~testReg="x", @@ -18,21 +18,21 @@ let runTests = () => { ) instr.execute(state) - let yVal = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let yVal = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) assert(yVal == 15) - Js.log(" Then-branch executes when test > 0") + Console.log(" Then-branch executes when test > 0") instr.invert(state) - let yAfter = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let yAfter = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) assert(yAfter == 10) - Js.log(" Invert restores original state (then-branch)") + Console.log(" Invert restores original state (then-branch)") } // Test 2: else-branch taken (testReg <= 0) { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", -3) // negative, so else-branch - Js.Dict.set(state, "y", 10) + Dict.set(state, "x", -3) // negative, so else-branch + Dict.set(state, "y", 10) let instr = IfPos.make( ~testReg="x", @@ -42,20 +42,20 @@ let runTests = () => { ) instr.execute(state) - let yVal = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let yVal = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) assert(yVal == -10) - Js.log(" Else-branch executes when test <= 0") + Console.log(" Else-branch executes when test <= 0") instr.invert(state) - let yAfter = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let yAfter = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) assert(yAfter == 10) - Js.log(" Invert restores original state (else-branch)") + Console.log(" Invert restores original state (else-branch)") } // Test 3: zero triggers else-branch (0 is NOT positive) { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "y", 7) + Dict.set(state, "y", 7) let instr = IfPos.make( ~testReg="x", @@ -65,10 +65,10 @@ let runTests = () => { ) instr.execute(state) - let yVal = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let yVal = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) assert(yVal == -7) - Js.log(" Zero triggers else-branch (0 is not positive)") + Console.log(" Zero triggers else-branch (0 is not positive)") } - Js.log("[SUCCESS] All IF_POS tests passed") + Console.log("[SUCCESS] All IF_POS tests passed") } diff --git a/vm/tests/unit/instructions/if_zero_test.res b/vm/tests/unit/instructions/if_zero_test.res index 312128e..b1be4be 100644 --- a/vm/tests/unit/instructions/if_zero_test.res +++ b/vm/tests/unit/instructions/if_zero_test.res @@ -2,14 +2,14 @@ // Tests for IF_ZERO reversible conditional let runTests = () => { - Js.log("[TEST] IF_ZERO instruction") + Console.log("[TEST] IF_ZERO instruction") // Test 1: then-branch taken (testReg == 0) { let state = State.createState(~variables=["x", "y", "flag"], ~initialValue=0) - Js.Dict.set(state, "x", 0) // test register = 0, so then-branch runs - Js.Dict.set(state, "y", 5) - Js.Dict.set(state, "flag", 0) + Dict.set(state, "x", 0) // test register = 0, so then-branch runs + Dict.set(state, "y", 5) + Dict.set(state, "flag", 0) let instr = IfZero.make( ~testReg="x", @@ -19,22 +19,22 @@ let runTests = () => { ) instr.execute(state) - let yVal = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let yVal = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) assert(yVal == 10) - Js.log(" Then-branch executes when test == 0") + Console.log(" Then-branch executes when test == 0") // Reverse it instr.invert(state) - let yAfter = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let yAfter = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) assert(yAfter == 5) - Js.log(" Invert restores original state (then-branch)") + Console.log(" Invert restores original state (then-branch)") } // Test 2: else-branch taken (testReg != 0) { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 7) // test register != 0, so else-branch runs - Js.Dict.set(state, "y", 5) + Dict.set(state, "x", 7) // test register != 0, so else-branch runs + Dict.set(state, "y", 5) let instr = IfZero.make( ~testReg="x", @@ -44,21 +44,21 @@ let runTests = () => { ) instr.execute(state) - let yVal = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let yVal = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) assert(yVal == -5) - Js.log(" Else-branch executes when test != 0") + Console.log(" Else-branch executes when test != 0") instr.invert(state) - let yAfter = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let yAfter = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) assert(yAfter == 5) - Js.log(" Invert restores original state (else-branch)") + Console.log(" Invert restores original state (else-branch)") } // Test 3: Multi-instruction then-branch { let state = State.createState(~variables=["x", "y", "z"], ~initialValue=0) - Js.Dict.set(state, "y", 3) - Js.Dict.set(state, "z", 7) + Dict.set(state, "y", 3) + Dict.set(state, "z", 7) let instr = IfZero.make( ~testReg="x", @@ -72,19 +72,19 @@ let runTests = () => { ) instr.execute(state) - let yVal = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) - let zVal = Js.Dict.get(state, "z")->Belt.Option.getWithDefault(-999) + let yVal = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let zVal = Dict.get(state, "z")->Belt.Option.getWithDefault(-999) assert(yVal == 7) assert(zVal == 17) - Js.log(" Multi-instruction then-branch executes correctly") + Console.log(" Multi-instruction then-branch executes correctly") instr.invert(state) - let yAfter = Js.Dict.get(state, "y")->Belt.Option.getWithDefault(-999) - let zAfter = Js.Dict.get(state, "z")->Belt.Option.getWithDefault(-999) + let yAfter = Dict.get(state, "y")->Belt.Option.getWithDefault(-999) + let zAfter = Dict.get(state, "z")->Belt.Option.getWithDefault(-999) assert(yAfter == 3) assert(zAfter == 7) - Js.log(" Multi-instruction then-branch reverses correctly") + Console.log(" Multi-instruction then-branch reverses correctly") } - Js.log("[SUCCESS] All IF_ZERO tests passed") + Console.log("[SUCCESS] All IF_ZERO tests passed") } diff --git a/vm/tests/unit/instructions/load_store_test.res b/vm/tests/unit/instructions/load_store_test.res index fde41fe..b3ff12c 100644 --- a/vm/tests/unit/instructions/load_store_test.res +++ b/vm/tests/unit/instructions/load_store_test.res @@ -10,35 +10,35 @@ let testLoadBasic = (): bool => { let instr = Load.make("x", 10) instr.execute(state) - Js.Dict.get(state, "x")->Option.getWithDefault(-1) == 42 + Dict.get(state, "x")->Option.getWithDefault(-1) == 42 } let testLoadAdditive = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 5) + Dict.set(state, "x", 5) VmState.setMemory(state, 10, 42) Load.make("x", 10).execute(state) // x should be 5 + 42 = 47 (additive load) - Js.Dict.get(state, "x")->Option.getWithDefault(-1) == 47 + Dict.get(state, "x")->Option.getWithDefault(-1) == 47 } let testLoadInverse = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 5) + Dict.set(state, "x", 5) VmState.setMemory(state, 10, 42) let instr = Load.make("x", 10) instr.execute(state) instr.invert(state) - Js.Dict.get(state, "x")->Option.getWithDefault(-1) == 5 + Dict.get(state, "x")->Option.getWithDefault(-1) == 5 } let testStoreBasic = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) Store.make(10, "x").execute(state) @@ -47,7 +47,7 @@ let testStoreBasic = (): bool => { let testStoreAdditive = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 10) + Dict.set(state, "x", 10) VmState.setMemory(state, 5, 20) Store.make(5, "x").execute(state) @@ -58,7 +58,7 @@ let testStoreAdditive = (): bool => { let testStoreInverse = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let instr = Store.make(10, "x") instr.execute(state) @@ -69,13 +69,13 @@ let testStoreInverse = (): bool => { let testLoadStoreRoundTrip = (): bool => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) // Store x into memory, load into y (both start from 0/empty) Store.make(0, "x").execute(state) Load.make("y", 0).execute(state) - let y = Js.Dict.get(state, "y")->Option.getWithDefault(-1) + let y = Dict.get(state, "y")->Option.getWithDefault(-1) y == 42 } @@ -84,7 +84,7 @@ let testMemoryReversibility = (): bool => { Array.every(testCases, ((value, addr)) => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", value) + Dict.set(state, "x", value) let store = Store.make(addr, "x") store.execute(state) @@ -95,14 +95,14 @@ let testMemoryReversibility = (): bool => { } let runAll = (): unit => { - Js.Console.log("[LOAD/STORE Tests]") - - Js.Console.log(testLoadBasic() ? " LOAD from memory" : " LOAD basic FAILED") - Js.Console.log(testLoadAdditive() ? " LOAD is additive" : " LOAD additive FAILED") - Js.Console.log(testLoadInverse() ? " LOAD inverse restores register" : " LOAD inverse FAILED") - Js.Console.log(testStoreBasic() ? " STORE to memory" : " STORE basic FAILED") - Js.Console.log(testStoreAdditive() ? " STORE is additive" : " STORE additive FAILED") - Js.Console.log(testStoreInverse() ? " STORE inverse restores memory" : " STORE inverse FAILED") - Js.Console.log(testLoadStoreRoundTrip() ? " LOAD/STORE round trip" : " Round trip FAILED") - Js.Console.log(testMemoryReversibility() ? " Memory reversibility property" : " Memory reversibility FAILED") + Console.log("[LOAD/STORE Tests]") + + Console.log(testLoadBasic() ? " LOAD from memory" : " LOAD basic FAILED") + Console.log(testLoadAdditive() ? " LOAD is additive" : " LOAD additive FAILED") + Console.log(testLoadInverse() ? " LOAD inverse restores register" : " LOAD inverse FAILED") + Console.log(testStoreBasic() ? " STORE to memory" : " STORE basic FAILED") + Console.log(testStoreAdditive() ? " STORE is additive" : " STORE additive FAILED") + Console.log(testStoreInverse() ? " STORE inverse restores memory" : " STORE inverse FAILED") + Console.log(testLoadStoreRoundTrip() ? " LOAD/STORE round trip" : " Round trip FAILED") + Console.log(testMemoryReversibility() ? " Memory reversibility property" : " Memory reversibility FAILED") } diff --git a/vm/tests/unit/instructions/loop_test.res b/vm/tests/unit/instructions/loop_test.res index 83d2f43..9e1c156 100644 --- a/vm/tests/unit/instructions/loop_test.res +++ b/vm/tests/unit/instructions/loop_test.res @@ -2,7 +2,7 @@ // Tests for LOOP reversible loop (Janus-style) let runTests = () => { - Js.log("[TEST] LOOP instruction") + Console.log("[TEST] LOOP instruction") // Test 1: Simple counting loop sum 1+2+3+4+5 = 15 // Registers: n (counter), sum (accumulator), done (exit flag) @@ -10,8 +10,8 @@ let runTests = () => { // Setup: n=5, sum=0, done=5 (non-zero means keep going) { let state = State.createState(~variables=["n", "sum", "one", "done"], ~initialValue=0) - Js.Dict.set(state, "n", 5) - Js.Dict.set(state, "one", 1) + Dict.set(state, "n", 5) + Dict.set(state, "one", 1) // done starts at 0 for entry assertion, gets set by body let loopInstr = Loop.make( @@ -26,30 +26,30 @@ let runTests = () => { ) loopInstr.execute(state) - let sumVal = Js.Dict.get(state, "sum")->Belt.Option.getWithDefault(-999) - let nVal = Js.Dict.get(state, "n")->Belt.Option.getWithDefault(-999) + let sumVal = Dict.get(state, "sum")->Belt.Option.getWithDefault(-999) + let nVal = Dict.get(state, "n")->Belt.Option.getWithDefault(-999) assert(sumVal == 15) assert(nVal == 0) - Js.log(" Loop computes sum 1..5 = 15") + Console.log(" Loop computes sum 1..5 = 15") // Save state before reversal let stateBefore = State.cloneState(state) // Reverse the loop loopInstr.invert(state) - let sumAfter = Js.Dict.get(state, "sum")->Belt.Option.getWithDefault(-999) - let nAfter = Js.Dict.get(state, "n")->Belt.Option.getWithDefault(-999) + let sumAfter = Dict.get(state, "sum")->Belt.Option.getWithDefault(-999) + let nAfter = Dict.get(state, "n")->Belt.Option.getWithDefault(-999) assert(sumAfter == 0) assert(nAfter == 5) - Js.log(" Reverse restores n=5, sum=0") + Console.log(" Reverse restores n=5, sum=0") ignore(stateBefore) } // Test 2: Single iteration loop (body runs once, exit immediately) { let state = State.createState(~variables=["x", "y", "flag"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 3) + Dict.set(state, "x", 10) + Dict.set(state, "y", 3) let loopInstr = Loop.make( ~entryReg="flag", // flag=0, entry ok @@ -63,15 +63,15 @@ let runTests = () => { ) loopInstr.execute(state) - let xVal = Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-999) + let xVal = Dict.get(state, "x")->Belt.Option.getWithDefault(-999) assert(xVal == 13) - Js.log(" Single-iteration loop executes body once") + Console.log(" Single-iteration loop executes body once") loopInstr.invert(state) - let xAfter = Js.Dict.get(state, "x")->Belt.Option.getWithDefault(-999) + let xAfter = Dict.get(state, "x")->Belt.Option.getWithDefault(-999) assert(xAfter == 10) - Js.log(" Single-iteration loop reverses correctly") + Console.log(" Single-iteration loop reverses correctly") } - Js.log("[SUCCESS] All LOOP tests passed") + Console.log("[SUCCESS] All LOOP tests passed") } diff --git a/vm/tests/unit/instructions/push_pop_test.res b/vm/tests/unit/instructions/push_pop_test.res index 2bc8264..67b3923 100644 --- a/vm/tests/unit/instructions/push_pop_test.res +++ b/vm/tests/unit/instructions/push_pop_test.res @@ -5,12 +5,12 @@ open Belt let testPushBasic = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let instr = Push.make("x") instr.execute(state) - let x = Js.Dict.get(state, "x")->Option.getWithDefault(-1) + let x = Dict.get(state, "x")->Option.getWithDefault(-1) let sp = VmState.getStackPointer(state) let s0 = VmState.getStackSlot(state, 0) @@ -19,12 +19,12 @@ let testPushBasic = (): bool => { let testPopBasic = (): bool => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) Push.make("x").execute(state) Pop.make("y").execute(state) - let y = Js.Dict.get(state, "y")->Option.getWithDefault(-1) + let y = Dict.get(state, "y")->Option.getWithDefault(-1) let sp = VmState.getStackPointer(state) y == 42 && sp == 0 @@ -32,7 +32,7 @@ let testPopBasic = (): bool => { let testPushInverse = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let original = State.cloneState(state) let instr = Push.make("x") @@ -40,13 +40,13 @@ let testPushInverse = (): bool => { instr.invert(state) // User registers should match - Js.Dict.get(state, "x")->Option.getWithDefault(-1) == 42 && + Dict.get(state, "x")->Option.getWithDefault(-1) == 42 && VmState.getStackPointer(state) == 0 } let testPopInverse = (): bool => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) // Push x, then pop into y Push.make("x").execute(state) @@ -56,7 +56,7 @@ let testPopInverse = (): bool => { pop.invert(state) // y=0, sp=1, stack[0]=42 - let y = Js.Dict.get(state, "y")->Option.getWithDefault(-1) + let y = Dict.get(state, "y")->Option.getWithDefault(-1) let sp = VmState.getStackPointer(state) let s0 = VmState.getStackSlot(state, 0) @@ -65,9 +65,9 @@ let testPopInverse = (): bool => { let testStackLIFO = (): bool => { let state = State.createState(~variables=["a", "b", "c", "r"], ~initialValue=0) - Js.Dict.set(state, "a", 10) - Js.Dict.set(state, "b", 20) - Js.Dict.set(state, "c", 30) + Dict.set(state, "a", 10) + Dict.set(state, "b", 20) + Dict.set(state, "c", 30) Push.make("a").execute(state) Push.make("b").execute(state) @@ -78,15 +78,15 @@ let testStackLIFO = (): bool => { else { // Pop in reverse order Pop.make("r").execute(state) - let r1 = Js.Dict.get(state, "r")->Option.getWithDefault(-1) - Js.Dict.set(state, "r", 0) + let r1 = Dict.get(state, "r")->Option.getWithDefault(-1) + Dict.set(state, "r", 0) Pop.make("r").execute(state) - let r2 = Js.Dict.get(state, "r")->Option.getWithDefault(-1) - Js.Dict.set(state, "r", 0) + let r2 = Dict.get(state, "r")->Option.getWithDefault(-1) + Dict.set(state, "r", 0) Pop.make("r").execute(state) - let r3 = Js.Dict.get(state, "r")->Option.getWithDefault(-1) + let r3 = Dict.get(state, "r")->Option.getWithDefault(-1) r1 == 30 && r2 == 20 && r3 == 10 } @@ -97,24 +97,24 @@ let testPushReversibility = (): bool => { Array.every(testCases, ((value, reg)) => { let state = State.createState(~variables=[reg], ~initialValue=0) - Js.Dict.set(state, reg, value) + Dict.set(state, reg, value) let instr = Push.make(reg) instr.execute(state) instr.invert(state) - Js.Dict.get(state, reg)->Option.getWithDefault(-1) == value && + Dict.get(state, reg)->Option.getWithDefault(-1) == value && VmState.getStackPointer(state) == 0 }) } let runAll = (): unit => { - Js.Console.log("[PUSH/POP Tests]") - - Js.Console.log(testPushBasic() ? " PUSH saves value, zeroes register" : " PUSH basic FAILED") - Js.Console.log(testPopBasic() ? " POP restores value from stack" : " POP basic FAILED") - Js.Console.log(testPushInverse() ? " PUSH inverse restores state" : " PUSH inverse FAILED") - Js.Console.log(testPopInverse() ? " POP inverse restores stack" : " POP inverse FAILED") - Js.Console.log(testStackLIFO() ? " Stack is LIFO" : " Stack LIFO FAILED") - Js.Console.log(testPushReversibility() ? " PUSH reversibility property" : " PUSH reversibility FAILED") + Console.log("[PUSH/POP Tests]") + + Console.log(testPushBasic() ? " PUSH saves value, zeroes register" : " PUSH basic FAILED") + Console.log(testPopBasic() ? " POP restores value from stack" : " POP basic FAILED") + Console.log(testPushInverse() ? " PUSH inverse restores state" : " PUSH inverse FAILED") + Console.log(testPopInverse() ? " POP inverse restores stack" : " POP inverse FAILED") + Console.log(testStackLIFO() ? " Stack is LIFO" : " Stack LIFO FAILED") + Console.log(testPushReversibility() ? " PUSH reversibility property" : " PUSH reversibility FAILED") } diff --git a/vm/tests/unit/instructions/rol_ror_test.res b/vm/tests/unit/instructions/rol_ror_test.res index b49f719..bc6c64c 100644 --- a/vm/tests/unit/instructions/rol_ror_test.res +++ b/vm/tests/unit/instructions/rol_ror_test.res @@ -13,7 +13,7 @@ let testRolReversibility = (): bool => { testCases->Belt.Array.every(((value, bits)) => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", value) + Dict.set(state, "x", value) let original = State.cloneState(state) let instr = Rol.make("x", ~bits, ()) @@ -41,7 +41,7 @@ let testRorReversibility = (): bool => { testCases->Belt.Array.every(((value, bits)) => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", value) + Dict.set(state, "x", value) let original = State.cloneState(state) let instr = Ror.make("x", ~bits, ()) @@ -63,7 +63,7 @@ let testRolRorInverse = (): bool => { testCases->Belt.Array.every(value => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", value) + Dict.set(state, "x", value) let original = State.cloneState(state) @@ -81,7 +81,7 @@ let testRolRorInverse = (): bool => { // Run all tests let runAllTests = (): bool => { - Js.Console.log("Testing ROL/ROR instructions...") + Console.log("Testing ROL/ROR instructions...") let testResults = [ ("ROL reversibility", testRolReversibility()), @@ -91,9 +91,9 @@ let runAllTests = (): bool => { testResults->Belt.Array.forEach(((name, passed)) => { if passed { - Js.Console.log(` ${name}`) + Console.log(` ${name}`) } else { - Js.Console.log(` ${name} FAILED`) + Console.log(` ${name} FAILED`) } }) diff --git a/vm/tests/unit/instructions/send_recv_test.res b/vm/tests/unit/instructions/send_recv_test.res index 2805d40..f236047 100644 --- a/vm/tests/unit/instructions/send_recv_test.res +++ b/vm/tests/unit/instructions/send_recv_test.res @@ -5,7 +5,7 @@ open Belt let testSendBasic = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) Send.make("firewall", "x").execute(state) @@ -17,13 +17,13 @@ let testSendBasic = (): bool => { let testSendMultiple = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 10) + Dict.set(state, "x", 10) Send.make("net", "x").execute(state) - Js.Dict.set(state, "x", 20) + Dict.set(state, "x", 20) Send.make("net", "x").execute(state) - Js.Dict.set(state, "x", 30) + Dict.set(state, "x", 30) Send.make("net", "x").execute(state) let ptr = VmState.getPortOutPointer(state, "net") @@ -36,7 +36,7 @@ let testSendMultiple = (): bool => { let testSendInverse = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) let instr = Send.make("display", "x") instr.execute(state) @@ -52,7 +52,7 @@ let testRecvBasic = (): bool => { Recv.make("sensor", "x").execute(state) - let x = Js.Dict.get(state, "x")->Option.getWithDefault(-1) + let x = Dict.get(state, "x")->Option.getWithDefault(-1) let ptr = VmState.getPortInPointer(state, "sensor") x == 42 && ptr == 1 @@ -60,25 +60,25 @@ let testRecvBasic = (): bool => { let testRecvAdditive = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 5) + Dict.set(state, "x", 5) VmState.setPortInSlot(state, "sensor", 0, 42) Recv.make("sensor", "x").execute(state) // Additive: 5 + 42 = 47 - Js.Dict.get(state, "x")->Option.getWithDefault(-1) == 47 + Dict.get(state, "x")->Option.getWithDefault(-1) == 47 } let testRecvInverse = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 5) + Dict.set(state, "x", 5) VmState.setPortInSlot(state, "sensor", 0, 42) let instr = Recv.make("sensor", "x") instr.execute(state) instr.invert(state) - let x = Js.Dict.get(state, "x")->Option.getWithDefault(-1) + let x = Dict.get(state, "x")->Option.getWithDefault(-1) let ptr = VmState.getPortInPointer(state, "sensor") x == 5 && ptr == 0 @@ -86,7 +86,7 @@ let testRecvInverse = (): bool => { let testSendRecvRoundTrip = (): bool => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) // Send x to port Send.make("pipe", "x").execute(state) @@ -98,13 +98,13 @@ let testSendRecvRoundTrip = (): bool => { // Recv into y Recv.make("pipe", "y").execute(state) - Js.Dict.get(state, "y")->Option.getWithDefault(-1) == 42 + Dict.get(state, "y")->Option.getWithDefault(-1) == 42 } let testMultiplePortsIndependent = (): bool => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", 10) - Js.Dict.set(state, "y", 20) + Dict.set(state, "x", 10) + Dict.set(state, "y", 20) Send.make("portA", "x").execute(state) Send.make("portB", "y").execute(state) @@ -119,7 +119,7 @@ let testMultiplePortsIndependent = (): bool => { let testIOReversibility = (): bool => { let state = State.createState(~variables=["x"], ~initialValue=0) - Js.Dict.set(state, "x", 42) + Dict.set(state, "x", 42) // SEND then undo let send = Send.make("out", "x") @@ -132,22 +132,22 @@ let testIOReversibility = (): bool => { let recv = Recv.make("in", "x") recv.execute(state) recv.invert(state) - let recvOk = Js.Dict.get(state, "x")->Option.getWithDefault(-1) == 42 + let recvOk = Dict.get(state, "x")->Option.getWithDefault(-1) == 42 && VmState.getPortInPointer(state, "in") == 0 sendOk && recvOk } let runAll = (): unit => { - Js.Console.log("[SEND/RECV Tests]") - - Js.Console.log(testSendBasic() ? " SEND to port" : " SEND basic FAILED") - Js.Console.log(testSendMultiple() ? " SEND multiple values" : " SEND multiple FAILED") - Js.Console.log(testSendInverse() ? " SEND inverse removes from buffer" : " SEND inverse FAILED") - Js.Console.log(testRecvBasic() ? " RECV from port" : " RECV basic FAILED") - Js.Console.log(testRecvAdditive() ? " RECV is additive" : " RECV additive FAILED") - Js.Console.log(testRecvInverse() ? " RECV inverse restores state" : " RECV inverse FAILED") - Js.Console.log(testSendRecvRoundTrip() ? " SEND/RECV round trip" : " Round trip FAILED") - Js.Console.log(testMultiplePortsIndependent() ? " Multiple ports are independent" : " Multiple ports FAILED") - Js.Console.log(testIOReversibility() ? " I/O reversibility property" : " I/O reversibility FAILED") + Console.log("[SEND/RECV Tests]") + + Console.log(testSendBasic() ? " SEND to port" : " SEND basic FAILED") + Console.log(testSendMultiple() ? " SEND multiple values" : " SEND multiple FAILED") + Console.log(testSendInverse() ? " SEND inverse removes from buffer" : " SEND inverse FAILED") + Console.log(testRecvBasic() ? " RECV from port" : " RECV basic FAILED") + Console.log(testRecvAdditive() ? " RECV is additive" : " RECV additive FAILED") + Console.log(testRecvInverse() ? " RECV inverse restores state" : " RECV inverse FAILED") + Console.log(testSendRecvRoundTrip() ? " SEND/RECV round trip" : " Round trip FAILED") + Console.log(testMultiplePortsIndependent() ? " Multiple ports are independent" : " Multiple ports FAILED") + Console.log(testIOReversibility() ? " I/O reversibility property" : " I/O reversibility FAILED") } diff --git a/vm/tests/unit/instructions/swap_test.res b/vm/tests/unit/instructions/swap_test.res index 5cfb5d6..48b8ee6 100644 --- a/vm/tests/unit/instructions/swap_test.res +++ b/vm/tests/unit/instructions/swap_test.res @@ -5,39 +5,39 @@ open Belt let testSwapBasic = (): bool => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 10) - Js.Dict.set(state, "b", 20) + Dict.set(state, "a", 10) + Dict.set(state, "b", 20) let instr = Swap.make("a", "b") instr.execute(state) - Js.Dict.get(state, "a")->Option.getWithDefault(0) == 20 && - Js.Dict.get(state, "b")->Option.getWithDefault(0) == 10 + Dict.get(state, "a")->Option.getWithDefault(0) == 20 && + Dict.get(state, "b")->Option.getWithDefault(0) == 10 } let testSwapSelfInverse = (): bool => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 10) - Js.Dict.set(state, "b", 20) + Dict.set(state, "a", 10) + Dict.set(state, "b", 20) let instr = Swap.make("a", "b") instr.execute(state) instr.execute(state) // Swap twice = identity - Js.Dict.get(state, "a")->Option.getWithDefault(0) == 10 && - Js.Dict.get(state, "b")->Option.getWithDefault(0) == 20 + Dict.get(state, "a")->Option.getWithDefault(0) == 10 && + Dict.get(state, "b")->Option.getWithDefault(0) == 20 } let testSwapNegative = (): bool => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", -5) - Js.Dict.set(state, "b", 15) + Dict.set(state, "a", -5) + Dict.set(state, "b", 15) let instr = Swap.make("a", "b") instr.execute(state) - Js.Dict.get(state, "a")->Option.getWithDefault(0) == 15 && - Js.Dict.get(state, "b")->Option.getWithDefault(0) == -5 + Dict.get(state, "a")->Option.getWithDefault(0) == 15 && + Dict.get(state, "b")->Option.getWithDefault(0) == -5 } let testSwapReversibility = (): bool => { @@ -45,8 +45,8 @@ let testSwapReversibility = (): bool => { Array.every(testCases, ((a, b)) => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", a) - Js.Dict.set(state, "y", b) + Dict.set(state, "x", a) + Dict.set(state, "y", b) let original = State.cloneState(state) let instr = Swap.make("x", "y") @@ -59,10 +59,10 @@ let testSwapReversibility = (): bool => { } let runAll = (): unit => { - Js.Console.log("[SWAP Tests]") + Console.log("[SWAP Tests]") - Js.Console.log(testSwapBasic() ? " Basic swap" : " Basic swap FAILED") - Js.Console.log(testSwapSelfInverse() ? " Self-inverse property" : " Self-inverse FAILED") - Js.Console.log(testSwapNegative() ? " Negative numbers" : " Negative numbers FAILED") - Js.Console.log(testSwapReversibility() ? " Reversibility property" : " Reversibility FAILED") + Console.log(testSwapBasic() ? " Basic swap" : " Basic swap FAILED") + Console.log(testSwapSelfInverse() ? " Self-inverse property" : " Self-inverse FAILED") + Console.log(testSwapNegative() ? " Negative numbers" : " Negative numbers FAILED") + Console.log(testSwapReversibility() ? " Reversibility property" : " Reversibility FAILED") } diff --git a/vm/tests/unit/instructions/xor_test.res b/vm/tests/unit/instructions/xor_test.res index 6c2cf25..f93a6e7 100644 --- a/vm/tests/unit/instructions/xor_test.res +++ b/vm/tests/unit/instructions/xor_test.res @@ -5,38 +5,38 @@ open Belt let testXorBasic = (): bool => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 0b1100) // 12 in binary - Js.Dict.set(state, "b", 0b1010) // 10 in binary + Dict.set(state, "a", 0b1100) // 12 in binary + Dict.set(state, "b", 0b1010) // 10 in binary let instr = Xor.make("a", "b") instr.execute(state) // 1100 XOR 1010 = 0110 = 6 - Js.Dict.get(state, "a")->Option.getWithDefault(0) == 0b0110 + Dict.get(state, "a")->Option.getWithDefault(0) == 0b0110 } let testXorSelfInverse = (): bool => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 42) - Js.Dict.set(state, "b", 17) + Dict.set(state, "a", 42) + Dict.set(state, "b", 17) let instr = Xor.make("a", "b") instr.execute(state) instr.execute(state) // XOR twice with same value = identity - Js.Dict.get(state, "a")->Option.getWithDefault(0) == 42 + Dict.get(state, "a")->Option.getWithDefault(0) == 42 } let testXorZero = (): bool => { let state = State.createState(~variables=["a", "b"], ~initialValue=0) - Js.Dict.set(state, "a", 42) - Js.Dict.set(state, "b", 0) + Dict.set(state, "a", 42) + Dict.set(state, "b", 0) let instr = Xor.make("a", "b") instr.execute(state) // n XOR 0 = n - Js.Dict.get(state, "a")->Option.getWithDefault(0) == 42 + Dict.get(state, "a")->Option.getWithDefault(0) == 42 } let testXorReversibility = (): bool => { @@ -44,8 +44,8 @@ let testXorReversibility = (): bool => { Array.every(testCases, ((a, b)) => { let state = State.createState(~variables=["x", "y"], ~initialValue=0) - Js.Dict.set(state, "x", a) - Js.Dict.set(state, "y", b) + Dict.set(state, "x", a) + Dict.set(state, "y", b) let original = State.cloneState(state) let instr = Xor.make("x", "y") @@ -58,10 +58,10 @@ let testXorReversibility = (): bool => { } let runAll = (): unit => { - Js.Console.log("[XOR Tests]") + Console.log("[XOR Tests]") - Js.Console.log(testXorBasic() ? " Basic XOR" : " Basic XOR FAILED") - Js.Console.log(testXorSelfInverse() ? " Self-inverse property" : " Self-inverse FAILED") - Js.Console.log(testXorZero() ? " XOR with zero" : " XOR with zero FAILED") - Js.Console.log(testXorReversibility() ? " Reversibility property" : " Reversibility FAILED") + Console.log(testXorBasic() ? " Basic XOR" : " Basic XOR FAILED") + Console.log(testXorSelfInverse() ? " Self-inverse property" : " Self-inverse FAILED") + Console.log(testXorZero() ? " XOR with zero" : " XOR with zero FAILED") + Console.log(testXorReversibility() ? " Reversibility property" : " Reversibility FAILED") } diff --git a/vm/tests/unit/test_runner.res b/vm/tests/unit/test_runner.res index 34571e3..f77abff 100644 --- a/vm/tests/unit/test_runner.res +++ b/vm/tests/unit/test_runner.res @@ -5,20 +5,20 @@ // Note: These would need to be compiled first let runAllTests = (): unit => { - Js.Console.log("") - Js.Console.log(" Idaptik VM Test Suite") - Js.Console.log("") - Js.Console.log("") + Console.log("") + Console.log(" Idaptik VM Test Suite") + Console.log("") + Console.log("") // Instruction tests would go here when modules are properly set up // AddTest.runAll() // SwapTest.runAll() // XorTest.runAll() - Js.Console.log("") - Js.Console.log("") - Js.Console.log(" Test Summary") - Js.Console.log("") + Console.log("") + Console.log("") + Console.log(" Test Summary") + Console.log("") } // Run tests when module is loaded