Files
rose-ash/lib/ocaml/test.sh
giles 9907c1c58c
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
ocaml: phase 4 'lazy EXPR' + Lazy.force (+2 tests, 496 total)
Tokenizer already had 'lazy' as a keyword. This commit wires it through:

  parser  : parse-prefix emits (:lazy EXPR), like the existing 'assert'
            handler.
  eval    : creates a one-element cell with state ('Thunk' expr env).
  host    : _lazy_force flips the cell to ('Forced' v) on first call
            and returns the cached value thereafter.
  runtime : module Lazy = struct let force lz = _lazy_force lz end.

Memoisation confirmed by tracking a side-effect counter through two
forces of the same lazy:

  let counter = ref 0 in
  let lz = lazy (counter := !counter + 1; 42) in
  let a = Lazy.force lz in
  let b = Lazy.force lz in
  (a + b) * 100 + !counter        = 8401   (= 84*100 + 1)
2026-05-09 01:03:40 +00:00

1971 lines
86 KiB
Bash
Executable File

#!/usr/bin/env bash
# Fast OCaml-on-SX test runner — epoch protocol direct to sx_server.exe.
# Mirrors lib/lua/test.sh.
#
# Usage:
# bash lib/ocaml/test.sh # run all tests
# bash lib/ocaml/test.sh -v # verbose
set -uo pipefail
cd "$(git rev-parse --show-toplevel)"
SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}"
if [ ! -x "$SX_SERVER" ]; then
SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe"
fi
if [ ! -x "$SX_SERVER" ]; then
echo "ERROR: sx_server.exe not found. Run: cd hosts/ocaml && dune build"
exit 1
fi
VERBOSE="${1:-}"
PASS=0
FAIL=0
ERRORS=""
TMPFILE=$(mktemp)
trap "rm -f $TMPFILE" EXIT
cat > "$TMPFILE" << 'EPOCHS'
(epoch 1)
(load "lib/guest/lex.sx")
(load "lib/guest/prefix.sx")
(load "lib/guest/pratt.sx")
(load "lib/guest/match.sx")
(load "lib/guest/hm.sx")
(load "lib/ocaml/tokenizer.sx")
(load "lib/ocaml/parser.sx")
(load "lib/ocaml/eval.sx")
(load "lib/ocaml/runtime.sx")
(load "lib/ocaml/infer.sx")
(load "lib/ocaml/tests/tokenize.sx")
(eval "(ocaml-load-stdlib!)")
;; ── empty / eof ────────────────────────────────────────────────
(epoch 100)
(eval "(ocaml-test-tok-count \"\")")
(epoch 101)
(eval "(ocaml-test-tok-type \"\" 0)")
;; ── numbers ────────────────────────────────────────────────────
(epoch 110)
(eval "(ocaml-test-tok-type \"42\" 0)")
(epoch 111)
(eval "(ocaml-test-tok-value \"42\" 0)")
(epoch 112)
(eval "(ocaml-test-tok-value \"3.14\" 0)")
(epoch 113)
(eval "(ocaml-test-tok-value \"0xff\" 0)")
(epoch 114)
(eval "(ocaml-test-tok-value \"1e3\" 0)")
(epoch 115)
(eval "(ocaml-test-tok-value \"1_000_000\" 0)")
(epoch 116)
(eval "(ocaml-test-tok-value \"3.14e-2\" 0)")
;; ── identifiers / constructors / keywords ─────────────────────
(epoch 120)
(eval "(ocaml-test-tok-type \"foo\" 0)")
(epoch 121)
(eval "(ocaml-test-tok-value \"foo_bar1\" 0)")
(epoch 122)
(eval "(ocaml-test-tok-type \"Some\" 0)")
(epoch 123)
(eval "(ocaml-test-tok-value \"Some\" 0)")
(epoch 124)
(eval "(ocaml-test-tok-type \"let\" 0)")
(epoch 125)
(eval "(ocaml-test-tok-value \"match\" 0)")
(epoch 126)
(eval "(ocaml-test-tok-type \"true\" 0)")
(epoch 127)
(eval "(ocaml-test-tok-value \"false\" 0)")
(epoch 128)
(eval "(ocaml-test-tok-value \"name'\" 0)")
;; ── strings ────────────────────────────────────────────────────
(epoch 130)
(eval "(ocaml-test-tok-type \"\\\"hi\\\"\" 0)")
(epoch 131)
(eval "(ocaml-test-tok-value \"\\\"hi\\\"\" 0)")
(epoch 132)
(eval "(ocaml-test-tok-value \"\\\"a\\\\nb\\\"\" 0)")
;; ── chars ──────────────────────────────────────────────────────
(epoch 140)
(eval "(ocaml-test-tok-type \"'a'\" 0)")
(epoch 141)
(eval "(ocaml-test-tok-value \"'a'\" 0)")
(epoch 142)
(eval "(ocaml-test-tok-value \"'\\\\n'\" 0)")
;; ── type variables ─────────────────────────────────────────────
(epoch 145)
(eval "(ocaml-test-tok-type \"'a\" 0)")
(epoch 146)
(eval "(ocaml-test-tok-value \"'a\" 0)")
;; ── multi-char operators ───────────────────────────────────────
(epoch 150)
(eval "(ocaml-test-tok-value \"->\" 0)")
(epoch 151)
(eval "(ocaml-test-tok-value \"|>\" 0)")
(epoch 152)
(eval "(ocaml-test-tok-value \"<-\" 0)")
(epoch 153)
(eval "(ocaml-test-tok-value \":=\" 0)")
(epoch 154)
(eval "(ocaml-test-tok-value \"::\" 0)")
(epoch 155)
(eval "(ocaml-test-tok-value \";;\" 0)")
(epoch 156)
(eval "(ocaml-test-tok-value \"@@\" 0)")
(epoch 157)
(eval "(ocaml-test-tok-value \"<>\" 0)")
(epoch 158)
(eval "(ocaml-test-tok-value \"&&\" 0)")
(epoch 159)
(eval "(ocaml-test-tok-value \"||\" 0)")
;; ── single-char punctuation ────────────────────────────────────
(epoch 160)
(eval "(ocaml-test-tok-value \"+\" 0)")
(epoch 161)
(eval "(ocaml-test-tok-value \"|\" 0)")
(epoch 162)
(eval "(ocaml-test-tok-value \";\" 0)")
(epoch 163)
(eval "(ocaml-test-tok-value \"(\" 0)")
(epoch 164)
(eval "(ocaml-test-tok-value \"!\" 0)")
(epoch 165)
(eval "(ocaml-test-tok-value \"@\" 0)")
;; ── comments ───────────────────────────────────────────────────
(epoch 170)
(eval "(ocaml-test-tok-count \"(* hi *)\")")
(epoch 171)
(eval "(ocaml-test-tok-value \"(* c *) 42\" 0)")
(epoch 172)
(eval "(ocaml-test-tok-count \"(* outer (* inner *) end *) 1\")")
(epoch 173)
(eval "(ocaml-test-tok-value \"(* outer (* inner *) end *) 1\" 0)")
;; ── compound expressions ───────────────────────────────────────
(epoch 180)
(eval "(ocaml-test-tok-count \"let x = 1\")")
(epoch 181)
(eval "(ocaml-test-tok-type \"let x = 1\" 0)")
(epoch 182)
(eval "(ocaml-test-tok-value \"let x = 1\" 0)")
(epoch 183)
(eval "(ocaml-test-tok-type \"let x = 1\" 1)")
(epoch 184)
(eval "(ocaml-test-tok-value \"let x = 1\" 2)")
(epoch 185)
(eval "(ocaml-test-tok-value \"let x = 1\" 3)")
(epoch 190)
(eval "(ocaml-test-tok-count \"match x with | None -> 0 | Some y -> y\")")
(epoch 191)
(eval "(ocaml-test-tok-value \"fun x -> x + 1\" 2)")
(epoch 192)
(eval "(ocaml-test-tok-type \"fun x -> x + 1\" 2)")
(epoch 193)
(eval "(ocaml-test-tok-type \"Some 42\" 0)")
(epoch 194)
(eval "(ocaml-test-tok-value \"a |> f |> g\" 1)")
(epoch 195)
(eval "(ocaml-test-tok-value \"x := !y\" 1)")
;; ── Phase 1.parse: parser ──────────────────────────────────────
;; Atoms
(epoch 200)
(eval "(ocaml-parse \"42\")")
(epoch 201)
(eval "(ocaml-parse \"3.14\")")
(epoch 202)
(eval "(ocaml-parse \"\\\"hi\\\"\")")
(epoch 203)
(eval "(ocaml-parse \"'a'\")")
(epoch 204)
(eval "(ocaml-parse \"true\")")
(epoch 205)
(eval "(ocaml-parse \"false\")")
(epoch 206)
(eval "(ocaml-parse \"x\")")
(epoch 207)
(eval "(ocaml-parse \"Some\")")
(epoch 208)
(eval "(ocaml-parse \"()\")")
;; Application (left-assoc)
(epoch 210)
(eval "(ocaml-parse \"f x\")")
(epoch 211)
(eval "(ocaml-parse \"f x y\")")
(epoch 212)
(eval "(ocaml-parse \"f (g x)\")")
(epoch 213)
(eval "(ocaml-parse \"Some 42\")")
;; Binops with precedence
(epoch 220)
(eval "(ocaml-parse \"1 + 2\")")
(epoch 221)
(eval "(ocaml-parse \"a + b * c\")")
(epoch 222)
(eval "(ocaml-parse \"a * b + c\")")
(epoch 223)
(eval "(ocaml-parse \"a && b || c\")")
(epoch 224)
(eval "(ocaml-parse \"a = b\")")
(epoch 225)
(eval "(ocaml-parse \"a ^ b ^ c\")")
(epoch 226)
(eval "(ocaml-parse \"a :: b :: []\")")
(epoch 227)
(eval "(ocaml-parse \"(a + b) * c\")")
(epoch 228)
(eval "(ocaml-parse \"a |> f |> g\")")
(epoch 229)
(eval "(ocaml-parse \"x mod 2\")")
;; Prefix
(epoch 230)
(eval "(ocaml-parse \"-x\")")
(epoch 231)
(eval "(ocaml-parse \"-1 + 2\")")
;; Tuples & lists
(epoch 240)
(eval "(ocaml-parse \"(1, 2, 3)\")")
(epoch 241)
(eval "(ocaml-parse \"[1; 2; 3]\")")
(epoch 242)
(eval "(ocaml-parse \"[]\")")
;; if / fun / let / let rec
(epoch 250)
(eval "(ocaml-parse \"if x then 1 else 2\")")
(epoch 251)
(eval "(ocaml-parse \"if c then x\")")
(epoch 252)
(eval "(ocaml-parse \"fun x -> x + 1\")")
(epoch 253)
(eval "(ocaml-parse \"fun x y -> x + y\")")
(epoch 254)
(eval "(ocaml-parse \"let x = 1 in x\")")
(epoch 255)
(eval "(ocaml-parse \"let f x = x + 1 in f 2\")")
(epoch 256)
(eval "(ocaml-parse \"let rec f x = f x in f 1\")")
(epoch 257)
(eval "(ocaml-parse \"let f x y = x + y in f 1 2\")")
;; begin/end
(epoch 260)
(eval "(ocaml-parse \"begin 1 + 2 end\")")
;; ── Top-level decls ────────────────────────────────────────────
(epoch 270)
(eval "(ocaml-parse-program \"let x = 1\")")
(epoch 271)
(eval "(ocaml-parse-program \"let x = 1 ;;\")")
(epoch 272)
(eval "(ocaml-parse-program \"let f x = x + 1\")")
(epoch 273)
(eval "(ocaml-parse-program \"let rec fact n = if n = 0 then 1 else n * fact (n - 1)\")")
(epoch 274)
(eval "(ocaml-parse-program \"let x = 1 let y = 2\")")
(epoch 275)
(eval "(ocaml-parse-program \"1 + 2 ;;\")")
(epoch 276)
(eval "(ocaml-parse-program \"let x = 1 ;; let y = 2 ;; x + y\")")
(epoch 277)
(eval "(len (ocaml-parse-program \"let x = 1 ;; let y = 2 ;; x + y\"))")
(epoch 278)
(eval "(ocaml-parse-program \"\")")
;; ── Match / patterns ───────────────────────────────────────────
(epoch 300)
(eval "(ocaml-parse \"match x with | None -> 0 | Some y -> y\")")
(epoch 301)
(eval "(ocaml-parse \"match x with None -> 0 | Some y -> y\")")
(epoch 302)
(eval "(ocaml-parse \"match l with | [] -> 0 | h :: t -> 1\")")
(epoch 303)
(eval "(ocaml-parse \"match p with | (a, b) -> a + b\")")
(epoch 304)
(eval "(ocaml-parse \"match n with | 0 -> 1 | _ -> n\")")
(epoch 305)
(eval "(ocaml-parse \"match x with | true -> 1 | false -> 0\")")
(epoch 306)
(eval "(ocaml-parse \"match x with | Pair (a, b) -> a + b\")")
(epoch 307)
(eval "(ocaml-parse \"match x with | \\\"hi\\\" -> 1 | _ -> 0\")")
(epoch 308)
(eval "(ocaml-parse \"match x with | () -> 0\")")
;; ── Sequences (;) ──────────────────────────────────────────────
(epoch 320)
(eval "(ocaml-parse \"1; 2\")")
(epoch 321)
(eval "(ocaml-parse \"1; 2; 3\")")
(epoch 322)
(eval "(ocaml-parse \"(1; 2)\")")
(epoch 323)
(eval "(ocaml-parse \"begin a; b; c end\")")
(epoch 324)
(eval "(ocaml-parse \"let x = 1 in x; x\")")
(epoch 325)
(eval "(ocaml-parse \"if c then (a; b) else c\")")
(epoch 326)
(eval "(ocaml-parse \"[1; 2; 3]\")")
(epoch 327)
(eval "(ocaml-parse \"1; 2;\")")
(epoch 328)
(eval "(ocaml-parse \"begin a; end\")")
(epoch 329)
(eval "(ocaml-parse \"match x with | _ -> a; b\")")
;; ── Phase 2: evaluator ─────────────────────────────────────────
;; Atoms
(epoch 400)
(eval "(ocaml-run \"42\")")
(epoch 401)
(eval "(ocaml-run \"3.14\")")
(epoch 402)
(eval "(ocaml-run \"true\")")
(epoch 403)
(eval "(ocaml-run \"false\")")
(epoch 404)
(eval "(ocaml-run \"\\\"hi\\\"\")")
;; Arithmetic
(epoch 410)
(eval "(ocaml-run \"1 + 2\")")
(epoch 411)
(eval "(ocaml-run \"10 - 3\")")
(epoch 412)
(eval "(ocaml-run \"4 * 5\")")
(epoch 413)
(eval "(ocaml-run \"20 / 4\")")
(epoch 414)
(eval "(ocaml-run \"10 mod 3\")")
(epoch 415)
(eval "(ocaml-run \"2 ** 10\")")
(epoch 416)
(eval "(ocaml-run \"(1 + 2) * 3\")")
(epoch 417)
(eval "(ocaml-run \"1 + 2 * 3\")")
(epoch 418)
(eval "(ocaml-run \"-5 + 10\")")
;; Comparison & boolean
(epoch 420)
(eval "(ocaml-run \"1 < 2\")")
(epoch 421)
(eval "(ocaml-run \"3 > 2\")")
(epoch 422)
(eval "(ocaml-run \"2 = 2\")")
(epoch 423)
(eval "(ocaml-run \"1 <> 2\")")
(epoch 424)
(eval "(ocaml-run \"true && false\")")
(epoch 425)
(eval "(ocaml-run \"true || false\")")
(epoch 426)
(eval "(ocaml-run \"not false\")")
;; String
(epoch 430)
(eval "(ocaml-run \"\\\"a\\\" ^ \\\"b\\\"\")")
(epoch 431)
(eval "(ocaml-run \"\\\"hello\\\" ^ \\\" \\\" ^ \\\"world\\\"\")")
;; Conditional
(epoch 440)
(eval "(ocaml-run \"if true then 1 else 2\")")
(epoch 441)
(eval "(ocaml-run \"if 1 > 2 then 100 else 200\")")
;; Let / lambda / app
(epoch 450)
(eval "(ocaml-run \"let x = 5 in x * 2\")")
(epoch 451)
(eval "(ocaml-run \"let f x = x + 1 in f 41\")")
(epoch 452)
(eval "(ocaml-run \"let f x y = x + y in f 3 4\")")
(epoch 453)
(eval "(ocaml-run \"(fun x -> x * x) 7\")")
(epoch 454)
(eval "(ocaml-run \"(fun x -> fun y -> x + y) 10 20\")")
(epoch 455)
(eval "(ocaml-run \"let f = fun x -> x + 1 in f 9\")")
;; Closures capture
(epoch 460)
(eval "(ocaml-run \"let x = 10 in let f y = x + y in f 5\")")
(epoch 461)
(eval "(ocaml-run \"let make_adder n = fun x -> n + x in (make_adder 100) 1\")")
;; Recursion
(epoch 470)
(eval "(ocaml-run \"let rec fact n = if n = 0 then 1 else n * fact (n - 1) in fact 5\")")
(epoch 471)
(eval "(ocaml-run \"let rec fib n = if n < 2 then n else fib (n - 1) + fib (n - 2) in fib 10\")")
(epoch 472)
(eval "(ocaml-run \"let rec sum n = if n = 0 then 0 else n + sum (n - 1) in sum 100\")")
;; Sequence
(epoch 480)
(eval "(ocaml-run \"1; 2; 3\")")
(epoch 481)
(eval "(ocaml-run \"begin 10 end\")")
;; Programs (top-level decls)
(epoch 490)
(eval "(ocaml-run-program \"let x = 1;; let y = 2;; x + y\")")
(epoch 491)
(eval "(ocaml-run-program \"let rec fact n = if n = 0 then 1 else n * fact (n - 1);; fact 6\")")
(epoch 492)
(eval "(ocaml-run-program \"let inc x = x + 1;; let double x = x * 2;; double (inc 4)\")")
;; Pipe
(epoch 495)
(eval "(ocaml-run \"let f x = x * 2 in 5 |> f\")")
;; ── Phase 3: ADTs + match (eval) ───────────────────────────────
;; Constructors
(epoch 500)
(eval "(ocaml-run \"None\")")
(epoch 501)
(eval "(ocaml-run \"Some 42\")")
(epoch 502)
(eval "(ocaml-run \"Some (1, 2)\")")
;; Match — option
(epoch 510)
(eval "(ocaml-run \"match Some 5 with | None -> 0 | Some y -> y\")")
(epoch 511)
(eval "(ocaml-run \"match None with | None -> 0 | Some y -> y\")")
;; Match — literals
(epoch 520)
(eval "(ocaml-run \"match 3 with | 1 -> 100 | 2 -> 200 | _ -> 999\")")
(epoch 521)
(eval "(ocaml-run \"match true with | true -> 1 | false -> 0\")")
(epoch 522)
(eval "(ocaml-run \"match \\\"hi\\\" with | \\\"hi\\\" -> 1 | _ -> 0\")")
;; Match — tuples
(epoch 530)
(eval "(ocaml-run \"match (1, 2) with | (a, b) -> a + b\")")
(epoch 531)
(eval "(ocaml-run \"match (1, 2, 3) with | (a, b, c) -> a * b * c\")")
;; Match — list cons / nil
(epoch 540)
(eval "(ocaml-run \"match [1; 2; 3] with | [] -> 0 | h :: _ -> h\")")
(epoch 541)
(eval "(ocaml-run \"match [] with | [] -> 0 | h :: _ -> h\")")
(epoch 542)
(eval "(ocaml-run \"match [1; 2; 3] with | [a; b; c] -> a + b + c | _ -> 0\")")
(epoch 543)
(eval "(ocaml-run \"let rec len lst = match lst with | [] -> 0 | _ :: t -> 1 + len t in len [1; 2; 3; 4; 5]\")")
(epoch 544)
(eval "(ocaml-run \"let rec sum lst = match lst with | [] -> 0 | h :: t -> h + sum t in sum [1; 2; 3; 4; 5]\")")
;; Match — wildcard + var
(epoch 550)
(eval "(ocaml-run \"match 99 with | _ -> 1\")")
(epoch 551)
(eval "(ocaml-run \"match 99 with | x -> x + 1\")")
;; Constructors with tuple args
(epoch 560)
(eval "(ocaml-run \"match Pair (1, 2) with | Pair (a, b) -> a * b\")")
;; ── References (ref / ! / :=) ──────────────────────────────────
(epoch 600)
(eval "(ocaml-run \"let r = ref 5 in !r\")")
(epoch 601)
(eval "(ocaml-run \"let r = ref 5 in r := 10; !r\")")
(epoch 602)
(eval "(ocaml-run \"let r = ref 0 in r := !r + 1; r := !r + 1; !r\")")
(epoch 603)
(eval "(ocaml-run \"let r = ref 100 in let f x = r := !r + x in f 5; f 10; !r\")")
(epoch 604)
(eval "(ocaml-run \"let r = ref \\\"a\\\" in r := \\\"b\\\"; !r\")")
(epoch 605)
(eval "(ocaml-run \"let count = ref 0 in let rec loop n = if n = 0 then !count else (count := !count + n; loop (n - 1)) in loop 5\")")
;; ── for / while loops ──────────────────────────────────────────
(epoch 620)
(eval "(ocaml-run \"let s = ref 0 in for i = 1 to 5 do s := !s + i done; !s\")")
(epoch 621)
(eval "(ocaml-run \"let s = ref 0 in for i = 5 downto 1 do s := !s + i done; !s\")")
(epoch 622)
(eval "(ocaml-run \"let i = ref 0 in let s = ref 0 in while !i < 5 do i := !i + 1; s := !s + !i done; !s\")")
(epoch 623)
(eval "(ocaml-run \"let s = ref 0 in for i = 1 to 100 do s := !s + i done; !s\")")
(epoch 624)
(eval "(ocaml-run \"let p = ref 1 in for i = 1 to 5 do p := !p * i done; !p\")")
;; ── function (sugar for fun + match) ───────────────────────────
(epoch 640)
(eval "(ocaml-run \"(function | None -> 0 | Some x -> x) (Some 7)\")")
(epoch 641)
(eval "(ocaml-run \"let f = function | None -> 0 | Some x -> x in f None\")")
(epoch 642)
(eval "(ocaml-run \"let rec len = function | [] -> 0 | _ :: t -> 1 + len t in len [1; 2; 3]\")")
(epoch 643)
(eval "(ocaml-run-program \"let rec map f = function | [] -> [] | h :: t -> f h :: map f t;; map (fun x -> x * x) [1; 2; 3; 4]\")")
;; ── try / with / raise ─────────────────────────────────────────
(epoch 660)
(eval "(ocaml-run \"try 1 + 2 with | _ -> 0\")")
(epoch 661)
(eval "(ocaml-run \"try raise (Foo 5) with | Foo x -> x | Bar -> 99\")")
(epoch 662)
(eval "(ocaml-run \"try raise Bar with | Foo x -> x | Bar -> 99\")")
(epoch 663)
(eval "(ocaml-run \"try failwith \\\"oops\\\" with | Failure msg -> msg\")")
(epoch 664)
(eval "(ocaml-run \"try (raise (Foo 1); 999) with | Foo x -> x + 100\")")
(epoch 665)
(eval "(ocaml-run \"let f x = if x < 0 then raise (NegArg x) else x * 2 in try f (-5) with | NegArg n -> n\")")
;; ── Phase 4: Modules + field access ────────────────────────────
(epoch 700)
(eval "(ocaml-parse \"M.x\")")
(epoch 701)
(eval "(ocaml-parse \"r.field\")")
(epoch 702)
(eval "(ocaml-parse \"M.M2.x\")")
(epoch 703)
(eval "(ocaml-parse \"f r.x\")")
(epoch 710)
(eval "(ocaml-run-program \"module M = struct let x = 42 end ;; M.x\")")
(epoch 711)
(eval "(ocaml-run-program \"module M = struct let f x = x + 1 end ;; M.f 41\")")
(epoch 712)
(eval "(ocaml-run-program \"module M = struct let x = 1 let y = 2 end ;; M.x + M.y\")")
(epoch 713)
(eval "(ocaml-run-program \"module Math = struct let pi = 3.14 let square x = x * x end ;; Math.square 5\")")
(epoch 714)
(eval "(ocaml-run-program \"module Outer = struct module Inner = struct let v = 99 end end ;; Outer.Inner.v\")")
(epoch 715)
(eval "(ocaml-run-program \"module M = struct let rec fact n = if n = 0 then 1 else n * fact (n - 1) end ;; M.fact 5\")")
(epoch 716)
(eval "(ocaml-run-program \"module Pair = struct let make a b = (a, b) let swap p = match p with | (x, y) -> (y, x) end ;; Pair.swap (Pair.make 1 2)\")")
;; ── open / include ─────────────────────────────────────────────
(epoch 730)
(eval "(ocaml-run-program \"module M = struct let x = 42 let f y = y + 1 end ;; open M ;; f x\")")
(epoch 731)
(eval "(ocaml-run-program \"module Math = struct let pi = 3 let sq x = x * x end ;; module Sphere = struct include Math let area r = 4 * pi * sq r end ;; Sphere.area 2\")")
(epoch 732)
(eval "(ocaml-run-program \"module M = struct let x = 1 end ;; module N = struct open M let y = x + 10 end ;; N.y\")")
(epoch 733)
(eval "(ocaml-run-program \"module Math = struct let pi = 3 let sq x = x * x end ;; module Sphere = struct include Math let area r = 4 * pi * sq r end ;; Sphere.pi\")")
(epoch 734)
(eval "(ocaml-run-program \"module M = struct let x = 1 let y = 2 end ;; module N = struct include M let z = x + y end ;; N.z\")")
;; ── Functors ───────────────────────────────────────────────────
(epoch 750)
(eval "(ocaml-run-program \"module Add (M) = struct let add x = x + M.n end ;; module Five = struct let n = 5 end ;; module AddFive = Add(Five) ;; AddFive.add 10\")")
(epoch 751)
(eval "(ocaml-run-program \"module M = struct let x = 1 end ;; module N = M ;; N.x\")")
(epoch 752)
(eval "(ocaml-run-program \"module Outer = struct module Inner = struct let v = 42 end end ;; module Alias = Outer.Inner ;; Alias.v\")")
(epoch 753)
(eval "(ocaml-run-program \"module Pair (A) (B) = struct let mk = (A.x, B.x) end ;; module One = struct let x = 1 end ;; module Two = struct let x = 2 end ;; module P = Pair(One)(Two) ;; P.mk\")")
(epoch 754)
(eval "(ocaml-run-program \"module Identity (M) = struct include M end ;; module Base = struct let v = 99 end ;; module Same = Identity(Base) ;; Same.v\")")
;; ── Phase 6: stdlib slice (List, Option, Result) ───────────────
(epoch 800)
(eval "(ocaml-run \"List.length [1; 2; 3; 4]\")")
(epoch 801)
(eval "(ocaml-run \"List.length []\")")
(epoch 802)
(eval "(ocaml-run \"List.map (fun x -> x * 2) [1; 2; 3]\")")
(epoch 803)
(eval "(ocaml-run \"List.filter (fun x -> x > 2) [1; 2; 3; 4; 5]\")")
(epoch 804)
(eval "(ocaml-run \"List.fold_left (fun a b -> a + b) 0 [1; 2; 3; 4; 5]\")")
(epoch 805)
(eval "(ocaml-run \"List.fold_right (fun x acc -> x :: acc) [1; 2; 3] []\")")
(epoch 806)
(eval "(ocaml-run \"List.rev [1; 2; 3]\")")
(epoch 807)
(eval "(ocaml-run \"List.append [1; 2] [3; 4]\")")
(epoch 808)
(eval "(ocaml-run \"List.mem 3 [1; 2; 3]\")")
(epoch 809)
(eval "(ocaml-run \"List.mem 99 [1; 2; 3]\")")
(epoch 810)
(eval "(ocaml-run \"List.for_all (fun x -> x > 0) [1; 2; 3]\")")
(epoch 811)
(eval "(ocaml-run \"List.exists (fun x -> x > 2) [1; 2; 3]\")")
(epoch 812)
(eval "(ocaml-run \"List.hd [10; 20; 30]\")")
(epoch 813)
(eval "(ocaml-run \"List.nth [10; 20; 30] 1\")")
(epoch 820)
(eval "(ocaml-run \"Option.map (fun x -> x + 1) (Some 41)\")")
(epoch 821)
(eval "(ocaml-run \"Option.map (fun x -> x + 1) None\")")
(epoch 822)
(eval "(ocaml-run \"Option.value (Some 7) 0\")")
(epoch 823)
(eval "(ocaml-run \"Option.value None 42\")")
(epoch 824)
(eval "(ocaml-run \"Option.is_some (Some 1)\")")
(epoch 825)
(eval "(ocaml-run \"Option.is_none None\")")
(epoch 830)
(eval "(ocaml-run \"Result.map (fun x -> x + 1) (Ok 5)\")")
(epoch 831)
(eval "(ocaml-run \"Result.is_ok (Ok 1)\")")
(epoch 832)
(eval "(ocaml-run \"Result.is_error (Error \\\"oops\\\")\")")
;; ── let ... and ... mutual recursion ──────────────────────────
(epoch 850)
(eval "(ocaml-run-program \"let rec even n = if n = 0 then true else odd (n - 1) and odd n = if n = 0 then false else even (n - 1);; even 10\")")
(epoch 851)
(eval "(ocaml-run-program \"let rec even n = if n = 0 then true else odd (n - 1) and odd n = if n = 0 then false else even (n - 1);; odd 7\")")
(epoch 852)
(eval "(ocaml-run-program \"let x = 1 and y = 2;; x + y\")")
;; ── Phase 5: Hindley-Milner type inference ────────────────────
(epoch 900)
(eval "(ocaml-type-of \"42\")")
(epoch 901)
(eval "(ocaml-type-of \"true\")")
(epoch 902)
(eval "(ocaml-type-of \"\\\"hi\\\"\")")
(epoch 903)
(eval "(ocaml-type-of \"1 + 2\")")
(epoch 904)
(eval "(ocaml-type-of \"fun x -> x + 1\")")
(epoch 905)
(eval "(ocaml-type-of \"fun x -> x\")")
(epoch 906)
(eval "(ocaml-type-of \"fun x y -> x + y\")")
(epoch 907)
(eval "(ocaml-type-of \"let f x = x + 1 in f 10\")")
(epoch 908)
(eval "(ocaml-type-of \"let id = fun x -> x in id 5\")")
(epoch 909)
(eval "(ocaml-type-of \"let id = fun x -> x in id true\")")
(epoch 910)
(eval "(ocaml-type-of \"if true then 1 else 2\")")
(epoch 911)
(eval "(ocaml-type-of \"fun f -> fun x -> f (f x)\")")
(epoch 912)
(eval "(ocaml-type-of \"fun b -> if b then 1 else 0\")")
(epoch 913)
(eval "(ocaml-type-of \"not true\")")
;; ── Phase 6 expanded: String / Char / Int / Float modules ─────
(epoch 950)
(eval "(ocaml-run \"String.length \\\"hello\\\"\")")
(epoch 951)
(eval "(ocaml-run \"String.uppercase_ascii \\\"hi\\\"\")")
(epoch 952)
(eval "(ocaml-run \"String.lowercase_ascii \\\"HI\\\"\")")
(epoch 953)
(eval "(ocaml-run \"String.sub \\\"hello\\\" 1 3\")")
(epoch 954)
(eval "(ocaml-run \"String.starts_with \\\"he\\\" \\\"hello\\\"\")")
(epoch 955)
(eval "(ocaml-run \"String.concat \\\",\\\" [\\\"a\\\"; \\\"b\\\"; \\\"c\\\"]\")")
(epoch 960)
(eval "(ocaml-run \"Char.code \\\"A\\\"\")")
(epoch 961)
(eval "(ocaml-run \"Char.chr 65\")")
(epoch 970)
(eval "(ocaml-run \"Int.to_string 42\")")
(epoch 971)
(eval "(ocaml-run \"Int.of_string \\\"123\\\"\")")
(epoch 972)
(eval "(ocaml-run \"Int.abs (-5)\")")
(epoch 973)
(eval "(ocaml-run \"Int.max 7 3\")")
(epoch 974)
(eval "(ocaml-run \"Int.min 7 3\")")
;; ── Unit / wildcard parameters ──────────────────────────────────
(epoch 1000)
(eval "(ocaml-run \"let f () = 42 in f ()\")")
(epoch 1001)
(eval "(ocaml-run \"(fun () -> 99) ()\")")
(epoch 1002)
(eval "(ocaml-run \"let f _ = 1 in f 5\")")
(epoch 1003)
(eval "(ocaml-run-program \"let f () = 7;; f ()\")")
(epoch 1004)
(eval "(ocaml-run-program \"let g _ x = x + 1;; g 99 41\")")
;; ── Records ────────────────────────────────────────────────────
(epoch 1100)
(eval "(ocaml-run \"let r = { x = 1; y = 2 } in r.x\")")
(epoch 1101)
(eval "(ocaml-run \"let r = { x = 1; y = 2 } in r.x + r.y\")")
(epoch 1102)
(eval "(ocaml-run \"let r = { x = 1; y = 2 } in let r2 = { r with x = 99 } in r2.x + r2.y\")")
(epoch 1103)
(eval "(ocaml-run \"let p = { name = \\\"Bob\\\"; age = 30 } in p.name\")")
(epoch 1104)
(eval "(ocaml-run \"let p = { name = \\\"Bob\\\"; age = 30 } in p.age\")")
(epoch 1105)
(eval "(ocaml-run-program \"let r = { x = 1; y = 2 };; r.x + r.y\")")
;; ── as / when in match ─────────────────────────────────────────
(epoch 1200)
(eval "(ocaml-run \"match Some 5 with | Some x as p -> x | None -> 0\")")
(epoch 1201)
(eval "(ocaml-run \"match 5 with | n when n > 0 -> 1 | n when n < 0 -> -1 | _ -> 0\")")
(epoch 1202)
(eval "(ocaml-run \"match (-3) with | n when n > 0 -> 1 | n when n < 0 -> -1 | _ -> 0\")")
(epoch 1203)
(eval "(ocaml-run \"match 0 with | n when n > 0 -> 1 | n when n < 0 -> -1 | _ -> 0\")")
(epoch 1204)
(eval "(ocaml-run \"match (Some 7) with | None -> 0 | Some x when x > 5 -> x * 10 | Some x -> x\")")
(epoch 1205)
(eval "(ocaml-run \"match (Some 3) with | None -> 0 | Some x when x > 5 -> x * 10 | Some x -> x\")")
;; ── type declarations (parser + runtime) ──────────────────────
(epoch 1300)
(eval "(ocaml-parse-program \"type color = Red | Green | Blue\")")
(epoch 1301)
(eval "(ocaml-parse-program \"type shape = Circle of int | Rect of int | Square of int\")")
(epoch 1302)
(eval "(ocaml-run-program \"type color = Red | Green | Blue ;; match Red with | Red -> 1 | Green -> 2 | Blue -> 3\")")
(epoch 1303)
(eval "(ocaml-run-program \"type color = Red | Green | Blue ;; match Blue with | Red -> 1 | Green -> 2 | Blue -> 3\")")
(epoch 1304)
(eval "(ocaml-run-program \"type shape = Circle of int | Square of int ;; match Circle 5 with | Circle r -> r | Square s -> s\")")
;; ── exception declarations ────────────────────────────────────
(epoch 1320)
(eval "(ocaml-parse-program \"exception MyExn\")")
(epoch 1321)
(eval "(ocaml-parse-program \"exception MyExn of int\")")
(epoch 1322)
(eval "(ocaml-run-program \"exception E of int ;; try raise (E 5) with | E n -> n\")")
(epoch 1323)
(eval "(ocaml-run-program \"exception E of string ;; try raise (E \\\"oops\\\") with | E s -> s\")")
;; ── Phase 6 expanded stdlib (List/Option/Result extensions) ───
(epoch 1400)
(eval "(ocaml-run \"List.concat [[1;2];[3];[4;5]]\")")
(epoch 1401)
(eval "(ocaml-run \"List.init 5 (fun i -> i * 10)\")")
(epoch 1402)
(eval "(ocaml-run \"List.find_opt (fun x -> x > 2) [1;2;3;4]\")")
(epoch 1403)
(eval "(ocaml-run \"List.find_opt (fun x -> x > 99) [1;2;3]\")")
(epoch 1404)
(eval "(ocaml-run \"List.mapi (fun i x -> i + x) [10;20;30]\")")
(epoch 1405)
(eval "(ocaml-run \"List.partition (fun x -> x > 2) [1;2;3;4]\")")
(epoch 1406)
(eval "(ocaml-run \"List.assoc 2 [(1, \\\"a\\\"); (2, \\\"b\\\"); (3, \\\"c\\\")]\")")
(epoch 1407)
(eval "(ocaml-run \"List.assoc_opt 99 [(1, \\\"a\\\")]\")")
(epoch 1410)
(eval "(ocaml-run \"Option.fold 0 (fun x -> x * 10) (Some 7)\")")
(epoch 1411)
(eval "(ocaml-run \"Option.fold 0 (fun x -> x * 10) None\")")
(epoch 1412)
(eval "(ocaml-run \"Option.to_list (Some 7)\")")
(epoch 1413)
(eval "(ocaml-run \"Option.to_list None\")")
(epoch 1420)
(eval "(ocaml-run \"Result.get_ok (Ok 42)\")")
(epoch 1421)
(eval "(ocaml-run \"Result.to_option (Ok 1)\")")
(epoch 1422)
(eval "(ocaml-run \"Result.map_error (fun e -> e + 1) (Error 5)\")")
;; ── HM extensions: tuples + lists ──────────────────────────────
(epoch 1500)
(eval "(ocaml-type-of \"(1, 2)\")")
(epoch 1501)
(eval "(ocaml-type-of \"(1, true, \\\"hi\\\")\")")
(epoch 1502)
(eval "(ocaml-type-of \"[1; 2; 3]\")")
(epoch 1503)
(eval "(ocaml-type-of \"[]\")")
(epoch 1504)
(eval "(ocaml-type-of \"fun x -> [x; x]\")")
(epoch 1505)
(eval "(ocaml-type-of \"fun x y -> (x, y)\")")
(epoch 1506)
(eval "(ocaml-type-of \"[true; false]\")")
;; ── Hashtbl ────────────────────────────────────────────────────
(epoch 1600)
(eval "(ocaml-run-program \"let t = Hashtbl.create 10;; Hashtbl.add t \\\"x\\\" 42;; Hashtbl.find t \\\"x\\\"\")")
(epoch 1601)
(eval "(ocaml-run-program \"let t = Hashtbl.create 10;; Hashtbl.add t 1 \\\"a\\\";; Hashtbl.add t 2 \\\"b\\\";; Hashtbl.length t\")")
(epoch 1602)
(eval "(ocaml-run-program \"let t = Hashtbl.create 10;; Hashtbl.add t 1 100;; Hashtbl.find_opt t 99\")")
(epoch 1603)
(eval "(ocaml-run-program \"let t = Hashtbl.create 10;; Hashtbl.add t 1 100;; Hashtbl.mem t 1\")")
(epoch 1604)
(eval "(ocaml-run-program \"let t = Hashtbl.create 10;; Hashtbl.add t \\\"a\\\" 1;; Hashtbl.replace t \\\"a\\\" 2;; Hashtbl.find t \\\"a\\\"\")")
(epoch 1605)
(eval "(ocaml-run-program \"let t = Hashtbl.create 10;; Hashtbl.add t \\\"k\\\" 5;; Hashtbl.find_opt t \\\"k\\\"\")")
;; ── List.sort + compare ────────────────────────────────────────
(epoch 1700)
(eval "(ocaml-run \"compare 1 2\")")
(epoch 1701)
(eval "(ocaml-run \"compare 5 5\")")
(epoch 1702)
(eval "(ocaml-run \"compare 9 1\")")
(epoch 1703)
(eval "(ocaml-run \"List.sort compare [3; 1; 4; 1; 5; 9; 2; 6]\")")
(epoch 1704)
(eval "(ocaml-run \"List.sort (fun a b -> b - a) [3; 1; 4]\")")
(epoch 1705)
(eval "(ocaml-run \"List.sort compare []\")")
(epoch 1706)
(eval "(ocaml-run \"List.sort compare [\\\"b\\\"; \\\"a\\\"; \\\"c\\\"]\")")
;; ── HM pattern-match inference ─────────────────────────────────
(epoch 1800)
(eval "(ocaml-type-of \"match 1 with | n -> n + 1\")")
(epoch 1801)
(eval "(ocaml-type-of \"match [1;2] with | [] -> 0 | h :: t -> h\")")
(epoch 1802)
(eval "(ocaml-type-of \"match (1, 2) with | (a, b) -> a + b\")")
(epoch 1803)
(eval "(ocaml-type-of \"fun x -> match x with | 0 -> 0 | n -> n + 1\")")
(epoch 1804)
(eval "(ocaml-type-of \"fun lst -> match lst with | [] -> 0 | h :: _ -> h\")")
;; ── HM constructor inference (option/result) ───────────────────
(epoch 1900)
(eval "(ocaml-type-of \"Some 5\")")
(epoch 1901)
(eval "(ocaml-type-of \"None\")")
(epoch 1902)
(eval "(ocaml-type-of \"Ok 1\")")
(epoch 1903)
(eval "(ocaml-type-of \"Error \\\"oops\\\"\")")
(epoch 1904)
(eval "(ocaml-type-of \"fun x -> Some x\")")
(epoch 1905)
(eval "(ocaml-type-of \"match Some 5 with | None -> 0 | Some n -> n\")")
(epoch 1906)
(eval "(ocaml-type-of \"fun o -> match o with | None -> 0 | Some n -> n\")")
;; ── HM let-rec inference + cons / append ──────────────────────
(epoch 2000)
(eval "(ocaml-type-of \"let rec fact n = if n = 0 then 1 else n * fact (n - 1) in fact\")")
(epoch 2001)
(eval "(ocaml-type-of \"let rec len lst = match lst with | [] -> 0 | _ :: t -> 1 + len t in len\")")
(epoch 2002)
(eval "(ocaml-type-of \"let rec map f xs = match xs with | [] -> [] | h :: t -> f h :: map f t in map\")")
(epoch 2003)
(eval "(ocaml-type-of \"1 :: [2; 3]\")")
(epoch 2004)
(eval "(ocaml-type-of \"[1] @ [2; 3]\")")
(epoch 2005)
(eval "(ocaml-type-of \"let rec sum lst = match lst with | [] -> 0 | h :: t -> h + sum t in sum [1; 2; 3]\")")
;; ── HM with user type declarations ─────────────────────────────
(epoch 2100)
(eval "(ocaml-type-of-program \"type color = Red | Green | Blue;; Red\")")
(epoch 2101)
(eval "(ocaml-type-of-program \"type shape = Circle of int | Square of int;; Circle 5\")")
(epoch 2102)
(eval "(ocaml-type-of-program \"type color = Red | Green;; let c = Red;; c\")")
(epoch 2103)
(eval "(ocaml-type-of-program \"type shape = Circle of int | Square of int;; let area s = match s with | Circle r -> r * r | Square s -> s * s;; area\")")
(epoch 2104)
(eval "(ocaml-type-of-program \"let x = 1;; let y = 2;; x + y\")")
(epoch 2105)
(eval "(ocaml-type-of-program \"let rec fact n = if n = 0 then 1 else n * fact (n - 1);; fact 5\")")
;; ── More List functions: combine/split/iter2/fold_left2/map2 ──
(epoch 2200)
(eval "(ocaml-run \"List.combine [1;2;3] [\\\"a\\\";\\\"b\\\";\\\"c\\\"]\")")
(epoch 2201)
(eval "(ocaml-run \"List.split [(1,\\\"a\\\");(2,\\\"b\\\")]\")")
(epoch 2202)
(eval "(ocaml-run \"List.fold_left2 (fun a b c -> a + b + c) 0 [1;2;3] [10;20;30]\")")
(epoch 2203)
(eval "(ocaml-run \"List.map2 (fun a b -> a + b) [1;2;3] [10;20;30]\")")
;; ── Float arithmetic ───────────────────────────────────────────
(epoch 2300)
(eval "(ocaml-run \"1.5 +. 2.5\")")
(epoch 2301)
(eval "(ocaml-run \"3.0 *. 2.0\")")
(epoch 2302)
(eval "(ocaml-run \"10.0 /. 4.0\")")
(epoch 2303)
(eval "(ocaml-type-of \"1.5 +. 2.5\")")
(epoch 2304)
(eval "(ocaml-type-of \"fun x y -> x +. y\")")
;; ── Float module ───────────────────────────────────────────────
(epoch 2400)
(eval "(ocaml-run \"Float.sqrt 16.0\")")
(epoch 2401)
(eval "(ocaml-run \"Float.sin 0.0\")")
(epoch 2402)
(eval "(ocaml-run \"Float.cos 0.0\")")
(epoch 2403)
(eval "(ocaml-run \"Float.pow 2.0 10.0\")")
(epoch 2404)
(eval "(ocaml-run \"Float.floor 3.7\")")
(epoch 2405)
(eval "(ocaml-run \"Float.ceil 3.2\")")
;; ── Polymorphic variants ──────────────────────────────────────
(epoch 2500)
(eval "(ocaml-run \"\`Red\")")
(epoch 2501)
(eval "(ocaml-run \"\`Some 42\")")
(epoch 2502)
(eval "(ocaml-run \"match \`Red with | \`Red -> 1 | \`Green -> 2 | \`Blue -> 3\")")
(epoch 2503)
(eval "(ocaml-run \"match \`Pair (1, 2) with | \`Pair (a, b) -> a + b\")")
;; ── Record patterns ───────────────────────────────────────────
(epoch 2600)
(eval "(ocaml-run \"match { x = 1; y = 2 } with | { x = a; y = b } -> a + b\")")
(epoch 2601)
(eval "(ocaml-run \"match { name = \\\"Bob\\\"; age = 30 } with | { name = n; age = a } -> a\")")
(epoch 2602)
(eval "(ocaml-run \"match { x = 1; y = 2 } with | { x = 1; y = y } -> y | _ -> 0\")")
(epoch 2603)
(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\")")
;; ── or-patterns (parens-only) ─────────────────────────────────
(epoch 2800)
(eval "(ocaml-run \"match 1 with | (1 | 2 | 3) -> 100 | _ -> 0\")")
(epoch 2801)
(eval "(ocaml-run \"match 2 with | (1 | 2 | 3) -> 100 | _ -> 0\")")
(epoch 2802)
(eval "(ocaml-run \"match 5 with | (1 | 2 | 3) -> 100 | _ -> 0\")")
(epoch 2803)
(eval "(ocaml-run \"match Red with | (Red | Green) -> 1 | Blue -> 2\")")
(epoch 2804)
(eval "(ocaml-run \"match Blue with | (Red | Green) -> 1 | Blue -> 2\")")
;; ── More List utilities (take/drop/filter_map/flat_map) ──────
(epoch 2900)
(eval "(ocaml-run \"List.take 3 [1;2;3;4;5]\")")
(epoch 2901)
(eval "(ocaml-run \"List.drop 2 [1;2;3;4;5]\")")
(epoch 2902)
(eval "(ocaml-run \"List.filter_map (fun x -> if x > 2 then Some (x * 10) else None) [1;2;3;4]\")")
(epoch 2903)
(eval "(ocaml-run \"List.flat_map (fun x -> [x; x]) [1;2;3]\")")
(epoch 2904)
(eval "(ocaml-run \"List.take 0 [1;2;3]\")")
(epoch 2905)
(eval "(ocaml-run \"List.take 100 [1;2;3]\")")
;; ── String extensions ──────────────────────────────────────────
(epoch 3000)
(eval "(ocaml-run \"String.ends_with \\\"lo\\\" \\\"hello\\\"\")")
(epoch 3001)
(eval "(ocaml-run \"String.contains \\\"hello\\\" \\\"ell\\\"\")")
(epoch 3002)
(eval "(ocaml-run \"String.trim \\\" hi \\\"\")")
(epoch 3003)
(eval "(ocaml-run \"String.split_on_char \\\" \\\" \\\"a b c\\\"\")")
(epoch 3004)
(eval "(ocaml-run \"String.replace_all \\\"hello\\\" \\\"l\\\" \\\"r\\\"\")")
(epoch 3005)
(eval "(ocaml-run \"String.index_of \\\"hello\\\" \\\"ll\\\"\")")
;; ── HM with parsed ctor arg types ──────────────────────────────
(epoch 3100)
(eval "(ocaml-type-of-program \"type shape = Circle of int | Square of int;; let area s = match s with | Circle r -> r * r | Square s -> s * s;; area\")")
(epoch 3101)
(eval "(ocaml-type-of-program \"type tag = TStr of string | TInt of int;; TStr \\\"hi\\\"\")")
(epoch 3102)
(eval "(ocaml-type-of-program \"type t = A of bool | B of float;; A true\")")
;; ── Sys module stubs ──────────────────────────────────────────
(epoch 3200)
(eval "(ocaml-run \"Sys.os_type\")")
(epoch 3201)
(eval "(ocaml-run \"Sys.word_size\")")
(epoch 3202)
(eval "(ocaml-run \"Sys.unix\")")
(epoch 3203)
(eval "(ocaml-run \"Sys.win32\")")
(epoch 3204)
(eval "(ocaml-run \"Sys.executable_name\")")
;; ── Map.Make / Set.Make functors ──────────────────────────────
(epoch 3300)
(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntMap = Map.Make(IntOrd) ;; let m = IntMap.add 1 \\\"a\\\" IntMap.empty ;; let m = IntMap.add 2 \\\"b\\\" m ;; IntMap.find 1 m\")")
(epoch 3301)
(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntMap = Map.Make(IntOrd) ;; IntMap.cardinal (IntMap.add 1 \\\"a\\\" (IntMap.add 2 \\\"b\\\" IntMap.empty))\")")
(epoch 3302)
(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntSet = Set.Make(IntOrd) ;; IntSet.elements (IntSet.add 3 (IntSet.add 1 (IntSet.add 2 IntSet.empty)))\")")
(epoch 3303)
(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntSet = Set.Make(IntOrd) ;; IntSet.mem 2 (IntSet.add 3 (IntSet.add 1 (IntSet.add 2 IntSet.empty)))\")")
;; ── Map/Set fold/iter/filter/union/inter ──────────────────────
(epoch 3400)
(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntMap = Map.Make(IntOrd) ;; let m = IntMap.add 1 10 (IntMap.add 2 20 IntMap.empty) ;; IntMap.fold (fun k v acc -> acc + v) m 0\")")
(epoch 3401)
(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntMap = Map.Make(IntOrd) ;; let m = IntMap.add 1 10 IntMap.empty ;; IntMap.is_empty m\")")
(epoch 3402)
(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntSet = Set.Make(IntOrd) ;; let a = IntSet.add 1 (IntSet.add 2 IntSet.empty) ;; let b = IntSet.add 2 (IntSet.add 3 IntSet.empty) ;; IntSet.elements (IntSet.union a b)\")")
(epoch 3403)
(eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntSet = Set.Make(IntOrd) ;; let a = IntSet.add 1 (IntSet.add 2 (IntSet.add 3 IntSet.empty)) ;; let b = IntSet.add 2 (IntSet.add 3 (IntSet.add 4 IntSet.empty)) ;; IntSet.elements (IntSet.inter a b)\")")
;; ── Buffer module ──────────────────────────────────────────────
(epoch 3500)
(eval "(ocaml-run-program \"let b = Buffer.create 16 ;; Buffer.add_string b \\\"Hello\\\" ;; Buffer.add_string b \\\", \\\" ;; Buffer.add_string b \\\"World\\\" ;; Buffer.contents b\")")
(epoch 3501)
(eval "(ocaml-run-program \"let b = Buffer.create 16 ;; Buffer.add_string b \\\"abc\\\" ;; Buffer.length b\")")
(epoch 3502)
(eval "(ocaml-run-program \"let b = Buffer.create 16 ;; Buffer.add_string b \\\"x\\\" ;; Buffer.clear b ;; Buffer.contents b\")")
;; ── Stack + Queue modules ─────────────────────────────────────
(epoch 3600)
(eval "(ocaml-run-program \"let s = Stack.create () ;; Stack.push 1 s ;; Stack.push 2 s ;; Stack.push 3 s ;; Stack.pop s\")")
(epoch 3601)
(eval "(ocaml-run-program \"let s = Stack.create () ;; Stack.push 1 s ;; Stack.push 2 s ;; Stack.length s\")")
(epoch 3602)
(eval "(ocaml-run-program \"let s = Stack.create () ;; Stack.push 1 s ;; Stack.top s\")")
(epoch 3603)
(eval "(ocaml-run-program \"let q = Queue.create () ;; Queue.push 1 q ;; Queue.push 2 q ;; Queue.push 3 q ;; Queue.pop q\")")
(epoch 3604)
(eval "(ocaml-run-program \"let q = Queue.create () ;; Queue.push 1 q ;; Queue.push 2 q ;; Queue.length q\")")
;; ── Option/Result/Bytes extensions ────────────────────────────
(epoch 3700)
(eval "(ocaml-run \"Option.join (Some (Some 5))\")")
(epoch 3701)
(eval "(ocaml-run \"Option.join None\")")
(epoch 3702)
(eval "(ocaml-run \"Option.to_result \\\"missing\\\" None\")")
(epoch 3703)
(eval "(ocaml-run \"Option.to_result \\\"missing\\\" (Some 7)\")")
(epoch 3704)
(eval "(ocaml-run \"Result.value (Ok 5) 0\")")
(epoch 3705)
(eval "(ocaml-run \"Result.value (Error \\\"e\\\") 99\")")
(epoch 3706)
(eval "(ocaml-run \"Result.fold (fun x -> x * 10) (fun e -> 0) (Ok 5)\")")
(epoch 3707)
(eval "(ocaml-run \"Bytes.length \\\"hello\\\"\")")
(epoch 3708)
(eval "(ocaml-run \"Bytes.concat \\\"-\\\" [\\\"a\\\";\\\"b\\\";\\\"c\\\"]\")")
;; ── HM let-mut / let-rec-mut ──────────────────────────────────
(epoch 3800)
(eval "(ocaml-type-of \"let x = 1 and y = 2 in x + y\")")
(epoch 3801)
(eval "(ocaml-type-of \"let rec even n = if n = 0 then true else odd (n - 1) and odd n = if n = 0 then false else even (n - 1) in even\")")
(epoch 3802)
(eval "(ocaml-type-of \"let f x = x + 1 and g x = x * 2 in f 1 + g 2\")")
;; ── let _ = expr top-level ─────────────────────────────────────
(epoch 3900)
(eval "(ocaml-run-program \"let _ = 1 + 2 ;; 42\")")
(epoch 3901)
(eval "(ocaml-run-program \"let x = 10 ;; let _ = x ;; x * 2\")")
;; ── Record type declarations ──────────────────────────────────
(epoch 4000)
(eval "(ocaml-parse-program \"type point = { x : int; y : int }\")")
(epoch 4001)
(eval "(ocaml-parse-program \"type r = { mutable x : int; y : string }\")")
(epoch 4002)
(eval "(ocaml-run-program \"type point = { x : int; y : int };; let p = { x = 3; y = 4 };; p.x + p.y\")")
;; ── Mutable record fields (r.f <- v) ──────────────────────────
(epoch 4100)
(eval "(ocaml-run \"let r = { x = 1; y = 2 } in r.x <- 5; r.x\")")
(epoch 4101)
(eval "(ocaml-run \"let r = { x = 0 } in for i = 1 to 5 do r.x <- r.x + i done; r.x\")")
(epoch 4102)
(eval "(ocaml-run \"let r = { name = \\\"Bob\\\"; age = 30 } in r.name <- \\\"Alice\\\"; r.name\")")
(epoch 4103)
(eval "(ocaml-run \"let r = { x = 1; y = 2 } in r.x <- r.y * 10; r.x\")")
;; ── HM top-level def-mut / def-rec-mut ────────────────────────
(epoch 4200)
(eval "(ocaml-type-of-program \"let x = 1 and y = 2;; x + y\")")
(epoch 4201)
(eval "(ocaml-type-of-program \"let rec even n = if n = 0 then true else odd (n - 1) and odd n = if n = 0 then false else even (n - 1);; even 10\")")
(epoch 4202)
(eval "(ocaml-type-of-program \"let rec map f xs = match xs with | [] -> [] | h :: t -> f h :: map f t and length lst = match lst with | [] -> 0 | _ :: t -> 1 + length t;; map\")")
;; ── Char predicate helpers ────────────────────────────────────
(epoch 4300)
(eval "(ocaml-run \"Char.is_digit \\\"5\\\"\")")
(epoch 4301)
(eval "(ocaml-run \"Char.is_digit \\\"x\\\"\")")
(epoch 4302)
(eval "(ocaml-run \"Char.is_alpha \\\"x\\\"\")")
(epoch 4303)
(eval "(ocaml-run \"Char.is_alnum \\\"5\\\"\")")
(epoch 4304)
(eval "(ocaml-run \"Char.is_whitespace \\\" \\\"\")")
(epoch 4305)
(eval "(ocaml-run \"Char.is_upper \\\"A\\\"\")")
(epoch 4306)
(eval "(ocaml-run \"Char.is_lower \\\"a\\\"\")")
;; ── function with `when` guard ────────────────────────────────
(epoch 4400)
(eval "(ocaml-run \"(function | n when n > 0 -> 1 | _ -> 0) 5\")")
(epoch 4401)
(eval "(ocaml-run \"(function | n when n > 0 -> 1 | _ -> 0) (-3)\")")
(epoch 4402)
(eval "(ocaml-run \"(function | n when n > 0 -> 1 | n when n < 0 -> -1 | _ -> 0) 0\")")
;; ── try/with `when` guard ─────────────────────────────────────
(epoch 4500)
(eval "(ocaml-run \"try raise (E 5) with | E n when n > 0 -> n | _ -> 0\")")
(epoch 4501)
(eval "(ocaml-run \"try raise (E (-3)) with | E n when n > 0 -> n | _ -> 0\")")
(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\")")
;; ── Type annotations: let x : T = e and (e : T) ──────────────
(epoch 4700)
(eval "(ocaml-run-program \"let x : int = 5;; x + 1\")")
(epoch 4701)
(eval "(ocaml-run \"let f (x : int) : int = x + 1 in f 41\")")
(epoch 4702)
(eval "(ocaml-run \"(5 : int)\")")
(epoch 4703)
(eval "(ocaml-run \"((1 + 2) : int) * 3\")")
;; ── Module body: def-mut / def-rec-mut ─────────────────────────
(epoch 4800)
(eval "(ocaml-run-program \"module M = struct let rec a n = if n = 0 then 0 else b (n - 1) and b n = if n = 0 then 1 else a (n - 1) end ;; M.a 5\")")
(epoch 4801)
(eval "(ocaml-run-program \"module M = struct let x = 1 and y = 2 end ;; M.x + M.y\")")
;; ── let open M in body ────────────────────────────────────────
(epoch 4900)
(eval "(ocaml-run \"let open List in length [1;2;3]\")")
(epoch 4901)
(eval "(ocaml-run \"let open List in map (fun x -> x * 2) [1;2;3]\")")
(epoch 4902)
(eval "(ocaml-run \"let open Option in map (fun x -> x + 1) (Some 5)\")")
;; ── M.(expr) local-open expression form ───────────────────────
(epoch 4910)
(eval "(ocaml-run \"List.(length [1;2;3])\")")
(epoch 4911)
(eval "(ocaml-run \"List.(map (fun x -> x + 1) [1;2;3])\")")
(epoch 4912)
(eval "(ocaml-run \"Option.(map (fun x -> x * 10) (Some 4))\")")
;; ── s.[i] string indexing ─────────────────────────────────────
(epoch 4920)
(eval "(ocaml-run \"let s = \\\"hello\\\" in s.[0]\")")
(epoch 4921)
(eval "(ocaml-run \"let s = \\\"abc\\\" in Char.code s.[2]\")")
(epoch 4922)
(eval "(ocaml-run \"let s = \\\"hi\\\" in let n = ref 0 in for i = 0 to String.length s - 1 do n := !n + Char.code s.[i] done; !n\")")
;; ── assert ────────────────────────────────────────────────────
(epoch 4930)
(eval "(ocaml-run \"assert true; 42\")")
(epoch 4931)
(eval "(ocaml-run \"let x = 5 in assert (x = 5); x + 1\")")
(epoch 4932)
(eval "(ocaml-run \"try (assert false; 0) with _ -> 99\")")
;; ── Printf.sprintf + global string_of_* ──────────────────────
(epoch 4940)
(eval "(ocaml-run \"Printf.sprintf \\\"hello\\\"\")")
(epoch 4941)
(eval "(ocaml-run \"Printf.sprintf \\\"x=%d\\\" 42\")")
(epoch 4942)
(eval "(ocaml-run \"Printf.sprintf \\\"%s = %d\\\" \\\"answer\\\" 42\")")
(epoch 4943)
(eval "(ocaml-run \"Printf.sprintf \\\"%d%%\\\" 50\")")
(epoch 4944)
(eval "(ocaml-run \"string_of_int 7 ^ \\\"-\\\" ^ string_of_bool true\")")
;; ── Hashtbl.iter / Hashtbl.fold ─────────────────────────────
(epoch 4950)
(eval "(ocaml-run \"let t = Hashtbl.create 4 in Hashtbl.add t \\\"a\\\" 1; Hashtbl.add t \\\"b\\\" 2; Hashtbl.add t \\\"c\\\" 3; Hashtbl.fold (fun _ v acc -> acc + v) t 0\")")
(epoch 4951)
(eval "(ocaml-run \"let t = Hashtbl.create 4 in Hashtbl.add t \\\"x\\\" 10; Hashtbl.add t \\\"y\\\" 20; let total = ref 0 in Hashtbl.iter (fun _ v -> total := !total + v) t; !total\")")
;; ── lazy / Lazy.force ─────────────────────────────────────────
(epoch 4960)
(eval "(ocaml-run \"let x = lazy (1 + 2) in Lazy.force x\")")
(epoch 4961)
(eval "(ocaml-run \"let counter = ref 0 in let lz = lazy (counter := !counter + 1; 42) in let a = Lazy.force lz in let b = Lazy.force lz in (a + b) * 100 + !counter\")")
EPOCHS
OUTPUT=$(timeout 360 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
check() {
local epoch="$1" desc="$2" expected="$3"
local actual
actual=$(echo "$OUTPUT" | grep -A1 "^(ok-len $epoch " | tail -1)
if [ -z "$actual" ]; then
actual=$(echo "$OUTPUT" | grep "^(ok $epoch " || true)
fi
if [ -z "$actual" ]; then
actual=$(echo "$OUTPUT" | grep "^(error $epoch " || true)
fi
if [ -z "$actual" ]; then
actual="<no output for epoch $epoch>"
fi
if echo "$actual" | grep -qF -- "$expected"; then
PASS=$((PASS + 1))
[ "$VERBOSE" = "-v" ] && echo " ok $desc"
else
FAIL=$((FAIL + 1))
ERRORS+=" FAIL $desc (epoch $epoch)
expected: $expected
actual: $actual
"
fi
}
# empty / eof
check 100 "empty tokens length" '1'
check 101 "empty first is eof" '"eof"'
# numbers
check 110 "int type" '"number"'
check 111 "int value" '42'
check 112 "float value" '3.14'
check 113 "hex value" '255'
check 114 "exponent" '1000'
check 115 "underscored int" '1000000'
check 116 "neg exponent" '0.0314'
# idents / ctors / keywords
check 120 "ident type" '"ident"'
check 121 "ident value" '"foo_bar1"'
check 122 "ctor type" '"ctor"'
check 123 "ctor value" '"Some"'
check 124 "let keyword type" '"keyword"'
check 125 "match keyword value" '"match"'
check 126 "true is keyword" '"keyword"'
check 127 "false value" '"false"'
check 128 "primed ident" "\"name'\""
# strings
check 130 "string type" '"string"'
check 131 "string value" '"hi"'
check 132 "escape sequence" '"a'
# chars
check 140 "char type" '"char"'
check 141 "char value" '"a"'
check 142 "char escape" '"'
# tyvars
check 145 "tyvar type" '"tyvar"'
check 146 "tyvar value" '"a"'
# multi-char ops
check 150 "->" '"->"'
check 151 "|>" '"|>"'
check 152 "<-" '"<-"'
check 153 ":=" '":="'
check 154 "::" '"::"'
check 155 ";;" '";;"'
check 156 "@@" '"@@"'
check 157 "<>" '"<>"'
check 158 "&&" '"&&"'
check 159 "||" '"||"'
# single ops
check 160 "+" '"+"'
check 161 "|" '"|"'
check 162 ";" '";"'
check 163 "(" '"("'
check 164 "!" '"!"'
check 165 "@" '"@"'
# comments
check 170 "block comment alone -> eof" '1'
check 171 "num after block comment" '42'
check 172 "nested comment count" '2'
check 173 "nested comment value" '1'
# compound
check 180 "let x = 1 count" '5'
check 181 "let is keyword" '"keyword"'
check 182 "let value" '"let"'
check 183 "x is ident" '"ident"'
check 184 "= value" '"="'
check 185 "1 value" '1'
check 190 "match expr count" '13'
check 191 "fun -> arrow value" '"->"'
check 192 "fun -> arrow type" '"op"'
check 193 "Some is ctor" '"ctor"'
check 194 "first |> value" '"|>"'
check 195 "ref assign :=" '":="'
# ── Parser tests ────────────────────────────────────────────────
check 200 "parse int" '("int" 42)'
check 201 "parse float" '("float" 3.14)'
check 202 "parse string" '("string" "hi")'
check 203 "parse char" '("char" "a")'
check 204 "parse true" '("bool" true)'
check 205 "parse false" '("bool" false)'
check 206 "parse var" '("var" "x")'
check 207 "parse ctor" '("con" "Some")'
check 208 "parse unit" '("unit")'
check 210 "parse f x" '("app" ("var" "f") ("var" "x"))'
check 211 "parse f x y left-assoc" '("app" ("app" ("var" "f") ("var" "x")) ("var" "y"))'
check 212 "parse f (g x)" '("app" ("var" "f") ("app" ("var" "g") ("var" "x")))'
check 213 "parse Some 42" '("app" ("con" "Some") ("int" 42))'
check 220 "parse 1+2" '("op" "+" ("int" 1) ("int" 2))'
check 221 "parse a + b * c prec" '("op" "+" ("var" "a") ("op" "*"'
check 222 "parse a*b + c prec" '("op" "+" ("op" "*"'
check 223 "parse && / || prec" '("op" "||" ("op" "&&"'
check 224 "parse a = b" '("op" "=" ("var" "a") ("var" "b"))'
check 225 "parse ^ right-assoc" '("op" "^" ("var" "a") ("op" "^"'
check 226 "parse :: right-assoc" '("op" "::" ("var" "a") ("op" "::"'
check 227 "parse parens override" '("op" "*" ("op" "+"'
check 228 "parse |> chain" '("op" "|>" ("op" "|>"'
check 229 "parse mod kw-binop" '("op" "mod" ("var" "x") ("int" 2))'
check 230 "parse -x" '("neg" ("var" "x"))'
check 231 "parse -1+2" '("op" "+" ("neg" ("int" 1)) ("int" 2))'
check 240 "parse tuple" '("tuple" ("int" 1) ("int" 2) ("int" 3))'
check 241 "parse list literal" '("list" ("int" 1) ("int" 2) ("int" 3))'
check 242 "parse []" '("list")'
check 250 "parse if/then/else" '("if" ("var" "x") ("int" 1) ("int" 2))'
check 251 "parse if w/o else" '("if" ("var" "c") ("var" "x") ("unit"))'
check 252 "parse fun x -> ..." '("fun" ("x") ("op" "+" ("var" "x") ("int" 1)))'
check 253 "parse fun x y ->" '("fun" ("x" "y")'
check 254 "parse let x = 1 in x" '("let" "x" () ("int" 1) ("var" "x"))'
check 255 "parse let f x =" '("let" "f" ("x") ("op" "+"'
check 256 "parse let rec f x =" '("let-rec" "f" ("x")'
check 257 "parse let f x y =" '("let" "f" ("x" "y")'
check 260 "parse begin/end" '("op" "+" ("int" 1) ("int" 2))'
# ── Top-level decls ─────────────────────────────────────────────
check 270 "program: let x = 1" '("program" ("def" "x" () ("int" 1)))'
check 271 "program: let x = 1 ;;" '("program" ("def" "x" () ("int" 1)))'
check 272 "program: let f x = x+1" '("program" ("def" "f" ("x") ("op" "+"'
check 273 "program: let rec fact" '("def-rec" "fact" ("n")'
check 274 "program: two decls" '("def" "x" () ("int" 1)) ("def" "y"'
check 275 "program: bare expr" '("program" ("expr" ("op" "+" ("int" 1) ("int" 2))))'
check 276 "program: mixed decls + expr" '("def" "y" () ("int" 2)) ("expr"'
check 277 "program: 4 forms incl head" '4'
check 278 "program: empty" '("program")'
# ── Match / patterns ────────────────────────────────────────────
check 300 "match Some/None" '("match" ("var" "x") (("case" ("pcon" "None") ("int" 0)) ("case" ("pcon" "Some" ("pvar" "y")) ("var" "y")))'
check 301 "match no leading bar" '("match" ("var" "x") (("case" ("pcon" "None") ("int" 0)) ("case" ("pcon" "Some"'
check 302 "match list cons" '("case" ("plist") ("int" 0)) ("case" ("pcons" ("pvar" "h") ("pvar" "t")) ("int" 1))'
check 303 "match tuple pat" '("ptuple" ("pvar" "a") ("pvar" "b"))'
check 304 "match int + wildcard" '("case" ("plit" ("int" 0)) ("int" 1)) ("case" ("pwild")'
check 305 "match bool literals" '("plit" ("bool" true))'
check 306 "match ctor with tuple arg" '("pcon" "Pair" ("pvar" "a") ("pvar" "b"))'
check 307 "match string literal" '("plit" ("string" "hi"))'
check 308 "match unit pattern" '("plit" ("unit"))'
# ── Sequences ───────────────────────────────────────────────────
check 320 "seq 1;2" '("seq" ("int" 1) ("int" 2))'
check 321 "seq 1;2;3" '("seq" ("int" 1) ("int" 2) ("int" 3))'
check 322 "seq in parens" '("seq" ("int" 1) ("int" 2))'
check 323 "seq in begin/end" '("seq" ("var" "a") ("var" "b") ("var" "c"))'
check 324 "let body absorbs seq" '("let" "x" () ("int" 1) ("seq" ("var" "x") ("var" "x")))'
check 325 "if-branch parens for seq" '("if" ("var" "c") ("seq" ("var" "a") ("var" "b"))'
check 326 "list ; is separator" '("list" ("int" 1) ("int" 2) ("int" 3))'
check 327 "trailing ; OK" '("seq" ("int" 1) ("int" 2))'
check 328 "begin a; end singleton seq" '("seq" ("var" "a"))'
check 329 "match clause body absorbs ;" '("case" ("pwild") ("seq" ("var" "a") ("var" "b")))'
# ── Phase 2: evaluator ──────────────────────────────────────────
# atoms
check 400 "eval int" '42'
check 401 "eval float" '3.14'
check 402 "eval true" 'true'
check 403 "eval false" 'false'
check 404 "eval string" '"hi"'
# arithmetic
check 410 "eval 1+2" '3'
check 411 "eval 10-3" '7'
check 412 "eval 4*5" '20'
check 413 "eval 20/4" '5'
check 414 "eval 10 mod 3" '1'
check 415 "eval 2 ** 10" '1024'
check 416 "eval (1+2)*3" '9'
check 417 "eval 1+2*3 prec" '7'
check 418 "eval -5+10" '5'
# comparison & boolean
check 420 "eval 1<2" 'true'
check 421 "eval 3>2" 'true'
check 422 "eval 2=2" 'true'
check 423 "eval 1<>2" 'true'
check 424 "eval true && false" 'false'
check 425 "eval true || false" 'true'
check 426 "eval not false" 'true'
# string
check 430 'eval "a" ^ "b"' '"ab"'
check 431 "eval string concat 3" '"hello world"'
# conditional
check 440 "eval if true 1 else 2" '1'
check 441 "eval if 1>2 100 else 200" '200'
# let / lambda / app
check 450 "eval let x=5 x*2" '10'
check 451 "eval let f x = x+1; f 41" '42'
check 452 "eval let f x y = x+y; f 3 4" '7'
check 453 "eval (fun x -> x*x) 7" '49'
check 454 "eval curried lambdas" '30'
check 455 "eval named lambda" '10'
# closures
check 460 "eval closure capture" '15'
check 461 "eval make_adder" '101'
# recursion
check 470 "eval fact 5" '120'
check 471 "eval fib 10" '55'
check 472 "eval sum 100" '5050'
# sequence
check 480 "eval 1; 2; 3 → 3" '3'
check 481 "eval begin 10 end" '10'
# programs
check 490 "run-prog x+y" '3'
check 491 "run-prog fact 6" '720'
check 492 "run-prog inc + double" '10'
# pipe
check 495 "eval x |> f" '10'
# ── Phase 3: ADTs + match (eval) ────────────────────────────────
# constructors
check 500 "eval None" '("None")'
check 501 "eval Some 42" '("Some" 42)'
check 502 "eval Pair tuple-arg" '("Some" 1 2)'
# option match
check 510 "match Some 5 -> 5" '5'
check 511 "match None -> 0" '0'
# literal match
check 520 "match 3 -> _ -> 999" '999'
check 521 "match bool true" '1'
check 522 "match string lit" '1'
# tuple match
check 530 "match (1,2)" '3'
check 531 "match (1,2,3)" '6'
# list match
check 540 "match list cons head" '1'
check 541 "match empty list" '0'
check 542 "match list literal pat" '6'
check 543 "match recursive len" '5'
check 544 "match recursive sum" '15'
# wildcard + var
check 550 "match _ -> 1" '1'
check 551 "match x -> x+1" '100'
# ctor with tuple arg
check 560 "Pair(a,b) → a*b" '2'
# ── References ──────────────────────────────────────────────────
check 600 "deref new ref" '5'
check 601 ":= then deref" '10'
check 602 "increment cell twice" '2'
check 603 "ref captured by closure" '115'
check 604 "ref of string" '"b"'
check 605 "ref + recursion" '15'
# ── for / while ─────────────────────────────────────────────────
check 620 "for 1..5 sum" '15'
check 621 "for 5 downto 1 sum" '15'
check 622 "while loop" '15'
check 623 "for 1..100 sum" '5050'
check 624 "for 1..5 product = 120" '120'
# ── function ────────────────────────────────────────────────────
check 640 "function None|Some Some 7" '7'
check 641 "function None=0" '0'
check 642 "rec function len" '3'
check 643 "rec function map x*x" '(1 4 9 16)'
# ── try / with / raise ──────────────────────────────────────────
check 660 "try success" '3'
check 661 "try Foo caught" '5'
check 662 "try Bar caught" '99'
check 663 "try failwith" '"oops"'
check 664 "try sequence raises" '101'
check 665 "raise from function" '-5'
# ── Phase 4: Modules + field access ─────────────────────────────
check 700 "parse M.x" '("field" ("con" "M") "x")'
check 701 "parse r.field" '("field" ("var" "r") "field")'
check 702 "parse M.M2.x left-assoc" '("field" ("field" ("con" "M") "M2") "x")'
check 703 "parse f r.x bind tighter" '("app" ("var" "f") ("field" ("var" "r") "x"))'
check 710 "module M.x = 42" '42'
check 711 "module M.f 41 = 42" '42'
check 712 "module two values" '3'
check 713 "module fn: square 5" '25'
check 714 "nested module Outer.Inner" '99'
check 715 "module rec fact 5" '120'
check 716 "module Pair.swap" '("tuple" 2 1)'
# ── open / include ──────────────────────────────────────────────
check 730 "open M; f x" '43'
check 731 "include Math; area" '48'
check 732 "module open inside" '11'
check 733 "Sphere.pi via include" '3'
check 734 "include M; N.z = x+y" '3'
# ── Functors ────────────────────────────────────────────────────
check 750 "functor app Add(Five).add 10" '15'
check 751 "module alias N = M" '1'
check 752 "submodule alias" '42'
check 753 "multi-param functor" '("tuple" 1 2)'
check 754 "Identity functor + include" '99'
# ── Phase 6: stdlib slice ───────────────────────────────────────
# List
check 800 "List.length [1..4]" '4'
check 801 "List.length []" '0'
check 802 "List.map x*2 [1;2;3]" '(2 4 6)'
check 803 "List.filter > 2" '(3 4 5)'
check 804 "List.fold_left + 0 [1..5]" '15'
check 805 "List.fold_right ::" '(1 2 3)'
check 806 "List.rev" '(3 2 1)'
check 807 "List.append" '(1 2 3 4)'
check 808 "List.mem 3" 'true'
check 809 "List.mem 99" 'false'
check 810 "List.for_all >0" 'true'
check 811 "List.exists >2" 'true'
check 812 "List.hd" '10'
check 813 "List.nth idx 1" '20'
# Option
check 820 "Option.map Some" '("Some" 42)'
check 821 "Option.map None" '("None")'
check 822 "Option.value Some" '7'
check 823 "Option.value None" '42'
check 824 "Option.is_some" 'true'
check 825 "Option.is_none" 'true'
# Result
check 830 "Result.map Ok" '("Ok" 6)'
check 831 "Result.is_ok" 'true'
check 832 "Result.is_error" 'true'
# ── let ... and ... mutual recursion ─────────────────────────────
check 850 "even 10 (mutual rec)" 'true'
check 851 "odd 7 (mutual rec)" 'true'
check 852 "let x = 1 and y = 2" '3'
# ── Phase 5: Hindley-Milner type inference ────────────────────
check 900 "type 42 = Int" '"Int"'
check 901 "type true = Bool" '"Bool"'
check 902 'type string lit' '"String"'
check 903 "type 1+2 = Int" '"Int"'
check 904 "type fun x->x+1 = Int->Int" '"Int -> Int"'
check 905 "type fun x->x = poly" ' -> '
check 906 "type fun x y->x+y" '"Int -> Int -> Int"'
check 907 "type let f x=x+1 in f 10" '"Int"'
check 908 "type let id; id 5" '"Int"'
check 909 "type let id; id true" '"Bool"'
check 910 "type if/then/else" '"Int"'
check 911 "type twice" ' -> '
check 912 "type bool branch" '"Bool -> Int"'
check 913 "type not true" '"Bool"'
# ── Phase 6 String / Char / Int ─────────────────────────────────
check 950 "String.length" '5'
check 951 "String.uppercase_ascii" '"HI"'
check 952 "String.lowercase_ascii" '"hi"'
check 953 "String.sub" '"ell"'
check 954 "String.starts_with" 'true'
check 955 "String.concat" '"a,b,c"'
check 960 "Char.code A" '65'
check 961 "Char.chr 65" '"A"'
check 970 "Int.to_string" '"42"'
check 971 "Int.of_string" '123'
check 972 "Int.abs -5" '5'
check 973 "Int.max" '7'
check 974 "Int.min" '3'
# ── Unit / wildcard parameters ──────────────────────────────────
check 1000 "let f () = 42 in f ()" '42'
check 1001 "(fun () -> 99) ()" '99'
check 1002 "let f _ = 1 in f 5" '1'
check 1003 "top-level let f () =" '7'
check 1004 "wildcard top-level" '42'
# ── Records ─────────────────────────────────────────────────────
check 1100 "record literal + access" '1'
check 1101 "record sum fields" '3'
check 1102 "record with-update" '101'
check 1103 "record string field" '"Bob"'
check 1104 "record int field" '30'
check 1105 "top-level record decl" '3'
# ── as / when in match ──────────────────────────────────────────
check 1200 "Some x as p" '5'
check 1201 "when sign +" '1'
check 1202 "when sign -" '-1'
check 1203 "when sign 0" '0'
check 1204 "when guard fires" '70'
check 1205 "when guard skips" '3'
# ── type declarations ───────────────────────────────────────────
check 1300 "type color enum" '("type-def" "color" () (("Red") ("Green") ("Blue")))'
check 1301 "type shape with-args" '("type-def" "shape"'
check 1302 "type-decl + match Red" '1'
check 1303 "type-decl + match Blue" '3'
check 1304 "type-decl + Circle r" '5'
# ── exception declarations ─────────────────────────────────────
check 1320 "exception nullary" '("exception-def" "MyExn")'
check 1321 "exception arg" '("exception-def" "MyExn"'
check 1322 "raise+catch with arg" '5'
check 1323 "raise+catch string arg" '"oops"'
# ── Phase 6 expanded stdlib ─────────────────────────────────────
check 1400 "List.concat" '(1 2 3 4 5)'
check 1401 "List.init" '(0 10 20 30 40)'
check 1402 "List.find_opt found" '("Some" 3)'
check 1403 "List.find_opt missing" '("None")'
check 1404 "List.mapi" '(10 21 32)'
check 1405 "List.partition" '("tuple" (3 4) (1 2))'
check 1406 "List.assoc" '"b"'
check 1407 "List.assoc_opt missing" '("None")'
check 1410 "Option.fold Some" '70'
check 1411 "Option.fold None" '0'
check 1412 "Option.to_list Some" '(7)'
check 1413 "Option.to_list None" '()'
check 1420 "Result.get_ok" '42'
check 1421 "Result.to_option Ok" '("Some" 1)'
check 1422 "Result.map_error" '("Error" 6)'
# ── HM tuples + lists ───────────────────────────────────────────
check 1500 "type tuple Int*Int" '"Int * Int"'
check 1501 "type 3-tuple" '"Int * Bool * String"'
check 1502 "type Int list" '"Int list"'
check 1503 "type [] poly" ' list'
check 1504 "type fn -> list" 'list"'
check 1505 "type fn -> tuple" ' * '
check 1506 "type Bool list" '"Bool list"'
# ── Hashtbl ─────────────────────────────────────────────────────
check 1600 "Hashtbl find" '42'
check 1601 "Hashtbl length" '2'
check 1602 "Hashtbl find_opt missing" '("None")'
check 1603 "Hashtbl mem" 'true'
check 1604 "Hashtbl replace" '2'
check 1605 "Hashtbl find_opt found" '("Some" 5)'
# ── List.sort + compare ─────────────────────────────────────────
check 1700 "compare 1<2" '-1'
check 1701 "compare 5=5" '0'
check 1702 "compare 9>1" '1'
check 1703 "List.sort ascending" '(1 1 2 3 4 5 6 9)'
check 1704 "List.sort descending" '(4 3 1)'
check 1705 "List.sort empty" '()'
check 1706 "List.sort strings" '("a" "b" "c")'
# ── HM match inference ──────────────────────────────────────────
check 1800 "match int" '"Int"'
check 1801 "match list" '"Int"'
check 1802 "match tuple" '"Int"'
check 1803 "fn match int -> int" '"Int -> Int"'
check 1804 "fn list -> elem" '"Int list -> Int"'
# ── HM ctor inference ──────────────────────────────────────────
check 1900 "Some 5 : Int option" '"Int option"'
check 1901 "None : 'a option" ' option'
check 1902 "Ok 1 : (Int, 'b) result" '"(Int'
check 1903 "Error 'oops'" 'String) result'
check 1904 "fun x -> Some x" ' option'
check 1905 "match Some/None -> Int" '"Int"'
check 1906 "Int option -> Int" '"Int option -> Int"'
# ── HM let-rec + :: / @ ─────────────────────────────────────────
check 2000 "type fact" '"Int -> Int"'
check 2001 "type len" 'list -> Int'
check 2002 "type map" 'list -> '
check 2003 "type 1::list" '"Int list"'
check 2004 "type [1] @ [2;3]" '"Int list"'
check 2005 "type sum" '"Int"'
# ── HM with user type-defs ──────────────────────────────────────
check 2100 "user type Red : color" '"color"'
check 2101 "user type Circle 5 : shape" '"shape"'
check 2102 "let c = Red; c" '"color"'
check 2103 "shape -> Int" '"shape -> Int"'
check 2104 "program x+y" '"Int"'
check 2105 "program fact 5" '"Int"'
# ── More List functions ─────────────────────────────────────────
check 2200 "List.combine" '("tuple" 3 "c")'
check 2201 "List.split" '("tuple" (1 2) ("a" "b"))'
check 2202 "List.fold_left2" '66'
check 2203 "List.map2" '(11 22 33)'
# ── Float arithmetic ────────────────────────────────────────────
check 2300 "1.5 +. 2.5" '4'
check 2301 "3.0 *. 2.0" '6'
check 2302 "10.0 /. 4.0" '2.5'
check 2303 "type 1.5 +. 2.5" '"Float"'
check 2304 "type fun x y -> x +. y" '"Float -> Float -> Float"'
# ── Float module ────────────────────────────────────────────────
check 2400 "Float.sqrt 16" '4'
check 2401 "Float.sin 0" '0'
check 2402 "Float.cos 0" '1'
check 2403 "Float.pow 2 10" '1024'
check 2404 "Float.floor 3.7" '3'
check 2405 "Float.ceil 3.2" '4'
# ── Polymorphic variants ───────────────────────────────────────
check 2500 "polyvar Red" '("Red")'
check 2501 "polyvar Some 42" '("Some" 42)'
check 2502 "polyvar match" '1'
check 2503 "polyvar Pair (a,b)" '3'
# ── Record patterns ────────────────────────────────────────────
check 2600 "match record bind both" '3'
check 2601 "match record name+age" '30'
check 2602 "match record literal x=1" '2'
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")'
# ── or-patterns (parens-only) ──────────────────────────────────
check 2800 "(1|2|3) match 1" '100'
check 2801 "(1|2|3) match 2" '100'
check 2802 "(1|2|3) match 5" '0'
check 2803 "(Red|Green) Red" '1'
check 2804 "(Red|Green) Blue" '2'
# ── List.take/drop/filter_map/flat_map ─────────────────────────
check 2900 "List.take 3" '(1 2 3)'
check 2901 "List.drop 2" '(3 4 5)'
check 2902 "List.filter_map" '(30 40)'
check 2903 "List.flat_map double" '(1 1 2 2 3 3)'
check 2904 "List.take 0" '()'
check 2905 "List.take overflow" '(1 2 3)'
# ── String extensions ──────────────────────────────────────────
check 3000 "String.ends_with" 'true'
check 3001 "String.contains" 'true'
check 3002 "String.trim" '"hi"'
check 3003 "String.split_on_char" '("a" "b" "c")'
check 3004 "String.replace_all" '"herro"'
check 3005 "String.index_of" '2'
# ── HM with parsed ctor arg types ──────────────────────────────
check 3100 "shape -> Int" '"shape -> Int"'
check 3101 "TStr 'hi' : tag" '"tag"'
check 3102 "A true : t" '"t"'
# ── Sys stubs ──────────────────────────────────────────────────
check 3200 "Sys.os_type" '"SX"'
check 3201 "Sys.word_size" '64'
check 3202 "Sys.unix" 'true'
check 3203 "Sys.win32" 'false'
check 3204 "Sys.executable_name" '"ocaml-on-sx"'
# ── Map.Make / Set.Make ────────────────────────────────────────
check 3300 "Map.find via functor" '"a"'
check 3301 "Map.cardinal" '2'
check 3302 "Set.elements sorted" '(1 2 3)'
check 3303 "Set.mem" 'true'
# ── Map/Set fold/iter/filter/union/inter ───────────────────────
check 3400 "Map.fold sum" '30'
check 3401 "Map.is_empty false" 'false'
check 3402 "Set.union" '(1 2 3)'
check 3403 "Set.inter" '(2 3)'
# ── Buffer module ──────────────────────────────────────────────
check 3500 "Buffer concat 'Hello, World'" '"Hello, World"'
check 3501 "Buffer.length 3" '3'
check 3502 "Buffer.clear empties" '""'
# ── Stack + Queue ──────────────────────────────────────────────
check 3600 "Stack.pop LIFO" '3'
check 3601 "Stack.length" '2'
check 3602 "Stack.top" '1'
check 3603 "Queue.pop FIFO" '1'
check 3604 "Queue.length" '2'
# ── Option/Result/Bytes extensions ─────────────────────────────
check 3700 "Option.join nested" '("Some" 5)'
check 3701 "Option.join None" '("None")'
check 3702 "Option.to_result None" '("Error" "missing")'
check 3703 "Option.to_result Some" '("Ok" 7)'
check 3704 "Result.value Ok" '5'
check 3705 "Result.value Error fallback" '99'
check 3706 "Result.fold Ok" '50'
check 3707 "Bytes.length" '5'
check 3708 "Bytes.concat" '"a-b-c"'
# ── HM let-mut / let-rec-mut ───────────────────────────────────
check 3800 "let-mut x+y : Int" '"Int"'
check 3801 "let-rec-mut even" '"Int -> Bool"'
check 3802 "let-mut f and g" '"Int"'
# ── let _ = expr top-level ─────────────────────────────────────
check 3900 "let _ = 1+2;; 42" '42'
check 3901 "two top-level lets, _" '20'
# ── Record type declarations ──────────────────────────────────
check 4000 "record type decl" '("type-def-record" "point" () (("x") ("y")))'
check 4001 "mutable field decl" '("mutable" "x")'
check 4002 "record decl + use" '7'
# ── Mutable record fields ──────────────────────────────────────
check 4100 "r.x <- 5; r.x" '5'
check 4101 "for-loop accum r.x" '15'
check 4102 "r.name <- str" '"Alice"'
check 4103 "r.x <- r.y * 10" '20'
# ── HM top-level def-mut / def-rec-mut ─────────────────────────
check 4200 "let-mut x+y" '"Int"'
check 4201 "let-rec-mut even 10" '"Bool"'
check 4202 "let-rec-mut map+length" 'list -> '
# ── Char predicates ────────────────────────────────────────────
check 4300 "Char.is_digit 5" 'true'
check 4301 "Char.is_digit x" 'false'
check 4302 "Char.is_alpha x" 'true'
check 4303 "Char.is_alnum 5" 'true'
check 4304 "Char.is_whitespace ' '" 'true'
check 4305 "Char.is_upper A" 'true'
check 4306 "Char.is_lower a" 'true'
# ── function with `when` guard ─────────────────────────────────
check 4400 "function when 5" '1'
check 4401 "function when -3" '0'
check 4402 "function sign 0" '0'
# ── try/with `when` guard ──────────────────────────────────────
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'
# ── Type annotations ───────────────────────────────────────────
check 4700 "let x : int = 5" '6'
check 4701 "let f (x : int) : int" '42'
check 4702 "(5 : int)" '5'
check 4703 "((1+2) : int) * 3" '9'
# ── Module body: def-mut / def-rec-mut ─────────────────────────
check 4800 "module rec a/b mutual" '1'
check 4801 "module x and y" '3'
# ── let open M in body ─────────────────────────────────────────
check 4900 "let open List; length" '3'
check 4901 "let open List; map" '(2 4 6)'
check 4902 "let open Option; map" '("Some" 6)'
# ── M.(expr) local-open expression form ──────────────────────────
check 4910 "M.(expr) length" '3'
check 4911 "M.(expr) map" '(2 3 4)'
check 4912 "M.(expr) Option map" '("Some" 40)'
# ── s.[i] string indexing ────────────────────────────────────────
check 4920 "s.[0] hello" '"h"'
check 4921 "Char.code s.[2] abc" '99'
check 4922 "for i s.[i] sum hi" '209'
# ── assert ───────────────────────────────────────────────────────
check 4930 "assert true; 42" '42'
check 4931 "assert (x = 5); x + 1" '6'
check 4932 "try (assert false; ...) with" '99'
# ── Printf.sprintf ───────────────────────────────────────────────
check 4940 "sprintf no args" '"hello"'
check 4941 "sprintf one %d" '"x=42"'
check 4942 "sprintf %s = %d" '"answer = 42"'
check 4943 "sprintf %d%% literal percent" '"50%"'
check 4944 "string_of_int + string_of_b" '"7-true"'
# ── Hashtbl.iter / Hashtbl.fold ─────────────────────────────────
check 4950 "Hashtbl.fold sum 1+2+3" '6'
check 4951 "Hashtbl.iter ref accum 10+20" '30'
# ── lazy / Lazy.force ─────────────────────────────────────────
check 4960 "lazy 1+2 force" '3'
check 4961 "lazy memoization counter=1" '8401'
TOTAL=$((PASS + FAIL))
if [ $FAIL -eq 0 ]; then
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
else
echo "FAIL $PASS/$TOTAL passed, $FAIL failed:"
echo ""
echo "$ERRORS"
fi
[ $FAIL -eq 0 ]