diff --git a/lib/ocaml/eval.sx b/lib/ocaml/eval.sx index 778e0d16..58c323dd 100644 --- a/lib/ocaml/eval.sx +++ b/lib/ocaml/eval.sx @@ -782,6 +782,55 @@ (begin (set! env (ocaml-env-extend env name v)) (set! result (merge result (dict name v)))))))))) + ((= tag "def-mut") + ;; let x = ... and y = ... — sequential top-level binds. + (let ((bs (nth decl 1))) + (begin + (define run-one + (fn (b) + (let ((nm (nth b 0)) (ps (nth b 1)) (rh (nth b 2))) + (let ((v (if (= (len ps) 0) + (ocaml-eval rh env) + (ocaml-make-curried ps rh env)))) + (begin + (set! env (ocaml-env-extend env nm v)) + (set! result (merge result (dict nm v)))))))) + (define loop + (fn (xs) + (when (not (= xs (list))) + (begin (run-one (first xs)) (loop (rest xs)))))) + (loop bs)))) + ((= tag "def-rec-mut") + ;; let rec f = ... and g = ... — mutual recursion. + (let ((bs (nth decl 1)) (cells (list))) + (begin + (define alloc + (fn (xs) + (when (not (= xs (list))) + (let ((b (first xs))) + (let ((c (list nil)) (nm (nth b 0))) + (begin + (append! cells c) + (set! env (ocaml-env-extend env nm + (fn (a) ((nth c 0) a)))) + (alloc (rest xs)))))))) + (alloc bs) + (let ((idx 0)) + (begin + (define fill + (fn (xs) + (when (not (= xs (list))) + (let ((b (first xs))) + (let ((nm (nth b 0)) (ps (nth b 1)) (rh (nth b 2))) + (let ((v (if (= (len ps) 0) + (ocaml-eval rh env) + (ocaml-make-curried ps rh env)))) + (begin + (set-nth! (nth cells idx) 0 v) + (set! result (merge result (dict nm v))) + (set! idx (+ idx 1)) + (fill (rest xs))))))))) + (fill bs)))))) ((= tag "expr") (ocaml-eval (nth decl 1) env)) ((= tag "module-def") diff --git a/lib/ocaml/test.sh b/lib/ocaml/test.sh index 3b5edbef..8499f101 100755 --- a/lib/ocaml/test.sh +++ b/lib/ocaml/test.sh @@ -1172,6 +1172,12 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 4703) (eval "(ocaml-run \"((1 + 2) : int) * 3\")") +;; ── Module body: def-mut / def-rec-mut ───────────────────────── +(epoch 4800) +(eval "(ocaml-run-program \"module M = struct let rec a n = if n = 0 then 0 else b (n - 1) and b n = if n = 0 then 1 else a (n - 1) end ;; M.a 5\")") +(epoch 4801) +(eval "(ocaml-run-program \"module M = struct let x = 1 and y = 2 end ;; M.x + M.y\")") + EPOCHS OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1857,6 +1863,10 @@ check 4701 "let f (x : int) : int" '42' check 4702 "(5 : int)" '5' check 4703 "((1+2) : int) * 3" '9' +# ── Module body: def-mut / def-rec-mut ───────────────────────── +check 4800 "module rec a/b mutual" '1' +check 4801 "module x and y" '3' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL OCaml-on-SX tests passed" diff --git a/plans/ocaml-on-sx.md b/plans/ocaml-on-sx.md index db6ce1b4..0ebacfc1 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -407,6 +407,10 @@ _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-08 Phase 4 — `:def-mut` / `:def-rec-mut` inside module + bodies (+2 tests, 475 total). `ocaml-eval-module` now handles + multi-binding `let .. and ..` decls. `module M = struct let rec a n = + ... and b n = ... end` works. - 2026-05-08 Phase 5.1 — bfs.ml baseline (20/20 pass). Graph breadth-first search using Queue + Hashtbl visited-set + List.assoc_opt + List.iter. Returns the count of reachable nodes (6 for the demo