From f05d405bace21fd094f84766514aa8a8c0f485bb Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 8 May 2026 17:03:32 +0000 Subject: [PATCH] ocaml: phase 6 Stack + Queue modules (+5 tests, 430 total) Stack: ref-holding-list LIFO. push/pop/top/length/is_empty/clear. Queue: two-list (front, back) amortised O(1) queue. push/pop/length/ is_empty/clear. Both in OCaml syntax in runtime.sx. --- lib/ocaml/runtime.sx | 42 ++++++++++++++++++++++++++++++++++++++++++ lib/ocaml/test.sh | 19 +++++++++++++++++++ plans/ocaml-on-sx.md | 10 ++++++++++ 3 files changed, 71 insertions(+) diff --git a/lib/ocaml/runtime.sx b/lib/ocaml/runtime.sx index 142eb3f2..29177685 100644 --- a/lib/ocaml/runtime.sx +++ b/lib/ocaml/runtime.sx @@ -366,6 +366,48 @@ let printf fmt = print_string fmt end ;; + module Stack = struct + let create () = ref [] + let push x s = s := x :: !s + let pop s = + match !s with + | [] -> failwith \"Stack.pop: empty\" + | h :: t -> s := t ; h + let top s = + match !s with + | [] -> failwith \"Stack.top: empty\" + | h :: _ -> h + let is_empty s = !s = [] + let length s = List.length !s + let clear s = s := [] + end ;; + + module Queue = struct + (* Simple two-list amortized queue: (front, back). pop drains + front; refill from rev back when front is empty. *) + let create () = ref ([], []) + let push x q = + let p = !q in + q := (List.append [] (match p with (f, b) -> f), x :: (match p with (_, b) -> b)) + let length q = + let p = !q in + let f = match p with (f, _) -> f in + let b = match p with (_, b) -> b in + List.length f + List.length b + let is_empty q = length q = 0 + let pop q = + let p = !q in + let f = match p with (f, _) -> f in + let b = match p with (_, b) -> b in + match f with + | h :: t -> q := (t, b) ; h + | [] -> + (match List.rev b with + | [] -> failwith \"Queue.pop: empty\" + | h :: t -> q := (t, []) ; h) + let clear q = q := ([], []) + end ;; + module Buffer = struct let create _ = ref [] let add_string b s = b := s :: !b diff --git a/lib/ocaml/test.sh b/lib/ocaml/test.sh index 23206812..6d467482 100755 --- a/lib/ocaml/test.sh +++ b/lib/ocaml/test.sh @@ -1052,6 +1052,18 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 3502) (eval "(ocaml-run-program \"let b = Buffer.create 16 ;; Buffer.add_string b \\\"x\\\" ;; Buffer.clear b ;; Buffer.contents b\")") +;; ── Stack + Queue modules ───────────────────────────────────── +(epoch 3600) +(eval "(ocaml-run-program \"let s = Stack.create () ;; Stack.push 1 s ;; Stack.push 2 s ;; Stack.push 3 s ;; Stack.pop s\")") +(epoch 3601) +(eval "(ocaml-run-program \"let s = Stack.create () ;; Stack.push 1 s ;; Stack.push 2 s ;; Stack.length s\")") +(epoch 3602) +(eval "(ocaml-run-program \"let s = Stack.create () ;; Stack.push 1 s ;; Stack.top s\")") +(epoch 3603) +(eval "(ocaml-run-program \"let q = Queue.create () ;; Queue.push 1 q ;; Queue.push 2 q ;; Queue.push 3 q ;; Queue.pop q\")") +(epoch 3604) +(eval "(ocaml-run-program \"let q = Queue.create () ;; Queue.push 1 q ;; Queue.push 2 q ;; Queue.length q\")") + EPOCHS OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1665,6 +1677,13 @@ check 3500 "Buffer concat 'Hello, World'" '"Hello, World"' check 3501 "Buffer.length 3" '3' check 3502 "Buffer.clear empties" '""' +# ── Stack + Queue ────────────────────────────────────────────── +check 3600 "Stack.pop LIFO" '3' +check 3601 "Stack.length" '2' +check 3602 "Stack.top" '1' +check 3603 "Queue.pop FIFO" '1' +check 3604 "Queue.length" '2' + 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 86260fea..dd4698bf 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -267,6 +267,11 @@ SX CEK evaluator (both JS and OCaml hosts) - [~] `Buffer`: `create`, `add_string`, `add_char`, `contents`, `length`, `clear`, `reset`. Backed by a ref holding a list of strings; reverse + `String.concat` on `contents`. Mostly-OCaml impl. +- [~] `Stack`: `create`, `push`, `pop`, `top`, `is_empty`, `length`, + `clear`. Backed by a ref-holding-list (LIFO). +- [~] `Queue`: `create`, `push`, `pop`, `is_empty`, `length`, `clear`. + Backed by a `(front, back)` tuple-of-lists pair (amortised O(1) + enqueue/dequeue via list reversal). - [~] `Sys`: `os_type` (`"SX"`), `word_size`, `max_array_length`, `max_string_length`, `executable_name`, `big_endian`, `unix`, `win32`, `cygwin`. Constants only; `argv`/`getenv_opt`/`command` @@ -394,6 +399,11 @@ _Newest first._ recognise `!` as the prefix-deref of an application argument, so `String.concat "" (List.rev !b)` parses as `(... (deref b))`. Buffer uses a ref holding a string list; contents reverses and concats. +- 2026-05-08 Phase 6 — `Stack` and `Queue` modules in OCaml (+5 tests, + 430 total). Stack: ref-holding-list LIFO with push/pop/top/length/ + is_empty/clear. Queue: amortised O(1) two-list `(front, back)` queue + with push/pop/length/is_empty/clear. Both written entirely in OCaml + via lib/ocaml/runtime.sx. - 2026-05-08 Phase 5.1+1+2 — calc.ml baseline (11/11 pass) — a recursive-descent calculator parsing `(1 + 2) * 3 + 4` to 13. Two parser bugs fixed along the way: parse-let now handles inline