ocaml: phase 4 modules + field access (+11 tests, 215 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s

module M = struct DECLS end parsed by sub-tokenising the body source
between struct and the matching end (nesting tracked via struct/begin/
sig/end). Field access is a postfix layer above parse-atom, binding
tighter than application: f r.x -> (:app f (:field r "x")).

Eval (:module-def NAME DECLS) builds a dict via ocaml-eval-module
running decls in a sub-env. (:field EXPR NAME) looks up dict fields,
treating (:con NAME) heads as module-name lookups instead of nullary
ctors so M.x works with M as a module.

Phase 4 LOC so far: ~110 lines (well under 2000 budget).
This commit is contained in:
2026-05-08 08:33:34 +00:00
parent 6a1f63f0d1
commit 317f93b2af
4 changed files with 199 additions and 4 deletions

View File

@@ -531,6 +531,30 @@ cat > "$TMPFILE" << 'EPOCHS'
(epoch 665)
(eval "(ocaml-run \"let f x = if x < 0 then raise (NegArg x) else x * 2 in try f (-5) with | NegArg n -> n\")")
;; ── Phase 4: Modules + field access ────────────────────────────
(epoch 700)
(eval "(ocaml-parse \"M.x\")")
(epoch 701)
(eval "(ocaml-parse \"r.field\")")
(epoch 702)
(eval "(ocaml-parse \"M.M2.x\")")
(epoch 703)
(eval "(ocaml-parse \"f r.x\")")
(epoch 710)
(eval "(ocaml-run-program \"module M = struct let x = 42 end ;; M.x\")")
(epoch 711)
(eval "(ocaml-run-program \"module M = struct let f x = x + 1 end ;; M.f 41\")")
(epoch 712)
(eval "(ocaml-run-program \"module M = struct let x = 1 let y = 2 end ;; M.x + M.y\")")
(epoch 713)
(eval "(ocaml-run-program \"module Math = struct let pi = 3.14 let square x = x * x end ;; Math.square 5\")")
(epoch 714)
(eval "(ocaml-run-program \"module Outer = struct module Inner = struct let v = 99 end end ;; Outer.Inner.v\")")
(epoch 715)
(eval "(ocaml-run-program \"module M = struct let rec fact n = if n = 0 then 1 else n * fact (n - 1) end ;; M.fact 5\")")
(epoch 716)
(eval "(ocaml-run-program \"module Pair = struct let make a b = (a, b) let swap p = match p with | (x, y) -> (y, x) end ;; Pair.swap (Pair.make 1 2)\")")
EPOCHS
OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
@@ -846,6 +870,19 @@ check 663 "try failwith" '"oops"'
check 664 "try sequence raises" '101'
check 665 "raise from function" '-5'
# ── Phase 4: Modules + field access ─────────────────────────────
check 700 "parse M.x" '("field" ("con" "M") "x")'
check 701 "parse r.field" '("field" ("var" "r") "field")'
check 702 "parse M.M2.x left-assoc" '("field" ("field" ("con" "M") "M2") "x")'
check 703 "parse f r.x bind tighter" '("app" ("var" "f") ("field" ("var" "r") "x"))'
check 710 "module M.x = 42" '42'
check 711 "module M.f 41 = 42" '42'
check 712 "module two values" '3'
check 713 "module fn: square 5" '25'
check 714 "nested module Outer.Inner" '99'
check 715 "module rec fact 5" '120'
check 716 "module Pair.swap" '("tuple" 2 1)'
TOTAL=$((PASS + FAIL))
if [ $FAIL -eq 0 ]; then
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"