1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
open! Core
module Set_kind = struct
type t =
{ optional : bool
; first_only : bool
}
[@@deriving sexp]
end
type t =
| Capture_unlabeled of t
| Capture_to_number of int * t
| Capture_to_name of string * t
| Any
| Atom of string
| Atom_regex of string
| Sequence of t list
| Star of t
| Star_greedy of t
| Plus of t
| Plus_greedy of t
| Maybe of t
| Maybe_greedy of t
| List of t
| Set of (t * Set_kind.t) list
| Subsearch of t
| And of t list
| Or_shortcircuiting of t list
| Or_all of t list
| First_match_only of t
[@@deriving sexp]
let rec iter t ~f =
f t;
match t with
| Any | Atom _ | Atom_regex _ -> ()
| Capture_unlabeled sub
| Capture_to_number (_, sub)
| Capture_to_name (_, sub)
| Subsearch sub
| First_match_only sub
| Star sub
| Star_greedy sub
| Plus sub
| Plus_greedy sub
| Maybe sub
| Maybe_greedy sub
| List sub -> iter sub ~f
| Sequence subs | And subs | Or_shortcircuiting subs | Or_all subs ->
List.iter subs ~f:(fun sub -> iter sub ~f)
| Set subs -> List.iter subs ~f:(fun (sub, _) -> iter sub ~f)
;;
module Capture_count = struct
type t =
{ num_number_captures : int
; num_named_captures : int
; num_unlabeled_captures : int
}
end
let count_captures t =
let num_number_captures = ref 0 in
let num_named_captures = ref 0 in
let num_unlabeled_captures = ref 0 in
iter t ~f:(function
| Capture_unlabeled _ -> incr num_unlabeled_captures
| Capture_to_number _ -> incr num_number_captures
| Capture_to_name _ -> incr num_named_captures
| Any
| Atom _
| Atom_regex _
| Subsearch _
| First_match_only _
| Star _
| Star_greedy _
| Plus _
| Plus_greedy _
| Maybe _
| Maybe_greedy _
| List _
| Sequence _
| And _
| Or_shortcircuiting _
| Or_all _
| Set _ -> ());
{ Capture_count.num_number_captures = !num_number_captures
; num_named_captures = !num_named_captures
; num_unlabeled_captures = !num_unlabeled_captures
}
;;
let validate_all_captures_labeled_or_all_unlabeled_exn t =
let { Capture_count.num_number_captures; num_named_captures; num_unlabeled_captures } =
count_captures t
in
if num_unlabeled_captures > 0 && (num_number_captures > 0 || num_named_captures > 0)
then
failwith
"Cannot mix unlabeled captures with named or numbered captures in the same pattern"
;;