ocaml: phase 6 Array module + (op) operator sections (+6 tests, 512 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s

Array module (runtime.sx, OCaml syntax):
  Backed by a 'ref of list'. make/length/get/init build the cell;
  set rewrites the underlying list with one cell changed (O(n) but
  works for short arrays in baseline programs). Includes
  iter/iteri/map/mapi/fold_left/to_list/of_list/copy/blit/fill.

(op) operator sections (parser.sx, parse-atom):
  When the token after '(' is a binop (any op with non-zero
  precedence in the binop table) and the next token is ')', emit
  (:fun ('a' 'b') (:op OP a b)) — i.e. (+)  becomes fun a b -> a + b.
  Recognises every binop including 'mod', 'land', '^', '@', '::',
  etc.

Lets us write:
  List.fold_left (+) 0 [1;2;3;4;5]    = 15
  let f = ( * ) in f 6 7              = 42
  List.map ((-) 10) [1;2;3]           = [9;8;7]
  let a = Array.make 5 7 in
  Array.set a 2 99;
  Array.fold_left (+) 0 a             = 127
This commit is contained in:
2026-05-09 01:59:13 +00:00
parent 5618dd1ef5
commit 1ed3216ba6
4 changed files with 96 additions and 0 deletions

View File

@@ -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)))

View File

@@ -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

View File

@@ -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"

View File

@@ -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