let () = let test_sources = [ "(define foo 42)"; ";; comment\n(define bar 1)\n\n;; another\n(define baz 2)\n"; "(define my-fn\n (fn (x)\n ;; check nil\n (if (nil? x) 0 x)))"; "(list 1 2 3)"; "{:key \"value\" :num 42}"; "'(a b c)"; "`(a ,b ,@c)"; "(define x \"hello\\nworld\")"; ";; top\n;; multi-line\n(define a 1)\n"; ""; " \n "; "(a)\n(b)\n(c)"; "(a ;; inline\n b)"; ] in let pass = ref 0 in let fail = ref 0 in List.iter (fun src -> let cst = Sx_parser.parse_all_cst src in let roundtrip = Sx_cst.cst_file_to_source cst.nodes cst.trailing_trivia in if roundtrip = src then begin incr pass; Printf.printf "PASS: %S\n" (if String.length src > 40 then String.sub src 0 40 ^ "..." else src) end else begin incr fail; Printf.printf "FAIL: %S\n expected: %S\n got: %S\n" (if String.length src > 40 then String.sub src 0 40 ^ "..." else src) src roundtrip end ) test_sources; (* Also test CST→AST matches AST parser *) let ast_tests = [ "(define foo 42)"; "(list 1 2 3)"; "{:key \"value\"}"; ";; comment\n(define bar 1)"; ] in Printf.printf "\nCST→AST equivalence:\n"; List.iter (fun src -> let ast_direct = Sx_parser.parse_all src in let cst = Sx_parser.parse_all_cst src in let ast_via_cst = List.map Sx_cst.cst_to_ast cst.nodes in let s1 = String.concat " " (List.map Sx_types.inspect ast_direct) in let s2 = String.concat " " (List.map Sx_types.inspect ast_via_cst) in if s1 = s2 then begin incr pass; Printf.printf "PASS: %S\n" src end else begin incr fail; Printf.printf "FAIL: %S\n AST: %s\n CST→AST: %s\n" src s1 s2 end ) ast_tests; (* Test real .sx files from the codebase *) Printf.printf "\nReal file round-trips:\n"; let test_file path = try let src = In_channel.with_open_text path In_channel.input_all in let cst = Sx_parser.parse_all_cst src in let roundtrip = Sx_cst.cst_file_to_source cst.nodes cst.trailing_trivia in if roundtrip = src then begin incr pass; Printf.printf "PASS: %s (%d bytes)\n" path (String.length src) end else begin incr fail; (* Find first difference *) let len = min (String.length src) (String.length roundtrip) in let diff_pos = ref len in for i = 0 to len - 1 do if src.[i] <> roundtrip.[i] && !diff_pos = len then diff_pos := i done; Printf.printf "FAIL: %s (diff at byte %d, src=%d rt=%d)\n" path !diff_pos (String.length src) (String.length roundtrip) end with e -> incr fail; Printf.printf "ERROR: %s — %s\n" path (Printexc.to_string e) in let spec_dir = try Sys.getenv "SX_SPEC_DIR" with Not_found -> "spec" in let project_dir = try Sys.getenv "SX_PROJECT_DIR" with Not_found -> "." in List.iter test_file [ spec_dir ^ "/evaluator.sx"; spec_dir ^ "/parser.sx"; spec_dir ^ "/primitives.sx"; spec_dir ^ "/render.sx"; project_dir ^ "/lib/tree-tools.sx"; project_dir ^ "/web/engine.sx"; project_dir ^ "/web/io.sx"; ]; Printf.printf "\n%d/%d passed\n" !pass (!pass + !fail); if !fail > 0 then exit 1