11open Import
22open Ast_helper
33open Printf
4+ module Section_map = String. Map
5+
6+ let section_map_of_assoc items =
7+ List. fold_left
8+ ~f: (fun acc (name , v ) ->
9+ match Section_map. find_opt name acc with
10+ | None -> Section_map. add name [ v ] acc
11+ | Some vs -> Section_map. add name (v :: vs) acc)
12+ ~init: Section_map. empty items
13+
14+ let doc_comment_from_attribue (attr : attribute ) =
15+ match attr.attr_name.txt with
16+ | "ocaml.doc" -> (
17+ match attr.attr_payload with
18+ | PStr
19+ [
20+ {
21+ pstr_desc =
22+ Pstr_eval
23+ ({ pexp_desc = Pexp_constant (Pconst_string (s, _, _)); _ }, _);
24+ _;
25+ };
26+ ] ->
27+ Some s
28+ | _ -> None )
29+ | _ -> None
30+
31+ let doc_comment ~node_name ~function_name attributes =
32+ let parsetree_comment =
33+ List. find_map ~f: doc_comment_from_attribue attributes
34+ in
35+ let pp_parsetree_comment ppf = function
36+ | None -> ()
37+ | Some pc -> Format. fprintf ppf " {b Example OCaml}\n\n %s" pc
38+ in
39+ Format. asprintf " [%s] constructs an {! Ast.%s}\n\n %a" function_name node_name
40+ pp_parsetree_comment parsetree_comment
441
542let prefix_of_record lds =
643 common_prefix (List. map lds ~f: (fun ld -> ld.pld_name.txt))
@@ -11,8 +48,25 @@ end) =
1148struct
1249 open Fixed_loc
1350
51+ let core_type_of_return_type (typ : type_declaration ) =
52+ let typ_name = typ.ptype_name.txt in
53+ let typ_name =
54+ match List. rev (String. split_on_char ~sep: '_' typ_name) with
55+ | "desc" :: _ ->
56+ String. sub ~pos: 0 ~len: (String. length typ_name - 5 ) typ_name
57+ | _ -> typ_name
58+ in
59+ match typ.ptype_params with
60+ | [] -> M. ctyp " %s" typ_name
61+ | params ->
62+ let params =
63+ List. map params ~f: (fun (ctyp , _ ) -> Format. asprintf " %a" A. ctyp ctyp)
64+ in
65+ M. ctyp " (%s) %s" (String. concat ~sep: " , " params) typ_name
66+
1467 let gen_combinator_for_constructor
15- ~wrapper :(wpath , wprefix , has_attrs , has_loc_stack ) path ~prefix cd =
68+ ~wrapper :(wpath , wprefix , has_attrs , has_loc_stack ) path ~prefix
69+ return_type cd =
1670 match cd.pcd_args with
1771 | Pcstr_record _ ->
1872 (* TODO. *)
@@ -66,31 +120,47 @@ struct
66120 let body =
67121 if fixed_loc then body else M. expr " fun ~loc -> %a" A. expr body
68122 in
69- M. stri " let %a = %a" A. patt
70- (pvar (function_name_of_id ~prefix cd.pcd_name.txt))
71- A. expr body
123+ let function_name = function_name_of_id ~prefix cd.pcd_name.txt in
124+ let pvar_function_name = pvar function_name in
125+ let str = M. stri " let %a = %a" A. patt pvar_function_name A. expr body in
126+ let return_type = core_type_of_return_type return_type in
127+ let typ =
128+ List. fold_right cd_args ~init: return_type ~f: (fun cty acc ->
129+ M. ctyp " %a -> %a" A. ctyp cty A. ctyp acc)
130+ in
131+ let typ =
132+ if fixed_loc then typ else M. ctyp " loc:Location.t -> %a" A. ctyp typ
133+ in
134+ let sign =
135+ M. sigi " val %a : %a (** %s *)" A. patt pvar_function_name A. ctyp typ
136+ (doc_comment ~function_name ~node_name: cd.pcd_name.txt
137+ cd.pcd_attributes)
138+ in
139+ (str, (Format. asprintf " %a" A. ctyp return_type, sign))
72140
73- let gen_combinator_for_record path ~prefix lds =
141+ let gen_combinator_for_record path ~prefix return_type lds =
74142 let fields =
75143 List. map lds ~f: (fun ld -> fqn_longident path ld.pld_name.txt)
76144 in
77145 let funcs =
78146 List. map lds ~f: (fun ld ->
79- map_keyword (without_prefix ~prefix ld.pld_name.txt))
147+ (ld.pld_type, map_keyword (without_prefix ~prefix ld.pld_name.txt) ))
80148 in
81149 let body =
82150 Exp. record
83- (List. map2 fields funcs ~f: (fun field func ->
151+ (List. map2 fields funcs ~f: (fun field ( _ , func ) ->
84152 ( Loc. mk field,
85153 if func = " attributes" then M. expr " []" else evar func )))
86154 None
87155 in
88156 let body =
89- let l = List. filter funcs ~f: (fun f -> f <> " loc" && f <> " attributes" ) in
157+ let l =
158+ List. filter funcs ~f: (fun (_ , f ) -> f <> " loc" && f <> " attributes" )
159+ in
90160 match l with
91- | [ x ] -> Exp. fun_ Nolabel None (pvar x) body
161+ | [ (_, x) ] -> Exp. fun_ Nolabel None (pvar x) body
92162 | _ ->
93- List. fold_right l ~init: body ~f: (fun func acc ->
163+ List. fold_right l ~init: body ~f: (fun ( _ , func ) acc ->
94164 Exp. fun_ (Labelled func) None (pvar func) acc)
95165 in
96166 (* let body =
@@ -99,12 +169,39 @@ struct
99169 else
100170 body
101171 in*)
172+ let has_loc_field =
173+ List. exists ~f: (function _ , "loc" -> true | _ -> false ) funcs
174+ in
102175 let body =
103- if List. mem " loc" ~set: funcs && not fixed_loc then
104- M. expr " fun ~loc -> %a" A. expr body
176+ if has_loc_field && not fixed_loc then M. expr " fun ~loc -> %a" A. expr body
105177 else body
106178 in
107- M. stri " let %a = %a" A. patt (pvar (function_name_of_path path)) A. expr body
179+ let return_ctyp = core_type_of_return_type return_type in
180+ let typ =
181+ let l =
182+ List. filter funcs ~f: (fun (_ , f ) -> f <> " loc" && f <> " attributes" )
183+ in
184+ match l with
185+ | [ (c, _) ] -> M. ctyp " %a -> %a" A. ctyp c A. ctyp return_ctyp
186+ | _ ->
187+ List. fold_right l ~init: return_ctyp ~f: (fun (typ , func ) acc ->
188+ M. ctyp " %s:%a -> %a" func A. ctyp typ A. ctyp acc)
189+ in
190+ let typ =
191+ if has_loc_field && not fixed_loc then
192+ M. ctyp " loc:Location.t -> %a" A. ctyp typ
193+ else typ
194+ in
195+ let pvar_function_name = pvar (function_name_of_path path) in
196+ let str = M. stri " let %a = %a" A. patt pvar_function_name A. expr body in
197+ let sign =
198+ M. sigi " val %a : %a (** %s *)" A. patt pvar_function_name A. ctyp typ
199+ (doc_comment
200+ ~function_name: (function_name_of_path path)
201+ ~node_name: (Format. asprintf " %a" A. ctyp return_ctyp)
202+ return_type.ptype_attributes)
203+ in
204+ (str, (Format. asprintf " %a" A. ctyp return_ctyp, sign))
108205
109206 let gen_td ?wrapper path td =
110207 if is_loc path then []
@@ -117,11 +214,11 @@ struct
117214 let prefix =
118215 common_prefix (List. map cds ~f: (fun cd -> cd.pcd_name.txt))
119216 in
120- List. map cds ~f: ( fun cd ->
121- gen_combinator_for_constructor ~wrapper path ~prefix cd ))
217+ List. map cds
218+ ~f: ( gen_combinator_for_constructor ~wrapper path ~prefix td ))
122219 | Ptype_record lds ->
123220 let prefix = prefix_of_record lds in
124- [ gen_combinator_for_record path ~prefix lds ]
221+ [ gen_combinator_for_record path ~prefix td lds ]
125222 | Ptype_abstract | Ptype_open -> []
126223end
127224
@@ -140,6 +237,26 @@ let dump fn ~ext printer x =
140237 Format. fprintf ppf " %a@." printer x;
141238 close_out oc
142239
240+ let floating_comment s =
241+ let doc =
242+ PStr
243+ [
244+ {
245+ pstr_desc =
246+ Pstr_eval
247+ ( {
248+ pexp_desc = Pexp_constant (Pconst_string (s, loc, None ));
249+ pexp_loc = loc;
250+ pexp_loc_stack = [] ;
251+ pexp_attributes = [] ;
252+ },
253+ [] );
254+ pstr_loc = loc;
255+ };
256+ ]
257+ in
258+ Sig. attribute (Attr. mk { txt = " ocaml.text" ; loc } doc)
259+
143260let generate filename =
144261 (* let fn = Misc.find_in_path_uncap !Config.load_path (unit ^ ".cmi") in*)
145262 let types = get_types ~filename in
@@ -196,10 +313,44 @@ let generate filename =
196313 path' td')
197314 |> List. flatten
198315 in
316+ let mod_items b = items b |> List. map ~f: fst in
317+ let mod_sig_items b = items b |> List. map ~f: snd |> section_map_of_assoc in
318+ let mk_intf ~name located =
319+ let ident : label with_loc = { txt = name; loc } in
320+ let longident = { txt = Lident name; loc } in
321+ let documented_items =
322+ Section_map. fold
323+ (fun label items acc ->
324+ let label =
325+ match String. split_on_char ~sep: '_' label with
326+ | [] -> assert false
327+ | l :: rest ->
328+ let bs = Bytes. of_string l in
329+ Bytes. set bs 0 (Char. uppercase_ascii @@ Bytes. get bs 0 );
330+ String. concat ~sep: " " (Bytes. to_string bs :: rest)
331+ in
332+ (floating_comment (Format. asprintf " {2 %s}" label) :: items) @ acc)
333+ (mod_sig_items located) []
334+ in
335+ let items =
336+ if located then M. sigi " val loc : Location.t" :: documented_items
337+ else documented_items
338+ in
339+ let intf = Str. modtype (Mtd. mk ~typ: (Mty. signature items) ident) in
340+ (longident, intf)
341+ in
342+ let intf_name, intf = mk_intf ~name: " Intf" false in
343+ let intf_located_name, intf_located = mk_intf ~name: " Intf_located" true in
199344 let st =
200345 [
201346 Str. open_ (Opn. mk (Mod. ident (Loc. lident " Import" )));
202- Str. module_ (Mb. mk (Loc. mk (Some " M" )) (Mod. structure (items false )));
347+ intf;
348+ intf_located;
349+ Str. module_
350+ (Mb. mk (Loc. mk (Some " M" ))
351+ (Mod. constraint_
352+ (Mod. structure (mod_items false ))
353+ (Mty. ident intf_name)));
203354 Str. module_
204355 (Mb. mk (Loc. mk (Some " Make" ))
205356 (Mod. functor_
@@ -208,7 +359,9 @@ let generate filename =
208359 Mty. signature
209360 [ Sig. value (Val. mk (Loc. mk " loc" ) (M. ctyp " Location.t" )) ]
210361 ))
211- (Mod. structure (M. stri " let loc = Loc.loc" :: items true ))));
362+ (Mod. constraint_
363+ (Mod. structure (M. stri " let loc = Loc.loc" :: mod_items true ))
364+ (Mty. ident intf_located_name))));
212365 ]
213366 in
214367 dump " ast_builder_generated" Pprintast. structure st ~ext: " .ml"
0 commit comments