diff --git a/lib/ocaml/runtime.sx b/lib/ocaml/runtime.sx index d734a29d..e39a43b3 100644 --- a/lib/ocaml/runtime.sx +++ b/lib/ocaml/runtime.sx @@ -1030,6 +1030,48 @@ let remove t k = _hashtbl_remove t k let reset t = _hashtbl_clear t let clear t = _hashtbl_clear t + + let copy t = + let t' = _hashtbl_create 8 in + List.iter + (fun (k, v) -> _hashtbl_add t' k v) + (_hashtbl_to_list t); + t' + end ;; + + module Either = struct + let left x = Left x + let right x = Right x + + let is_left e = match e with Left _ -> true | Right _ -> false + let is_right e = match e with Left _ -> false | Right _ -> true + + let find_left e = + match e with Left x -> Some x | Right _ -> None + let find_right e = + match e with Left _ -> None | Right x -> Some x + + let map_left f e = + match e with Left x -> Left (f x) | Right x -> Right x + let map_right f e = + match e with Left x -> Left x | Right x -> Right (f x) + + let fold lf rf e = + match e with Left x -> lf x | Right x -> rf x + + let equal eq_l eq_r a b = + match a with + | Left x -> + (match b with Left y -> eq_l x y | Right _ -> false) + | Right x -> + (match b with Left _ -> false | Right y -> eq_r x y) + + let compare cmp_l cmp_r a b = + match a with + | Left x -> + (match b with Left y -> cmp_l x y | Right _ -> -1) + | Right x -> + (match b with Left _ -> 1 | Right y -> cmp_r x y) end ;; module Map = struct diff --git a/lib/ocaml/test.sh b/lib/ocaml/test.sh index 02fcfcc5..5bd5989a 100755 --- a/lib/ocaml/test.sh +++ b/lib/ocaml/test.sh @@ -1494,6 +1494,16 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 5245) (eval "(ocaml-run \"Float.zero +. Float.one\")") +;; ── Either module + Hashtbl.copy ───────────────────────────── +(epoch 5250) +(eval "(ocaml-run \"Either.is_left (Left 5)\")") +(epoch 5251) +(eval "(ocaml-run \"Either.fold (fun x -> x + 100) (fun x -> x * 10) (Left 7)\")") +(epoch 5252) +(eval "(ocaml-run \"Either.fold (fun x -> x + 100) (fun x -> x * 10) (Right 7)\")") +(epoch 5253) +(eval "(ocaml-run \"let t = Hashtbl.create 4 in Hashtbl.add t \\\"a\\\" 1; let t2 = Hashtbl.copy t in Hashtbl.add t2 \\\"b\\\" 2; Hashtbl.length t + Hashtbl.length t2\")") + EPOCHS OUTPUT=$(timeout 360 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -2376,6 +2386,12 @@ check 5243 "Float.max 3.14 2.71" '3.14' check 5244 "Float.equal 1.5 1.5" 'true' check 5245 "Float.zero +. Float.one" '1' +# ── Either module + Hashtbl.copy ──────────────────────────────── +check 5250 "Either.is_left Left" 'true' +check 5251 "Either.fold Left 7+100" '107' +check 5252 "Either.fold Right 7*10" '70' +check 5253 "Hashtbl.copy independent" '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 840fbdc9..51911fb1 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -407,6 +407,13 @@ _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-09 Phase 6 — Either module + Hashtbl.copy (+4 tests, 602 + total). Either: left, right, is_left, is_right, find_left, + find_right, map_left, map_right, fold, equal, compare. Constructors + are bare `Left x` / `Right x` (per OCaml 4.12+). Hashtbl.copy + builds a fresh cell, walks `_hashtbl_to_list`, and re-adds; mutating + one copy doesn't touch the other (verified by `Hashtbl.length t + + Hashtbl.length t2 = 3` after a fork-and-add). - 2026-05-09 Phase 5.1 — json_pretty.ml baseline (recursive ADT serialization). Defines a JSON-like ADT (JNull / JBool / JInt / JStr / JList) and recursively pretty-prints to a string, then