ocaml: phase 4 module type S = sig … end parser (+3 tests, 389 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 33s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 33s
module type S = sig DECLS end is parsed-and-discarded — sig..end balanced skipping in parse-decl-module-type. AST (:module-type-def NAME). Runtime no-op (signatures are type-level only). Allows real OCaml programs with module type decls to parse and run without stripping the sig blocks.
This commit is contained in:
@@ -704,6 +704,7 @@
|
|||||||
(set! result (merge result (dict mname mod-val))))))))
|
(set! result (merge result (dict mname mod-val))))))))
|
||||||
((= tag "type-def") nil)
|
((= tag "type-def") nil)
|
||||||
((= tag "exception-def") nil)
|
((= tag "exception-def") nil)
|
||||||
|
((= tag "module-type-def") nil)
|
||||||
((= tag "open")
|
((= tag "open")
|
||||||
(let ((mod-val (ocaml-resolve-module-path (nth decl 1) env)))
|
(let ((mod-val (ocaml-resolve-module-path (nth decl 1) env)))
|
||||||
(cond
|
(cond
|
||||||
@@ -884,6 +885,9 @@
|
|||||||
;; exception E [of T] — purely declarative; raise+match
|
;; exception E [of T] — purely declarative; raise+match
|
||||||
;; already work on tagged ctor values.
|
;; already work on tagged ctor values.
|
||||||
nil)
|
nil)
|
||||||
|
((= tag "module-type-def")
|
||||||
|
;; module type S = sig … end — no-op at runtime.
|
||||||
|
nil)
|
||||||
((or (= tag "open") (= tag "include"))
|
((or (= tag "open") (= tag "include"))
|
||||||
;; open M / include M — bring M's bindings into scope.
|
;; open M / include M — bring M's bindings into scope.
|
||||||
(let ((mod-val (ocaml-resolve-module-path (nth decl 1) env)))
|
(let ((mod-val (ocaml-resolve-module-path (nth decl 1) env)))
|
||||||
|
|||||||
@@ -1282,10 +1282,53 @@
|
|||||||
(else (begin (advance-tok!) (skip))))))
|
(else (begin (advance-tok!) (skip))))))
|
||||||
(skip)))))))
|
(skip)))))))
|
||||||
|
|
||||||
|
;; module type S = sig ... end
|
||||||
|
;; Parsed-and-discarded (signatures are type-level only). Returns
|
||||||
|
;; a (:module-type-def NAME) marker for the eval loop to ignore.
|
||||||
|
(define
|
||||||
|
parse-decl-module-type
|
||||||
|
(fn ()
|
||||||
|
(advance-tok!) ;; "type"
|
||||||
|
(let ((name (ocaml-tok-value (consume! "ctor" nil))))
|
||||||
|
(begin
|
||||||
|
(consume! "op" "=")
|
||||||
|
(cond
|
||||||
|
((at-kw? "sig")
|
||||||
|
(begin
|
||||||
|
(advance-tok!)
|
||||||
|
(let ((depth 1))
|
||||||
|
(begin
|
||||||
|
(define skip
|
||||||
|
(fn ()
|
||||||
|
(cond
|
||||||
|
((>= idx tok-len) nil)
|
||||||
|
((= (ocaml-tok-type (peek-tok)) "eof") nil)
|
||||||
|
((or (at-kw? "sig") (at-kw? "struct") (at-kw? "begin"))
|
||||||
|
(begin (set! depth (+ depth 1)) (advance-tok!) (skip)))
|
||||||
|
((at-kw? "end")
|
||||||
|
(cond
|
||||||
|
((= depth 1) nil)
|
||||||
|
(else
|
||||||
|
(begin (set! depth (- depth 1)) (advance-tok!) (skip)))))
|
||||||
|
(else (begin (advance-tok!) (skip))))))
|
||||||
|
(skip)
|
||||||
|
(consume! "keyword" "end")))))
|
||||||
|
(else
|
||||||
|
;; module type S = AnotherSig — skip-to-boundary.
|
||||||
|
(skip-to-boundary!)))
|
||||||
|
(list :module-type-def name)))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
parse-decl-module
|
parse-decl-module
|
||||||
(fn ()
|
(fn ()
|
||||||
(advance-tok!)
|
(advance-tok!)
|
||||||
|
(cond
|
||||||
|
((at-kw? "type") (parse-decl-module-type))
|
||||||
|
(else (parse-decl-module-rest)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
parse-decl-module-rest
|
||||||
|
(fn ()
|
||||||
(let ((name (ocaml-tok-value (consume! "ctor" nil)))
|
(let ((name (ocaml-tok-value (consume! "ctor" nil)))
|
||||||
(params (list)))
|
(params (list)))
|
||||||
(begin
|
(begin
|
||||||
|
|||||||
@@ -956,6 +956,14 @@ cat > "$TMPFILE" << 'EPOCHS'
|
|||||||
(epoch 2603)
|
(epoch 2603)
|
||||||
(eval "(ocaml-run \"match { x = 5; y = 2 } with | { x = 1; y = y } -> y | _ -> 0\")")
|
(eval "(ocaml-run \"match { x = 5; y = 2 } with | { x = 1; y = y } -> y | _ -> 0\")")
|
||||||
|
|
||||||
|
;; ── module type S = sig … end ─────────────────────────────────
|
||||||
|
(epoch 2700)
|
||||||
|
(eval "(ocaml-parse-program \"module type S = sig val x : int val f : int -> int end\")")
|
||||||
|
(epoch 2701)
|
||||||
|
(eval "(ocaml-run-program \"module type S = sig val x : int end ;; module M = struct let x = 42 end ;; M.x\")")
|
||||||
|
(epoch 2702)
|
||||||
|
(eval "(ocaml-parse-program \"module type EMPTY = sig end\")")
|
||||||
|
|
||||||
EPOCHS
|
EPOCHS
|
||||||
|
|
||||||
OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
||||||
@@ -1512,6 +1520,11 @@ check 2601 "match record name+age" '30'
|
|||||||
check 2602 "match record literal x=1" '2'
|
check 2602 "match record literal x=1" '2'
|
||||||
check 2603 "match record literal fail" '0'
|
check 2603 "match record literal fail" '0'
|
||||||
|
|
||||||
|
# ── module type S = sig … end ──────────────────────────────────
|
||||||
|
check 2700 "module type S parses" '("module-type-def" "S")'
|
||||||
|
check 2701 "module type then module" '42'
|
||||||
|
check 2702 "module type EMPTY" '("module-type-def" "EMPTY")'
|
||||||
|
|
||||||
TOTAL=$((PASS + FAIL))
|
TOTAL=$((PASS + FAIL))
|
||||||
if [ $FAIL -eq 0 ]; then
|
if [ $FAIL -eq 0 ]; then
|
||||||
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
|
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
|
||||||
|
|||||||
@@ -191,9 +191,10 @@ SX CEK evaluator (both JS and OCaml hosts)
|
|||||||
|
|
||||||
- [x] `module M = struct let x = 1 let f y = x + y end` → SX dict
|
- [x] `module M = struct let x = 1 let f y = x + y end` → SX dict
|
||||||
`{"x" 1 "f" <fn>}`.
|
`{"x" 1 "f" <fn>}`.
|
||||||
- [~] `module type S = sig val x : int val f : int -> int end` — signature
|
- [x] `module type S = sig val x : int val f : int -> int end` parses
|
||||||
annotations are parsed-and-skipped (`skip-optional-sig`); typed
|
via `parse-decl-module-type`. Signature contents are skipped
|
||||||
checking deferred to Phase 5.
|
(sig..end nesting tracked) — runtime no-op since types are
|
||||||
|
structural. AST: `(:module-type-def NAME)`.
|
||||||
- [x] `module M : S = struct ... end` — coercive sealing (signature ignored).
|
- [x] `module M : S = struct ... end` — coercive sealing (signature ignored).
|
||||||
- [x] `functor (M : S) -> struct ... end` via shorthand `module F (M) = …`.
|
- [x] `functor (M : S) -> struct ... end` via shorthand `module F (M) = …`.
|
||||||
- [x] `module F = Functor(Base)` — functor application; multi-param via
|
- [x] `module F = Functor(Base)` — functor application; multi-param via
|
||||||
@@ -376,6 +377,11 @@ the "mother tongue" closure: OCaml → SX → OCaml. This means:
|
|||||||
|
|
||||||
_Newest first._
|
_Newest first._
|
||||||
|
|
||||||
|
- 2026-05-08 Phase 4 — `module type S = sig … end` parser (+3 tests,
|
||||||
|
389 total). Signatures are parsed-and-discarded — sig..end balanced
|
||||||
|
skipping. AST: `(:module-type-def NAME)`. Runtime no-op (signatures
|
||||||
|
are type-level). Allows real OCaml code with module type decls to
|
||||||
|
parse and run without removing the sig blocks.
|
||||||
- 2026-05-08 Phase 1+3 — record patterns `{ f = pat; … }` (+4 tests,
|
- 2026-05-08 Phase 1+3 — record patterns `{ f = pat; … }` (+4 tests,
|
||||||
386 total). Parser adds `(:precord (FIELD PAT) …)` alongside
|
386 total). Parser adds `(:precord (FIELD PAT) …)` alongside
|
||||||
the existing record-literal `{` handling. Eval matches against
|
the existing record-literal `{` handling. Eval matches against
|
||||||
|
|||||||
Reference in New Issue
Block a user