Skip to content

Commit d413b1f

Browse files
author
Christoph Höger
committed
Add support for OCaml 4.03
This (rather large) commit introduces basic support for OCaml's new constructors with inline arguments. (No, I am not proud of the quality of this code ...) Signed-off-by: Christoph Höger <[email protected]>
1 parent 30b6972 commit d413b1f

File tree

7 files changed

+211
-60
lines changed

7 files changed

+211
-60
lines changed

_tags

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
true: warn(@5@8@10@11@12@14@23@24@26@29@40), bin_annot, safe_string
2-
1+
true: warn(@5@8@10@11@12@14@23@24@26@29@40), bin_annot, safe_string, cppo_V_OCAML
32
"src": include
43
<src/*.{ml,mli,byte,native}>: package(ppx_tools.metaquot), package(ppx_deriving.api)
54
<src_test/*.{ml,byte,native}>: debug, package(oUnit), use_morphism

myocamlbuild.ml

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
open Ocamlbuild_plugin
1+
open Ocamlbuild_plugin
22

33
let () = dispatch (
4-
function
5-
| After_rules ->
6-
let ppx_loc = (Findlib.query "ppx_deriving").Findlib.location in
7-
let std_deriver deriver =
8-
ppx_loc ^ "/" ^ deriver
9-
in
10-
flag ["ocaml";"link";"byte";"use_morphism"] &
11-
A (ppx_loc ^ "/ppx_deriving_runtime.cma") ;
12-
13-
flag ["ocaml";"link";"native";"use_morphism"] &
14-
A (ppx_loc ^ "/ppx_deriving_runtime.cmxa") ;
15-
16-
flag ["ocaml"; "compile"; "use_morphism"] &
17-
S[A"-ppx"; A"ocamlfind ppx_import/ppx_import";
18-
A"-I"; A ppx_loc ;
19-
A"-ppx"; A("ocamlfind ppx_deriving/ppx_deriving "^
20-
"src/ppx_deriving_folder.cma "^
21-
"src/ppx_deriving_mapper.cma "^
22-
(std_deriver "ppx_deriving_show.cma")) ;
23-
];
24-
| _ -> ())
4+
fun hook ->
5+
Ocamlbuild_cppo.dispatcher hook ;
6+
match hook with
7+
| After_rules ->
8+
let ppx_loc = (Findlib.query "ppx_deriving").Findlib.location in
9+
let std_deriver deriver =
10+
ppx_loc ^ "/" ^ deriver
11+
in
12+
flag ["ocaml";"link";"byte";"use_morphism"] &
13+
A (ppx_loc ^ "/ppx_deriving_runtime.cma") ;
14+
15+
flag ["ocaml";"link";"native";"use_morphism"] &
16+
A (ppx_loc ^ "/ppx_deriving_runtime.cmxa") ;
17+
18+
flag ["ocaml"; "compile"; "use_morphism"] &
19+
S[A"-I"; A ppx_loc ;
20+
A"-ppx"; A("ocamlfind ppx_deriving/ppx_deriving "^
21+
"src/ppx_deriving_folder.cma "^
22+
"src/ppx_deriving_mapper.cma "^
23+
(std_deriver "ppx_deriving_show.cma")) ;
24+
];
25+
| _ -> ())

opam

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ build-test: [
1818
]
1919
depends: [
2020
"ppx_deriving" {>= "3.0" & < "4.0" }
21+
"ppx_tools" {>= "4.02.3"}
2122
"ocamlfind" {build}
23+
"cppo" {build}
2224
"ounit" {test}
2325
"ppx_import" {test}
2426
]
25-
27+
available: [ ocaml-version >= "4.02.1" & opam-version >= "1.2" ]

pkg/build.ml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
#directory "pkg"
33
#use "topkg.ml"
44

5+
6+
let ocamlbuild =
7+
"ocamlbuild -use-ocamlfind -classic-display -plugin-tag 'package(cppo_ocamlbuild)'"
8+
59
let () =
6-
Pkg.describe "ppx_deriving_morphism" ~builder:`OCamlbuild [
10+
Pkg.describe "ppx_deriving_morphism" ~builder:(`Other (ocamlbuild, "_build")) [
711
Pkg.lib "pkg/META";
812
Pkg.lib ~exts:Exts.library "src/ppx_deriving_morphism";
913
Pkg.lib ~exts:Exts.library "src/ppx_deriving_folder";

src/ppx_deriving_folder.ml renamed to src/ppx_deriving_folder.cppo.ml

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2626
*
2727
*)
28+
#if OCAML_VERSION < (4, 03, 0)
29+
#define STR_TYPE_RECURSIVE
30+
#define Pcstr_tuple(core_types) core_types
31+
#else
32+
#define STR_TYPE_RECURSIVE Recursive
33+
#endif
2834

2935
(** Generate folder-record(s) for a given type.
3036
@@ -100,7 +106,9 @@ let attr_fold attrs =
100106

101107
let argn =
102108
Printf.sprintf "arg%d"
103-
109+
let argl =
110+
Printf.sprintf "arg%s"
111+
104112
let opt_pattn folds =
105113
List.mapi (fun i e -> match e with Some _ -> pvar (argn i) | None -> Pat.any ()) folds
106114

@@ -115,6 +123,10 @@ let pat_tuple = function
115123
| [p] -> p
116124
| ps -> Pat.tuple ps
117125

126+
let pattl labels = List.map (fun { pld_name = { txt = n } } -> n, pvar (argl n)) labels
127+
128+
let pconstrrec name fields = pconstr name [precord ~closed:Closed fields]
129+
118130
let core_type_of_decl ~options ~path type_decl =
119131
let typ = Ppx_deriving.core_type_of_type_decl type_decl in
120132
Ppx_deriving.poly_arrow_of_type_decl
@@ -180,7 +192,7 @@ let rec expr_of_typ names quoter typ =
180192

181193
(* select the approppriate fold_routines for arguments and pass them through *)
182194
let arg_folds = args |> List.map
183-
(fun ct -> match expr_of_typ ct with Some e -> ("", e) | None -> ("", fold_pass))
195+
(fun ct -> match expr_of_typ ct with Some e -> (Label.nolabel, e) | None -> (Label.nolabel, fold_pass))
184196
in
185197
if arg_folds = [] then Some [%expr [%e fold_fn] self] else
186198
Some [%expr [%e Exp.apply fold_fn arg_folds] self]
@@ -222,11 +234,29 @@ let process_decl quoter fold_arg_t
222234
(* create a default folder implementation for each variant *)
223235
let fields =
224236
constrs |>
225-
List.map (fun { pcd_name; pcd_args = typs } ->
226-
let folds = List.map (expr_of_typ names quoter) typs in
227-
let pat = pat_tuple (opt_pattn folds) in
237+
List.map (
238+
fun { pcd_name; pcd_args } ->
239+
(* Get a pattern and a corresponding folding-sequence from the constructor args *)
240+
let (pat, folds) = match pcd_args with
241+
(* Classic constructor arguments, we make up names *)
242+
Pcstr_tuple typs ->
243+
let appls = List.map (expr_of_typ names quoter) typs in
244+
(pat_tuple (opt_pattn appls), List.combine (varn typs) appls)
245+
#if OCAML_VERSION >= (4, 03, 0)
246+
| Pcstr_record labels ->
247+
(* New stuff, args have names themselves *)
248+
let opt_pattl {pld_name = { txt = l } ; pld_type = typ} (ps,fs) =
249+
let fold = expr_of_typ names quoter typ in
250+
(** Filter unused variables on-the-fly *)
251+
match fold with None -> ((Pat.any ()) :: ps,fs)
252+
| Some _ -> ((pvar (argl l))::ps, (evar (argl l), fold)::fs)
253+
in
254+
let (ps, fs) = List.fold_right opt_pattl labels ([],[]) in
255+
(pat_tuple ps, fs)
256+
#endif
257+
in
228258
let subfield = Ppx_deriving.mangle_lid (`Prefix "fold") (Lident pcd_name.txt) in
229-
(mknoloc subfield, [%expr fun self [%p pat] -> [%e reduce_fold_seq (List.combine (varn typs) folds)]]))
259+
(mknoloc subfield, [%expr fun self [%p pat] -> [%e reduce_fold_seq folds]]))
230260
in
231261
(mknoloc (Lident on_var), (Exp.record fields None)) :: defaults
232262
| _ -> defaults
@@ -240,12 +270,20 @@ let process_decl quoter fold_arg_t
240270
*)
241271
let cases =
242272
constrs |>
243-
List.map (fun { pcd_name; pcd_args = typs } ->
273+
List.map (fun { pcd_name; pcd_args} ->
274+
let (pat, vars) = match pcd_args with
275+
Pcstr_tuple typs -> (pconstr pcd_name.txt (pattn typs), varn typs)
276+
#if OCAML_VERSION >= (4, 03, 0)
277+
| Pcstr_record labels ->
278+
(pconstrrec pcd_name.txt (pattl labels),
279+
List.map (fun {pld_name = {txt=l}} -> evar (argl l)) labels)
280+
#endif
281+
in
244282
let subfield = Ppx_deriving.mangle_lid (`Prefix "fold") (Lident pcd_name.txt) in
245-
Exp.case (pconstr pcd_name.txt (pattn typs))
283+
Exp.case (pat)
246284
(Exp.apply
247285
(Exp.field (Exp.field [%expr self] (mknoloc (Lident on_var))) (mknoloc subfield))
248-
["", evar "self"; "", tuple (varn typs)]))
286+
[Label.nolabel, evar "self"; Label.nolabel, tuple vars]))
249287
in
250288
[%expr fun self x -> [%e Exp.match_ [%expr x] cases]]
251289
| Ptype_record labels ->
@@ -282,8 +320,15 @@ let process_decl quoter fold_arg_t
282320
| ts -> [%type: ([%t fold_arg_t], [%t Typ.tuple ts]) fold_routine]
283321
in
284322
(* create the fold_fn signature for the rhs of a constructor *)
285-
let typs_to_field { pcd_name; pcd_args = typs } =
286-
Type.field (mknoloc ("fold_" ^ pcd_name.txt)) (merge_typs typs) in
323+
let typs_to_field { pcd_name; pcd_args} =
324+
let typs =
325+
match pcd_args with
326+
Pcstr_tuple typs -> typs
327+
#if OCAML_VERSION >= (4, 03, 0)
328+
| Pcstr_record labels -> List.map (fun {pld_type} -> pld_type) labels
329+
#endif
330+
in Type.field (mknoloc ("fold_" ^ pcd_name.txt)) (merge_typs typs)
331+
in
287332

288333
let fields = constrs |> List.map typs_to_field in
289334
( (Type.field (mknoloc field_name) [%type: ([%t fold_arg_t], [%t folded]) fold_routine])::
@@ -307,7 +352,8 @@ let process_decl quoter fold_arg_t
307352

308353
let folder_to_str fold_arg_t {names; defaults; sub_folders; folder_fields} =
309354
[
310-
(Str.type_ (
355+
(Str.type_ STR_TYPE_RECURSIVE
356+
(
311357
Type.mk
312358
~params:[fold_arg_t, Invariant]
313359
~kind:(Ptype_record folder_fields) (mknoloc "fold_routines") ::

src/ppx_deriving_mapper.ml renamed to src/ppx_deriving_mapper.cppo.ml

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2626
*
2727
*)
28+
#if OCAML_VERSION < (4, 03, 0)
29+
#define STR_TYPE_RECURSIVE
30+
#define Pcstr_tuple(core_types) core_types
31+
#else
32+
#define STR_TYPE_RECURSIVE Recursive
33+
#endif
2834

2935
(** Generate mapper-record(s) for a given type.
3036
@@ -113,13 +119,20 @@ let attr_map attrs =
113119

114120
let argn =
115121
Printf.sprintf "arg%d"
122+
let argl =
123+
Printf.sprintf "arg%s"
116124

117125
let opt_pattn maps =
118126
List.mapi (fun i e -> match e with Some _ -> pvar (argn i) | None -> Pat.any ()) maps
119127

120128
let pattn typs =
121129
List.mapi (fun i _ -> pvar (argn i)) typs
122130

131+
let pattl labels = List.map (fun { pld_name = { txt = n } } -> n, pvar (argl n)) labels
132+
133+
let pconstrrec name fields = pconstr name [precord ~closed:Closed fields]
134+
let constrrec name fields = constr name [record fields]
135+
123136
let varn typs =
124137
List.mapi (fun i _ -> evar (argn i)) typs
125138

@@ -156,10 +169,19 @@ let reduce_map_seq ets =
156169
| (x,None) :: es -> reduce_ (x::ds) es
157170
in
158171

159-
match reduce_ [] ets with
160-
[] -> None
161-
| [e] -> Some e
162-
| es -> Some (Exp.tuple es)
172+
reduce_ [] ets
173+
174+
let reduce_record_map_seq ets =
175+
(* Reduce a set of mapped arguments from record labels by applying all map-routines and create a tuple *)
176+
let rec reduce_ ds = function
177+
(* For each argument with a map-routine, apply that routine *)
178+
[] -> List.rev ds
179+
| (x,Some f) :: es -> reduce_ ((x, [%expr [%e f] [%e (evar (argl x))]])::ds) es
180+
| (x,None) :: es -> reduce_ ((x, evar (argl x))::ds) es
181+
in
182+
183+
reduce_ [] ets
184+
163185

164186
(* generate the map routine for a given type.
165187
In case of unknown types, returns None
@@ -189,8 +211,8 @@ let rec expr_of_typ names quoter typ =
189211
let maps = List.map expr_of_typ typs in
190212
let pat = pat_tuple (pattn maps) in
191213
let map = match reduce_map_seq (List.combine (varn typs) maps) with
192-
Some e -> e
193-
| None -> raise (Failure "Tuple invariant broken")
214+
e::es -> tuple (e::es)
215+
| [] -> raise (Failure "Tuple invariant broken")
194216
in
195217
Some [%expr fun [%p pat] -> [%e map]]
196218

@@ -202,7 +224,7 @@ let rec expr_of_typ names quoter typ =
202224

203225
(* select the approppriate map_routines for arguments and pass them through *)
204226
let arg_maps = args |> List.map
205-
(fun ct -> match expr_of_typ ct with Some e -> ("", e) | None -> ("", map_pass))
227+
(fun ct -> match expr_of_typ ct with Some e -> (Label.nolabel, e) | None -> (Label.nolabel, map_pass))
206228
in
207229
if arg_maps = [] then Some [%expr [%e map_fn] self] else
208230
Some [%expr [%e Exp.apply map_fn arg_maps] self]
@@ -230,16 +252,19 @@ let process_decl quoter
230252
(* create a default mapper implementation for each variant *)
231253
let fields =
232254
constrs |>
233-
List.map (fun { pcd_name; pcd_args = typs } ->
234-
let maps = List.map (expr_of_typ names quoter) typs in
235-
let pat = pat_tuple (pattn maps) in
255+
List.map (fun { pcd_name; pcd_args } ->
256+
let (pat, rhs) = match pcd_args with
257+
Pcstr_tuple typs ->
258+
let maps = List.map (expr_of_typ names quoter) typs in
259+
(pat_tuple (pattn maps), constr pcd_name.txt (reduce_map_seq (List.combine (varn typs) maps)))
260+
#if OCAML_VERSION >= (4, 03, 0)
261+
| Pcstr_record labels ->
262+
let map_seq = List.map (fun {pld_name = {txt=l}; pld_type} -> (l, expr_of_typ names quoter pld_type)) labels in
263+
(pat_tuple (List.map (fun {pld_name={txt=l}} -> pvar (argl l)) labels), constrrec pcd_name.txt (reduce_record_map_seq map_seq))
264+
#endif
265+
in
236266
let subfield = Ppx_deriving.mangle_lid (`Prefix "map") (Lident pcd_name.txt) in
237-
(mknoloc subfield, [%expr fun self [%p pat] ->
238-
[%e
239-
(Exp.construct
240-
{pcd_name with txt=Lident pcd_name.txt}
241-
(reduce_map_seq (List.combine (varn typs) maps)))]
242-
]))
267+
(mknoloc subfield, [%expr fun self [%p pat] -> [%e rhs]]))
243268
in
244269
(mknoloc (Lident on_var), (Exp.record fields None)) :: defaults
245270
| _ -> defaults
@@ -253,14 +278,22 @@ let process_decl quoter
253278
*)
254279
let cases =
255280
constrs |>
256-
List.map (fun { pcd_name; pcd_args = typs } ->
281+
List.map (fun { pcd_name; pcd_args } ->
257282
let subfield = Ppx_deriving.mangle_lid (`Prefix "map") (Lident pcd_name.txt) in
258-
Exp.case (pconstr pcd_name.txt (pattn typs))
283+
let (pat, mk) = match pcd_args with
284+
Pcstr_tuple typs -> ((pconstr pcd_name.txt (pattn typs)), tuple (varn typs))
285+
#if OCAML_VERSION >= (4, 03, 0)
286+
| Pcstr_record labels ->
287+
(pconstrrec pcd_name.txt (pattl labels),
288+
tuple (List.map (fun {pld_name = { txt = l } } -> evar (argl l)) labels))
289+
#endif
290+
in
291+
Exp.case pat
259292
(Exp.apply
260293
(Exp.field
261294
(Exp.field [%expr self] (mknoloc (Lident on_var)))
262295
(mknoloc subfield))
263-
["", evar "self"; "", tuple (varn typs)]))
296+
[Label.nolabel, evar "self"; Label.nolabel, mk]))
264297
in
265298
[%expr fun self x -> [%e Exp.match_ [%expr x] cases]]
266299
| Ptype_record labels ->
@@ -272,7 +305,7 @@ let process_decl quoter
272305
({pld_name with txt = Lident pld_name.txt},
273306
match expr_of_typ names quoter pld_type with
274307
None -> evar pld_name.txt
275-
| Some map -> Exp.apply map ["", evar pld_name.txt]
308+
| Some map -> Exp.apply map [Label.nolabel, evar pld_name.txt]
276309
)
277310
end
278311
in
@@ -312,7 +345,14 @@ let process_decl quoter
312345
in
313346

314347
(* create the map_fn signature for the rhs of a constructor *)
315-
let typs_to_field { pcd_name; pcd_args = typs } =
348+
let typs_to_field { pcd_name; pcd_args} =
349+
let typs =
350+
match pcd_args with
351+
Pcstr_tuple typs -> typs
352+
#if OCAML_VERSION >= (4, 03, 0)
353+
| Pcstr_record labels -> List.map (fun {pld_type} -> pld_type) labels
354+
#endif
355+
in
316356
Type.field (mknoloc ("map_" ^ pcd_name.txt)) (merge_typs typs) in
317357

318358
let fields = constrs |> List.map typs_to_field in
@@ -336,7 +376,7 @@ let process_decl quoter
336376

337377
let mapper_to_str {names; defaults; sub_mappers; mapper_fields} =
338378
[
339-
(Str.type_ (
379+
(Str.type_ STR_TYPE_RECURSIVE (
340380
Type.mk
341381
~kind:(Ptype_record mapper_fields) (mknoloc "map_routines") ::
342382
Type.mk

0 commit comments

Comments
 (0)