|
1 | | -let collect_cmi_files dir = |
| 1 | +let mod_name file = |
| 2 | + String.capitalize_ascii Filename.(remove_extension (basename file)) |
| 3 | + |
| 4 | +let lazy_sig path = |
| 5 | + Lazy.from_fun (fun () -> |
| 6 | + let cmi_infos = Cmi_format.read_cmi path in |
| 7 | + cmi_infos.cmi_sign) |
| 8 | + |
| 9 | +let collect_modules dir = |
2 | 10 | try |
3 | 11 | let files = Sys.readdir dir in |
4 | | - Ok |
5 | | - (Array.fold_left |
6 | | - (fun acc file -> |
7 | | - let path = Filename.concat dir file in |
8 | | - if (not (Sys.is_directory path)) && Filename.check_suffix file ".cmi" |
9 | | - then path :: acc |
10 | | - else acc) |
11 | | - [] files) |
| 12 | + let map = |
| 13 | + Array.fold_left |
| 14 | + (fun acc file -> |
| 15 | + let path = Filename.concat dir file in |
| 16 | + if (not (Sys.is_directory path)) && Filename.check_suffix file ".cmi" |
| 17 | + then String_map.add (mod_name file) (lazy_sig path) acc |
| 18 | + else acc) |
| 19 | + String_map.empty files |
| 20 | + in |
| 21 | + Ok map |
12 | 22 | with Sys_error e -> |
13 | 23 | Error (Printf.sprintf "Error reading directory %s: %s" dir e) |
14 | 24 |
|
| 25 | +let get_sig modname map = |
| 26 | + Option.map Lazy.force (String_map.find_opt modname map) |
| 27 | + |
15 | 28 | let load_cmi file_path = |
16 | 29 | try |
17 | 30 | let cmi_infos = Cmi_format.read_cmi file_path in |
18 | 31 | Ok (cmi_infos.cmi_sign, cmi_infos.cmi_name) |
19 | 32 | with e -> Error (Printexc.to_string e) |
20 | 33 |
|
21 | | -let load project_path = |
22 | | - let open CCResult.Infix in |
23 | | - let* cmi_files = collect_cmi_files project_path in |
24 | | - let* signatures = |
25 | | - CCResult.map_l |
26 | | - (fun cmi_file -> |
27 | | - let+ signature, module_name = load_cmi cmi_file in |
28 | | - (module_name, signature)) |
29 | | - cmi_files |
| 34 | +(* Attach a module name to its various representations, e.g. a [signature] or a |
| 35 | + [module_type]. |
| 36 | + Mostly used to report lookup failures. *) |
| 37 | +type 'a named = { name : string; value : 'a } |
| 38 | + |
| 39 | +module Flat_path = struct |
| 40 | + type component = Id of Ident.t | Comp of string |
| 41 | + type t = component list |
| 42 | + |
| 43 | + let from_path path = |
| 44 | + match Path.flatten path with |
| 45 | + | `Contains_apply -> None |
| 46 | + | `Ok (id, comps) -> Some (Id id :: List.map (fun s -> Comp s) comps) |
| 47 | + |
| 48 | + let modname_from_component = function Id id -> Ident.name id | Comp s -> s |
| 49 | + let to_string t = String.concat "." (List.map modname_from_component t) |
| 50 | +end |
| 51 | + |
| 52 | +let rec path_in_module ~module_path flat_path = |
| 53 | + match flat_path with |
| 54 | + | [] -> module_path |
| 55 | + | hd :: tl -> |
| 56 | + let module_path = |
| 57 | + Path.Pdot (module_path, Flat_path.modname_from_component hd) |
| 58 | + in |
| 59 | + path_in_module ~module_path tl |
| 60 | + |
| 61 | +let rewrite_mty_path mty path = |
| 62 | + let open Types in |
| 63 | + match mty with |
| 64 | + | Mty_ident _ -> Mty_ident path |
| 65 | + | Mty_alias _ -> Mty_alias path |
| 66 | + | _ -> assert false |
| 67 | + |
| 68 | +let lookup_error ~path ~module_name = |
| 69 | + Error (Printf.sprintf "Could not find module %s in %s" path module_name) |
| 70 | + |
| 71 | +let find_module modname sig_ = |
| 72 | + let open Types in |
| 73 | + let mty_opt = |
| 74 | + List.find_map |
| 75 | + (function |
| 76 | + | Sig_module (id, _, { md_type; _ }, _, _) |
| 77 | + when String.equal (Ident.name id) modname -> |
| 78 | + Some md_type |
| 79 | + | _ -> None) |
| 80 | + sig_.value |
30 | 81 | in |
31 | | - let merged_signature = |
32 | | - List.fold_left |
33 | | - (fun acc (module_name, signature) -> |
34 | | - String_map.add module_name signature acc) |
35 | | - String_map.empty signatures |
| 82 | + match mty_opt with |
| 83 | + | Some mty -> Ok mty |
| 84 | + | None -> lookup_error ~path:modname ~module_name:sig_.name |
| 85 | + |
| 86 | +let rec find_module_in_sig ~library_modules path sig_ = |
| 87 | + let open CCResult.Infix in |
| 88 | + match (path : Flat_path.t) with |
| 89 | + | [ last ] -> |
| 90 | + let modname = Flat_path.modname_from_component last in |
| 91 | + find_module modname sig_ |
| 92 | + | hd :: tl -> |
| 93 | + let modname = Flat_path.modname_from_component hd in |
| 94 | + let* mty = find_module modname sig_ in |
| 95 | + find_module_in_md_type ~library_modules tl { name = modname; value = mty } |
| 96 | + | [] -> assert false |
| 97 | + |
| 98 | +and find_module_in_md_type ~library_modules path mty = |
| 99 | + let open CCResult.Infix in |
| 100 | + match mty.value with |
| 101 | + | Mty_signature s -> |
| 102 | + find_module_in_sig ~library_modules path { name = mty.name; value = s } |
| 103 | + | Mty_ident mty_path | Mty_alias mty_path -> ( |
| 104 | + let* expanded = |
| 105 | + match Flat_path.from_path mty_path with |
| 106 | + | None -> Ok None |
| 107 | + | Some flat_mty_path -> |
| 108 | + find_module_in_lib ~library_modules flat_mty_path |
| 109 | + in |
| 110 | + match expanded with |
| 111 | + | Some expanded_mty -> |
| 112 | + find_module_in_md_type ~library_modules path |
| 113 | + { name = Path.name mty_path; value = expanded_mty } |
| 114 | + | None -> |
| 115 | + let expanded_path = path_in_module ~module_path:mty_path path in |
| 116 | + Ok (rewrite_mty_path mty.value expanded_path)) |
| 117 | + | _ -> lookup_error ~path:(Flat_path.to_string path) ~module_name:mty.name |
| 118 | + |
| 119 | +and find_module_in_lib ~library_modules path : |
| 120 | + (Types.module_type option, string) result = |
| 121 | + let open Types in |
| 122 | + let open CCResult.Infix in |
| 123 | + match path with |
| 124 | + | [ comp ] -> |
| 125 | + let modname = Flat_path.modname_from_component comp in |
| 126 | + let sig_opt = get_sig modname library_modules in |
| 127 | + Ok (Option.map (fun s -> Mty_signature s) sig_opt) |
| 128 | + | comp :: inner_path -> ( |
| 129 | + let modname = Flat_path.modname_from_component comp in |
| 130 | + match get_sig modname library_modules with |
| 131 | + | None -> Ok None |
| 132 | + | Some parent_sig -> ( |
| 133 | + let* mty = |
| 134 | + find_module_in_sig ~library_modules inner_path |
| 135 | + { name = modname; value = parent_sig } |
| 136 | + in |
| 137 | + match mty with |
| 138 | + | Mty_signature _ | Mty_functor _ -> Ok (Some mty) |
| 139 | + | Mty_ident path' | Mty_alias path' -> ( |
| 140 | + match Flat_path.from_path path' with |
| 141 | + | None -> Ok (Some mty) |
| 142 | + | Some fpath -> find_module_in_lib ~library_modules fpath))) |
| 143 | + | _ -> Ok None |
| 144 | + |
| 145 | +let rec expand_sig ~library_modules sig_ = |
| 146 | + let open Types in |
| 147 | + let open CCResult.Infix in |
| 148 | + CCResult.map_l |
| 149 | + (fun item -> |
| 150 | + match item with |
| 151 | + | Sig_module |
| 152 | + ( id, |
| 153 | + presence, |
| 154 | + ({ md_type = Mty_ident path | Mty_alias path; _ } as mod_decl), |
| 155 | + rs, |
| 156 | + vis ) -> ( |
| 157 | + match Flat_path.from_path path with |
| 158 | + | None -> Ok item |
| 159 | + | Some fpath -> ( |
| 160 | + let* mty_opt = find_module_in_lib ~library_modules fpath in |
| 161 | + match mty_opt with |
| 162 | + | None -> Ok item |
| 163 | + | Some mty -> |
| 164 | + let* expanded = |
| 165 | + match mty with |
| 166 | + | Mty_signature s -> |
| 167 | + let* expanded = expand_sig ~library_modules s in |
| 168 | + Ok (Mty_signature expanded) |
| 169 | + | _ -> Ok mty |
| 170 | + in |
| 171 | + let presence = |
| 172 | + match expanded with |
| 173 | + | Mty_alias _ -> presence |
| 174 | + | _ -> Mp_present |
| 175 | + in |
| 176 | + let mod_decl' = { mod_decl with md_type = expanded } in |
| 177 | + Ok (Sig_module (id, presence, mod_decl', rs, vis)))) |
| 178 | + | _ -> Ok item) |
| 179 | + sig_ |
| 180 | + |
| 181 | +let load ~main_module project_path = |
| 182 | + let open CCResult.Infix in |
| 183 | + let* library_modules = collect_modules project_path in |
| 184 | + let* main_sig = |
| 185 | + match get_sig main_module library_modules with |
| 186 | + | Some s -> Ok s |
| 187 | + | None -> |
| 188 | + Error |
| 189 | + (Printf.sprintf "Could not find main module %s in %s" main_module |
| 190 | + project_path) |
36 | 191 | in |
37 | | - Ok |
38 | | - (String_map.fold |
39 | | - (fun module_name module_sig acc -> |
40 | | - Types.Sig_module |
41 | | - ( Ident.create_local module_name, |
42 | | - Mp_present, |
43 | | - { |
44 | | - md_type = Mty_signature module_sig; |
45 | | - md_attributes = []; |
46 | | - md_loc = Location.none; |
47 | | - md_uid = Types.Uid.internal_not_actually_unique; |
48 | | - }, |
49 | | - Trec_not, |
50 | | - Exported ) |
51 | | - :: acc) |
52 | | - merged_signature []) |
| 192 | + expand_sig ~library_modules main_sig |
0 commit comments