Skip to content

Commit fa03fea

Browse files
author
Nathan Rebours
committed
Add support for labeled tuple expressions
Signed-off-by: Nathan Rebours <[email protected]>
1 parent 1a3e434 commit fa03fea

File tree

11 files changed

+247
-52
lines changed

11 files changed

+247
-52
lines changed

astlib/encoding_504.ml

Lines changed: 111 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,45 @@
11
module Ext_name = struct
22
let ptyp_labeled_tuple = "ppxlib.migration.ptyp_labeled_tuple_504"
3+
let pexp_labeled_tuple = "ppxlib.migration.pexp_labeled_tuple_504"
34
end
45

6+
let invalid_encoding ~loc name =
7+
Location.raise_errorf ~loc "Invalid %s encoding" name
8+
59
module type AST = sig
610
type payload
711
type core_type
812
type core_type_desc
13+
type expression
14+
type expression_desc
915

1016
module Construct : sig
1117
val ptyp_extension_desc : string Location.loc -> payload -> core_type_desc
1218
val ptyp_tuple : loc:Location.t -> core_type list -> core_type
1319
val ptyp_var : loc:Location.t -> string -> core_type
1420
val ptyp_any : loc:Location.t -> core_type
1521
val ptyp : core_type -> payload
22+
val pexp_extension_desc : string Location.loc -> payload -> expression_desc
23+
val pexp_tuple : loc:Location.t -> expression list -> expression
24+
25+
val pexp_variant :
26+
loc:Location.t -> string -> expression option -> expression
27+
28+
val pstr_eval : loc:Location.t -> expression -> payload
1629
end
1730

1831
module Destruct : sig
1932
val ptyp : payload -> core_type option
2033
val ptyp_tuple : core_type -> core_type list option
2134
val ptyp_var : core_type -> string option
2235
val ptyp_any : core_type -> unit option
36+
val pstr_eval : payload -> expression option
37+
val pexp_tuple : expression -> expression list option
38+
val pexp_variant : expression -> (string * expression option) option
2339
end
2440
end
2541

26-
module type S = sig
27-
type payload
28-
type core_type
29-
type core_type_desc
30-
31-
val encode_ptyp_labeled_tuple :
32-
loc:Location.t -> (string option * core_type) list -> core_type_desc
33-
34-
val decode_ptyp_labeled_tuple :
35-
loc:Location.t -> payload -> (string option * core_type) list
36-
end
37-
38-
module Make (X : AST) :
39-
S
40-
with type core_type = X.core_type
41-
and type core_type_desc = X.core_type_desc
42-
and type payload = X.payload = struct
43-
type payload = X.payload
44-
type core_type = X.core_type
45-
type core_type_desc = X.core_type_desc
46-
42+
module Make (X : AST) = struct
4743
let encode_ptyp_labeled_tuple ~loc args =
4844
let payload =
4945
let l =
@@ -80,9 +76,54 @@ module Make (X : AST) :
8076
in
8177
match res with
8278
| Some res -> res
83-
| None ->
84-
Location.raise_errorf ~loc "Invalid %s encoding"
85-
Ext_name.ptyp_labeled_tuple
79+
| None -> invalid_encoding ~loc Ext_name.ptyp_labeled_tuple
80+
81+
let encode_pexp_labeled_tuple ~loc args =
82+
let payload =
83+
let l =
84+
List.map
85+
(fun (label_opt, expr) ->
86+
let label =
87+
match label_opt with
88+
| None -> X.Construct.pexp_variant ~loc "None" None
89+
| Some s ->
90+
let string_as_variant =
91+
X.Construct.pexp_variant ~loc s None
92+
in
93+
X.Construct.pexp_variant ~loc "Some" (Some string_as_variant)
94+
in
95+
X.Construct.pexp_tuple ~loc [ label; expr ])
96+
args
97+
in
98+
X.Construct.pexp_tuple ~loc l
99+
in
100+
X.Construct.pexp_extension_desc
101+
{ txt = Ext_name.pexp_labeled_tuple; loc }
102+
(X.Construct.pstr_eval ~loc payload)
103+
104+
let decode_pexp_labeled_tuple ~loc payload =
105+
let open Stdlib0.Option.Op in
106+
let res =
107+
let* exp = X.Destruct.pstr_eval payload in
108+
let* exp_list = X.Destruct.pexp_tuple exp in
109+
Stdlib0.Option.List.map exp_list ~f:(fun exp ->
110+
let* exp_pair = X.Destruct.pexp_tuple exp in
111+
match exp_pair with
112+
| [ label; exp ] -> (
113+
let* opt_variant = X.Destruct.pexp_variant label in
114+
match opt_variant with
115+
| "None", None -> Some (None, exp)
116+
| "Some", Some exp' -> (
117+
let* label_variant = X.Destruct.pexp_variant exp' in
118+
match label_variant with
119+
| s, None -> Some (Some s, exp)
120+
| _, _ -> None)
121+
| _ -> None)
122+
| _ -> None)
123+
in
124+
match res with
125+
| Some res -> res
126+
| None -> invalid_encoding ~loc Ext_name.pexp_labeled_tuple
86127
end
87128

88129
module Ast_503 = struct
@@ -92,11 +133,22 @@ module Ast_503 = struct
92133
let core_type ~loc ptyp_desc =
93134
{ ptyp_desc; ptyp_loc = loc; ptyp_attributes = []; ptyp_loc_stack = [] }
94135

136+
let expression ~loc pexp_desc =
137+
{ pexp_desc; pexp_loc = loc; pexp_attributes = []; pexp_loc_stack = [] }
138+
95139
let ptyp_extension_desc name payload = Ptyp_extension (name, payload)
96140
let ptyp_tuple ~loc typs = core_type ~loc (Ptyp_tuple typs)
97141
let ptyp_var ~loc s = core_type ~loc (Ptyp_var s)
98142
let ptyp_any ~loc = core_type ~loc Ptyp_any
99143
let ptyp typ = PTyp typ
144+
let pexp_extension_desc name payload = Pexp_extension (name, payload)
145+
let pexp_tuple ~loc l = expression ~loc (Pexp_tuple l)
146+
147+
let pexp_variant ~loc v exp_opt =
148+
expression ~loc (Pexp_variant (v, exp_opt))
149+
150+
let pstr_eval ~loc expr =
151+
PStr [ { pstr_desc = Pstr_eval (expr, []); pstr_loc = loc } ]
100152
end
101153

102154
module Destruct = struct
@@ -111,6 +163,18 @@ module Ast_503 = struct
111163
| _ -> None
112164

113165
let ptyp_any = function { ptyp_desc = Ptyp_any; _ } -> Some () | _ -> None
166+
167+
let pstr_eval = function
168+
| PStr [ { pstr_desc = Pstr_eval (expr, []); _ } ] -> Some expr
169+
| _ -> None
170+
171+
let pexp_tuple = function
172+
| { pexp_desc = Pexp_tuple l; _ } -> Some l
173+
| _ -> None
174+
175+
let pexp_variant = function
176+
| { pexp_desc = Pexp_variant (s, e); _ } -> Some (s, e)
177+
| _ -> None
114178
end
115179
end
116180

@@ -121,11 +185,22 @@ module Ast_502 = struct
121185
let core_type ~loc ptyp_desc =
122186
{ ptyp_desc; ptyp_loc = loc; ptyp_attributes = []; ptyp_loc_stack = [] }
123187

188+
let expression ~loc pexp_desc =
189+
{ pexp_desc; pexp_loc = loc; pexp_attributes = []; pexp_loc_stack = [] }
190+
124191
let ptyp_extension_desc name payload = Ptyp_extension (name, payload)
125192
let ptyp_tuple ~loc typs = core_type ~loc (Ptyp_tuple typs)
126193
let ptyp_var ~loc s = core_type ~loc (Ptyp_var s)
127194
let ptyp_any ~loc = core_type ~loc Ptyp_any
128195
let ptyp typ = PTyp typ
196+
let pexp_extension_desc name payload = Pexp_extension (name, payload)
197+
let pexp_tuple ~loc l = expression ~loc (Pexp_tuple l)
198+
199+
let pexp_variant ~loc v exp_opt =
200+
expression ~loc (Pexp_variant (v, exp_opt))
201+
202+
let pstr_eval ~loc expr =
203+
PStr [ { pstr_desc = Pstr_eval (expr, []); pstr_loc = loc } ]
129204
end
130205

131206
module Destruct = struct
@@ -140,6 +215,18 @@ module Ast_502 = struct
140215
| _ -> None
141216

142217
let ptyp_any = function { ptyp_desc = Ptyp_any; _ } -> Some () | _ -> None
218+
219+
let pstr_eval = function
220+
| PStr [ { pstr_desc = Pstr_eval (expr, []); _ } ] -> Some expr
221+
| _ -> None
222+
223+
let pexp_tuple = function
224+
| { pexp_desc = Pexp_tuple l; _ } -> Some l
225+
| _ -> None
226+
227+
let pexp_variant = function
228+
| { pexp_desc = Pexp_variant (s, e); _ } -> Some (s, e)
229+
| _ -> None
143230
end
144231
end
145232

astlib/encoding_504.mli

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Ext_name : sig
22
val ptyp_labeled_tuple : string
3+
val pexp_labeled_tuple : string
34
end
45

56
module To_503 : sig
@@ -10,6 +11,12 @@ module To_503 : sig
1011

1112
val decode_ptyp_labeled_tuple :
1213
loc:Location.t -> payload -> (string option * core_type) list
14+
15+
val encode_pexp_labeled_tuple :
16+
loc:Location.t -> (string option * expression) list -> expression_desc
17+
18+
val decode_pexp_labeled_tuple :
19+
loc:Location.t -> payload -> (string option * expression) list
1320
end
1421

1522
module To_502 : sig
@@ -20,4 +27,10 @@ module To_502 : sig
2027

2128
val decode_ptyp_labeled_tuple :
2229
loc:Location.t -> payload -> (string option * core_type) list
30+
31+
val encode_pexp_labeled_tuple :
32+
loc:Location.t -> (string option * expression) list -> expression_desc
33+
34+
val decode_pexp_labeled_tuple :
35+
loc:Location.t -> payload -> (string option * expression) list
2336
end

astlib/migrate_503_504.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ and copy_expression_desc_with_loc :
179179
Ast_504.Parsetree.Pexp_open (copy_open_declaration x0, copy_expression x1)
180180
| Ast_503.Parsetree.Pexp_letop x0 ->
181181
Ast_504.Parsetree.Pexp_letop (copy_letop x0)
182+
| Ast_503.Parsetree.Pexp_extension ({ txt; loc }, payload)
183+
when String.equal txt Encoding_504.Ext_name.pexp_labeled_tuple ->
184+
let xs = Encoding_504.To_503.decode_pexp_labeled_tuple ~loc payload in
185+
Ast_504.Parsetree.Pexp_tuple
186+
(List.map (fun (lbl, exp) -> (lbl, copy_expression exp)) xs)
182187
| Ast_503.Parsetree.Pexp_extension x0 ->
183188
Ast_504.Parsetree.Pexp_extension (copy_extension x0)
184189
| Ast_503.Parsetree.Pexp_unreachable -> Ast_504.Parsetree.Pexp_unreachable

astlib/migrate_504_503.ml

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,15 @@ and copy_expression :
5555
Ast_504.Parsetree.pexp_loc_stack;
5656
Ast_504.Parsetree.pexp_attributes;
5757
} ->
58+
let loc = copy_location pexp_loc in
5859
{
59-
Ast_503.Parsetree.pexp_desc = copy_expression_desc pexp_desc;
60-
Ast_503.Parsetree.pexp_loc = copy_location pexp_loc;
60+
Ast_503.Parsetree.pexp_desc = copy_expression_desc ~loc pexp_desc;
61+
Ast_503.Parsetree.pexp_loc = loc;
6162
Ast_503.Parsetree.pexp_loc_stack = copy_location_stack pexp_loc_stack;
6263
Ast_503.Parsetree.pexp_attributes = copy_attributes pexp_attributes;
6364
}
6465

65-
and copy_expression_desc :
66+
and copy_expression_desc ~loc :
6667
Ast_504.Parsetree.expression_desc -> Ast_503.Parsetree.expression_desc =
6768
function
6869
| Ast_504.Parsetree.Pexp_ident x0 ->
@@ -90,15 +91,12 @@ and copy_expression_desc :
9091
| Ast_504.Parsetree.Pexp_try (x0, x1) ->
9192
Ast_503.Parsetree.Pexp_try (copy_expression x0, List.map copy_case x1)
9293
| Ast_504.Parsetree.Pexp_tuple x0 ->
93-
let args =
94-
List.map
95-
(function
96-
| None, arg -> arg
97-
| Some _l, (arg : Ast_504.Parsetree.expression) ->
98-
migration_error arg.pexp_loc "labelled tuples")
99-
x0
94+
let exps =
95+
List.map (fun (label, exp) -> (label, copy_expression exp)) x0
10096
in
101-
Ast_503.Parsetree.Pexp_tuple (List.map copy_expression args)
97+
if List.exists (function Some _, _ -> true | _ -> false) exps then
98+
Encoding_504.To_503.encode_pexp_labeled_tuple ~loc exps
99+
else Ast_503.Parsetree.Pexp_tuple (List.map snd exps)
102100
| Ast_504.Parsetree.Pexp_construct (x0, x1) ->
103101
Ast_503.Parsetree.Pexp_construct
104102
(copy_loc copy_Longident_t x0, Option.map copy_expression x1)

src/ast_builder.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,12 @@ module Default = struct
270270
in
271271
{ ptyp_desc; ptyp_loc = loc; ptyp_attributes = []; ptyp_loc_stack = [] }
272272

273+
let pexp_labeled_tuple ~loc l =
274+
let pexp_desc =
275+
Astlib__.Encoding_504.To_502.encode_pexp_labeled_tuple ~loc l
276+
in
277+
{ pexp_desc; pexp_loc = loc; pexp_attributes = []; pexp_loc_stack = [] }
278+
273279
let pexp_tuple_opt ~loc l =
274280
match l with [] -> None | _ :: _ -> Some (pexp_tuple ~loc l)
275281

@@ -562,6 +568,7 @@ end) : S = struct
562568
let ppat_tuple l = Default.ppat_tuple ~loc l
563569
let ptyp_tuple l = Default.ptyp_tuple ~loc l
564570
let ptyp_labeled_tuple l = Default.ptyp_labeled_tuple ~loc l
571+
let pexp_labeled_tuple l = Default.pexp_labeled_tuple ~loc l
565572
let pexp_tuple_opt l = Default.pexp_tuple_opt ~loc l
566573
let ppat_tuple_opt l = Default.ppat_tuple_opt ~loc l
567574
let ptyp_poly vars ty = Default.ptyp_poly ~loc vars ty

src/ast_builder_intf.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ module type Additional_helpers = sig
170170
val ptyp_labeled_tuple :
171171
((string option * core_type) list -> core_type) with_loc
172172
(** Returns an encoded labeled tuple type as introduced in OCaml 5.4. *)
173+
174+
val pexp_labeled_tuple :
175+
((string option * expression) list -> expression) with_loc
176+
(** Returns an encoded labeled tuple expression as introduced in OCaml 5.4. *)
173177
end
174178

175179
module type Located = sig

src/ast_pattern.ml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,20 @@ let ptyp_labeled_tuple (T f0) =
292292
let k = f0 ctx loc x0 k in
293293
k
294294
| _ -> fail loc "labeled tuple")
295+
296+
let pexp_labeled_tuple (T f0) =
297+
T
298+
(fun ctx _loc x k ->
299+
let loc = x.pexp_loc in
300+
let x = x.pexp_desc in
301+
match x with
302+
| Pexp_extension ({ txt; _ }, payload)
303+
when String.equal txt Astlib__.Encoding_504.Ext_name.pexp_labeled_tuple
304+
->
305+
let x0 =
306+
Astlib__.Encoding_504.To_502.decode_pexp_labeled_tuple ~loc payload
307+
in
308+
ctx.matched <- ctx.matched + 1;
309+
let k = f0 ctx loc x0 k in
310+
k
311+
| _ -> fail loc "labeled tuple")

src/ast_pattern.mli

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,11 @@ val ptyp_labeled_tuple :
227227
228228
It will fail on a regular tuple type and as a consequence, if it matches, at
229229
least one type in the tuple is guaranteed to be labeled. *)
230+
231+
val pexp_labeled_tuple :
232+
((string option * expression) list, 'a, 'b) t -> (expression, 'a, 'b) t
233+
(** Match over an encoded OCaml 5.4 labeled tuple expression.
234+
235+
It will fail on a regular tuple expression and as a consequence, if it
236+
matches, at least one expression in the tuple is guaranteed to be labeled.
237+
*)

0 commit comments

Comments
 (0)