@@ -173,6 +173,27 @@ module Regexp = struct
173
173
Format. fprintf ppf " => %S" (to_string e);
174
174
Format. pp_print_flush ppf () ;
175
175
Buffer. contents buf
176
+
177
+ let rec to_re e =
178
+ (match e.Loc. txt with
179
+ | Code re -> Re.Perl. re re
180
+ | Seq es -> Re. seq (List. map to_re es)
181
+ | Alt es -> Re. alt (List. map to_re es)
182
+ | Opt e -> Re. opt (to_re e)
183
+ | Repeat ({Loc. txt = (i , j ); _} , e ) -> Re. repn (to_re e) i j
184
+ | Nongreedy e -> Re. non_greedy (to_re e)
185
+ | Capture e -> Re. group (to_re e)
186
+ | Capture_as (_ , e ) -> Re. group (to_re e)
187
+ | Call _ -> raise Re.Perl. Not_supported )
188
+
189
+ let rec has_anon_capture e =
190
+ (match e.Loc. txt with
191
+ | Code _ | Call _ -> false
192
+ | Seq es | Alt es -> List. exists has_anon_capture es
193
+ | Opt e | Repeat (_ , e ) | Capture_as (_ , e ) | Nongreedy e ->
194
+ has_anon_capture e
195
+ | Capture _ -> true )
196
+
176
197
end
177
198
178
199
let gen_name =
@@ -252,17 +273,47 @@ let shrink_regexp =
252
273
let arb_regexp =
253
274
Q. make ~print: Regexp. show_debug ~shrink: shrink_regexp gen_regexp
254
275
276
+ let test_parse s =
277
+ let r =
278
+ (match Regexp. parse_exn s with
279
+ | exception Location. Error err -> Error err
280
+ | e ->
281
+ Ok (e,
282
+ (try Ok (Regexp. to_re e) with
283
+ | Re.Perl. Parse_error -> Error `Parse_error
284
+ | Re.Perl. Not_supported -> Error `Not_supported )))
285
+ in
286
+ let r' =
287
+ try Ok (Re.Perl. re s) with
288
+ | Re.Perl. Parse_error -> Error `Parse_error
289
+ | Re.Perl. Not_supported -> Error `Not_supported
290
+ in
291
+ (match r, r' with
292
+ | (Error _ | Ok (_ , Error _ )), Error _ -> true
293
+ | Ok _ , Error `Not_supported -> true
294
+ | Ok (e , Ok _ ), Error `Parse_error ->
295
+ if Regexp. has_anon_capture e then true else
296
+ Q.Test. fail_reportf " Parsed to %a and converted to Re.t, \
297
+ but should be invalid" Regexp. pp_debug e
298
+ | Error err , Ok _ ->
299
+ Q.Test. fail_reportf " Failed to parse valid %s: %a" s
300
+ Location. report_error err
301
+ | Ok (e , Error _ ), Ok _ ->
302
+ Q.Test. fail_reportf " Parsed to %a but conversion to Re.t failed"
303
+ Regexp. pp_debug e
304
+ | Ok (_ , Ok _ ), Ok _ ->
305
+ (* TODO: Would have been nice to compare the two Re.t here. *)
306
+ true )
307
+
255
308
let tests = [
256
- Q.Test. make ~name: " parse ∘ to_string" arb_regexp
309
+ Q.Test. make ~long_factor: 100 ~ name:" parse ∘ to_string" arb_regexp
257
310
(fun e ->
258
311
(match Regexp. parse_exn (Regexp. to_string e) with
259
312
| exception Location. Error err ->
260
313
Q.Test. fail_reportf " %a" Location. report_error err
261
314
| e' -> Regexp. equal e' e));
262
- (* TODO:
263
- * - `to_string ∘ parse`: An arbitrary string will not be canonical, but even
264
- * controlled generation should add coverage compared to the above identity.
265
- * - Compare to PCRE. *)
315
+ Q.Test. make ~long_factor: 100 ~name: " to_string ∘ parse"
316
+ (Q. string_gen Q.Gen. printable) test_parse;
266
317
]
267
318
268
319
let () = QCheck_runner. run_tests_main tests
0 commit comments