diff --git a/lib/ocaml/runtime.sx b/lib/ocaml/runtime.sx index ce31974f..5afd020f 100644 --- a/lib/ocaml/runtime.sx +++ b/lib/ocaml/runtime.sx @@ -389,6 +389,90 @@ | Some v -> v let mem t k = _hashtbl_mem t k let length t = _hashtbl_length t + end ;; + + module Map = struct + module Make (Ord) = struct + let empty = [] + + let rec add k v m = + match m with + | [] -> [(k, v)] + | (k2, v2) :: rest -> + begin + let c = Ord.compare k k2 in + if c = 0 then (k, v) :: rest + else if c < 0 then (k, v) :: m + else (k2, v2) :: add k v rest + end + + let rec find_opt k m = + match m with + | [] -> None + | (k2, v) :: rest -> + if Ord.compare k k2 = 0 then Some v + else find_opt k rest + + let find k m = + match find_opt k m with + | None -> failwith \"Map.find: not found\" + | Some v -> v + + let rec mem k m = + match m with + | [] -> false + | (k2, _) :: rest -> if Ord.compare k k2 = 0 then true else mem k rest + + let rec remove k m = + match m with + | [] -> [] + | (k2, v) :: rest -> + if Ord.compare k k2 = 0 then rest else (k2, v) :: remove k rest + + let rec bindings m = m + + let rec cardinal m = + match m with + | [] -> 0 + | _ :: t -> 1 + cardinal t + end + end ;; + + module Set = struct + module Make (Ord) = struct + let empty = [] + + let rec mem x s = + match s with + | [] -> false + | h :: t -> + let c = Ord.compare x h in + if c = 0 then true + else if c < 0 then false + else mem x t + + let rec add x s = + match s with + | [] -> [x] + | h :: t -> + let c = Ord.compare x h in + if c = 0 then s + else if c < 0 then x :: s + else h :: add x t + + let rec remove x s = + match s with + | [] -> [] + | h :: t -> + if Ord.compare x h = 0 then t else h :: remove x t + + let rec elements s = s + + let rec cardinal s = + match s with + | [] -> 0 + | _ :: t -> 1 + cardinal t + end end") (define ocaml-stdlib-loaded false) diff --git a/lib/ocaml/test.sh b/lib/ocaml/test.sh index a9eec931..0c2fdd5d 100755 --- a/lib/ocaml/test.sh +++ b/lib/ocaml/test.sh @@ -1024,6 +1024,16 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 3204) (eval "(ocaml-run \"Sys.executable_name\")") +;; ── Map.Make / Set.Make functors ────────────────────────────── +(epoch 3300) +(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntMap = Map.Make(IntOrd) ;; let m = IntMap.add 1 \\\"a\\\" IntMap.empty ;; let m = IntMap.add 2 \\\"b\\\" m ;; IntMap.find 1 m\")") +(epoch 3301) +(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntMap = Map.Make(IntOrd) ;; IntMap.cardinal (IntMap.add 1 \\\"a\\\" (IntMap.add 2 \\\"b\\\" IntMap.empty))\")") +(epoch 3302) +(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntSet = Set.Make(IntOrd) ;; IntSet.elements (IntSet.add 3 (IntSet.add 1 (IntSet.add 2 IntSet.empty)))\")") +(epoch 3303) +(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntSet = Set.Make(IntOrd) ;; IntSet.mem 2 (IntSet.add 3 (IntSet.add 1 (IntSet.add 2 IntSet.empty)))\")") + EPOCHS OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1620,6 +1630,12 @@ check 3202 "Sys.unix" 'true' check 3203 "Sys.win32" 'false' check 3204 "Sys.executable_name" '"ocaml-on-sx"' +# ── Map.Make / Set.Make ──────────────────────────────────────── +check 3300 "Map.find via functor" '"a"' +check 3301 "Map.cardinal" '2' +check 3302 "Set.elements sorted" '(1 2 3)' +check 3303 "Set.mem" 'true' + 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 bdec70c7..d009b9d7 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -286,8 +286,11 @@ SX CEK evaluator (both JS and OCaml hosts) - [ ] `Int`/`Float`: arithmetic, `to_string`, `of_string_opt`, `min_int`, `max_int`. - [ ] `Hashtbl`: `create`, `add`, `replace`, `find`, `find_opt`, `remove`, `mem`, `iter`, `fold`, `length` — backed by SX mutable dict. -- [ ] `Map.Make` functor — balanced BST backed by SX sorted dict. -- [ ] `Set.Make` functor. +- [x] `Map.Make` functor — sorted association list backed + (insert/find/remove/mem/cardinal/bindings); not a balanced tree + but linear with parametric `Ord` ordering. +- [x] `Set.Make` functor — sorted list backed + (add/mem/remove/elements/cardinal). - [ ] `Printf`: `sprintf`, `printf`, `eprintf` — format strings via `(format ...)`. - [ ] `Sys`: `argv`, `getenv_opt`, `getcwd` — via `perform` IO. - [ ] Scoreboard runner: `lib/ocaml/conformance.sh` + `scoreboard.json`. @@ -383,6 +386,14 @@ the "mother tongue" closure: OCaml → SX → OCaml. This means: _Newest first._ +- 2026-05-08 Phase 6 — `Map.Make` / `Set.Make` functors written in + OCaml (+4 tests, 418 total). Sorted association list / sorted list + backed (linear ops, but correct). Both take an `Ord` module supplying + `compare`. Tested: `module IntMap = Map.Make(IntOrd) ;; IntMap.find + …` and `IntSet.elements (IntSet.add 3 (IntSet.add 1 …))` returning + `[1; 2; 3]`. Strong substrate-validation for the functor system — + Map.Make is a non-trivial functor implemented entirely on top of the + OCaml-on-SX evaluator. - 2026-05-08 Phase 6 — `Sys` module constants (+5 tests, 414 total). os_type, word_size, max_array_length, max_string_length, executable_name, big_endian, unix, win32, cygwin. Constants-only