@@ -21,6 +21,18 @@ let read_file path =
2121 close_in ic;
2222 s
2323
24+ (* Substring search — OCaml's [String] only has char membership. *)
25+ let contains haystack needle =
26+ let hn = String. length haystack and nn = String. length needle in
27+ if nn = 0 then true
28+ else
29+ let rec loop i =
30+ if i + nn > hn then false
31+ else if String. sub haystack i nn = needle then true
32+ else loop (i + 1 )
33+ in
34+ loop 0
35+
2436let tree_sitter_available () =
2537 Sys. command " command -v tree-sitter > /dev/null 2>&1" = 0
2638
@@ -194,6 +206,65 @@ let test_walker_finds_oversized_function () =
194206 gated end-to-end tests above exercise it through real
195207 tree-sitter output. *)
196208
209+ (* ---- Phase 3 slice 1: structural type-declaration translation -------------
210+
211+ [fixtures/phase3.res] holds three translatable structural type decls
212+ (a primitive alias and two simple sum types) plus forms that slice 1
213+ must skip (a generic, a qualified-path alias, and a let/switch). These
214+ gated tests assert the translator's output structurally. *)
215+
216+ let phase3_fixture = " fixtures/phase3.res"
217+
218+ let translate_phase3 () =
219+ let source = read_file phase3_fixture in
220+ let path = Filename. concat (Sys. getcwd () ) phase3_fixture in
221+ Walker. translate ~grammar_dir: (grammar_dir () ) ~path ~source
222+
223+ let translate_phase3_blob () =
224+ String. concat " \n " (List. map snd (translate_phase3 () ))
225+
226+ let test_translate_count () =
227+ skip_unless_ready () ;
228+ Alcotest. (check int )
229+ " exactly the three structural type decls are translated"
230+ 3 (List. length (translate_phase3 () ))
231+
232+ let test_translate_alias () =
233+ skip_unless_ready () ;
234+ Alcotest. (check bool )
235+ " primitive alias -> capitalised TyCon + Int"
236+ true (contains (translate_phase3_blob () ) " type UserId = Int" )
237+
238+ let test_translate_nullary_sum () =
239+ skip_unless_ready () ;
240+ let blob = translate_phase3_blob () in
241+ let ok =
242+ contains blob " type Color =" && contains blob " | Red"
243+ && contains blob " | Green" && contains blob " | Blue"
244+ in
245+ Alcotest. (check bool ) " nullary sum -> leading-pipe variant form" true ok
246+
247+ let test_translate_payload_sum () =
248+ skip_unless_ready () ;
249+ let blob = translate_phase3_blob () in
250+ let ok =
251+ contains blob " type Shape =" && contains blob " | Circle(Float)"
252+ && contains blob " | Rect(Int, Int)"
253+ in
254+ Alcotest. (check bool ) " primitive-payload sum -> mapped param types" true ok
255+
256+ let test_translate_skips_non_structural () =
257+ skip_unless_ready () ;
258+ let blob = translate_phase3_blob () in
259+ (* the generic Box, qualified Belt.Map.t, and the let/switch must all be
260+ absent from the translation — slice 1 never guesses them. *)
261+ let leaked =
262+ contains blob " switch" || contains blob " area" || contains blob " Box"
263+ || contains blob " Belt" || contains blob " 'a"
264+ in
265+ Alcotest. (check bool ) " non-structural / generic / qualified forms skipped"
266+ false leaked
267+
197268let () =
198269 Alcotest. run " res-to-affine-walker"
199270 [
@@ -221,4 +292,17 @@ let () =
221292 Alcotest. test_case " oversized-function found on phase2c.res"
222293 `Quick test_walker_finds_oversized_function;
223294 ] );
295+ ( " walker-phase3-translate" ,
296+ [
297+ Alcotest. test_case " three structural type decls translated"
298+ `Quick test_translate_count;
299+ Alcotest. test_case " primitive alias -> type UserId = Int"
300+ `Quick test_translate_alias;
301+ Alcotest. test_case " nullary sum -> leading-pipe variants"
302+ `Quick test_translate_nullary_sum;
303+ Alcotest. test_case " primitive-payload sum -> mapped params"
304+ `Quick test_translate_payload_sum;
305+ Alcotest. test_case " generic / qualified / non-type forms skipped"
306+ `Quick test_translate_skips_non_structural;
307+ ] );
224308 ]
0 commit comments