diff --git a/lib/ocaml/parser.sx b/lib/ocaml/parser.sx index 23007963..2d1757f9 100644 --- a/lib/ocaml/parser.sx +++ b/lib/ocaml/parser.sx @@ -419,6 +419,23 @@ (advance-tok!) (cond ((at-op? ")") (begin (advance-tok!) (list :unit))) + ;; (op) — operator section: build (fun a b -> a op b). + ;; Recognises ops with precedence > 0 (i.e. binops), + ;; followed immediately by `)`. + ((and (or (= (ocaml-tok-type (peek-tok)) "op") + (= (ocaml-tok-type (peek-tok)) "keyword")) + (not (= (ocaml-binop-prec + (ocaml-tok-value (peek-tok))) 0)) + (let ((t1 (nth tokens (+ idx 1)))) + (and (= (ocaml-tok-type t1) "op") + (= (ocaml-tok-value t1) ")")))) + (let ((opv (ocaml-tok-value (peek-tok)))) + (begin + (advance-tok!) + (advance-tok!) + (list :fun (list "a" "b") + (list :op opv (list :var "a") + (list :var "b")))))) (else (let ((e (parse-expr))) diff --git a/lib/ocaml/runtime.sx b/lib/ocaml/runtime.sx index 58fea697..546afabb 100644 --- a/lib/ocaml/runtime.sx +++ b/lib/ocaml/runtime.sx @@ -525,6 +525,49 @@ let force lz = _lazy_force lz end ;; + module Array = struct + (* Backed by a ref-of-list. Mutating set! replaces the underlying + list with a new one in which one cell is changed. O(n) set, but + good enough for short arrays in baseline programs. *) + let make n v = + let rec build i acc = + if i = 0 then acc else build (i - 1) (v :: acc) + in + ref (build n []) + + let length a = List.length !a + let get a i = List.nth !a i + + let set a i v = + let rec replace lst k = + match lst with + | [] -> [] + | h :: t -> if k = 0 then v :: t else h :: replace t (k - 1) + in + a := replace !a i + + let init n f = + let rec build i acc = + if i = 0 then acc else build (i - 1) (f (i - 1) :: acc) + in + ref (build n []) + + let iter f a = List.iter f !a + let iteri f a = List.iteri f !a + let map f a = ref (List.map f !a) + let mapi f a = ref (List.mapi f !a) + let fold_left f init a = List.fold_left f init !a + let to_list a = !a + let of_list xs = ref xs + let copy a = ref !a + let blit src si dst di n = + for k = 0 to n - 1 do + set dst (di + k) (get src (si + k)) + done + let fill a pos n v = + for k = 0 to n - 1 do set a (pos + k) v done + end ;; + module Stack = struct let create () = ref [] let push x s = s := x :: !s diff --git a/lib/ocaml/test.sh b/lib/ocaml/test.sh index d3a1fe03..3cdd55a5 100755 --- a/lib/ocaml/test.sh +++ b/lib/ocaml/test.sh @@ -1262,6 +1262,22 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 5002) (eval "(ocaml-run \"List.map (fun x -> match x with `On -> 1 | `Off -> 0) [`On; `Off; `On]\")") +;; ── Array module ───────────────────────────────────────────── +(epoch 5010) +(eval "(ocaml-run \"let a = Array.make 5 7 in Array.set a 2 99; Array.fold_left (+) 0 a\")") +(epoch 5011) +(eval "(ocaml-run \"let a = Array.init 4 (fun i -> i * i) in Array.fold_left (+) 0 a\")") +(epoch 5012) +(eval "(ocaml-run \"let a = Array.make 3 0 in for i = 0 to 2 do Array.set a i (i + 1) done; Array.length a + Array.get a 0 + Array.get a 1 + Array.get a 2\")") + +;; ── (op) operator sections ─────────────────────────────────── +(epoch 5020) +(eval "(ocaml-run \"List.fold_left (+) 0 [1;2;3;4;5]\")") +(epoch 5021) +(eval "(ocaml-run \"let f = ( * ) in f 6 7\")") +(epoch 5022) +(eval "(ocaml-run \"List.map ((-) 10) [1;2;3]\")") + EPOCHS OUTPUT=$(timeout 360 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -2004,6 +2020,16 @@ check 5000 'match polyvar Foo / Bar' '1' check 5001 'match polyvar Pair (a, b)' '12' check 5002 'List.map polyvar On / Off' '(1 0 1)' +# ── Array module ──────────────────────────────────────────────── +check 5010 "Array.make + set 2 99" '127' +check 5011 "Array.init 4 i*i" '14' +check 5012 "Array.make 3 + for + length" '9' + +# ── (op) operator sections ────────────────────────────────────── +check 5020 "fold_left (+) sum" '15' +check 5021 "let f = (*) in f 6 7" '42' +check 5022 "List.map ((-) 10) [1;2;3]" '(9 8 7)' + 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 29cfc0cf..52345331 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -407,6 +407,16 @@ _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 — Array module (ref-of-list backing) + (op) + operator sections (+6 tests, 512 total). Array implements + make/length/get/set/init/iter/iteri/map/mapi/fold_left/to_list/ + of_list/copy/blit/fill in OCaml syntax in runtime.sx; backing is a + `ref of list` so set is O(n) but mutation works. (op) sections in + parse-atom: when the token after `(` is a binop and the next is + `)`, emit `(:fun ("a" "b") (:op OP a b))` — `(+)` becomes `fun a b + -> a + b`. Recognises any binop in the precedence table including + `mod`, `land`, `^`, `@`, `::`, etc. Lets us write `List.fold_left + (+) 0 xs` and `((-) 10)` partial applications. - 2026-05-09 Phase 5.1 — csv.ml baseline (split on '\n' then ',', parse-int the second field, fold-left). Exercises char escapes inside string literals, two-stage String.split_on_char, mixed