ocaml: phase 4 'arr.(i)' and 'arr.(i) <- v' array indexing (+3 tests, 515 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 21s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 21s
parse-atom-postfix's '.()' branch now disambiguates between let-open
and array-get based on whether the head is a module path (':con' or
':field' chain rooted in ':con'). Module paths still emit
(:let-open M EXPR); everything else emits (:array-get ARR I).
Eval handles :array-get by reading the cell's underlying list at
index. The '<-' assignment handler now also accepts :array-get lhs
and rewrites the cell with one position changed.
Idiomatic OCaml array code now works:
let a = Array.make 5 0 in
for i = 0 to 4 do a.(i) <- i * i done;
a.(3) + a.(4) = 25
let a = Array.init 4 (fun i -> i + 1) in
a.(0) + a.(1) + a.(2) + a.(3) = 10
List.(length [1;2;3]) = 3 (* unchanged: List is a module *)
This commit is contained in:
@@ -490,6 +490,22 @@
|
|||||||
(let ((target (ocaml-eval (nth lhs-ast 1) env))
|
(let ((target (ocaml-eval (nth lhs-ast 1) env))
|
||||||
(fname (nth lhs-ast 2)))
|
(fname (nth lhs-ast 2)))
|
||||||
(begin (dict-set! target fname new-val) nil)))
|
(begin (dict-set! target fname new-val) nil)))
|
||||||
|
((= (ocaml-tag-of lhs-ast) "array-get")
|
||||||
|
;; (:array-get ARR I) <- v : rewrite the underlying
|
||||||
|
;; list in the ref cell with one cell changed.
|
||||||
|
(let ((arr (ocaml-eval (nth lhs-ast 1) env))
|
||||||
|
(i (ocaml-eval (nth lhs-ast 2) env)))
|
||||||
|
(begin
|
||||||
|
(define replace
|
||||||
|
(fn (lst k)
|
||||||
|
(cond
|
||||||
|
((= lst (list)) (list))
|
||||||
|
((= k 0) (cons new-val (rest lst)))
|
||||||
|
(else
|
||||||
|
(cons (first lst)
|
||||||
|
(replace (rest lst) (- k 1)))))))
|
||||||
|
(set-nth! arr 0 (replace (nth arr 0) i))
|
||||||
|
nil)))
|
||||||
(else
|
(else
|
||||||
(error
|
(error
|
||||||
(str "ocaml-eval: <- expects a field-access lhs, got "
|
(str "ocaml-eval: <- expects a field-access lhs, got "
|
||||||
@@ -602,6 +618,11 @@
|
|||||||
(let ((s (ocaml-eval (nth ast 1) env))
|
(let ((s (ocaml-eval (nth ast 1) env))
|
||||||
(i (ocaml-eval (nth ast 2) env)))
|
(i (ocaml-eval (nth ast 2) env)))
|
||||||
(nth s i)))
|
(nth s i)))
|
||||||
|
((= tag "array-get")
|
||||||
|
;; (:array-get ARR I) — Array.get on a ref-of-list.
|
||||||
|
(let ((arr (ocaml-eval (nth ast 1) env))
|
||||||
|
(i (ocaml-eval (nth ast 2) env)))
|
||||||
|
(nth (nth arr 0) i)))
|
||||||
((= tag "for")
|
((= tag "for")
|
||||||
;; (:for NAME LO HI DIR BODY) — DIR is "ascend" or "descend".
|
;; (:for NAME LO HI DIR BODY) — DIR is "ascend" or "descend".
|
||||||
(let ((name (nth ast 1))
|
(let ((name (nth ast 1))
|
||||||
|
|||||||
@@ -574,6 +574,16 @@
|
|||||||
;; `(:field (:field M "x") "y")`.
|
;; `(:field (:field M "x") "y")`.
|
||||||
(let ((head (parse-atom)))
|
(let ((head (parse-atom)))
|
||||||
(begin
|
(begin
|
||||||
|
;; Module-path detector: head is :con, or :field chain
|
||||||
|
;; whose innermost subject is :con. Used to choose between
|
||||||
|
;; (:let-open M EXPR) and (:array-get ARR I) after `.(`.
|
||||||
|
(define is-module-path?
|
||||||
|
(fn (h)
|
||||||
|
(cond
|
||||||
|
((not (list? h)) false)
|
||||||
|
((= (first h) "con") true)
|
||||||
|
((= (first h) "field") (is-module-path? (nth h 1)))
|
||||||
|
(else false))))
|
||||||
(define loop
|
(define loop
|
||||||
(fn ()
|
(fn ()
|
||||||
(when (at-op? ".")
|
(when (at-op? ".")
|
||||||
@@ -586,7 +596,10 @@
|
|||||||
(let ((inner (parse-expr)))
|
(let ((inner (parse-expr)))
|
||||||
(begin
|
(begin
|
||||||
(consume! "op" ")")
|
(consume! "op" ")")
|
||||||
(set! head (list :let-open head inner))
|
(set! head
|
||||||
|
(if (is-module-path? head)
|
||||||
|
(list :let-open head inner)
|
||||||
|
(list :array-get head inner)))
|
||||||
(loop)))))
|
(loop)))))
|
||||||
((at-op? "[")
|
((at-op? "[")
|
||||||
(begin
|
(begin
|
||||||
|
|||||||
@@ -1278,6 +1278,14 @@ cat > "$TMPFILE" << 'EPOCHS'
|
|||||||
(epoch 5022)
|
(epoch 5022)
|
||||||
(eval "(ocaml-run \"List.map ((-) 10) [1;2;3]\")")
|
(eval "(ocaml-run \"List.map ((-) 10) [1;2;3]\")")
|
||||||
|
|
||||||
|
;; ── arr.(i) and arr.(i) <- v ────────────────────────────────
|
||||||
|
(epoch 5030)
|
||||||
|
(eval "(ocaml-run \"let a = Array.make 5 7 in a.(2) <- 99; a.(2) + a.(0)\")")
|
||||||
|
(epoch 5031)
|
||||||
|
(eval "(ocaml-run \"let a = Array.init 4 (fun i -> i + 1) in a.(0) + a.(1) + a.(2) + a.(3)\")")
|
||||||
|
(epoch 5032)
|
||||||
|
(eval "(ocaml-run \"let a = Array.make 5 0 in for i = 0 to 4 do a.(i) <- i * i done; a.(3) + a.(4)\")")
|
||||||
|
|
||||||
EPOCHS
|
EPOCHS
|
||||||
|
|
||||||
OUTPUT=$(timeout 360 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
OUTPUT=$(timeout 360 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
||||||
@@ -2030,6 +2038,11 @@ check 5020 "fold_left (+) sum" '15'
|
|||||||
check 5021 "let f = (*) in f 6 7" '42'
|
check 5021 "let f = (*) in f 6 7" '42'
|
||||||
check 5022 "List.map ((-) 10) [1;2;3]" '(9 8 7)'
|
check 5022 "List.map ((-) 10) [1;2;3]" '(9 8 7)'
|
||||||
|
|
||||||
|
# ── arr.(i) and arr.(i) <- v ────────────────────────────────────
|
||||||
|
check 5030 "a.(2) <- 99; a.(2) + a.(0)" '106'
|
||||||
|
check 5031 "Array.init 4 + sum a.(0..3)" '10'
|
||||||
|
check 5032 "for + a.(i) <- i*i + sum" '25'
|
||||||
|
|
||||||
TOTAL=$((PASS + FAIL))
|
TOTAL=$((PASS + FAIL))
|
||||||
if [ $FAIL -eq 0 ]; then
|
if [ $FAIL -eq 0 ]; then
|
||||||
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
|
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
|
||||||
|
|||||||
@@ -407,6 +407,18 @@ _Newest first._
|
|||||||
binary search tree (`type 'a tree = Leaf | Node of 'a * 'a tree *
|
binary search tree (`type 'a tree = Leaf | Node of 'a * 'a tree *
|
||||||
'a tree`) with insert + in-order traversal. Tests parametric ADT,
|
'a tree`) with insert + in-order traversal. Tests parametric ADT,
|
||||||
recursive match, List.append, List.fold_left.
|
recursive match, List.append, List.fold_left.
|
||||||
|
- 2026-05-09 Phase 4 — `arr.(i)` and `arr.(i) <- v` array indexing
|
||||||
|
syntax (+3 tests, 515 total). parse-atom-postfix's `.(...)` branch
|
||||||
|
now disambiguates between let-open and array-get based on whether
|
||||||
|
the head is a module path (`:con` or a `:field` chain rooted in a
|
||||||
|
`:con`). Module paths still emit `(:let-open M EXPR)`; everything
|
||||||
|
else emits `(:array-get ARR I)`. Eval handles `:array-get` by
|
||||||
|
reading the cell's underlying list at index. The `<-` assignment
|
||||||
|
handler now also accepts `:array-get` lhs and rewrites the cell
|
||||||
|
with one position changed. Lets us write idiomatic OCaml array code:
|
||||||
|
let a = Array.make 5 0 in
|
||||||
|
for i = 0 to 4 do a.(i) <- i * i done;
|
||||||
|
a.(3) + a.(4) (* = 25 *)
|
||||||
- 2026-05-09 Phase 6 — Array module (ref-of-list backing) + (op)
|
- 2026-05-09 Phase 6 — Array module (ref-of-list backing) + (op)
|
||||||
operator sections (+6 tests, 512 total). Array implements
|
operator sections (+6 tests, 512 total). Array implements
|
||||||
make/length/get/set/init/iter/iteri/map/mapi/fold_left/to_list/
|
make/length/get/set/init/iter/iteri/map/mapi/fold_left/to_list/
|
||||||
|
|||||||
Reference in New Issue
Block a user