From ce75bd684865e017a3d4bc22251911195db02d7f Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 8 May 2026 20:49:26 +0000 Subject: [PATCH] ocaml: phase 1+5.1 type aliases + poly_stack baseline (+3 tests, 469 / 19 baseline) Parser: in parse-decl-type, dispatch on the post-= token: '|' or Ctor -> sum type '{' -> record type otherwise -> type alias (skip to boundary) AST (:type-alias NAME PARAMS) with body discarded. Runtime no-op since SX has no nominal types. poly_stack.ml baseline exercises: module type ELEMENT = sig type t val show : t -> string end module IntElem = struct type t = int let show x = ... end module Make (E : ELEMENT) = struct ... use E.show ... end module IntStack = Make(IntElem) Demonstrates the substrate handles signature decls + abstract types + functor parameter with sig constraint. --- lib/ocaml/baseline/expected.json | 1 + lib/ocaml/baseline/poly_stack.ml | 27 +++++++++++++++++++++++++++ lib/ocaml/eval.sx | 4 ++++ lib/ocaml/parser.sx | 23 +++++++++++++++++++++++ lib/ocaml/test.sh | 10 ++++++++++ plans/ocaml-on-sx.md | 7 +++++++ 6 files changed, 72 insertions(+) create mode 100644 lib/ocaml/baseline/poly_stack.ml diff --git a/lib/ocaml/baseline/expected.json b/lib/ocaml/baseline/expected.json index c22a2ed9..2c99498f 100644 --- a/lib/ocaml/baseline/expected.json +++ b/lib/ocaml/baseline/expected.json @@ -13,6 +13,7 @@ "module_use.ml": 3, "mutable_record.ml": 10, "option_match.ml": 5, + "poly_stack.ml": 5, "queens.ml": 2, "quicksort.ml": 44, "sum_squares.ml": 385, diff --git a/lib/ocaml/baseline/poly_stack.ml b/lib/ocaml/baseline/poly_stack.ml new file mode 100644 index 00000000..fde6b6df --- /dev/null +++ b/lib/ocaml/baseline/poly_stack.ml @@ -0,0 +1,27 @@ +(* Baseline: polymorphic stack via functor over an Element module *) +module type ELEMENT = sig type t val show : t -> string end ;; + +module IntElem = struct + type t = int + let show x = Int.to_string x +end ;; + +module Make (E : ELEMENT) = struct + let create () = ref [] + let push x s = s := x :: !s + let pop s = + match !s with + | [] -> None + | h :: t -> s := t ; Some h + let length s = List.length !s + let to_string s = + String.concat "," (List.map E.show !s) +end ;; + +module IntStack = Make(IntElem) ;; + +let s = IntStack.create () ;; +IntStack.push 1 s ;; +IntStack.push 2 s ;; +IntStack.push 3 s ;; +String.length (IntStack.to_string s) diff --git a/lib/ocaml/eval.sx b/lib/ocaml/eval.sx index 87e55790..778e0d16 100644 --- a/lib/ocaml/eval.sx +++ b/lib/ocaml/eval.sx @@ -807,6 +807,7 @@ (set! result (merge result (dict mname mod-val)))))))) ((= tag "type-def") nil) ((= tag "type-def-record") nil) + ((= tag "type-alias") nil) ((= tag "exception-def") nil) ((= tag "module-type-def") nil) ((= tag "open") @@ -993,6 +994,9 @@ ;; type r = { x : T; y : T } — runtime no-op; records ;; are already dynamic dicts. nil) + ((= tag "type-alias") + ;; type t = SomeType — runtime no-op (no nominal types). + nil) ((= tag "module-type-def") ;; module type S = sig … end — no-op at runtime. nil) diff --git a/lib/ocaml/parser.sx b/lib/ocaml/parser.sx index e425d6e4..3abdd84a 100644 --- a/lib/ocaml/parser.sx +++ b/lib/ocaml/parser.sx @@ -1242,6 +1242,29 @@ (field-more) (consume! "op" "}") (list :type-def-record name tparams fields))))) + ;; Type alias: type t = int / type t = 'a list / etc. + ;; Detected when next token is NOT `|` and NOT a ctor. + ((and (not (at-op? "|")) + (not (= (ocaml-tok-type (peek-tok)) "ctor"))) + (begin + ;; Skip the alias source up to the next boundary. + (define skip-alias + (fn () + (cond + ((>= idx tok-len) nil) + ((= (ocaml-tok-type (peek-tok)) "eof") nil) + ((at-op? ";;") nil) + ((at-kw? "let") nil) + ((at-kw? "type") nil) + ((at-kw? "and") nil) + ((at-kw? "module") nil) + ((at-kw? "exception") nil) + ((at-kw? "open") nil) + ((at-kw? "include") nil) + ((at-kw? "end") nil) + (else (begin (advance-tok!) (skip-alias)))))) + (skip-alias) + (list :type-alias name tparams))) (else (begin (when (at-op? "|") (advance-tok!)) diff --git a/lib/ocaml/test.sh b/lib/ocaml/test.sh index bb87b15e..88ef8c59 100755 --- a/lib/ocaml/test.sh +++ b/lib/ocaml/test.sh @@ -1156,6 +1156,12 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 4502) (eval "(ocaml-run \"try raise (E 5) with | E n when n > 100 -> n | E n -> n + 1000\")") +;; ── type aliases ────────────────────────────────────────────── +(epoch 4600) +(eval "(ocaml-parse-program \"type t = int\")") +(epoch 4601) +(eval "(ocaml-run-program \"type t = int;; 42\")") + EPOCHS OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1831,6 +1837,10 @@ check 4500 "try when guard fires" '5' check 4501 "try when guard skips" '0' check 4502 "try when fall through" '1005' +# ── type aliases ─────────────────────────────────────────────── +check 4600 "type t = int parses" '("type-alias" "t" ())' +check 4601 "type alias decl + use" '42' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL OCaml-on-SX tests passed" diff --git a/plans/ocaml-on-sx.md b/plans/ocaml-on-sx.md index 64922541..f03f97fa 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -407,6 +407,13 @@ _Newest first._ binary search tree (`type 'a tree = Leaf | Node of 'a * 'a tree * 'a tree`) with insert + in-order traversal. Tests parametric ADT, recursive match, List.append, List.fold_left. +- 2026-05-08 Phase 1+5.1 — type aliases + poly_stack baseline (+3 + tests, 469 total + 19 baseline). Parser dispatch on the post-`=` + token: `|` or `Ctor` → sum, `{` → record, otherwise → alias (skip + to boundary). AST `(:type-alias NAME PARAMS)` with body discarded. + Runtime no-op. poly_stack.ml baseline exercises a functor whose + parameter has `type t = int` (record alias) + `let show : t -> + string`. Stack uses ref + module field lookup to format ints. - 2026-05-08 Phase 2+3 — `try ... with | pat when GUARD -> body` guard support (+3 tests, 467 total). parse-try mirrors match/function; eval-try clause loop now dispatches on `case`/`case-when` and falls