@@ -137,69 +137,166 @@ let rec wrap_group_bindings ~loc rhs offG = function
137137 let [% p ppat_var ~loc varG] = [% e eG] in
138138 [% e wrap_group_bindings ~loc rhs offG bs]]
139139
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+
140178let transform_cases ~loc cases =
141179 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
153205 end
206+ [] cases
154207 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
172223 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+
181239 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
184265 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 ]
195278 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 =
197284 [% 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]]
201298 in
202- (cases , re_binding)
299+ [ % expr [ % e match_expr]] , re_binding
203300
204301let transformation = object
205302 inherit [value_binding list ] Ast_traverse. fold_map as super
0 commit comments