@@ -137,69 +137,166 @@ let rec wrap_group_bindings ~loc rhs offG = function
137
137
let [% p ppat_var ~loc varG] = [% e eG] in
138
138
[% e wrap_group_bindings ~loc rhs offG bs]]
139
139
140
+ let rec separate_defaults acc = function
141
+ | [] -> List. rev acc, []
142
+ | ({ pc_lhs = { ppat_desc = Ppat_any ; _ } ; _ } as case ) :: rest -> acc, case :: rest
143
+ | ({ pc_lhs = { ppat_desc = Ppat_var _ ; _ } ; _ } as case ) :: rest -> acc, case :: rest
144
+ | case :: rest -> separate_defaults (case :: acc) rest
145
+
146
+ let make_default_rhs ~loc = function
147
+ | [] ->
148
+ let open Lexing in
149
+ let pos = loc.Location. loc_start in
150
+ let e0 = estring ~loc pos.pos_fname in
151
+ let e1 = eint ~loc pos.pos_lnum in
152
+ let e2 = eint ~loc (pos.pos_cnum - pos.pos_bol) in
153
+ let e = [% expr raise (Match_failure ([% e e0], [% e e1], [% e e2]))] in
154
+ warn ~loc " A universal case is recommended." e
155
+ | default_cases ->
156
+ let transformed =
157
+ List. map
158
+ (fun case ->
159
+ match case.pc_lhs.ppat_desc with
160
+ | Ppat_var var ->
161
+ {
162
+ case with
163
+ pc_lhs = ppat_any ~loc ;
164
+ pc_rhs =
165
+ [% expr
166
+ let [% p ppat_var ~loc var] = _ppx_regexp_v in
167
+ [% e case.pc_rhs]];
168
+ }
169
+ | _ -> case)
170
+ default_cases
171
+ in
172
+ match transformed with
173
+ | [{ pc_lhs = { ppat_desc = Ppat_any ; _ }; pc_guard = None ; pc_rhs; _ }] ->
174
+ pc_rhs
175
+ | _ ->
176
+ pexp_match ~loc [% expr _ppx_regexp_v] transformed
177
+
140
178
let transform_cases ~loc cases =
141
179
let aux case =
142
- if case.pc_guard <> None then
143
- error ~loc " Guards are not implemented for match%%pcre."
144
- else
145
- Ast_pattern. (parse (pstring __')) loc case.pc_lhs
146
- begin fun {txt = re_src ; loc = {loc_start; loc_end; _} } ->
147
- let re_offset =
148
- (loc_end.pos_cnum - loc_start.pos_cnum - String. length re_src) / 2
149
- in
150
- let pos = {loc_start with pos_cnum = loc_start.pos_cnum + re_offset} in
151
- let re, bs, nG = extract_bindings ~pos re_src in
152
- (re, nG, bs, case.pc_rhs)
180
+ Ast_pattern. (parse (pstring __'))
181
+ loc case.pc_lhs
182
+ begin
183
+ fun { txt = re_src ; loc = { loc_start; loc_end; _ } } ->
184
+ let re_offset = (loc_end.pos_cnum - loc_start.pos_cnum - String. length re_src) / 2 in
185
+ let pos = { loc_start with pos_cnum = loc_start.pos_cnum + re_offset; pos_lnum = loc_end.pos_lnum } in
186
+ let re, bs, nG = extract_bindings ~pos re_src in
187
+ re, nG, bs, case.pc_rhs, case.pc_guard
188
+ end
189
+ in
190
+ let group_by_pattern cases =
191
+ List. fold_left
192
+ begin
193
+ fun acc (re , nG , bs , rhs , guard ) ->
194
+ let found, groups =
195
+ List. fold_left
196
+ begin
197
+ fun (found , acc_groups ) (re' , cases ) ->
198
+ if found then found, (re', cases) :: acc_groups
199
+ else if re = re' then true , (re', (nG, bs, rhs, guard) :: cases) :: acc_groups
200
+ else false , (re', cases) :: acc_groups
201
+ end
202
+ (false , [] ) acc
203
+ in
204
+ if found then groups else (re, [ nG, bs, rhs, guard ]) :: groups
153
205
end
206
+ [] cases
154
207
in
155
- let cases, default_rhs =
156
- (match List. rev (* _map rewrite_case*) cases with
157
- | {pc_lhs = {ppat_desc = Ppat_any ; _} ; pc_rhs; pc_guard = None } :: cases ->
158
- (cases, pc_rhs)
159
- | {pc_lhs = {ppat_desc = Ppat_var var; _}; pc_rhs; pc_guard = None } ::
160
- cases ->
161
- let rhs =
162
- [% expr let [% p ppat_var ~loc var] = _ppx_regexp_v in [% e pc_rhs]] in
163
- (cases, rhs)
164
- | cases ->
165
- let open Lexing in
166
- let pos = loc.Location. loc_start in
167
- let e0 = estring ~loc pos.pos_fname in
168
- let e1 = eint ~loc pos.pos_lnum in
169
- let e2 = eint ~loc (pos.pos_cnum - pos.pos_bol) in
170
- let e = [% expr raise (Match_failure ([% e e0], [% e e1], [% e e2]))] in
171
- (cases, warn ~loc " A universal case is recommended for %pcre." e))
208
+
209
+ let compute_offsets l =
210
+ let result, _ =
211
+ List. fold_left
212
+ begin
213
+ fun (acc , offG ) (re , case_group ) ->
214
+ let nG =
215
+ let n, _, _, _ = List. hd (List. rev case_group) in
216
+ n
217
+ in
218
+ (re, case_group, offG) :: acc, offG + nG
219
+ end
220
+ ([] , 0 ) l
221
+ in
222
+ List. rev result
172
223
in
173
- let cases = List. rev_map aux cases in
174
- let res = pexp_array ~loc (List. map (fun (re , _ , _ , _ ) -> re) cases) in
175
- let comp = [% expr
176
- let a = Array. map (fun s -> Re. mark (Re.Perl. re s)) [% e res] in
177
- let marks = Array. map fst a in
178
- let re = Re. compile (Re. alt (Array. to_list (Array. map snd a))) in
179
- (re, marks)
180
- ] in
224
+
225
+ let cases, default_cases = separate_defaults [] cases in
226
+ let default_rhs = make_default_rhs ~loc default_cases in
227
+ let processed_cases = List. map aux cases |> group_by_pattern |> compute_offsets in
228
+
229
+ let res = pexp_array ~loc @@ List. map (fun (re , _ , _ ) -> re) processed_cases in
230
+
231
+ let comp =
232
+ [% expr
233
+ let a = Array. map (fun s -> Re. mark (Re.Perl. re s)) [% e res] in
234
+ let marks = Array. map fst a in
235
+ let re = Re. compile (Re. alt (Array. to_list (Array. map snd a))) in
236
+ re, marks]
237
+ in
238
+
181
239
let var = fresh_var () in
182
- let re_binding =
183
- value_binding ~loc ~pat: (ppat_var ~loc {txt = var; loc}) ~expr: comp
240
+ let re_binding = value_binding ~loc ~pat: (ppat_var ~loc { txt = var; loc }) ~expr: comp in
241
+ let e_comp = pexp_ident ~loc { txt = Lident var; loc } in
242
+
243
+ let case_handlers =
244
+ List. mapi
245
+ begin
246
+ fun i (_ , case_group , offG ) ->
247
+ let handler_name = Printf. sprintf " _case_%d" i in
248
+ let handler_body =
249
+ let rec mk_guard_chains = function
250
+ | [] -> [% expr None ]
251
+ | (_ , bs , rhs , guard ) :: rest ->
252
+ let bs = List. rev bs in
253
+ begin
254
+ match guard with
255
+ | None -> [% expr Some [% e wrap_group_bindings ~loc rhs offG bs]]
256
+ | Some guard_expr ->
257
+ let guarded = [% expr if [% e guard_expr] then Some [% e rhs] else [% e mk_guard_chains rest]] in
258
+ wrap_group_bindings ~loc guarded offG bs
259
+ end
260
+ in
261
+ [% expr fun _g -> [% e mk_guard_chains (List. rev case_group)]]
262
+ in
263
+ handler_name, handler_body
264
+ end
184
265
in
185
- let e_comp = pexp_ident ~loc {txt = Lident var; loc} in
186
-
187
- let rec handle_cases i offG = function
188
- | [] -> [% expr assert false ]
189
- | (_ , nG , bs , rhs ) :: cases ->
190
- [% expr
191
- if Re.Mark. test _g (snd [% e e_comp]).([% e eint ~loc i]) then
192
- [% e wrap_group_bindings ~loc rhs offG bs]
193
- else
194
- [% e handle_cases (i + 1 ) (offG + nG) cases]]
266
+
267
+ let mk_checks cases_with_offsets =
268
+ let indexed = List. mapi (fun i x -> i, x) cases_with_offsets in
269
+ List. fold_right
270
+ begin
271
+ fun (i , _ ) acc ->
272
+ let handler_name = Printf. sprintf " _case_%d" i in
273
+ [% expr
274
+ if Re.Mark. test _g (snd [% e e_comp]).([% e eint ~loc i]) then [% e pexp_ident ~loc { txt = Lident handler_name; loc }] _g
275
+ else [% e acc]]
276
+ end
277
+ indexed [% expr None ]
195
278
in
196
- let cases =
279
+
280
+ let handlers = case_handlers processed_cases in
281
+ let dispatchers = mk_checks processed_cases in
282
+
283
+ let match_expr =
197
284
[% expr
198
- (match Re. exec_opt (fst [% e e_comp]) _ppx_regexp_v with
199
- | None -> [% e default_rhs]
200
- | Some _g -> [% e handle_cases 0 0 cases])]
285
+ match Re. exec_opt (fst [% e e_comp]) _ppx_regexp_v with
286
+ | None -> [% e default_rhs]
287
+ | Some _g ->
288
+ [% e
289
+ List. fold_left
290
+ begin
291
+ fun acc (name , body ) ->
292
+ [% expr
293
+ let [% p ppat_var ~loc { txt = name; loc }] = [% e body] in
294
+ [% e acc]]
295
+ end
296
+ [% expr match [% e dispatchers] with Some result -> result | None -> [% e default_rhs]]
297
+ handlers]]
201
298
in
202
- (cases , re_binding)
299
+ [ % expr [ % e match_expr]] , re_binding
203
300
204
301
let transformation = object
205
302
inherit [value_binding list ] Ast_traverse. fold_map as super
0 commit comments