Files
rose-ash/hosts/ocaml/bin/run_tests.ml
giles 818e5d53f0 OCaml bootstrapper: transpiler compiles full CEK evaluator (61/61 tests)
SX-to-OCaml transpiler (transpiler.sx) generates sx_ref.ml (~90KB, ~135
mutually recursive functions) from the spec evaluator. Foundation tests
all pass: parser, primitives, env operations, type system.

Key design decisions:
- Env variant added to value type for CEK state dict storage
- Continuation carries optional data dict for captured frames
- Dynamic var tracking distinguishes OCaml fn calls from SX value dispatch
- Single let rec...and block for forward references between all defines
- Unused ref pre-declarations eliminated via let-bound name detection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 20:51:59 +00:00

178 lines
7.5 KiB
OCaml

(** Minimal test runner — verifies the OCaml foundation (types, parser, primitives).
Eventually this will load test-framework.sx and run the full spec test
suite against the transpiled evaluator. For now it exercises the parser
and primitives directly. *)
open Sx.Sx_types
open Sx.Sx_parser
open Sx.Sx_primitives
let pass_count = ref 0
let fail_count = ref 0
let assert_eq name expected actual =
if expected = actual then begin
incr pass_count;
Printf.printf " PASS: %s\n" name
end else begin
incr fail_count;
Printf.printf " FAIL: %s — expected %s, got %s\n" name (inspect expected) (inspect actual)
end
let assert_true name v =
if sx_truthy v then begin
incr pass_count;
Printf.printf " PASS: %s\n" name
end else begin
incr fail_count;
Printf.printf " FAIL: %s — expected truthy, got %s\n" name (inspect v)
end
let call name args =
match Hashtbl.find_opt primitives name with
| Some f -> f args
| None -> failwith ("Unknown primitive: " ^ name)
let () =
Printf.printf "=== SX OCaml Foundation Tests ===\n\n";
(* --- Parser tests --- *)
Printf.printf "Suite: parser\n";
let exprs = parse_all "42" in
assert_eq "number" (Number 42.0) (List.hd exprs);
let exprs = parse_all "\"hello\"" in
assert_eq "string" (String "hello") (List.hd exprs);
let exprs = parse_all "true" in
assert_eq "bool true" (Bool true) (List.hd exprs);
let exprs = parse_all "nil" in
assert_eq "nil" Nil (List.hd exprs);
let exprs = parse_all ":class" in
assert_eq "keyword" (Keyword "class") (List.hd exprs);
let exprs = parse_all "foo" in
assert_eq "symbol" (Symbol "foo") (List.hd exprs);
let exprs = parse_all "(+ 1 2)" in
assert_eq "list" (List [Symbol "+"; Number 1.0; Number 2.0]) (List.hd exprs);
let exprs = parse_all "(div :class \"card\" (p \"hi\"))" in
(match List.hd exprs with
| List [Symbol "div"; Keyword "class"; String "card"; List [Symbol "p"; String "hi"]] ->
incr pass_count; Printf.printf " PASS: nested list\n"
| v -> incr fail_count; Printf.printf " FAIL: nested list — got %s\n" (inspect v));
let exprs = parse_all "'(1 2 3)" in
(match List.hd exprs with
| List [Symbol "quote"; List [Number 1.0; Number 2.0; Number 3.0]] ->
incr pass_count; Printf.printf " PASS: quote sugar\n"
| v -> incr fail_count; Printf.printf " FAIL: quote sugar — got %s\n" (inspect v));
let exprs = parse_all "{:a 1 :b 2}" in
(match List.hd exprs with
| Dict d when dict_has d "a" && dict_has d "b" ->
incr pass_count; Printf.printf " PASS: dict literal\n"
| v -> incr fail_count; Printf.printf " FAIL: dict literal — got %s\n" (inspect v));
let exprs = parse_all ";; comment\n42" in
assert_eq "comment" (Number 42.0) (List.hd exprs);
let exprs = parse_all "(fn (x) (+ x 1))" in
(match List.hd exprs with
| List [Symbol "fn"; List [Symbol "x"]; List [Symbol "+"; Symbol "x"; Number 1.0]] ->
incr pass_count; Printf.printf " PASS: fn form\n"
| v -> incr fail_count; Printf.printf " FAIL: fn form — got %s\n" (inspect v));
let exprs = parse_all "\"hello\\nworld\"" in
assert_eq "string escape" (String "hello\nworld") (List.hd exprs);
let exprs = parse_all "(1 2 3) (4 5)" in
assert_eq "multiple exprs" (Number 2.0) (Number (float_of_int (List.length exprs)));
Printf.printf "\nSuite: primitives\n";
(* --- Primitive tests --- *)
assert_eq "+" (Number 6.0) (call "+" [Number 1.0; Number 2.0; Number 3.0]);
assert_eq "-" (Number 3.0) (call "-" [Number 5.0; Number 2.0]);
assert_eq "*" (Number 12.0) (call "*" [Number 3.0; Number 4.0]);
assert_eq "/" (Number 2.5) (call "/" [Number 5.0; Number 2.0]);
assert_eq "mod" (Number 1.0) (call "mod" [Number 5.0; Number 2.0]);
assert_eq "inc" (Number 6.0) (call "inc" [Number 5.0]);
assert_eq "abs" (Number 5.0) (call "abs" [Number (-5.0)]);
assert_true "=" (call "=" [Number 1.0; Number 1.0]);
assert_true "!=" (call "!=" [Number 1.0; Number 2.0]);
assert_true "<" (call "<" [Number 1.0; Number 2.0]);
assert_true ">" (call ">" [Number 2.0; Number 1.0]);
assert_true "nil?" (call "nil?" [Nil]);
assert_true "number?" (call "number?" [Number 1.0]);
assert_true "string?" (call "string?" [String "hi"]);
assert_true "list?" (call "list?" [List [Number 1.0]]);
assert_true "empty? list" (call "empty?" [List []]);
assert_true "empty? string" (call "empty?" [String ""]);
assert_eq "str" (String "hello42") (call "str" [String "hello"; Number 42.0]);
assert_eq "upper" (String "HI") (call "upper" [String "hi"]);
assert_eq "trim" (String "hi") (call "trim" [String " hi "]);
assert_eq "join" (String "a,b,c") (call "join" [String ","; List [String "a"; String "b"; String "c"]]);
assert_true "starts-with?" (call "starts-with?" [String "hello"; String "hel"]);
assert_true "contains?" (call "contains?" [List [Number 1.0; Number 2.0; Number 3.0]; Number 2.0]);
assert_eq "list" (List [Number 1.0; Number 2.0]) (call "list" [Number 1.0; Number 2.0]);
assert_eq "len" (Number 3.0) (call "len" [List [Number 1.0; Number 2.0; Number 3.0]]);
assert_eq "first" (Number 1.0) (call "first" [List [Number 1.0; Number 2.0]]);
assert_eq "rest" (List [Number 2.0; Number 3.0]) (call "rest" [List [Number 1.0; Number 2.0; Number 3.0]]);
assert_eq "nth" (Number 2.0) (call "nth" [List [Number 1.0; Number 2.0]; Number 1.0]);
assert_eq "cons" (List [Number 0.0; Number 1.0]) (call "cons" [Number 0.0; List [Number 1.0]]);
assert_eq "append" (List [Number 1.0; Number 2.0; Number 3.0])
(call "append" [List [Number 1.0]; List [Number 2.0; Number 3.0]]);
assert_eq "reverse" (List [Number 3.0; Number 2.0; Number 1.0])
(call "reverse" [List [Number 1.0; Number 2.0; Number 3.0]]);
assert_eq "range" (List [Number 0.0; Number 1.0; Number 2.0]) (call "range" [Number 3.0]);
assert_eq "slice" (List [Number 2.0; Number 3.0]) (call "slice" [List [Number 1.0; Number 2.0; Number 3.0]; Number 1.0]);
assert_eq "type-of" (String "number") (call "type-of" [Number 1.0]);
assert_eq "type-of nil" (String "nil") (call "type-of" [Nil]);
Printf.printf "\nSuite: env\n";
(* --- Environment tests --- *)
let e = make_env () in
ignore (env_bind e "x" (Number 42.0));
assert_eq "env-bind + get" (Number 42.0) (env_get e "x");
assert_true "env-has" (Bool (env_has e "x"));
let child = env_extend e in
ignore (env_bind child "y" (Number 10.0));
assert_eq "child sees parent" (Number 42.0) (env_get child "x");
assert_eq "child own binding" (Number 10.0) (env_get child "y");
ignore (env_set child "x" (Number 99.0));
assert_eq "set! walks chain" (Number 99.0) (env_get e "x");
Printf.printf "\nSuite: types\n";
(* --- Type tests --- *)
assert_true "sx_truthy true" (Bool (sx_truthy (Bool true)));
assert_true "sx_truthy 0" (Bool (sx_truthy (Number 0.0)));
assert_true "sx_truthy \"\"" (Bool (sx_truthy (String "")));
assert_eq "not truthy nil" (Bool false) (Bool (sx_truthy Nil));
assert_eq "not truthy false" (Bool false) (Bool (sx_truthy (Bool false)));
let l = { l_params = ["x"]; l_body = Symbol "x"; l_closure = make_env (); l_name = None } in
assert_true "is_lambda" (Bool (is_lambda (Lambda l)));
ignore (Sx.Sx_types.set_lambda_name (Lambda l) "my-fn");
assert_eq "lambda name mutated" (String "my-fn") (lambda_name (Lambda l));
(* --- Summary --- *)
Printf.printf "\n============================================================\n";
Printf.printf "Results: %d passed, %d failed\n" !pass_count !fail_count;
Printf.printf "============================================================\n";
if !fail_count > 0 then exit 1