@@ -32,18 +32,11 @@ module List = struct
32
32
end
33
33
34
34
module Ctx = struct
35
- (* name -> (value, is_used ) *)
36
- type t = (string , string * bool ) Hashtbl .t
35
+ (* name -> parsed value ) *)
36
+ type t = (string , label Regexp .t ) Hashtbl .t
37
37
38
38
let empty () = Hashtbl. create 16
39
39
let find name ctx = Hashtbl. find_opt ctx name
40
-
41
- let update_used name ctx =
42
- match Hashtbl. find_opt ctx name with
43
- | Some (old_value , _ ) -> Hashtbl. replace ctx name (old_value, true )
44
- | None -> ()
45
-
46
- let is_used name ctx = Hashtbl. find_opt ctx name |> Option. value ~default: (" " , false ) |> snd
47
40
end
48
41
49
42
module Regexp = struct
@@ -75,15 +68,14 @@ module Regexp = struct
75
68
let delimit_if b s = if b then " (?:" ^ s ^ " )" else s in
76
69
let rec recurse p (e' : _ Location.loc ) =
77
70
let loc = e'.Location. loc in
78
- let parse_inside idr =
71
+ let get_parsed idr =
79
72
let var_name = idr.txt in
80
73
let content =
81
74
match Ctx. find var_name ctx with
82
- | Some ( value , _ ) -> parse_exn value
75
+ | Some value -> value
83
76
| None ->
84
77
error ~loc " Variable '%s' not found. %%pcre only supports global let bindings for substitution." var_name
85
78
in
86
- Ctx. update_used var_name ctx;
87
79
content
88
80
in
89
81
match e'.Location. txt with
@@ -101,10 +93,10 @@ module Regexp = struct
101
93
| Capture _ -> error ~loc " Unnamed capture is not allowed for %%pcre."
102
94
| Capture_as (_ , e ) -> " (" ^ recurse p_alt e ^ " )"
103
95
| Named_subs (idr , _ , _ ) ->
104
- let content = parse_inside idr in
96
+ let content = get_parsed idr in
105
97
" (" ^ recurse p_alt content ^ " )"
106
98
| Unnamed_subs (idr , _ ) ->
107
- let content = parse_inside idr in
99
+ let content = get_parsed idr in
108
100
recurse p_alt content
109
101
| Call _ -> error ~loc " (&...) is not implemented for %%pcre."
110
102
in
@@ -217,26 +209,49 @@ let transform_cases ~opts ~loc ~ctx cases =
217
209
in
218
210
cases, re_binding
219
211
212
+ let check_unbounded_recursion var_name content =
213
+ let contains_regex pattern str =
214
+ let re = Re.Str. regexp pattern in
215
+ try
216
+ Re.Str. search_forward re str 0 |> ignore;
217
+ true
218
+ with Not_found -> false
219
+ in
220
+ let u = Printf. sprintf {| (\?U < % s> )| } var_name in
221
+ let n = Printf. sprintf {| (\?N < % s> )| } var_name in
222
+ let n_as = Printf. sprintf {| (\?N < % s as [^> ]*> )| } var_name in
223
+ contains_regex u content || contains_regex n content || contains_regex n_as content
224
+
225
+ let transform_let ~ctx =
226
+ List. map
227
+ begin
228
+ fun vb ->
229
+ match vb.pvb_pat.ppat_desc, vb.pvb_expr.pexp_desc with
230
+ | Ppat_var { txt = var_name ; loc } , Pexp_constant (Pconst_string (value , _ , _ )) ->
231
+ if check_unbounded_recursion var_name value then error ~loc " Unbounded recursion detected!"
232
+ else begin
233
+ let parsed = Regexp. parse_exn value in
234
+ Hashtbl. replace ctx var_name parsed;
235
+ let warning_attr =
236
+ attribute ~loc ~name: { txt = " ocaml.warning" ; loc }
237
+ ~payload: (PStr [ { pstr_desc = Pstr_eval (estring ~loc " -32" , [] ); pstr_loc = loc } ])
238
+ in
239
+ { vb with pvb_attributes = warning_attr :: vb .pvb_attributes }
240
+ end
241
+ | _ -> vb
242
+ end
243
+
220
244
let transformation ctx =
221
245
object
222
246
inherit [value_binding list ] Ast_traverse. fold_map as super
223
247
224
248
method! structure_item item acc =
225
- begin
226
- match item.pstr_desc with
227
- | Pstr_value (_ , vbs ) ->
228
- List. iter
229
- begin
230
- fun vb ->
231
- match vb.pvb_pat.ppat_desc, vb.pvb_expr.pexp_desc with
232
- | Ppat_var { txt = name ; _ } , Pexp_constant (Pconst_string (value , _ , _ )) ->
233
- Hashtbl. replace ctx name (value, false )
234
- | _ -> ()
235
- end
236
- vbs
237
- | _ -> ()
238
- end;
239
- super#structure_item item acc
249
+ match item.pstr_desc with
250
+ | Pstr_extension (({ txt = "pcre" ; _ } , PStr [ { pstr_desc = Pstr_value (rec_flag , vbs ); _ } ]), _ ) ->
251
+ let bindings = transform_let ~ctx vbs in
252
+ let new_item = { item with pstr_desc = Pstr_value (rec_flag, bindings) } in
253
+ new_item, acc
254
+ | _ -> super#structure_item item acc
240
255
241
256
method! expression e_ext acc =
242
257
let e_ext, acc = super#expression e_ext acc in
@@ -248,10 +263,9 @@ let transformation ctx =
248
263
[% e cases]],
249
264
binding :: acc )
250
265
| Pexp_function cases ->
251
- (* | Pexp_function (_, _, Pfunction_cases (cases, _, _)) -> *)
252
266
let cases, binding = transform_cases ~opts ~loc ~ctx cases in
253
267
[% expr fun _ppx_regexp_v -> [% e cases]], binding :: acc
254
- | _ -> error ~loc " [%%pcre] only applies to match and function ."
268
+ | _ -> error ~loc " [%%pcre] only applies to match, function and global let declarations of strings ."
255
269
in
256
270
match e_ext.pexp_desc with
257
271
| Pexp_extension ({ txt = "pcre" ; _ } , PStr [ { pstr_desc = Pstr_eval (e , _ ); _ } ]) ->
@@ -263,43 +277,11 @@ let transformation ctx =
263
277
| _ -> e_ext, acc
264
278
end
265
279
266
- let suppress_unused_inlined ctx =
267
- object
268
- inherit Ast_traverse. map as super
269
-
270
- method! structure_item item =
271
- match item.pstr_desc with
272
- | Pstr_value (rec_flag , bindings ) ->
273
- let bindings =
274
- List. map
275
- begin
276
- fun binding ->
277
- match binding.pvb_pat.ppat_desc, binding.pvb_expr.pexp_desc with
278
- | Ppat_var { txt = var_name ; _ } , Pexp_constant (Pconst_string (_ , _ , _ )) ->
279
- let needs_suppression = Ctx. is_used var_name ctx in
280
- if needs_suppression then begin
281
- let loc = binding.pvb_loc in
282
- let warning_attr =
283
- attribute ~loc ~name: { txt = " ocaml.warning" ; loc }
284
- ~payload: (PStr [ { pstr_desc = Pstr_eval (estring ~loc " -32" , [] ); pstr_loc = loc } ])
285
- in
286
- { binding with pvb_attributes = warning_attr :: binding .pvb_attributes }
287
- end
288
- else binding
289
- | _ -> binding
290
- end
291
- bindings
292
- in
293
- { item with pstr_desc = Pstr_value (rec_flag, bindings) }
294
- | _ -> super#structure_item item
295
- end
296
-
297
280
let impl str =
298
281
let ctx = Ctx. empty () in
299
282
let str, rev_bindings = (transformation ctx)#structure str [] in
300
283
if rev_bindings = [] then str
301
284
else (
302
- let str = (suppress_unused_inlined ctx)#structure str in
303
285
let re_str =
304
286
let loc = Location. none in
305
287
[% str
0 commit comments