ocaml: phase 2 for/while loops (+5 tests, 194 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 52s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 52s
Parser: for i = lo to|downto hi do body done, while cond do body done. AST: (:for NAME LO HI :ascend|:descend BODY) and (:while COND BODY). Eval re-binds the loop var per iteration; both forms evaluate to unit.
This commit is contained in:
@@ -300,6 +300,51 @@
|
||||
(fn-val arg-val))))))
|
||||
((= tag "match")
|
||||
(ocaml-match-eval (nth ast 1) (nth ast 2) env))
|
||||
((= tag "for")
|
||||
;; (:for NAME LO HI DIR BODY) — DIR is "ascend" or "descend".
|
||||
(let ((name (nth ast 1))
|
||||
(lo (ocaml-eval (nth ast 2) env))
|
||||
(hi (ocaml-eval (nth ast 3) env))
|
||||
(dir (nth ast 4))
|
||||
(body (nth ast 5)))
|
||||
(begin
|
||||
(cond
|
||||
((= dir "ascend")
|
||||
(let ((i lo))
|
||||
(begin
|
||||
(define loop
|
||||
(fn ()
|
||||
(when (<= i hi)
|
||||
(begin
|
||||
(ocaml-eval body
|
||||
(ocaml-env-extend env name i))
|
||||
(set! i (+ i 1))
|
||||
(loop)))))
|
||||
(loop))))
|
||||
((= dir "descend")
|
||||
(let ((i lo))
|
||||
(begin
|
||||
(define loop
|
||||
(fn ()
|
||||
(when (>= i hi)
|
||||
(begin
|
||||
(ocaml-eval body
|
||||
(ocaml-env-extend env name i))
|
||||
(set! i (- i 1))
|
||||
(loop)))))
|
||||
(loop)))))
|
||||
nil)))
|
||||
((= tag "while")
|
||||
(let ((cond-ast (nth ast 1)) (body (nth ast 2)))
|
||||
(begin
|
||||
(define loop
|
||||
(fn ()
|
||||
(when (ocaml-eval cond-ast env)
|
||||
(begin
|
||||
(ocaml-eval body env)
|
||||
(loop)))))
|
||||
(loop)
|
||||
nil)))
|
||||
((= tag "let")
|
||||
(let ((name (nth ast 1)) (params (nth ast 2))
|
||||
(rhs (nth ast 3)) (body (nth ast 4)))
|
||||
|
||||
@@ -573,6 +573,33 @@
|
||||
(begin (advance-tok!) (one) (loop)))))
|
||||
(loop)
|
||||
(cons :match (cons scrut (list cases)))))))))
|
||||
(define parse-for
|
||||
(fn ()
|
||||
(let ((name (ocaml-tok-value (consume! "ident" nil))))
|
||||
(begin
|
||||
(consume! "op" "=")
|
||||
(let ((lo (parse-expr-no-seq)))
|
||||
(let ((dir
|
||||
(cond
|
||||
((at-kw? "to") (begin (advance-tok!) :ascend))
|
||||
((at-kw? "downto") (begin (advance-tok!) :descend))
|
||||
(else (error "ocaml-parse: expected to/downto in for")))))
|
||||
(let ((hi (parse-expr-no-seq)))
|
||||
(begin
|
||||
(consume! "keyword" "do")
|
||||
(let ((body (parse-expr)))
|
||||
(begin
|
||||
(consume! "keyword" "done")
|
||||
(list :for name lo hi dir body)))))))))))
|
||||
(define parse-while
|
||||
(fn ()
|
||||
(let ((cond-expr (parse-expr-no-seq)))
|
||||
(begin
|
||||
(consume! "keyword" "do")
|
||||
(let ((body (parse-expr)))
|
||||
(begin
|
||||
(consume! "keyword" "done")
|
||||
(list :while cond-expr body)))))))
|
||||
(set!
|
||||
parse-expr-no-seq
|
||||
(fn
|
||||
@@ -582,6 +609,8 @@
|
||||
((at-kw? "let") (begin (advance-tok!) (parse-let)))
|
||||
((at-kw? "if") (begin (advance-tok!) (parse-if)))
|
||||
((at-kw? "match") (begin (advance-tok!) (parse-match)))
|
||||
((at-kw? "for") (begin (advance-tok!) (parse-for)))
|
||||
((at-kw? "while") (begin (advance-tok!) (parse-while)))
|
||||
(else (parse-tuple)))))
|
||||
(set!
|
||||
parse-expr
|
||||
|
||||
@@ -495,6 +495,18 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(epoch 605)
|
||||
(eval "(ocaml-run \"let count = ref 0 in let rec loop n = if n = 0 then !count else (count := !count + n; loop (n - 1)) in loop 5\")")
|
||||
|
||||
;; ── for / while loops ──────────────────────────────────────────
|
||||
(epoch 620)
|
||||
(eval "(ocaml-run \"let s = ref 0 in for i = 1 to 5 do s := !s + i done; !s\")")
|
||||
(epoch 621)
|
||||
(eval "(ocaml-run \"let s = ref 0 in for i = 5 downto 1 do s := !s + i done; !s\")")
|
||||
(epoch 622)
|
||||
(eval "(ocaml-run \"let i = ref 0 in let s = ref 0 in while !i < 5 do i := !i + 1; s := !s + !i done; !s\")")
|
||||
(epoch 623)
|
||||
(eval "(ocaml-run \"let s = ref 0 in for i = 1 to 100 do s := !s + i done; !s\")")
|
||||
(epoch 624)
|
||||
(eval "(ocaml-run \"let p = ref 1 in for i = 1 to 5 do p := !p * i done; !p\")")
|
||||
|
||||
EPOCHS
|
||||
|
||||
OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
||||
@@ -789,6 +801,13 @@ check 603 "ref captured by closure" '115'
|
||||
check 604 "ref of string" '"b"'
|
||||
check 605 "ref + recursion" '15'
|
||||
|
||||
# ── for / while ─────────────────────────────────────────────────
|
||||
check 620 "for 1..5 sum" '15'
|
||||
check 621 "for 5 downto 1 sum" '15'
|
||||
check 622 "while loop" '15'
|
||||
check 623 "for 1..100 sum" '5050'
|
||||
check 624 "for 1..5 product = 120" '120'
|
||||
|
||||
TOTAL=$((PASS + FAIL))
|
||||
if [ $FAIL -eq 0 ]; then
|
||||
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
|
||||
|
||||
@@ -155,7 +155,8 @@ SX CEK evaluator (both JS and OCaml hosts)
|
||||
- [x] Unit `()` value; `ignore`.
|
||||
- [x] References: `ref`, `!`, `:=`.
|
||||
- [ ] Mutable record fields.
|
||||
- [ ] `for i = lo to hi do ... done` loop; `while cond do ... done`.
|
||||
- [x] `for i = lo to hi do ... done` loop; `while cond do ... done` (incl.
|
||||
`downto` direction).
|
||||
- [ ] `try`/`with` — maps to SX `guard`; `raise` via perform.
|
||||
- [ ] Tests in `lib/ocaml/tests/eval.sx` — 50+ tests, pure + imperative.
|
||||
|
||||
@@ -320,6 +321,9 @@ the "mother tongue" closure: OCaml → SX → OCaml. This means:
|
||||
|
||||
_Newest first._
|
||||
|
||||
- 2026-05-08 Phase 2 — `for`/`while` loops. `(:for NAME LO HI DIR BODY)`
|
||||
with `:ascend`/`:descend` direction (`to`/`downto`); `(:while COND BODY)`.
|
||||
Both eval to unit and re-bind the loop var per iteration. 194/194 (+5).
|
||||
- 2026-05-08 Phase 2 — references (`ref`/`!`/`:=`). `ref` is a builtin
|
||||
that boxes its argument in a one-element list (the mutable cell);
|
||||
prefix `!` parses to `(:deref EXPR)` and reads `(nth cell 0)`; `:=`
|
||||
|
||||
Reference in New Issue
Block a user