Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 52s
ocaml-eval walks the AST and yields SX values. ocaml-run / ocaml-run-program wrap parse + eval. Coverage: atoms, vars, app (curried), 22 binary ops, prefix - and not, if/seq/tuple/list, fun (auto-curried via host SX lambdas), let, let-rec (mutable-cell knot for recursive functions). Initial env: not/succ/pred/abs/max/min/fst/snd/ignore. Tests: arithmetic, comparison, string concat, closures, fact 5 / fib 10 / sum 100, top-level decls, |> pipe.
696 lines
24 KiB
Bash
Executable File
696 lines
24 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/ocaml/tokenizer.sx")
|
|
(load "lib/ocaml/parser.sx")
|
|
(load "lib/ocaml/eval.sx")
|
|
(load "lib/ocaml/tests/tokenize.sx")
|
|
|
|
;; ── 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\")")
|
|
|
|
EPOCHS
|
|
|
|
OUTPUT=$(timeout 60 "$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'
|
|
|
|
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 ]
|