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

@@ -179,7 +179,8 @@ SX CEK evaluator (both JS and OCaml hosts)
### Phase 4 — Modules + functors
- [ ] `module M = struct let x = 1 let f y = x + y end` → SX dict `{:x 1 :f <fn>}`.
- [x] `module M = struct let x = 1 let f y = x + y end` → SX dict
`{"x" 1 "f" <fn>}`.
- [ ] `module type S = sig val x : int val f : int -> int end` → interface record
(runtime stub; typed checking in Phase 5).
- [ ] `module M : S = struct ... end` — coercive sealing (runtime: pass-through).
@@ -187,7 +188,7 @@ SX CEK evaluator (both JS and OCaml hosts)
- [ ] `module F = Functor(Base)` — functor application.
- [ ] `open M` — merge M's dict into current env (`env-merge`).
- [ ] `include M` — same as open at structure level.
- [ ] `M.name` — dict get via `:name` key.
- [x] `M.name` — dict get via field access.
- [ ] First-class modules (pack/unpack) — deferred to Phase 5.
- [ ] Standard module hierarchy: `List`, `Option`, `Result`, `String`, `Char`,
`Int`, `Float`, `Bool`, `Unit`, `Printf`, `Format` (stubs, filled in Phase 6).
@@ -321,6 +322,18 @@ the "mother tongue" closure: OCaml → SX → OCaml. This means:
_Newest first._
- 2026-05-08 Phase 4 — modules + field access (+11 tests, 215 total). Parser:
`module M = struct DECLS end` decl in `ocaml-parse-program`. Body parsed
by sub-tokenising the source between `struct` and the matching `end`,
tracking nesting via `struct`/`begin`/`sig`/`end`. Field access added
as 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 new `ocaml-eval-module` that runs decls in a sub-env;
`(:field EXPR NAME)` looks up the field, with the special case that
`(:con NAME)` heads are interpreted as module-name lookups instead of
nullary ctors. Tested: simple module, multi-decl module, nested modules
(`Outer.Inner.v`), `let rec` inside a module, module containing tuple
pattern match. Phase 4 LOC: ~110 (well under 2000 budget).
- 2026-05-08 Phase 2 — `try`/`with` + `raise` builtin. Parser produces
`(:try EXPR CLAUSES)`; eval delegates to SX `guard` with `else`
matching the raised value against clause patterns and re-raising on