ocaml: phase 5.1 calc.ml baseline (11/11 pass) + inline let-rec-and parser fix
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s

Recursive-descent calculator parses '(1 + 2) * 3 + 4' = 13. Two parser
bugs fixed:

1. parse-let now handles inline 'let rec a () = ... and b () = ... in
   body' via new (:let-rec-mut BINDINGS BODY) and (:let-mut BINDINGS
   BODY) AST shapes; eval handles both.

2. has-matching-in? lookahead no longer stops at 'and' — 'and' is
   internal to let-rec, not a decl boundary. Without this fix, the
   inner 'let rec a () = ... and b () = ...' inside a let-decl rhs
   would have been treated as the start of a new top-level decl.

Baseline exercises mutually-recursive functions, while-loops, ref-cell
imperative parsing, and ADT-based AST construction.
This commit is contained in:
2026-05-08 16:53:44 +00:00
parent ecdd90345e
commit ffa74399fd
5 changed files with 170 additions and 28 deletions

View File

@@ -619,6 +619,57 @@
(ocaml-eval rhs env)
(ocaml-make-curried params rhs env))))
(ocaml-eval body (ocaml-env-extend env name rhs-val)))))
((= tag "let-mut")
;; (:let-mut BINDINGS BODY) — non-rec multi-binding let-in.
;; Each rhs evaluated in the parent env, then names bound
;; sequentially before evaluating BODY.
(let ((bindings (nth ast 1)) (body (nth ast 2)) (env-cur env))
(begin
(define 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-cur)
(ocaml-make-curried ps rh env-cur))))
(set! env-cur (ocaml-env-extend env-cur nm v))))))
(define loop
(fn (xs)
(when (not (= xs (list)))
(begin (one (first xs)) (loop (rest xs))))))
(loop bindings)
(ocaml-eval body env-cur))))
((= tag "let-rec-mut")
;; (:let-rec-mut BINDINGS BODY) — mutually-recursive let-in.
(let ((bindings (nth ast 1)) (body (nth ast 2))
(env2 env) (cells (list)))
(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 bindings)
(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))
(fill (rest xs)))))))))
(fill bindings)
(ocaml-eval body env2))))))
((= tag "let-rec")
;; Tie the knot via a mutable cell when rhs is function-typed.
;; The placeholder closure dereferences the cell on each call.