diff --git a/lib/ocaml/baseline/bfs_grid.ml b/lib/ocaml/baseline/bfs_grid.ml new file mode 100644 index 00000000..240f42fa --- /dev/null +++ b/lib/ocaml/baseline/bfs_grid.ml @@ -0,0 +1,42 @@ +let h = 5 +let w = 5 + +let grid = [| + [| 0; 0; 1; 0; 0 |]; + [| 1; 0; 1; 0; 1 |]; + [| 0; 0; 0; 0; 0 |]; + [| 0; 1; 1; 1; 0 |]; + [| 0; 0; 0; 0; 0 |] +|] + +let step dist q r c nr nc = + if nr >= 0 && nr < h && nc >= 0 && nc < w + && grid.(nr).(nc) = 0 && dist.(nr).(nc) = -1 then begin + dist.(nr).(nc) <- dist.(r).(c) + 1; + Queue.push (nr * 10 + nc) q + end + +let bfs sr sc tr tc = + let dist = Array.init h (fun _ -> Array.make w (-1)) in + let q = Queue.create () in + dist.(sr).(sc) <- 0; + Queue.push (sr * 10 + sc) q; + let go = ref true in + while !go do + if Queue.is_empty q then go := false + else if dist.(tr).(tc) <> -1 then go := false + else begin + let rc = Queue.pop q in + let r = rc / 10 in + let c = rc mod 10 in + step dist q r c (r - 1) c; + step dist q r c (r + 1) c; + step dist q r c r (c - 1); + step dist q r c r (c + 1) + end + done; + dist.(tr).(tc) + +;; + +bfs 0 0 4 4 diff --git a/lib/ocaml/baseline/expected.json b/lib/ocaml/baseline/expected.json index 42e161f2..e09b1de9 100644 --- a/lib/ocaml/baseline/expected.json +++ b/lib/ocaml/baseline/expected.json @@ -16,6 +16,7 @@ "balance.ml": 3, "base_n.ml": 17, "bfs.ml": 6, + "bfs_grid.ml": 8, "btree.ml": 39, "brainfuck.ml": 75, "bsearch.ml": 7, diff --git a/lib/ocaml/eval.sx b/lib/ocaml/eval.sx index 71b2eabc..0de8c408 100644 --- a/lib/ocaml/eval.sx +++ b/lib/ocaml/eval.sx @@ -657,6 +657,14 @@ (error (str "ocaml-eval: <- expects a field-access lhs, got " (ocaml-tag-of lhs-ast))))))) + ;; && and || short-circuit — must NOT evaluate rhs when + ;; lhs already decides. Mirrors real OCaml semantics. + ((= op "&&") + (let ((lhs (ocaml-eval (nth ast 2) env))) + (if lhs (ocaml-eval (nth ast 3) env) false))) + ((= op "||") + (let ((lhs (ocaml-eval (nth ast 2) env))) + (if lhs true (ocaml-eval (nth ast 3) env)))) (else (ocaml-eval-op op (ocaml-eval (nth ast 2) env) diff --git a/plans/ocaml-on-sx.md b/plans/ocaml-on-sx.md index c881517c..f40cd5f2 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -407,6 +407,19 @@ _Newest first._ binary search tree (`type 'a tree = Leaf | Node of 'a * 'a tree * 'a tree`) with insert + in-order traversal. Tests parametric ADT, recursive match, List.append, List.fold_left. +- 2026-05-10 Phase 5.1 — `&&` / `||` short-circuit fix + bfs_grid.ml + baseline (BFS shortest path on 5×5 grid with walls, dist 8). The + evaluator's `:op` handler was evaluating BOTH sides of `&&`/`||` + before dispatching, mismatching OCaml's left-to-right + short-circuit semantics. This was invisible in earlier baselines + because their rhs was always safe regardless of lhs, but a guard + like `if nr >= 0 && grid.(nr).(nc) = 0 then …` crashes with + "nth: list/string and number" when nr = -1 because `grid.(-1)` + still gets evaluated. Fix: dispatch `&&` and `||` specially in + `:op` to evaluate lhs first and only evaluate rhs if needed. + Bug latent since baseline 1; surfaced by bfs_grid with bounds- + checked grid access. bfs_grid optimal path 0→1→1→2→2→3→3→3→4 of + weighted length 8. 155 baseline programs total. - 2026-05-10 Phase 5.1 — tarjan_scc.ml baseline (Tarjan's strongly-connected components on 8-node digraph → 4 SCCs). Graph: 0→1→2→0 (cycle) plus 2→3, 3→4, 4→5→6→4 (cycle), 4→7.