Skip to content

Commit 132fbd7

Browse files
authored
Merge pull request ocaml-ppx#607 from NathanReb/support-labeled-tuples
Add support for 5.4 labeled tuples
2 parents 8d9dcca + 5b46ab0 commit 132fbd7

File tree

16 files changed

+819
-38
lines changed

16 files changed

+819
-38
lines changed

.ocamlformat-ignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ test/driver/non-compressible-suffix/test.ml
4747
test/driver/transformations/test.ml
4848
test/driver/transformations/test_412.ml
4949
test/driver/transformations/test_510.ml
50+
test/encoding/504/api/test.ml
5051
test/expand-header-and-footer/test.ml
5152
test/expansion_helpers/mangle/test.ml
5253
test/expansion_inside_payloads/test.ml

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ unreleased
1414
- Fix a bug that inserted `Location.none` into `Longident`s when using OCaml
1515
5.4 and above (#619, @patricoferris)
1616

17+
- Add support for OCaml 5.4 labeled tuples, they can now be used alongside
18+
ppx-es. Also adds Ast_builder and Ast_pattern utilities to manipulate them.
19+
(#607, @NathanReb)
20+
1721
0.37.0
1822
------
1923

astlib/encoding_504.ml

Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
module Ext_name = struct
2+
let ptyp_labeled_tuple = "ppxlib.migration.ptyp_labeled_tuple_504"
3+
let pexp_labeled_tuple = "ppxlib.migration.pexp_labeled_tuple_504"
4+
let ppat_labeled_tuple = "ppxlib.migration.ppat_labeled_tuple_504"
5+
end
6+
7+
let invalid_encoding ~loc name =
8+
Location.raise_errorf ~loc "Invalid %s encoding" name
9+
10+
module type AST = sig
11+
type payload
12+
type core_type
13+
type core_type_desc
14+
type expression
15+
type expression_desc
16+
type pattern
17+
type pattern_desc
18+
type closed_flag
19+
20+
module Construct : sig
21+
val ptyp_extension_desc : string Location.loc -> payload -> core_type_desc
22+
val ptyp_tuple : loc:Location.t -> core_type list -> core_type
23+
val ptyp_var : loc:Location.t -> string -> core_type
24+
val ptyp_any : loc:Location.t -> core_type
25+
val ptyp : core_type -> payload
26+
val pexp_extension_desc : string Location.loc -> payload -> expression_desc
27+
val pexp_tuple : loc:Location.t -> expression list -> expression
28+
29+
val pexp_variant :
30+
loc:Location.t -> string -> expression option -> expression
31+
32+
val pstr_eval : loc:Location.t -> expression -> payload
33+
val ppat_extension_desc : string Location.loc -> payload -> pattern_desc
34+
val ppat_tuple : loc:Location.t -> pattern list -> pattern
35+
val ppat_var : loc:Location.t -> string -> pattern
36+
val ppat_any : loc:Location.t -> pattern
37+
val ppat : pattern -> payload
38+
val closed_flag_to_string : closed_flag -> string
39+
end
40+
41+
module Destruct : sig
42+
val ptyp : payload -> core_type option
43+
val ptyp_tuple : core_type -> core_type list option
44+
val ptyp_var : core_type -> string option
45+
val ptyp_any : core_type -> unit option
46+
val pstr_eval : payload -> expression option
47+
val pexp_tuple : expression -> expression list option
48+
val pexp_variant : expression -> (string * expression option) option
49+
val ppat : payload -> pattern option
50+
val ppat_tuple : pattern -> pattern list option
51+
val ppat_var : pattern -> string option
52+
val ppat_any : pattern -> unit option
53+
val closed_flag_from_string : string -> closed_flag option
54+
end
55+
end
56+
57+
module Make (X : AST) = struct
58+
let encode_ptyp_labeled_tuple ~loc args =
59+
let payload =
60+
let l =
61+
List.map
62+
(fun (label_opt, typ) ->
63+
let label =
64+
match label_opt with
65+
| None -> X.Construct.ptyp_any ~loc
66+
| Some s -> X.Construct.ptyp_var ~loc s
67+
in
68+
X.Construct.ptyp_tuple ~loc [ label; typ ])
69+
args
70+
in
71+
X.Construct.ptyp_tuple ~loc l
72+
in
73+
X.Construct.ptyp_extension_desc
74+
{ txt = Ext_name.ptyp_labeled_tuple; loc }
75+
(X.Construct.ptyp payload)
76+
77+
let decode_ptyp_labeled_tuple ~loc payload =
78+
let open Stdlib0.Option.Op in
79+
let res =
80+
let* typ = X.Destruct.ptyp payload in
81+
let* typ_list = X.Destruct.ptyp_tuple typ in
82+
Stdlib0.Option.List.map typ_list ~f:(fun typ ->
83+
let* typ_pair = X.Destruct.ptyp_tuple typ in
84+
match typ_pair with
85+
| [ label; typ ] -> (
86+
match (X.Destruct.ptyp_var label, X.Destruct.ptyp_any label) with
87+
| Some s, _ -> Some (Some s, typ)
88+
| _, Some () -> Some (None, typ)
89+
| None, None -> None)
90+
| _ -> None)
91+
in
92+
match res with
93+
| Some res -> res
94+
| None -> invalid_encoding ~loc Ext_name.ptyp_labeled_tuple
95+
96+
let encode_pexp_labeled_tuple ~loc args =
97+
let payload =
98+
let l =
99+
List.map
100+
(fun (label_opt, expr) ->
101+
let label =
102+
match label_opt with
103+
| None -> X.Construct.pexp_variant ~loc "None" None
104+
| Some s ->
105+
let string_as_variant =
106+
X.Construct.pexp_variant ~loc s None
107+
in
108+
X.Construct.pexp_variant ~loc "Some" (Some string_as_variant)
109+
in
110+
X.Construct.pexp_tuple ~loc [ label; expr ])
111+
args
112+
in
113+
X.Construct.pexp_tuple ~loc l
114+
in
115+
X.Construct.pexp_extension_desc
116+
{ txt = Ext_name.pexp_labeled_tuple; loc }
117+
(X.Construct.pstr_eval ~loc payload)
118+
119+
let decode_pexp_labeled_tuple ~loc payload =
120+
let open Stdlib0.Option.Op in
121+
let res =
122+
let* exp = X.Destruct.pstr_eval payload in
123+
let* exp_list = X.Destruct.pexp_tuple exp in
124+
Stdlib0.Option.List.map exp_list ~f:(fun exp ->
125+
let* exp_pair = X.Destruct.pexp_tuple exp in
126+
match exp_pair with
127+
| [ label; exp ] -> (
128+
let* opt_variant = X.Destruct.pexp_variant label in
129+
match opt_variant with
130+
| "None", None -> Some (None, exp)
131+
| "Some", Some exp' -> (
132+
let* label_variant = X.Destruct.pexp_variant exp' in
133+
match label_variant with
134+
| s, None -> Some (Some s, exp)
135+
| _, _ -> None)
136+
| _ -> None)
137+
| _ -> None)
138+
in
139+
match res with
140+
| Some res -> res
141+
| None -> invalid_encoding ~loc Ext_name.pexp_labeled_tuple
142+
143+
let encode_ppat_labeled_tuple ~loc pats closed_flag =
144+
let payload =
145+
let flag =
146+
let s = X.Construct.closed_flag_to_string closed_flag in
147+
X.Construct.ppat_var ~loc s
148+
in
149+
let pats =
150+
let l =
151+
List.map
152+
(fun (label_opt, pat) ->
153+
let label =
154+
match label_opt with
155+
| None -> X.Construct.ppat_any ~loc
156+
| Some s -> X.Construct.ppat_var ~loc s
157+
in
158+
X.Construct.ppat_tuple ~loc [ label; pat ])
159+
pats
160+
in
161+
X.Construct.ppat_tuple ~loc l
162+
in
163+
X.Construct.ppat_tuple ~loc [ pats; flag ]
164+
in
165+
X.Construct.ppat_extension_desc
166+
{ txt = Ext_name.ppat_labeled_tuple; loc }
167+
(X.Construct.ppat payload)
168+
169+
let decode_ppat_labeled_tuple ~loc payload =
170+
let open Stdlib0.Option.Op in
171+
let res =
172+
let* pat = X.Destruct.ppat payload in
173+
let* pats_and_flag = X.Destruct.ppat_tuple pat in
174+
match pats_and_flag with
175+
| [ pats; flag ] ->
176+
let* flag_s = X.Destruct.ppat_var flag in
177+
let* closed_flag = X.Destruct.closed_flag_from_string flag_s in
178+
let* pat_list = X.Destruct.ppat_tuple pats in
179+
let* pats =
180+
Stdlib0.Option.List.map pat_list ~f:(fun pat ->
181+
let* pat_pair = X.Destruct.ppat_tuple pat in
182+
match pat_pair with
183+
| [ label; pat ] -> (
184+
match
185+
(X.Destruct.ppat_var label, X.Destruct.ppat_any label)
186+
with
187+
| Some s, _ -> Some (Some s, pat)
188+
| _, Some () -> Some (None, pat)
189+
| None, None -> None)
190+
| _ -> None)
191+
in
192+
Some (pats, closed_flag)
193+
| _ -> None
194+
in
195+
match res with
196+
| Some res -> res
197+
| None -> invalid_encoding ~loc Ext_name.ppat_labeled_tuple
198+
end
199+
200+
module Ast_503 = struct
201+
include Ast_503.Asttypes
202+
include Ast_503.Parsetree
203+
204+
module Construct = struct
205+
let core_type ~loc ptyp_desc =
206+
{ ptyp_desc; ptyp_loc = loc; ptyp_attributes = []; ptyp_loc_stack = [] }
207+
208+
let expression ~loc pexp_desc =
209+
{ pexp_desc; pexp_loc = loc; pexp_attributes = []; pexp_loc_stack = [] }
210+
211+
let pattern ~loc ppat_desc =
212+
{ ppat_desc; ppat_loc = loc; ppat_attributes = []; ppat_loc_stack = [] }
213+
214+
let ptyp_extension_desc name payload = Ptyp_extension (name, payload)
215+
let ptyp_tuple ~loc typs = core_type ~loc (Ptyp_tuple typs)
216+
let ptyp_var ~loc s = core_type ~loc (Ptyp_var s)
217+
let ptyp_any ~loc = core_type ~loc Ptyp_any
218+
let ptyp typ = PTyp typ
219+
let pexp_extension_desc name payload = Pexp_extension (name, payload)
220+
let pexp_tuple ~loc l = expression ~loc (Pexp_tuple l)
221+
222+
let pexp_variant ~loc v exp_opt =
223+
expression ~loc (Pexp_variant (v, exp_opt))
224+
225+
let pstr_eval ~loc expr =
226+
PStr [ { pstr_desc = Pstr_eval (expr, []); pstr_loc = loc } ]
227+
228+
let ppat_extension_desc name payload = Ppat_extension (name, payload)
229+
let ppat_tuple ~loc l = pattern ~loc (Ppat_tuple l)
230+
let ppat_var ~loc txt = pattern ~loc (Ppat_var { txt; loc })
231+
let ppat_any ~loc = pattern ~loc Ppat_any
232+
let ppat pat = PPat (pat, None)
233+
let closed_flag_to_string = function Closed -> "closed_" | Open -> "open_"
234+
end
235+
236+
module Destruct = struct
237+
let ptyp = function PTyp typ -> Some typ | _ -> None
238+
239+
let ptyp_tuple = function
240+
| { ptyp_desc = Ptyp_tuple typs; _ } -> Some typs
241+
| _ -> None
242+
243+
let ptyp_var = function
244+
| { ptyp_desc = Ptyp_var s; _ } -> Some s
245+
| _ -> None
246+
247+
let ptyp_any = function { ptyp_desc = Ptyp_any; _ } -> Some () | _ -> None
248+
249+
let pstr_eval = function
250+
| PStr [ { pstr_desc = Pstr_eval (expr, []); _ } ] -> Some expr
251+
| _ -> None
252+
253+
let pexp_tuple = function
254+
| { pexp_desc = Pexp_tuple l; _ } -> Some l
255+
| _ -> None
256+
257+
let pexp_variant = function
258+
| { pexp_desc = Pexp_variant (s, e); _ } -> Some (s, e)
259+
| _ -> None
260+
261+
let ppat = function PPat (pat, None) -> Some pat | _ -> None
262+
263+
let ppat_tuple = function
264+
| { ppat_desc = Ppat_tuple pats; _ } -> Some pats
265+
| _ -> None
266+
267+
let ppat_var = function
268+
| { ppat_desc = Ppat_var { txt; _ }; _ } -> Some txt
269+
| _ -> None
270+
271+
let ppat_any = function { ppat_desc = Ppat_any; _ } -> Some () | _ -> None
272+
273+
let closed_flag_from_string = function
274+
| "closed_" -> Some Closed
275+
| "open_" -> Some Open
276+
| _ -> None
277+
end
278+
end
279+
280+
module Ast_502 = struct
281+
include Ast_502.Asttypes
282+
include Ast_502.Parsetree
283+
284+
module Construct = struct
285+
let core_type ~loc ptyp_desc =
286+
{ ptyp_desc; ptyp_loc = loc; ptyp_attributes = []; ptyp_loc_stack = [] }
287+
288+
let expression ~loc pexp_desc =
289+
{ pexp_desc; pexp_loc = loc; pexp_attributes = []; pexp_loc_stack = [] }
290+
291+
let pattern ~loc ppat_desc =
292+
{ ppat_desc; ppat_loc = loc; ppat_attributes = []; ppat_loc_stack = [] }
293+
294+
let ptyp_extension_desc name payload = Ptyp_extension (name, payload)
295+
let ptyp_tuple ~loc typs = core_type ~loc (Ptyp_tuple typs)
296+
let ptyp_var ~loc s = core_type ~loc (Ptyp_var s)
297+
let ptyp_any ~loc = core_type ~loc Ptyp_any
298+
let ptyp typ = PTyp typ
299+
let pexp_extension_desc name payload = Pexp_extension (name, payload)
300+
let pexp_tuple ~loc l = expression ~loc (Pexp_tuple l)
301+
302+
let pexp_variant ~loc v exp_opt =
303+
expression ~loc (Pexp_variant (v, exp_opt))
304+
305+
let pstr_eval ~loc expr =
306+
PStr [ { pstr_desc = Pstr_eval (expr, []); pstr_loc = loc } ]
307+
308+
let ppat_extension_desc name payload = Ppat_extension (name, payload)
309+
let ppat_tuple ~loc l = pattern ~loc (Ppat_tuple l)
310+
let ppat_var ~loc txt = pattern ~loc (Ppat_var { txt; loc })
311+
let ppat_any ~loc = pattern ~loc Ppat_any
312+
let ppat pat = PPat (pat, None)
313+
let closed_flag_to_string = function Closed -> "closed_" | Open -> "open_"
314+
end
315+
316+
module Destruct = struct
317+
let ptyp = function PTyp typ -> Some typ | _ -> None
318+
319+
let ptyp_tuple = function
320+
| { ptyp_desc = Ptyp_tuple typs; _ } -> Some typs
321+
| _ -> None
322+
323+
let ptyp_var = function
324+
| { ptyp_desc = Ptyp_var s; _ } -> Some s
325+
| _ -> None
326+
327+
let ptyp_any = function { ptyp_desc = Ptyp_any; _ } -> Some () | _ -> None
328+
329+
let pstr_eval = function
330+
| PStr [ { pstr_desc = Pstr_eval (expr, []); _ } ] -> Some expr
331+
| _ -> None
332+
333+
let pexp_tuple = function
334+
| { pexp_desc = Pexp_tuple l; _ } -> Some l
335+
| _ -> None
336+
337+
let pexp_variant = function
338+
| { pexp_desc = Pexp_variant (s, e); _ } -> Some (s, e)
339+
| _ -> None
340+
341+
let ppat = function PPat (pat, None) -> Some pat | _ -> None
342+
343+
let ppat_tuple = function
344+
| { ppat_desc = Ppat_tuple pats; _ } -> Some pats
345+
| _ -> None
346+
347+
let ppat_var = function
348+
| { ppat_desc = Ppat_var { txt; _ }; _ } -> Some txt
349+
| _ -> None
350+
351+
let ppat_any = function { ppat_desc = Ppat_any; _ } -> Some () | _ -> None
352+
353+
let closed_flag_from_string = function
354+
| "closed_" -> Some Closed
355+
| "open_" -> Some Open
356+
| _ -> None
357+
end
358+
end
359+
360+
module To_503 = struct
361+
include Make (Ast_503)
362+
end
363+
364+
module To_502 = struct
365+
include Make (Ast_502)
366+
end

0 commit comments

Comments
 (0)