ocaml: phase 4 modules + field access (+11 tests, 215 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s
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:
@@ -314,6 +314,23 @@
|
||||
;; matches its argument against the clauses.
|
||||
(let ((clauses (nth ast 1)) (captured env))
|
||||
(fn (arg) (ocaml-match-clauses arg clauses captured))))
|
||||
((= tag "field")
|
||||
;; `e.name` — evaluate e, expect a dict (record/module), get name.
|
||||
;; Special case: `(:field (:con "M") "x")` looks up M as a module
|
||||
;; binding rather than evaluating it as a nullary ctor.
|
||||
(let ((target-ast (nth ast 1)) (fname (nth ast 2)))
|
||||
(let ((target
|
||||
(cond
|
||||
((= (ocaml-tag-of target-ast) "con")
|
||||
(cond
|
||||
((ocaml-env-has? env (nth target-ast 1))
|
||||
(ocaml-env-lookup env (nth target-ast 1)))
|
||||
(else (list (nth target-ast 1)))))
|
||||
(else (ocaml-eval target-ast env)))))
|
||||
(cond
|
||||
((dict? target) (get target fname))
|
||||
(else (error
|
||||
(str "ocaml-eval: not a record/module on .field: " target)))))))
|
||||
((= tag "for")
|
||||
;; (:for NAME LO HI DIR BODY) — DIR is "ascend" or "descend".
|
||||
(let ((name (nth ast 1))
|
||||
@@ -417,6 +434,64 @@
|
||||
(else (error
|
||||
(str "ocaml-eval: unknown AST tag " tag)))))))
|
||||
|
||||
;; 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`.
|
||||
(define ocaml-eval-module
|
||||
(fn (decls parent-env)
|
||||
(let ((env parent-env) (result {}))
|
||||
(begin
|
||||
(define run-decl
|
||||
(fn (decl)
|
||||
(let ((tag (ocaml-tag-of decl)))
|
||||
(cond
|
||||
((= tag "def")
|
||||
(let ((name (nth decl 1)) (params (nth decl 2)) (rhs (nth decl 3)))
|
||||
(let ((v (if (= (len params) 0)
|
||||
(ocaml-eval rhs env)
|
||||
(ocaml-make-curried params rhs env))))
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env name v))
|
||||
(set! result (merge result (dict name v)))))))
|
||||
((= tag "def-rec")
|
||||
(let ((name (nth decl 1)) (params (nth decl 2)) (rhs (nth decl 3)))
|
||||
(let ((rhs-fn?
|
||||
(or (> (len params) 0)
|
||||
(= (ocaml-tag-of rhs) "fun")
|
||||
(= (ocaml-tag-of rhs) "function"))))
|
||||
(cond
|
||||
(rhs-fn?
|
||||
(let ((cell (list nil)))
|
||||
(let ((env2 (ocaml-env-extend env name
|
||||
(fn (arg) ((nth cell 0) arg)))))
|
||||
(let ((v (if (= (len params) 0)
|
||||
(ocaml-eval rhs env2)
|
||||
(ocaml-make-curried params rhs env2))))
|
||||
(begin
|
||||
(set-nth! cell 0 v)
|
||||
(set! env env2)
|
||||
(set! result (merge result (dict name v))))))))
|
||||
(else
|
||||
(let ((v (ocaml-eval rhs env)))
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env name v))
|
||||
(set! result (merge result (dict name v))))))))))
|
||||
((= tag "expr")
|
||||
(ocaml-eval (nth decl 1) env))
|
||||
((= tag "module-def")
|
||||
(let ((mname (nth decl 1)) (mdecls (nth decl 2)))
|
||||
(let ((mod-val (ocaml-eval-module mdecls env)))
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env mname mod-val))
|
||||
(set! result (merge result (dict mname mod-val)))))))
|
||||
(else (error (str "ocaml-eval-module: bad decl " tag)))))))
|
||||
(define loop
|
||||
(fn (xs)
|
||||
(when (not (= xs (list)))
|
||||
(begin (run-decl (first xs)) (loop (rest xs))))))
|
||||
(loop decls)
|
||||
result))))
|
||||
|
||||
;; ocaml-run — convenience wrapper: parse + eval.
|
||||
(define ocaml-run
|
||||
(fn (src)
|
||||
@@ -466,6 +541,16 @@
|
||||
(set! last v))))))))
|
||||
((= tag "expr")
|
||||
(set! last (ocaml-eval (nth decl 1) env)))
|
||||
((= tag "module-def")
|
||||
;; module M = struct DECLS end — evaluate the inner
|
||||
;; decls in a fresh sub-env layered on the current
|
||||
;; one, then collect the new bindings into a dict that
|
||||
;; we bind under M.
|
||||
(let ((mname (nth decl 1)) (mdecls (nth decl 2)))
|
||||
(let ((mod-val (ocaml-eval-module mdecls env)))
|
||||
(begin
|
||||
(set! env (ocaml-env-extend env mname mod-val))
|
||||
(set! last mod-val)))))
|
||||
(else (error (str "ocaml-run-program: bad decl " tag)))))))
|
||||
(define loop
|
||||
(fn (xs)
|
||||
|
||||
Reference in New Issue
Block a user