ocaml: phase 4 functors + module aliases (+5 tests, 225 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
Parser: module F (M) (N) ... = struct DECLS end -> (:functor-def NAME PARAMS DECLS). module N = expr (non-struct) -> (:module-alias NAME BODY-SRC). Functor params accept (P) or (P : Sig) — signatures parsed-and-skipped via skip-optional-sig. Eval: ocaml-make-functor builds curried host-SX closures from module dicts to a module dict. ocaml-resolve-module-path extended for :app so F(A), F(A)(B), and Outer.Inner all resolve to dicts. Phase 4 LOC ~290 cumulative (still well under 2000).
This commit is contained in:
@@ -67,9 +67,11 @@
|
||||
(fn (env name val)
|
||||
(cons (list name val) env)))
|
||||
|
||||
;; Resolve a module path like `M` or `M.Sub` to a dict. Mirrors the
|
||||
;; field-access escape hatch where `(:con NAME)` is treated as an env
|
||||
;; lookup rather than a nullary ctor.
|
||||
;; Resolve a module path / functor-application expression to a module dict.
|
||||
;; Mirrors the field-access escape hatch where `(:con NAME)` is treated as
|
||||
;; an env lookup rather than a nullary ctor; also handles `(:app FN ARG)`
|
||||
;; for functor applications, `(:field …)` for sub-modules, and `(:var …)`
|
||||
;; for lower-case bindings.
|
||||
(define ocaml-resolve-module-path
|
||||
(fn (path-expr env)
|
||||
(let ((tag (ocaml-tag-of path-expr)))
|
||||
@@ -79,12 +81,22 @@
|
||||
((ocaml-env-has? env (nth path-expr 1))
|
||||
(ocaml-env-lookup env (nth path-expr 1)))
|
||||
(else (error (str "ocaml-eval: unknown module " (nth path-expr 1))))))
|
||||
((= tag "var")
|
||||
(cond
|
||||
((ocaml-env-has? env (nth path-expr 1))
|
||||
(ocaml-env-lookup env (nth path-expr 1)))
|
||||
(else (error (str "ocaml-eval: unknown module-var " (nth path-expr 1))))))
|
||||
((= tag "field")
|
||||
(let ((parent (ocaml-resolve-module-path (nth path-expr 1) env)))
|
||||
(cond
|
||||
((dict? parent) (get parent (nth path-expr 2)))
|
||||
(else (error
|
||||
(str "ocaml-eval: not a module on path: " parent))))))
|
||||
((= tag "app")
|
||||
(let ((fn-val (ocaml-resolve-module-path (nth path-expr 1) env))
|
||||
(arg-val (ocaml-resolve-module-path (nth path-expr 2) env)))
|
||||
(fn-val arg-val)))
|
||||
((= tag "unit") {})
|
||||
(else (ocaml-eval path-expr env))))))
|
||||
|
||||
;; Merge a dict's bindings into an env (used by `open`/`include`).
|
||||
@@ -470,6 +482,21 @@
|
||||
(else (error
|
||||
(str "ocaml-eval: unknown AST tag " tag)))))))
|
||||
|
||||
;; ocaml-make-functor — build a curried host-SX closure that accepts
|
||||
;; argument modules (one per param) and returns the resulting module dict
|
||||
;; produced by evaluating the functor's body.
|
||||
(define ocaml-make-functor
|
||||
(fn (params decls captured-env)
|
||||
(cond
|
||||
((= (len params) 1)
|
||||
(fn (arg-mod)
|
||||
(ocaml-eval-module decls
|
||||
(ocaml-env-extend captured-env (first params) arg-mod))))
|
||||
(else
|
||||
(fn (arg-mod)
|
||||
(ocaml-make-functor (rest params) decls
|
||||
(ocaml-env-extend captured-env (first params) arg-mod)))))))
|
||||
|
||||
;; ocaml-eval-module — evaluate a list of decls in a fresh sub-env layered
|
||||
;; on top of the parent. Returns a dict mapping each declared name to its
|
||||
;; value. Used by `module M = struct DECLS end`.
|
||||
@@ -520,6 +547,21 @@
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env mname mod-val))
|
||||
(set! result (merge result (dict mname mod-val)))))))
|
||||
((= tag "functor-def")
|
||||
(let ((mname (nth decl 1))
|
||||
(mparams (nth decl 2))
|
||||
(mdecls (nth decl 3)))
|
||||
(let ((fn-val (ocaml-make-functor mparams mdecls env)))
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env mname fn-val))
|
||||
(set! result (merge result (dict mname fn-val)))))))
|
||||
((= tag "module-alias")
|
||||
(let ((mname (nth decl 1)) (body-src (nth decl 2)))
|
||||
(let ((body-expr (ocaml-parse body-src)))
|
||||
(let ((mod-val (ocaml-resolve-module-path body-expr env)))
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env mname mod-val))
|
||||
(set! result (merge result (dict mname mod-val))))))))
|
||||
((= tag "open")
|
||||
(let ((mod-val (ocaml-resolve-module-path (nth decl 1) env)))
|
||||
(cond
|
||||
@@ -605,6 +647,26 @@
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env mname mod-val))
|
||||
(set! last mod-val)))))
|
||||
((= tag "functor-def")
|
||||
;; module F (M1) (M2) ... = struct DECLS end — bind F
|
||||
;; to a curried function from module dicts to a module
|
||||
;; dict.
|
||||
(let ((mname (nth decl 1))
|
||||
(mparams (nth decl 2))
|
||||
(mdecls (nth decl 3)))
|
||||
(let ((functor-val
|
||||
(ocaml-make-functor mparams mdecls env)))
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env mname functor-val))
|
||||
(set! last functor-val)))))
|
||||
((= tag "module-alias")
|
||||
;; module N = M / module N = F(A) / module N = M.Sub
|
||||
(let ((mname (nth decl 1)) (body-src (nth decl 2)))
|
||||
(let ((body-expr (ocaml-parse body-src)))
|
||||
(let ((mod-val (ocaml-resolve-module-path body-expr env)))
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env mname mod-val))
|
||||
(set! last mod-val))))))
|
||||
((or (= tag "open") (= tag "include"))
|
||||
;; open M / include M — bring M's bindings into scope.
|
||||
(let ((mod-val (ocaml-resolve-module-path (nth decl 1) env)))
|
||||
|
||||
Reference in New Issue
Block a user