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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
open Common
let find_exn key obj =
match List.assoc_opt key obj with
| Some v ->
v
| None ->
error (key ^ " not found.")
let get_dict = function
| `Assoc d | `Dict d | `O d ->
d
| _ ->
error "Type error: Expected dict."
let get_string = function
| `String s ->
s
| _ ->
error "Type error: Expected string."
let get_list f = function
| `A l | `Array l | `List l ->
List.map f l
| _ ->
error "Type error: Expected list."
let rec get_captures_helper :
'a.
(int -> 'a -> capture_key) ->
(int -> 'a -> union) ->
'a list ->
(capture_key, capture) Hashtbl.t =
fun idx_fun capture_fun captures ->
let tbl = Hashtbl.create 21 in
let rec loop i = function
| [] ->
()
| capture :: captures ->
let k = idx_fun i capture in
let v = get_dict (capture_fun i capture) in
let capture_name =
match List.assoc_opt "name" v with
| None ->
None
| Some name ->
Some (get_string name)
in
let capture_patterns =
match List.assoc_opt "patterns" v with
| None ->
[]
| Some v ->
get_pattern_list v
in
Hashtbl.replace tbl k { capture_name; capture_patterns };
loop (i + 1) captures
in
loop 0 captures;
tbl
and get_pattern_list l = get_list (fun x -> patterns_of_plist (get_dict x)) l
and get_patterns obj = find_exn "patterns" obj |> get_pattern_list
and get_captures_from_dict dict =
get_captures_helper
(fun _ (k, _) ->
match int_of_string_opt k with
| Some int ->
Capture_idx int
| None ->
Capture_name k
)
(fun _ (_, v) -> v)
dict
and get_captures_from_list list =
get_captures_helper (fun i _ -> Capture_idx i) (fun _ v -> v) list
and get_captures = function
| `Assoc d | `Dict d | `O d ->
get_captures_from_dict d
| `A l | `Array l | `List l ->
get_captures_from_list l
| _ ->
error "Type error: Expected dict or list."
and patterns_of_plist obj =
match List.assoc_opt "include" obj with
| Some s -> (
match get_string s with
| "$base" ->
Include_base
| "$self" ->
Include_self
| s ->
let len = String.length s in
if len > 0 && s.[0] = '#' then
Include_local (String.sub s 1 (len - 1))
else
Include_scope s
)
| None -> (
match (List.assoc_opt "match" obj, List.assoc_opt "begin" obj) with
| Some s, None ->
let pattern_source = get_string s in
Match
{
pattern_source;
pattern = compile_regex pattern_source;
name = Option.map get_string (List.assoc_opt "name" obj);
captures =
( match List.assoc_opt "captures" obj with
| None ->
Hashtbl.create 0
| Some value ->
get_captures value
);
}
| None, Some b ->
let delim_begin_source = get_string b in
let e, key, delim_kind =
match (List.assoc_opt "end" obj, List.assoc_opt "while" obj) with
| Some e, None ->
(e, "endCaptures", End)
| None, Some e ->
(e, "whileCaptures", While)
| _, _ ->
error "Begin patterns must either have an end or while."
in
let delim_begin_captures, delim_end_captures =
match List.assoc_opt "captures" obj with
| Some value ->
let captures = get_captures value in
(captures, captures)
| None ->
( ( match List.assoc_opt "beginCaptures" obj with
| Some value ->
get_captures value
| None ->
Hashtbl.create 0
),
match List.assoc_opt key obj with
| Some value ->
get_captures value
| None ->
Hashtbl.create 0
)
in
Delim
{
delim_begin_source;
delim_begin = compile_regex delim_begin_source;
delim_end = get_string e;
delim_patterns =
( match List.assoc_opt "patterns" obj with
| None ->
[]
| Some v ->
get_pattern_list v
);
delim_name = Option.map get_string (List.assoc_opt "name" obj);
delim_content_name =
Option.map get_string (List.assoc_opt "contentName" obj);
delim_begin_captures;
delim_end_captures;
delim_apply_end_pattern_last =
( match List.assoc_opt "applyEndPatternLast" obj with
| Some (`Int 1) ->
true
| _ ->
false
);
delim_kind;
}
| None, None ->
let scope_name = Option.map get_string (List.assoc_opt "name" obj) in
let child_patterns =
match List.assoc_opt "patterns" obj with
| None ->
[]
| Some v ->
get_pattern_list v
in
Scope_patterns { scope_name; child_patterns }
| Some _, Some _ ->
error "Pattern must not have both match and begin."
)
let parse_injection_selector raw =
let s = String.trim raw in
if String.length s >= 2 && s.[0] = 'L' && s.[1] = ':' then
let rest = String.trim (String.sub s 2 (String.length s - 2)) in
{
selector_left = true;
selector_segments =
String.split_on_char ' ' rest
|> List.map String.trim
|> List.filter (fun x -> x <> "");
}
else
let rest =
if String.length s >= 2 && s.[0] = 'R' && s.[1] = ':' then
String.trim (String.sub s 2 (String.length s - 2))
else
s
in
{
selector_left = false;
selector_segments =
String.split_on_char ' ' rest
|> List.map String.trim
|> List.filter (fun x -> x <> "");
}
let get_injections obj =
match List.assoc_opt "injections" obj with
| None ->
[]
| Some inj_obj ->
let entries = get_dict inj_obj in
List.filter_map
(fun (selector_str, value) ->
let selector = parse_injection_selector selector_str in
if selector.selector_segments = [] then
None
else
let patterns =
let d = get_dict value in
match List.assoc_opt "patterns" d with
| None -> (
match
(List.assoc_opt "match" d, List.assoc_opt "begin" d)
with
| None, None ->
[]
| _, _ ->
[ patterns_of_plist d ]
)
| Some v ->
get_pattern_list v
in
Some (selector, patterns)
)
entries
let of_doc_exn (plist : union) =
let rec get_repo_item obj =
{
repo_item_kind =
( match (List.assoc_opt "match" obj, List.assoc_opt "begin" obj) with
| None, None ->
Repo_patterns (get_patterns obj)
| _, _ ->
Repo_rule (patterns_of_plist obj)
);
repo_inner =
( match List.assoc_opt "repository" obj with
| None ->
Hashtbl.create 0
| Some obj ->
get_repo obj
);
}
and get_repo obj =
let hashtbl = Hashtbl.create 31 in
List.iter
(fun (k, v) ->
let v = get_dict v in
let item = get_repo_item v in
Hashtbl.add hashtbl k item
)
(get_dict obj);
hashtbl
in
let obj = get_dict plist in
{
name = Option.map get_string (List.assoc_opt "name" obj);
scope_name = get_string (find_exn "scopeName" obj);
injection_selector =
Option.map get_string (List.assoc_opt "injectionSelector" obj);
filetypes =
( match List.assoc_opt "fileTypes" obj with
| None ->
[]
| Some filetypes ->
get_list get_string filetypes
);
patterns = get_patterns obj;
repository =
( match List.assoc_opt "repository" obj with
| None ->
Hashtbl.create 0
| Some obj ->
get_repo obj
);
injections = get_injections obj;
}
let of_plist_exn = (of_doc_exn :> plist -> grammar)
let of_ezjsonm_exn = (of_doc_exn :> ezjsonm -> grammar)
let of_yojson_exn = (of_doc_exn :> yojson -> grammar)