Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
Parser: try-consume-param! handles ident, wildcard _ (fresh __wild_N name), unit () (fresh __unit_N), typed (x : T) (skips signature). parse-fun and parse-let (inline) reuse the helper; top-level parse-decl-let inlines a similar test. test.sh timeout bumped from 60s to 180s — the growing suite was hitting the cap and reporting spurious failures.
1144 lines
44 KiB
Bash
Executable File
1144 lines
44 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\")")
|
|
|
|
EPOCHS
|
|
|
|
OUTPUT=$(timeout 180 "$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'
|
|
|
|
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 ]
|