Skip to content

Commit ed2f409

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

File tree

10 files changed

+265
-14
lines changed

10 files changed

+265
-14
lines changed

astlib/encoding_504.ml

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Ext_name = struct
22
let ptyp_labeled_tuple = "ppxlib.migration.ptyp_labeled_tuple_504"
33
let pexp_labeled_tuple = "ppxlib.migration.pexp_labeled_tuple_504"
4+
let ppat_labeled_tuple = "ppxlib.migration.ppat_labeled_tuple_504"
45
end
56

67
let invalid_encoding ~loc name =
@@ -12,6 +13,9 @@ module type AST = sig
1213
type core_type_desc
1314
type expression
1415
type expression_desc
16+
type pattern
17+
type pattern_desc
18+
type closed_flag
1519

1620
module Construct : sig
1721
val ptyp_extension_desc : string Location.loc -> payload -> core_type_desc
@@ -26,6 +30,12 @@ module type AST = sig
2630
loc:Location.t -> string -> expression option -> expression
2731

2832
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
2939
end
3040

3141
module Destruct : sig
@@ -36,6 +46,11 @@ module type AST = sig
3646
val pstr_eval : payload -> expression option
3747
val pexp_tuple : expression -> expression list option
3848
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
3954
end
4055
end
4156

@@ -124,9 +139,66 @@ module Make (X : AST) = struct
124139
match res with
125140
| Some res -> res
126141
| 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
127198
end
128199

129200
module Ast_503 = struct
201+
include Ast_503.Asttypes
130202
include Ast_503.Parsetree
131203

132204
module Construct = struct
@@ -136,6 +208,9 @@ module Ast_503 = struct
136208
let expression ~loc pexp_desc =
137209
{ pexp_desc; pexp_loc = loc; pexp_attributes = []; pexp_loc_stack = [] }
138210

211+
let pattern ~loc ppat_desc =
212+
{ ppat_desc; ppat_loc = loc; ppat_attributes = []; ppat_loc_stack = [] }
213+
139214
let ptyp_extension_desc name payload = Ptyp_extension (name, payload)
140215
let ptyp_tuple ~loc typs = core_type ~loc (Ptyp_tuple typs)
141216
let ptyp_var ~loc s = core_type ~loc (Ptyp_var s)
@@ -149,6 +224,13 @@ module Ast_503 = struct
149224

150225
let pstr_eval ~loc expr =
151226
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_"
152234
end
153235

154236
module Destruct = struct
@@ -175,10 +257,28 @@ module Ast_503 = struct
175257
let pexp_variant = function
176258
| { pexp_desc = Pexp_variant (s, e); _ } -> Some (s, e)
177259
| _ -> 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
178277
end
179278
end
180279

181280
module Ast_502 = struct
281+
include Ast_502.Asttypes
182282
include Ast_502.Parsetree
183283

184284
module Construct = struct
@@ -188,6 +288,9 @@ module Ast_502 = struct
188288
let expression ~loc pexp_desc =
189289
{ pexp_desc; pexp_loc = loc; pexp_attributes = []; pexp_loc_stack = [] }
190290

291+
let pattern ~loc ppat_desc =
292+
{ ppat_desc; ppat_loc = loc; ppat_attributes = []; ppat_loc_stack = [] }
293+
191294
let ptyp_extension_desc name payload = Ptyp_extension (name, payload)
192295
let ptyp_tuple ~loc typs = core_type ~loc (Ptyp_tuple typs)
193296
let ptyp_var ~loc s = core_type ~loc (Ptyp_var s)
@@ -201,6 +304,13 @@ module Ast_502 = struct
201304

202305
let pstr_eval ~loc expr =
203306
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_"
204314
end
205315

206316
module Destruct = struct
@@ -227,6 +337,23 @@ module Ast_502 = struct
227337
let pexp_variant = function
228338
| { pexp_desc = Pexp_variant (s, e); _ } -> Some (s, e)
229339
| _ -> 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
230357
end
231358
end
232359

astlib/encoding_504.mli

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
module Ext_name : sig
22
val ptyp_labeled_tuple : string
33
val pexp_labeled_tuple : string
4+
val ppat_labeled_tuple : string
45
end
56

67
module To_503 : sig
8+
open Ast_503.Asttypes
79
open Ast_503.Parsetree
810

911
val encode_ptyp_labeled_tuple :
@@ -17,9 +19,19 @@ module To_503 : sig
1719

1820
val decode_pexp_labeled_tuple :
1921
loc:Location.t -> payload -> (string option * expression) list
22+
23+
val encode_ppat_labeled_tuple :
24+
loc:Location.t ->
25+
(string option * pattern) list ->
26+
closed_flag ->
27+
pattern_desc
28+
29+
val decode_ppat_labeled_tuple :
30+
loc:Location.t -> payload -> (string option * pattern) list * closed_flag
2031
end
2132

2233
module To_502 : sig
34+
open Ast_502.Asttypes
2335
open Ast_502.Parsetree
2436

2537
val encode_ptyp_labeled_tuple :
@@ -33,4 +45,13 @@ module To_502 : sig
3345

3446
val decode_pexp_labeled_tuple :
3547
loc:Location.t -> payload -> (string option * expression) list
48+
49+
val encode_ppat_labeled_tuple :
50+
loc:Location.t ->
51+
(string option * pattern) list ->
52+
closed_flag ->
53+
pattern_desc
54+
55+
val decode_ppat_labeled_tuple :
56+
loc:Location.t -> payload -> (string option * pattern) list * closed_flag
3657
end

astlib/migrate_503_504.ml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,10 +356,14 @@ and copy_pattern_desc_with_loc :
356356
(copy_loc (fun x -> Option.map (fun x -> x) x) x0)
357357
| Ast_503.Parsetree.Ppat_exception x0 ->
358358
Ast_504.Parsetree.Ppat_exception (copy_pattern x0)
359-
| Ast_503.Parsetree.Ppat_extension
360-
( { txt = "ppxlib.migration.ppat_effect"; _ },
361-
PPat ({ ppat_desc = Ppat_tuple [ e; c ]; _ }, None) ) ->
362-
Ast_504.Parsetree.Ppat_effect (copy_pattern e, copy_pattern c)
359+
| Ast_503.Parsetree.Ppat_extension ({ txt; loc }, payload)
360+
when String.equal txt Encoding_504.Ext_name.ppat_labeled_tuple ->
361+
let pats, flag =
362+
Encoding_504.To_503.decode_ppat_labeled_tuple ~loc payload
363+
in
364+
Ast_504.Parsetree.Ppat_tuple
365+
( List.map (fun (lbl, pat) -> (lbl, copy_pattern pat)) pats,
366+
copy_closed_flag flag )
363367
| Ast_503.Parsetree.Ppat_extension x0 ->
364368
Ast_504.Parsetree.Ppat_extension (copy_extension x0)
365369
| Ast_503.Parsetree.Ppat_open (x0, x1) ->

astlib/migrate_504_503.ml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -321,15 +321,16 @@ and copy_pattern_desc loc :
321321
Ast_503.Parsetree.Ppat_constant (copy_constant x0)
322322
| Ast_504.Parsetree.Ppat_interval (x0, x1) ->
323323
Ast_503.Parsetree.Ppat_interval (copy_constant x0, copy_constant x1)
324-
| Ast_504.Parsetree.Ppat_tuple (x0, _) ->
325-
let args =
326-
List.map
327-
(function
328-
| None, arg -> arg
329-
| Some l, _ -> migration_error loc "labelled tuples")
330-
x0
324+
| Ast_504.Parsetree.Ppat_tuple (x0, flag) -> (
325+
let flag = copy_closed_flag flag in
326+
let args = List.map (fun (lbl, pat) -> (lbl, copy_pattern pat)) x0 in
327+
let has_label =
328+
List.exists (function Some _, _ -> true | _ -> false) args
331329
in
332-
Ast_503.Parsetree.Ppat_tuple (List.map copy_pattern args)
330+
match (has_label, flag) with
331+
| true, _ | false, Open ->
332+
Encoding_504.To_503.encode_ppat_labeled_tuple ~loc args flag
333+
| _, _ -> Ast_503.Parsetree.Ppat_tuple (List.map snd args))
333334
| Ast_504.Parsetree.Ppat_construct (x0, x1) ->
334335
Ast_503.Parsetree.Ppat_construct
335336
( copy_loc copy_Longident_t x0,

src/ast_builder.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,12 @@ module Default = struct
276276
in
277277
{ pexp_desc; pexp_loc = loc; pexp_attributes = []; pexp_loc_stack = [] }
278278

279+
let ppat_labeled_tuple ~loc l flag =
280+
let ppat_desc =
281+
Astlib__.Encoding_504.To_502.encode_ppat_labeled_tuple ~loc l flag
282+
in
283+
{ ppat_desc; ppat_loc = loc; ppat_attributes = []; ppat_loc_stack = [] }
284+
279285
let pexp_tuple_opt ~loc l =
280286
match l with [] -> None | _ :: _ -> Some (pexp_tuple ~loc l)
281287

@@ -569,6 +575,7 @@ end) : S = struct
569575
let ptyp_tuple l = Default.ptyp_tuple ~loc l
570576
let ptyp_labeled_tuple l = Default.ptyp_labeled_tuple ~loc l
571577
let pexp_labeled_tuple l = Default.pexp_labeled_tuple ~loc l
578+
let ppat_labeled_tuple l flag = Default.ppat_labeled_tuple ~loc l flag
572579
let pexp_tuple_opt l = Default.pexp_tuple_opt ~loc l
573580
let ppat_tuple_opt l = Default.ppat_tuple_opt ~loc l
574581
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
@@ -174,6 +174,10 @@ module type Additional_helpers = sig
174174
val pexp_labeled_tuple :
175175
((string option * expression) list -> expression) with_loc
176176
(** Returns an encoded labeled tuple expression as introduced in OCaml 5.4. *)
177+
178+
val ppat_labeled_tuple :
179+
((string option * pattern) list -> closed_flag -> pattern) with_loc
180+
(** Returns an encoded labeled tuple pattern as introduced in OCaml 5.4. *)
177181
end
178182

179183
module type Located = sig

src/ast_pattern.ml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,3 +309,20 @@ let pexp_labeled_tuple (T f0) =
309309
let k = f0 ctx loc x0 k in
310310
k
311311
| _ -> fail loc "labeled tuple")
312+
313+
let ppat_labeled_tuple (T f0) =
314+
T
315+
(fun ctx _loc x k ->
316+
let loc = x.ppat_loc in
317+
let x = x.ppat_desc in
318+
match x with
319+
| Ppat_extension ({ txt; _ }, payload)
320+
when String.equal txt Astlib__.Encoding_504.Ext_name.ppat_labeled_tuple
321+
->
322+
let x0 =
323+
Astlib__.Encoding_504.To_502.decode_ppat_labeled_tuple ~loc payload
324+
in
325+
ctx.matched <- ctx.matched + 1;
326+
let k = f0 ctx loc x0 k in
327+
k
328+
| _ -> fail loc "labeled tuple")

src/ast_pattern.mli

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,12 @@ val pexp_labeled_tuple :
235235
It will fail on a regular tuple expression and as a consequence, if it
236236
matches, at least one expression in the tuple is guaranteed to be labeled.
237237
*)
238+
239+
val ppat_labeled_tuple :
240+
((string option * pattern) list * closed_flag, 'a, 'b) t ->
241+
(pattern, 'a, 'b) t
242+
(** Match over an encoded OCaml 5.4 labeled tuple pattern.
243+
244+
It will fail on a regular tuple expression and as a consequence, if it
245+
matches, either at least one pattern in the tuple is guaranteed to be
246+
labeled or the flag to be [Open]. *)

0 commit comments

Comments
 (0)