···8686 String.capitalize_ascii base = m)
8787 bindings
88888989+let err_parse s = Error (Fmt.str "cannot parse expression: %S" s)
9090+let err_syntax s = Error (Fmt.str "syntax error in: %S" s)
9191+let err_lexer s = Error (Fmt.str "lexer error in: %S" s)
9292+8993let parse_expr s =
9094 let lexbuf = Lexing.from_string s in
9195 match Parse.implementation lexbuf with
9296 | [ { Parsetree.pstr_desc = Pstr_eval (e, _); _ } ] -> Ok e
9397 | [ { Parsetree.pstr_desc = Pstr_value (_, [ vb ]); _ } ] -> Ok vb.pvb_expr
9494- | _ -> Error (Fmt.str "cannot parse expression: %S" s)
9595- | exception Syntaxerr.Error _ -> Error (Fmt.str "syntax error in: %S" s)
9696- | exception Lexer.Error (_, _) -> Error (Fmt.str "lexer error in: %S" s)
9898+ | _ -> err_parse s
9999+ | exception Syntaxerr.Error _ -> err_syntax s
100100+ | exception Lexer.Error (_, _) -> err_lexer s
9710198102let is_hex_hash s =
99103 String.length s = 32
···144148 in
145149 Dupfind.Report.output ~format ~top [ cluster ]
146150147147-let find_by_expr ~format ~top index query =
151151+let by_expr ~format ~top index query =
148152 let expr =
149153 match parse_expr query with Ok e -> e | Error msg -> Fmt.failwith "%s" msg
150154 in
···155159 let matches = Dupfind.Index.get index target_hash in
156160 report_matches ~format ~top ~qualified_name:query expr matches target_hash
157161158158-let find_by_name ~format ~top index query all_files =
162162+let by_name ~format ~top index query all_files =
159163 let mod_name, func_name = resolve_qualified query in
160164 let target =
161165 List.find_map
···184188 let index = index_files ~min_size ~progress all_files in
185189 if is_hex_hash query then
186190 report_hash_matches ~format ~top query (Dupfind.Index.get index query)
187187- else if is_inline_expr query then find_by_expr ~format ~top index query
188188- else find_by_name ~format ~top index query all_files
191191+ else if is_inline_expr query then by_expr ~format ~top index query
192192+ else by_name ~format ~top index query all_files
189193190194(* similar subcommand *)
191195···245249 Fmt.pr "@[<v>AST: %a@,Hash: %s@]@." Dupfind.Normalize.pp_expr normalized
246250 h
247251252252+(* show subcommand *)
253253+254254+let run_show eio min_size hash_query paths =
255255+ let fs = Eio.Stdenv.fs eio in
256256+ let all_files = collect_files ~fs ~exclude:[] paths in
257257+ Log.info (fun m -> m "Indexing %d files" (List.length all_files));
258258+ let progress =
259259+ Tty.Progress.create ~enabled:(Tty.is_tty ()) ~total:(List.length all_files)
260260+ "Indexing"
261261+ in
262262+ let index = index_files ~min_size ~progress all_files in
263263+ let matches = Dupfind.Index.get index hash_query in
264264+ if matches = [] then (
265265+ Fmt.epr "No matches for hash %s.@." hash_query;
266266+ Stdlib.exit 1)
267267+ else begin
268268+ (* Show normalized AST from the first match *)
269269+ let first = List.hd matches in
270270+ let file = first.Dupfind.Fragment.location.file in
271271+ let structure = Dupfind.Source.parse_file file in
272272+ let bindings = Dupfind.Source.extract_bindings structure in
273273+ let target =
274274+ List.find_opt
275275+ (fun (name, _, line) ->
276276+ name = first.binding_name && line = first.location.line)
277277+ bindings
278278+ in
279279+ (match target with
280280+ | Some (_name, expr, _line) ->
281281+ let normalized = Dupfind.Normalize.apply expr in
282282+ Fmt.pr
283283+ "@[<v>Hash: %s@,\
284284+ @,\
285285+ Normalized AST:@,\
286286+ \ %a@,\
287287+ @,\
288288+ Locations (%d matches):@]@."
289289+ hash_query Dupfind.Normalize.pp_expr normalized (List.length matches)
290290+ | None ->
291291+ Fmt.pr "Hash: %s@.@.Locations (%d matches):@." hash_query
292292+ (List.length matches));
293293+ List.iter
294294+ (fun (f : Dupfind.Fragment.t) ->
295295+ Fmt.pr " %s:%d %s@." f.location.file f.location.line f.binding_name)
296296+ matches
297297+ end
298298+248299(* Cmdliner terms *)
249300250301let setup = Vlog.setup ~json_reporter:None "dupfind"
···335386 & info [] ~docv:"EXPR"
336387 ~doc:"OCaml expression to hash (e.g. $(b,\"let f x = x + 1\")).")
337388389389+let show_hash =
390390+ Arg.(
391391+ required
392392+ & pos 0 (some string) None
393393+ & info [] ~docv:"HASH" ~doc:"The hex hash of the cluster to show.")
394394+395395+let show_paths =
396396+ Arg.(
397397+ value & pos_right 0 string [ "." ]
398398+ & info [] ~docv:"DIR" ~doc:"Directories to scan.")
399399+400400+let show_cmd eio =
401401+ let doc = "Show normalized AST for a hash cluster." in
402402+ let info = Cmd.info "show" ~doc in
403403+ Cmd.v info
404404+ Term.(
405405+ const (fun () -> run_show eio) $ setup $ min_size $ show_hash $ show_paths)
406406+338407let hash_cmd =
339408 let doc = "Show normalized AST and hash for an expression." in
340409 let info = Cmd.info "hash" ~doc in
···354423 let info = Cmd.info "dupfind" ~version:"0.1.0" ~doc in
355424 Cmd.group info
356425 ~default:Term.(ret (const (`Help (`Pager, None))))
357357- [ scan_cmd eio; search_cmd eio; hash_cmd; similar_cmd eio ]
426426+ [ scan_cmd eio; search_cmd eio; show_cmd eio; hash_cmd; similar_cmd eio ]
358427359428let () =
360429 Memtrace.trace_if_requested ();
+199-5
lib/normalize.ml
···55let fresh env =
66 let n = env.counter in
77 env.counter <- n + 1;
88- Printf.sprintf "_%d" n
88+ Fmt.str "_%d" n
991010let rename env name =
1111 let canonical = fresh env in
···5555 | E_assert of expr
5656 | E_lazy of expr
5757 | E_try of expr * case list
5858+ | E_while of expr * expr
5959+ | E_for of string * expr * expr * Asttypes.direction_flag * expr
6060+ | E_setfield of expr * string * expr
6161+ | E_array of expr list
6262+ | E_send of expr * string
5863 | E_other
59646065and binding = { bpat : pat; bexpr : expr }
···164169 | Pexp_try (e, cases) ->
165170 E_try (convert_expr env e, List.map (convert_case env) cases)
166171 | Pexp_open (_, e) -> convert_expr env e
172172+ | Pexp_while (cond, body) ->
173173+ E_while (convert_expr env cond, convert_expr env body)
174174+ | Pexp_for (pat, lo, hi, dir, body) ->
175175+ let name =
176176+ match pat.ppat_desc with
177177+ | Ppat_var { txt; _ } -> rename env txt
178178+ | _ -> "_"
179179+ in
180180+ E_for
181181+ ( name,
182182+ convert_expr env lo,
183183+ convert_expr env hi,
184184+ dir,
185185+ convert_expr env body )
186186+ | Pexp_setfield (e, { txt = lid; _ }, v) ->
187187+ E_setfield
188188+ (convert_expr env e, longident_to_string lid, convert_expr env v)
189189+ | Pexp_array items -> E_array (List.map (convert_expr env) items)
190190+ | Pexp_letop { let_; ands; body } ->
191191+ (* Desugar let* x = e in body → ( let* ) e (fun x -> body) *)
192192+ let let_op = E_ident let_.pbop_op.txt in
193193+ let let_rhs = convert_expr env let_.pbop_exp in
194194+ let ands_pre =
195195+ List.map
196196+ (fun bop ->
197197+ ( E_ident bop.Parsetree.pbop_op.txt,
198198+ convert_expr env bop.pbop_exp,
199199+ bop.Parsetree.pbop_pat ))
200200+ ands
201201+ in
202202+ (* Bind all patterns, then convert body *)
203203+ let let_pat = convert_pat env let_.pbop_pat in
204204+ let ands_bound =
205205+ List.map
206206+ (fun (op, rhs, raw_pat) -> (op, rhs, convert_pat env raw_pat))
207207+ ands_pre
208208+ in
209209+ let body = convert_expr env body in
210210+ let body =
211211+ List.fold_right
212212+ (fun (op, rhs, pat) acc ->
213213+ E_apply
214214+ ( op,
215215+ [
216216+ (Asttypes.Nolabel, rhs);
217217+ ( Asttypes.Nolabel,
218218+ E_fun
219219+ ( [ Param_val (Asttypes.Nolabel, None, pat) ],
220220+ Body_expr acc ) );
221221+ ] ))
222222+ ands_bound body
223223+ in
224224+ E_apply
225225+ ( let_op,
226226+ [
227227+ (Asttypes.Nolabel, let_rhs);
228228+ ( Asttypes.Nolabel,
229229+ E_fun
230230+ ([ Param_val (Asttypes.Nolabel, None, let_pat) ], Body_expr body)
231231+ );
232232+ ] )
233233+ | Pexp_letmodule (_, _, e) -> convert_expr env e
234234+ | Pexp_letexception (_, e) -> convert_expr env e
235235+ | Pexp_send (e, { txt; _ }) -> E_send (convert_expr env e, txt)
236236+ | Pexp_new { txt; _ } -> E_ident (longident_to_string txt)
167237 | _ -> E_other
168238169239and convert_param env (p : Parsetree.function_param) =
···229299 | E_try (e, cases) ->
230300 E_try
231301 (subst name replacement e, List.map (subst_case name replacement) cases)
302302+ | E_while (c, b) ->
303303+ E_while (subst name replacement c, subst name replacement b)
304304+ | E_for (v, lo, hi, dir, b) ->
305305+ E_for
306306+ ( v,
307307+ subst name replacement lo,
308308+ subst name replacement hi,
309309+ dir,
310310+ subst name replacement b )
311311+ | E_setfield (e, f, v) ->
312312+ E_setfield (subst name replacement e, f, subst name replacement v)
313313+ | E_array es -> E_array (List.map (subst name replacement) es)
314314+ | E_send (e, m) -> E_send (subst name replacement e, m)
232315233316and subst_params name replacement params =
234317 List.map
···283366 ident_in_expr name c || ident_in_expr name t
284367 || opt_exists (ident_in_expr name) f
285368 | E_sequence (e1, e2) -> ident_in_expr name e1 || ident_in_expr name e2
369369+ | E_while (c, b) -> ident_in_expr name c || ident_in_expr name b
370370+ | E_for (_, lo, hi, _, b) ->
371371+ ident_in_expr name lo || ident_in_expr name hi || ident_in_expr name b
372372+ | E_setfield (e, _, v) -> ident_in_expr name e || ident_in_expr name v
373373+ | E_array es -> List.exists (ident_in_expr name) es
374374+ | E_send (e, _) -> ident_in_expr name e
286375287376and ident_in_body name = function
288377 | Body_expr e -> ident_in_expr name e
···334423 | E_assert e -> E_assert (inline_lets e)
335424 | E_lazy e -> E_lazy (inline_lets e)
336425 | E_try (e, cases) -> E_try (inline_lets e, List.map inline_lets_case cases)
426426+ | E_while (c, b) -> E_while (inline_lets c, inline_lets b)
427427+ | E_for (v, lo, hi, dir, b) ->
428428+ E_for (v, inline_lets lo, inline_lets hi, dir, inline_lets b)
429429+ | E_setfield (e, f, v) -> E_setfield (inline_lets e, f, inline_lets v)
430430+ | E_array es -> E_array (List.map inline_lets es)
431431+ | E_send (e, m) -> E_send (inline_lets e, m)
337432 | (E_ident _ | E_const _ | E_other) as e -> e
338433339434and inline_lets_params params =
···395490 | E_assert e -> E_assert (beta_reduce e)
396491 | E_lazy e -> E_lazy (beta_reduce e)
397492 | E_try (e, cases) -> E_try (beta_reduce e, List.map beta_reduce_case cases)
493493+ | E_while (c, b) -> E_while (beta_reduce c, beta_reduce b)
494494+ | E_for (v, lo, hi, dir, b) ->
495495+ E_for (v, beta_reduce lo, beta_reduce hi, dir, beta_reduce b)
496496+ | E_setfield (e, f, v) -> E_setfield (beta_reduce e, f, beta_reduce v)
497497+ | E_array es -> E_array (List.map beta_reduce es)
498498+ | E_send (e, m) -> E_send (beta_reduce e, m)
398499 | (E_ident _ | E_const _ | E_other) as e -> e
399500400501and beta_reduce_params params =
···447548 | E_assert e -> E_assert (eta_reduce e)
448549 | E_lazy e -> E_lazy (eta_reduce e)
449550 | E_try (e, cases) -> E_try (eta_reduce e, List.map eta_reduce_case cases)
551551+ | E_while (c, b) -> E_while (eta_reduce c, eta_reduce b)
552552+ | E_for (v, lo, hi, dir, b) ->
553553+ E_for (v, eta_reduce lo, eta_reduce hi, dir, eta_reduce b)
554554+ | E_setfield (e, f, v) -> E_setfield (eta_reduce e, f, eta_reduce v)
555555+ | E_array es -> E_array (List.map eta_reduce es)
556556+ | E_send (e, m) -> E_send (eta_reduce e, m)
450557 | (E_ident _ | E_const _ | E_other) as e -> e
451558452559(* Try the eta rule on a fun node. For a single-param function
···523630 | E_assert e -> E_assert (flatten_funs e)
524631 | E_lazy e -> E_lazy (flatten_funs e)
525632 | E_try (e, cases) -> E_try (flatten_funs e, List.map flatten_funs_case cases)
633633+ | E_while (c, b) -> E_while (flatten_funs c, flatten_funs b)
634634+ | E_for (v, lo, hi, dir, b) ->
635635+ E_for (v, flatten_funs lo, flatten_funs hi, dir, flatten_funs b)
636636+ | E_setfield (e, f, v) -> E_setfield (flatten_funs e, f, flatten_funs v)
637637+ | E_array es -> E_array (List.map flatten_funs es)
638638+ | E_send (e, m) -> E_send (flatten_funs e, m)
526639 | (E_ident _ | E_const _ | E_other) as e -> e
527640528641and flatten_funs_params params =
···696809 buf_tag buf 15;
697810 hash_expr buf e;
698811 buf_list buf (hash_case buf) cases
812812+ | E_while (c, b) ->
813813+ buf_tag buf 17;
814814+ hash_expr buf c;
815815+ hash_expr buf b
816816+ | E_for (v, lo, hi, dir, b) ->
817817+ buf_tag buf 18;
818818+ buf_str buf v;
819819+ hash_expr buf lo;
820820+ hash_expr buf hi;
821821+ buf_tag buf (match dir with Asttypes.Upto -> 0 | Downto -> 1);
822822+ hash_expr buf b
823823+ | E_setfield (e, f, v) ->
824824+ buf_tag buf 19;
825825+ hash_expr buf e;
826826+ buf_str buf f;
827827+ hash_expr buf v
828828+ | E_array es ->
829829+ buf_tag buf 20;
830830+ buf_list buf (hash_expr buf) es
831831+ | E_send (e, m) ->
832832+ buf_tag buf 21;
833833+ hash_expr buf e;
834834+ buf_str buf m
699835 | E_other -> buf_tag buf 16
700836701837and hash_param buf = function
···753889 1 + expr_size c + expr_size t
754890 + match f with None -> 0 | Some e -> expr_size e)
755891 | E_sequence (e1, e2) -> 1 + expr_size e1 + expr_size e2
892892+ | E_while (c, b) -> 1 + expr_size c + expr_size b
893893+ | E_for (_, lo, hi, _, b) -> 1 + expr_size lo + expr_size hi + expr_size b
894894+ | E_setfield (e, _, v) -> 1 + expr_size e + expr_size v
895895+ | E_array es -> 1 + List.fold_left (fun acc e -> acc + expr_size e) 0 es
896896+ | E_send (e, _) -> 1 + expr_size e
756897757898and binding_size { bpat = _; bexpr } = 1 + expr_size bexpr
758899···801942 | E_sequence (e1, e2) ->
802943 walk_expr e1;
803944 walk_expr e2
945945+ | E_while (c, b) ->
946946+ walk_expr c;
947947+ walk_expr b
948948+ | E_for (_, lo, hi, _, b) ->
949949+ walk_expr lo;
950950+ walk_expr hi;
951951+ walk_expr b
952952+ | E_setfield (e, _, v) ->
953953+ walk_expr e;
954954+ walk_expr v
955955+ | E_array es -> List.iter walk_expr es
956956+ | E_send (e, _) -> walk_expr e
804957 and walk_body = function
805958 | Body_expr e -> walk_expr e
806959 | Body_cases cases -> List.iter walk_case cases
···813966814967(* Pass 5: Common sub-expression elimination *)
815968816816-let cse expr =
817817- (* Count occurrences of each sub-expression by hash *)
969969+let cse_count_occurrences expr =
818970 let counts = Hashtbl.create 64 in
819971 let rec count_expr e =
820972 let sz = expr_size e in
···8551007 | E_sequence (e1, e2) ->
8561008 count_expr e1;
8571009 count_expr e2
10101010+ | E_while (c, b) ->
10111011+ count_expr c;
10121012+ count_expr b
10131013+ | E_for (_, lo, hi, _, b) ->
10141014+ count_expr lo;
10151015+ count_expr hi;
10161016+ count_expr b
10171017+ | E_setfield (e, _, v) ->
10181018+ count_expr e;
10191019+ count_expr v
10201020+ | E_array es -> List.iter count_expr es
10211021+ | E_send (e, _) -> count_expr e
8581022 and count_body = function
8591023 | Body_expr e -> count_expr e
8601024 | Body_cases cases -> List.iter count_case cases
···8631027 count_expr rhs
8641028 in
8651029 count_expr expr;
866866- (* Extract shared sub-expressions *)
10301030+ counts
10311031+10321032+let cse_extract counts expr =
8671033 let seen : (string, string) Hashtbl.t = Hashtbl.create 16 in
8681034 let bindings = ref [] in
8691035 let cse_counter = ref 0 in
8701036 let cse_fresh () =
8711037 let n = !cse_counter in
8721038 incr cse_counter;
873873- Printf.sprintf "_cse%d" n
10391039+ Fmt.str "_cse%d" n
8741040 in
8751041 let rec extract e =
8761042 let sz = expr_size e in
···9131079 | E_assert e -> E_assert (extract e)
9141080 | E_lazy e -> E_lazy (extract e)
9151081 | E_try (e, cases) -> E_try (extract e, List.map extract_case cases)
10821082+ | E_while (c, b) -> E_while (extract c, extract b)
10831083+ | E_for (v, lo, hi, dir, b) ->
10841084+ E_for (v, extract lo, extract hi, dir, extract b)
10851085+ | E_setfield (e, f, v) -> E_setfield (extract e, f, extract v)
10861086+ | E_array es -> E_array (List.map extract es)
10871087+ | E_send (e, m) -> E_send (extract e, m)
9161088 and extract_params params =
9171089 List.map
9181090 (function
···9301102 match List.rev !bindings with
9311103 | [] -> body
9321104 | bs -> E_let (Asttypes.Nonrecursive, bs, body)
11051105+11061106+let cse expr =
11071107+ let counts = cse_count_occurrences expr in
11081108+ cse_extract counts expr
93311099341110(* Pass 6: Renumber — assign fresh sequential _0, _1, ... to all binding sites *)
9351111···9671143 | E_assert e -> E_assert (ren_expr e)
9681144 | E_lazy e -> E_lazy (ren_expr e)
9691145 | E_try (e, cases) -> E_try (ren_expr e, List.map ren_case cases)
11461146+ | E_while (c, b) -> E_while (ren_expr c, ren_expr b)
11471147+ | E_for (v, lo, hi, dir, b) ->
11481148+ let v = bind v in
11491149+ E_for (v, ren_expr lo, ren_expr hi, dir, ren_expr b)
11501150+ | E_setfield (e, f, v) -> E_setfield (ren_expr e, f, ren_expr v)
11511151+ | E_array es -> E_array (List.map ren_expr es)
11521152+ | E_send (e, m) -> E_send (ren_expr e, m)
9701153 and ren_pat = function
9711154 | P_var s -> P_var (bind s)
9721155 | P_tuple ps -> P_tuple (List.map ren_pat ps)
···10751258 | E_lazy e -> Fmt.pf fmt "lazy %a" pp_expr e
10761259 | E_try (e, cases) ->
10771260 Fmt.pf fmt "@[<v>try %a with@ %a@]" pp_expr e pp_cases cases
12611261+ | E_while (c, b) ->
12621262+ Fmt.pf fmt "@[<v>while %a do@ %a@ done@]" pp_expr c pp_expr b
12631263+ | E_for (v, lo, hi, dir, b) ->
12641264+ let dir_s =
12651265+ match dir with Asttypes.Upto -> "to" | Asttypes.Downto -> "downto"
12661266+ in
12671267+ Fmt.pf fmt "@[<v>for %s = %a %s %a do@ %a@ done@]" v pp_expr lo dir_s
12681268+ pp_expr hi pp_expr b
12691269+ | E_setfield (e, f, v) -> Fmt.pf fmt "%a.%s <- %a" pp_expr e f pp_expr v
12701270+ | E_array es -> Fmt.pf fmt "[| %a |]" Fmt.(list ~sep:semi pp_expr) es
12711271+ | E_send (e, m) -> Fmt.pf fmt "%a#%s" pp_expr e m
10781272 | E_other -> Fmt.string fmt "<expr>"
1079127310801274and pp_binding fmt { bpat; bexpr } =
+1
test/test_cluster.mli
···11val suite : string * unit Alcotest.test_case list
22+(** Alcotest test suite. *)
+1
test/test_discover.mli
···11val suite : string * unit Alcotest.test_case list
22+(** Alcotest test suite. *)
+1
test/test_fingerprint.mli
···11val suite : string * unit Alcotest.test_case list
22+(** Alcotest test suite. *)
+1
test/test_fragment.mli
···11val suite : string * unit Alcotest.test_case list
22+(** Alcotest test suite. *)
+1
test/test_index.mli
···11val suite : string * unit Alcotest.test_case list
22+(** Alcotest test suite. *)
+96
test/test_normalize.ml
···143143 (same "fun x -> x.foo" "fun y -> y.foo");
144144 Alcotest.test_case "misc: lazy alpha" `Quick
145145 (same "fun x -> lazy x" "fun y -> lazy y");
146146+ (* --- While loops --- *)
147147+ Alcotest.test_case "while: alpha" `Quick
148148+ (same "fun x -> while f x do g x done" "fun y -> while f y do g y done");
149149+ Alcotest.test_case "while: diff body" `Quick
150150+ (diff "fun x -> while f x do g x done" "fun x -> while f x do h x done");
151151+ Alcotest.test_case "while: diff cond" `Quick
152152+ (diff "fun x -> while f x do g x done" "fun x -> while h x do g x done");
153153+ Alcotest.test_case "while: not confused with sequence" `Quick
154154+ (diff "fun x -> while f x do g x done" "fun x -> f x; g x");
155155+ (* --- For loops --- *)
156156+ Alcotest.test_case "for: alpha" `Quick
157157+ (same "fun x -> for i = 0 to x do f i done"
158158+ "fun y -> for j = 0 to y do f j done");
159159+ Alcotest.test_case "for: diff direction" `Quick
160160+ (diff "fun x -> for i = 0 to x do f i done"
161161+ "fun x -> for i = x downto 0 do f i done");
162162+ Alcotest.test_case "for: diff body" `Quick
163163+ (diff "fun x -> for i = 0 to x do f i done"
164164+ "fun x -> for i = 0 to x do g i done");
165165+ Alcotest.test_case "for: diff bounds" `Quick
166166+ (diff "fun x -> for i = 0 to x do f i done"
167167+ "fun x -> for i = 1 to x do f i done");
168168+ (* --- Setfield --- *)
169169+ Alcotest.test_case "setfield: alpha" `Quick
170170+ (same "fun x v -> x.foo <- v" "fun y w -> y.foo <- w");
171171+ Alcotest.test_case "setfield: diff field" `Quick
172172+ (diff "fun x v -> x.foo <- v" "fun x v -> x.bar <- v");
173173+ Alcotest.test_case "setfield: not confused with field" `Quick
174174+ (diff "fun x -> x.foo <- 1" "fun x -> x.foo");
175175+ (* --- Array --- *)
176176+ Alcotest.test_case "array: alpha" `Quick
177177+ (same "fun x -> [| x; x + 1 |]" "fun y -> [| y; y + 1 |]");
178178+ Alcotest.test_case "array: diff elements" `Quick
179179+ (diff "fun x -> [| x; 1 |]" "fun x -> [| x; 2 |]");
180180+ Alcotest.test_case "array: diff length" `Quick
181181+ (diff "fun x -> [| x |]" "fun x -> [| x; x |]");
182182+ Alcotest.test_case "array: not confused with tuple" `Quick
183183+ (diff "[| 1; 2; 3 |]" "(1, 2, 3)");
184184+ (* --- Send (method call) --- *)
185185+ Alcotest.test_case "send: alpha" `Quick
186186+ (same "fun x -> x#foo" "fun y -> y#foo");
187187+ Alcotest.test_case "send: diff method" `Quick
188188+ (diff "fun x -> x#foo" "fun x -> x#bar");
189189+ Alcotest.test_case "send: not confused with field" `Quick
190190+ (diff "fun x -> x#foo" "fun x -> x.foo");
191191+ (* --- Letop desugaring --- *)
192192+ Alcotest.test_case "letop: different from let" `Quick
193193+ (diff "fun x -> let* y = f x in g y" "fun x -> let y = f x in g y");
194194+ Alcotest.test_case "letop: alpha" `Quick
195195+ (same "fun x -> let* a = f x in g a" "fun y -> let* b = f y in g b");
196196+ Alcotest.test_case "letop: desugared to apply" `Quick
197197+ (same "fun x -> let* y = f x in g y"
198198+ "fun x -> ( let* ) (f x) (fun y -> g y)");
199199+ (* --- Letmodule passthrough --- *)
200200+ Alcotest.test_case "letmodule: transparent" `Quick
201201+ (same "fun x -> let module M = List in f x" "fun x -> f x");
202202+ (* --- Letexception passthrough --- *)
203203+ Alcotest.test_case "letexception: transparent" `Quick
204204+ (same "fun x -> let exception E in f x" "fun x -> f x");
205205+ (* --- New desugaring --- *)
206206+ Alcotest.test_case "new: becomes ident" `Quick (same "new foo" "foo");
207207+ (* --- Distinctness: new constructors differ from each other --- *)
208208+ Alcotest.test_case "distinct: while vs for" `Quick
209209+ (diff "fun x -> while f x do g x done"
210210+ "fun x -> for i = 0 to 10 do g x done");
211211+ Alcotest.test_case "distinct: array vs tuple" `Quick
212212+ (diff "[| 1; 2 |]" "(1, 2)");
213213+ Alcotest.test_case "distinct: setfield vs sequence" `Quick
214214+ (diff "fun x -> x.f <- 1" "fun x -> x.f; 1");
215215+ Alcotest.test_case "distinct: send vs field" `Quick
216216+ (diff "fun x -> x#m" "fun x -> x.m");
217217+ (* --- New constructs don't hash to E_other --- *)
218218+ Alcotest.test_case "not-other: while different from other while" `Quick
219219+ (diff "fun x -> while true do f x done"
220220+ "fun x -> while true do g x done");
221221+ Alcotest.test_case "not-other: for different from other for" `Quick
222222+ (diff "fun x -> for i = 0 to 10 do f i done"
223223+ "fun x -> for i = 0 to 10 do g i done");
224224+ Alcotest.test_case "not-other: setfield different from other setfield"
225225+ `Quick
226226+ (diff "fun x -> x.a <- 1" "fun x -> x.b <- 1");
227227+ (* --- Combined passes with new constructs --- *)
228228+ Alcotest.test_case "combined: cse in while" `Quick
229229+ (same "fun x -> while (x + 1) > 0 do f (x + 1) done"
230230+ "fun x -> let y = x + 1 in while y > 0 do f y done");
231231+ Alcotest.test_case "combined: cse in array" `Quick
232232+ (same "fun x -> [| x + 1; x + 1 |]"
233233+ "fun x -> let y = x + 1 in [| y; y |]");
234234+ Alcotest.test_case "combined: beta in for body" `Quick
235235+ (same "fun x -> for i = 0 to x do (fun y -> f y) i done"
236236+ "fun x -> for i = 0 to x do f i done");
237237+ Alcotest.test_case "combined: eta in setfield" `Quick
238238+ (same "fun x -> x.foo <- (fun y -> g y) 1" "fun x -> x.foo <- g 1");
239239+ Alcotest.test_case "combined: inline in array" `Quick
240240+ (same "fun x -> let y = x + 1 in [| y; y |]"
241241+ "fun x -> [| x + 1; x + 1 |]");
146242 ] )
+1
test/test_normalize.mli
···11val suite : string * unit Alcotest.test_case list
22+(** Alcotest test suite. *)
+1
test/test_report.mli
···11val suite : string * unit Alcotest.test_case list
22+(** Alcotest test suite. *)
+1
test/test_similar.mli
···11val suite : string * unit Alcotest.test_case list
22+(** Alcotest test suite. *)
+1
test/test_source.mli
···11val suite : string * unit Alcotest.test_case list
22+(** Alcotest test suite. *)