ocaml: phase 2 let..and.. mutual recursion (+3 tests, 251 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 51s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 51s
Parser collects multiple bindings via 'and', emitting (:def-rec-mut BINDINGS) for let-rec chains and (:def-mut BINDINGS) for non-rec. Single bindings keep the existing (:def …) / (:def-rec …) shapes. Eval (def-rec-mut): allocate placeholder cell per binding, build joint env where each name forwards through its cell, then evaluate each rhs against the joint env and fill the cells. Even/odd mutual-rec works.
This commit is contained in:
@@ -647,6 +647,60 @@
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env name v))
|
||||
(set! last v))))))))
|
||||
((= tag "def-mut")
|
||||
;; let x = ... and y = ... — non-recursive; each rhs is
|
||||
;; evaluated in the parent env, then all names bind in
|
||||
;; sequence.
|
||||
(let ((bs (nth decl 1)))
|
||||
(begin
|
||||
(define run-one
|
||||
(fn (b)
|
||||
(let ((nm (nth b 0)) (ps (nth b 1)) (rh (nth b 2)))
|
||||
(let ((v (if (= (len ps) 0)
|
||||
(ocaml-eval rh env)
|
||||
(ocaml-make-curried ps rh env))))
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env nm v))
|
||||
(set! last v))))))
|
||||
(define loop
|
||||
(fn (xs)
|
||||
(when (not (= xs (list)))
|
||||
(begin (run-one (first xs)) (loop (rest xs))))))
|
||||
(loop bs))))
|
||||
((= tag "def-rec-mut")
|
||||
;; let rec f = ... and g = ... — mutually recursive;
|
||||
;; bind all names with placeholder cells first, then
|
||||
;; evaluate each rhs in the joint env, finally fill cells.
|
||||
(let ((bs (nth decl 1)) (cells (list)) (env2 env))
|
||||
(begin
|
||||
(define alloc
|
||||
(fn (xs)
|
||||
(when (not (= xs (list)))
|
||||
(let ((b (first xs)))
|
||||
(let ((c (list nil)) (nm (nth b 0)))
|
||||
(begin
|
||||
(append! cells c)
|
||||
(set! env2 (ocaml-env-extend env2 nm
|
||||
(fn (a) ((nth c 0) a))))
|
||||
(alloc (rest xs))))))))
|
||||
(alloc bs)
|
||||
(let ((idx 0))
|
||||
(begin
|
||||
(define fill
|
||||
(fn (xs)
|
||||
(when (not (= xs (list)))
|
||||
(let ((b (first xs)))
|
||||
(let ((nm (nth b 0)) (ps (nth b 1)) (rh (nth b 2)))
|
||||
(let ((v (if (= (len ps) 0)
|
||||
(ocaml-eval rh env2)
|
||||
(ocaml-make-curried ps rh env2))))
|
||||
(begin
|
||||
(set-nth! (nth cells idx) 0 v)
|
||||
(set! idx (+ idx 1))
|
||||
(set! last v)
|
||||
(fill (rest xs)))))))))
|
||||
(fill bs)
|
||||
(set! env env2))))))
|
||||
((= tag "expr")
|
||||
(set! last (ocaml-eval (nth decl 1) env)))
|
||||
((= tag "module-def")
|
||||
|
||||
@@ -790,46 +790,51 @@
|
||||
((at-kw? "module") nil)
|
||||
((at-kw? "open") nil)
|
||||
((at-kw? "include") nil)
|
||||
((at-kw? "and") nil)
|
||||
(else (begin (advance-tok!) (skip-to-boundary!))))))
|
||||
(define
|
||||
parse-decl-let
|
||||
(fn
|
||||
()
|
||||
(fn ()
|
||||
(advance-tok!)
|
||||
(let
|
||||
((reccy false))
|
||||
(let ((reccy false) (bindings (list)))
|
||||
(begin
|
||||
(when
|
||||
(at-kw? "rec")
|
||||
(begin (advance-tok!) (set! reccy true)))
|
||||
(let
|
||||
((name (ocaml-tok-value (consume! "ident" nil)))
|
||||
(params (list)))
|
||||
(begin
|
||||
(define
|
||||
collect-params
|
||||
(fn
|
||||
()
|
||||
(when
|
||||
(check-tok? "ident" nil)
|
||||
(begin
|
||||
(append! params (ocaml-tok-value (peek-tok)))
|
||||
(advance-tok!)
|
||||
(collect-params)))))
|
||||
(collect-params)
|
||||
(consume! "op" "=")
|
||||
(let
|
||||
((expr-start (cur-pos)))
|
||||
(when (at-kw? "rec") (begin (advance-tok!) (set! reccy true)))
|
||||
(define parse-one!
|
||||
(fn ()
|
||||
(let ((nm (ocaml-tok-value (consume! "ident" nil)))
|
||||
(ps (list)))
|
||||
(begin
|
||||
(skip-to-boundary!)
|
||||
(let
|
||||
((expr-src (slice src expr-start (cur-pos))))
|
||||
(let
|
||||
((expr (ocaml-parse expr-src)))
|
||||
(if
|
||||
reccy
|
||||
(list :def-rec name params expr)
|
||||
(list :def name params expr))))))))))))
|
||||
(define collect-params
|
||||
(fn ()
|
||||
(when (check-tok? "ident" nil)
|
||||
(begin
|
||||
(append! ps (ocaml-tok-value (peek-tok)))
|
||||
(advance-tok!)
|
||||
(collect-params)))))
|
||||
(collect-params)
|
||||
(consume! "op" "=")
|
||||
(let ((expr-start (cur-pos)))
|
||||
(begin
|
||||
(skip-to-boundary!)
|
||||
(let ((expr-src (slice src expr-start (cur-pos))))
|
||||
(let ((expr (ocaml-parse expr-src)))
|
||||
(append! bindings (list nm ps expr))))))))))
|
||||
(parse-one!)
|
||||
(define more
|
||||
(fn ()
|
||||
(when (at-kw? "and")
|
||||
(begin (advance-tok!) (parse-one!) (more)))))
|
||||
(more)
|
||||
(cond
|
||||
((= (len bindings) 1)
|
||||
(let ((b (first bindings)))
|
||||
(if reccy
|
||||
(list :def-rec (nth b 0) (nth b 1) (nth b 2))
|
||||
(list :def (nth b 0) (nth b 1) (nth b 2)))))
|
||||
(else
|
||||
(if reccy
|
||||
(list :def-rec-mut bindings)
|
||||
(list :def-mut bindings))))))))
|
||||
(define
|
||||
parse-decl-expr
|
||||
(fn
|
||||
|
||||
@@ -631,6 +631,14 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(epoch 832)
|
||||
(eval "(ocaml-run \"Result.is_error (Error \\\"oops\\\")\")")
|
||||
|
||||
;; ── let ... and ... mutual recursion ──────────────────────────
|
||||
(epoch 850)
|
||||
(eval "(ocaml-run-program \"let rec even n = if n = 0 then true else odd (n - 1) and odd n = if n = 0 then false else even (n - 1);; even 10\")")
|
||||
(epoch 851)
|
||||
(eval "(ocaml-run-program \"let rec even n = if n = 0 then true else odd (n - 1) and odd n = if n = 0 then false else even (n - 1);; odd 7\")")
|
||||
(epoch 852)
|
||||
(eval "(ocaml-run-program \"let x = 1 and y = 2;; x + y\")")
|
||||
|
||||
EPOCHS
|
||||
|
||||
OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
||||
@@ -1003,6 +1011,11 @@ check 830 "Result.map Ok" '("Ok" 6)'
|
||||
check 831 "Result.is_ok" 'true'
|
||||
check 832 "Result.is_error" 'true'
|
||||
|
||||
# ── let ... and ... mutual recursion ─────────────────────────────
|
||||
check 850 "even 10 (mutual rec)" 'true'
|
||||
check 851 "odd 7 (mutual rec)" 'true'
|
||||
check 852 "let x = 1 and y = 2" '3'
|
||||
|
||||
TOTAL=$((PASS + FAIL))
|
||||
if [ $FAIL -eq 0 ]; then
|
||||
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
|
||||
|
||||
@@ -145,8 +145,11 @@ SX CEK evaluator (both JS and OCaml hosts)
|
||||
### Phase 2 — Core evaluator (untyped)
|
||||
|
||||
- [x] `ocaml-eval` entry: walks OCaml AST, produces SX values.
|
||||
- [~] `let`/`let rec`/`let ... in` (single-binding done; mutually recursive
|
||||
`and` deferred).
|
||||
- [x] `let`/`let rec`/`let ... in`. Mutually recursive `let rec f = … and
|
||||
g = …` works at top level via `(:def-rec-mut BINDINGS)`; placeholders
|
||||
are bound first, rhs evaluated in the joint env, cells filled in.
|
||||
`let x = … and y = …` (non-rec) emits `(:def-mut BINDINGS)` —
|
||||
sequential bindings against the parent env.
|
||||
- [x] Lambda + application (curried by default — auto-curry multi-param defs).
|
||||
- [x] `fun`/`function` (single-arg lambda with immediate match on arg).
|
||||
- [x] `if`/`then`/`else`, `begin`/`end`, sequence `;`.
|
||||
@@ -327,6 +330,11 @@ the "mother tongue" closure: OCaml → SX → OCaml. This means:
|
||||
|
||||
_Newest first._
|
||||
|
||||
- 2026-05-08 Phase 2 — `let ... and ...` mutual recursion at top level.
|
||||
Parser collects all bindings into a list, emitting `(:def-rec-mut)` or
|
||||
`(:def-mut)` when there are 2+. Eval allocates a placeholder cell per
|
||||
recursive binding, builds an env with all of them visible, then fills
|
||||
the cells. Even/odd mutual-recursion test passes. 251/251 (+3).
|
||||
- 2026-05-08 Phase 6 — `lib/ocaml/runtime.sx` minimal stdlib slice
|
||||
written entirely in OCaml syntax: List (length, rev, rev_append, map,
|
||||
filter, fold_left/right, append, iter, mem, for_all, exists, hd, tl,
|
||||
|
||||
Reference in New Issue
Block a user