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>
178 lines
7.5 KiB
OCaml
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
|