tree-sitter-sed 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.toml ADDED
@@ -0,0 +1,43 @@
1
+ [package]
2
+ authors = ["konomanoasa"]
3
+ autoexamples = false
4
+ categories = ["parser-implementations", "parsing", "text-editors"]
5
+ description = "Tree-sitter grammars for POSIX sed."
6
+ edition = "2024"
7
+ keywords = ["incremental", "parsing", "tree-sitter", "sed"]
8
+ license = "MIT"
9
+ name = "tree-sitter-sed"
10
+ publish = false
11
+ readme = "README.md"
12
+ repository = "https://github.com/konomanoasa/tree-sitter-sed"
13
+ version = "0.9.0"
14
+
15
+ build = "bindings/rust/build.rs"
16
+ include = [
17
+ "/LICENSE",
18
+ "/README.md",
19
+ "/bindings/rust/*.rs",
20
+ "/common/scanner.h",
21
+ "/queries/*.scm",
22
+ "/sed_ere/queries/*.scm",
23
+ "/sed_ere/src/node-types.json",
24
+ "/sed_ere/src/parser.c",
25
+ "/sed_ere/src/scanner.c",
26
+ "/sed_ere/src/tree_sitter/*.h",
27
+ "/src/node-types.json",
28
+ "/src/parser.c",
29
+ "/src/scanner.c",
30
+ "/src/tree_sitter/*.h",
31
+ ]
32
+
33
+ [lib]
34
+ path = "bindings/rust/lib.rs"
35
+
36
+ [dependencies]
37
+ tree-sitter-language = "0.1"
38
+
39
+ [build-dependencies]
40
+ cc = "1.2"
41
+
42
+ [dev-dependencies]
43
+ tree-sitter = "0.27.0"
package/README.md CHANGED
@@ -8,6 +8,8 @@ POSIX.1-2024 `sed`.
8
8
 
9
9
  ## Installation
10
10
 
11
+ ### npm
12
+
11
13
  ```sh
12
14
  npm install tree-sitter-sed
13
15
  ```
@@ -16,10 +18,10 @@ npm install tree-sitter-sed
16
18
 
17
19
  This repository contains the following two grammars.
18
20
 
19
- | Grammar | Regexp | C function |
20
- | --------- | ------ | ----------------------- |
21
- | `sed` | BRE | `tree_sitter_sed()` |
22
- | `sed_ere` | ERE | `tree_sitter_sed_ere()` |
21
+ | Grammar | Regexp | Rust constant |
22
+ | --------- | ------ | -------------- |
23
+ | `sed` | BRE | `LANGUAGE` |
24
+ | `sed_ere` | ERE | `LANGUAGE_ERE` |
23
25
 
24
26
  ## Development
25
27
 
@@ -0,0 +1,40 @@
1
+ use std::path::Path;
2
+
3
+ fn compile_parser(source_dir: &Path, library_name: &str, wasm_headers: Option<&Path>) {
4
+ let parser_path = source_dir.join("parser.c");
5
+ let scanner_path = source_dir.join("scanner.c");
6
+ let mut c_config = cc::Build::new();
7
+ c_config.std("c11").include(source_dir);
8
+
9
+ #[cfg(target_env = "msvc")]
10
+ c_config.flag("-utf-8");
11
+
12
+ if let Some(headers) = wasm_headers {
13
+ c_config.include(headers);
14
+ }
15
+
16
+ c_config.file(&parser_path).file(&scanner_path);
17
+ c_config.compile(library_name);
18
+
19
+ println!("cargo:rerun-if-changed={}", parser_path.display());
20
+ println!("cargo:rerun-if-changed={}", scanner_path.display());
21
+ }
22
+
23
+ fn main() {
24
+ let target = std::env::var("TARGET").expect("Cargo must provide TARGET");
25
+ let wasm_headers = (target == "wasm32-unknown-unknown").then(|| {
26
+ std::env::var_os("DEP_TREE_SITTER_LANGUAGE_WASM_HEADERS")
27
+ .map(std::path::PathBuf::from)
28
+ .expect(
29
+ "tree-sitter-language must provide its headers when compiling for wasm32-unknown-unknown",
30
+ )
31
+ });
32
+
33
+ compile_parser(Path::new("src"), "tree-sitter-sed", wasm_headers.as_deref());
34
+ compile_parser(
35
+ Path::new("sed_ere/src"),
36
+ "tree-sitter-sed-ere",
37
+ wasm_headers.as_deref(),
38
+ );
39
+ println!("cargo:rerun-if-changed=common/scanner.h");
40
+ }
@@ -0,0 +1,572 @@
1
+ //! POSIX sed grammars for the Tree-sitter parsing library.
2
+ //!
3
+ //! [`LANGUAGE`] parses basic regular expressions, while [`LANGUAGE_ERE`] parses
4
+ //! extended regular expressions.
5
+ //!
6
+ //! ```
7
+ //! let mut parser = tree_sitter::Parser::new();
8
+ //! parser
9
+ //! .set_language(&tree_sitter_sed::LANGUAGE.into())
10
+ //! .expect("POSIX sed grammar must load");
11
+ //! let tree = parser.parse("p\n", None).expect("parser must return a tree");
12
+ //! assert!(!tree.root_node().has_error());
13
+ //! ```
14
+
15
+ use tree_sitter_language::LanguageFn;
16
+
17
+ unsafe extern "C" {
18
+ fn tree_sitter_sed() -> *const ();
19
+ fn tree_sitter_sed_ere() -> *const ();
20
+ }
21
+
22
+ /// The Tree-sitter [`LanguageFn`] for POSIX sed with basic regular expressions.
23
+ pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_sed) };
24
+
25
+ /// The Tree-sitter [`LanguageFn`] for POSIX sed with extended regular expressions.
26
+ pub const LANGUAGE_ERE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_sed_ere) };
27
+
28
+ /// The node type definitions for [`LANGUAGE`].
29
+ pub const NODE_TYPES: &str = include_str!("../../src/node-types.json");
30
+
31
+ /// The node type definitions for [`LANGUAGE_ERE`].
32
+ pub const NODE_TYPES_ERE: &str = include_str!("../../sed_ere/src/node-types.json");
33
+
34
+ /// The syntax highlighting query for [`LANGUAGE`].
35
+ pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm");
36
+
37
+ /// The syntax highlighting query for [`LANGUAGE_ERE`].
38
+ pub const HIGHLIGHTS_QUERY_ERE: &str = include_str!("../../sed_ere/queries/highlights.scm");
39
+
40
+ #[cfg(test)]
41
+ mod tests {
42
+ fn assert_parses(language: tree_sitter_language::LanguageFn) {
43
+ let mut parser = tree_sitter::Parser::new();
44
+ parser
45
+ .set_language(&language.into())
46
+ .expect("generated grammar must be compatible with the Tree-sitter runtime");
47
+ let tree = parser
48
+ .parse("p\n", None)
49
+ .expect("parser must return a tree");
50
+ assert_eq!(tree.root_node().kind(), "script");
51
+ assert!(!tree.root_node().has_error());
52
+ }
53
+
54
+ #[test]
55
+ fn sed_grammar_loads_and_parses() {
56
+ assert_parses(super::LANGUAGE);
57
+ }
58
+
59
+ #[test]
60
+ fn sed_ere_grammar_loads_and_parses() {
61
+ assert_parses(super::LANGUAGE_ERE);
62
+ }
63
+
64
+ #[test]
65
+ fn sed_and_sed_ere_are_distinct_grammars() {
66
+ let sed = tree_sitter::Language::new(super::LANGUAGE);
67
+ let sed_ere = tree_sitter::Language::new(super::LANGUAGE_ERE);
68
+ assert_ne!(sed, sed_ere);
69
+ }
70
+
71
+ fn issue_signatures(tree: &tree_sitter::Tree) -> Vec<(String, String, tree_sitter::Range)> {
72
+ let mut issues = Vec::new();
73
+ let mut nodes = vec![tree.root_node()];
74
+ while let Some(node) = nodes.pop() {
75
+ if node.kind() == "syntax_issue" {
76
+ let outcome = node.named_child(0).expect("issue must have an outcome");
77
+ let reason = outcome.named_child(0).expect("issue must have a reason");
78
+ issues.push((
79
+ outcome.kind().to_owned(),
80
+ reason.kind().to_owned(),
81
+ node.range(),
82
+ ));
83
+ }
84
+ for index in (0..node.named_child_count()).rev() {
85
+ nodes.push(
86
+ node.named_child(index as u32)
87
+ .expect("named child must exist"),
88
+ );
89
+ }
90
+ }
91
+ issues
92
+ }
93
+
94
+ #[test]
95
+ fn invalid_byte_issues_preserve_classification_and_ranges_through_edits() {
96
+ use tree_sitter::{InputEdit, Point, Range};
97
+
98
+ for (language, mode) in [(super::LANGUAGE, "BRE"), (super::LANGUAGE_ERE, "ERE")] {
99
+ let mut parser = tree_sitter::Parser::new();
100
+ parser
101
+ .set_language(&language.into())
102
+ .expect("generated grammar must load");
103
+ for (name, source, start, end, row, column, reason) in [
104
+ (
105
+ "first function",
106
+ &b"p\nq\n"[..],
107
+ 0,
108
+ 1,
109
+ 0,
110
+ 0,
111
+ "unknown_function",
112
+ ),
113
+ (
114
+ "function after newline",
115
+ &b"p\np\nq\n"[..],
116
+ 2,
117
+ 3,
118
+ 1,
119
+ 0,
120
+ "unknown_function",
121
+ ),
122
+ (
123
+ "substitute delimiter",
124
+ &b"p\ns///\nq\n"[..],
125
+ 3,
126
+ 6,
127
+ 1,
128
+ 1,
129
+ "invalid_delimiter",
130
+ ),
131
+ (
132
+ "translate delimiter",
133
+ &b"p\ny///\nq\n"[..],
134
+ 3,
135
+ 6,
136
+ 1,
137
+ 1,
138
+ "invalid_delimiter",
139
+ ),
140
+ (
141
+ "address delimiter",
142
+ &b"p\n\\##p\nq\n"[..],
143
+ 3,
144
+ 5,
145
+ 1,
146
+ 1,
147
+ "invalid_delimiter",
148
+ ),
149
+ (
150
+ "trailing command text",
151
+ &b"p\np\nq\n"[..],
152
+ 3,
153
+ 3,
154
+ 1,
155
+ 1,
156
+ "unexpected_command_text",
157
+ ),
158
+ ] {
159
+ for invalid_byte in [0, 0xff] {
160
+ let mut damaged = source.to_vec();
161
+ damaged.splice(start..end, [invalid_byte]);
162
+ let mut tree = parser
163
+ .parse(source, None)
164
+ .expect("parser must return a tree");
165
+ assert!(!tree.root_node().has_error(), "{mode}, {name}");
166
+ assert!(issue_signatures(&tree).is_empty(), "{mode}, {name}");
167
+ for damage in [true, false, true] {
168
+ let (next, old_length, new_length) = if damage {
169
+ (damaged.as_slice(), end - start, 1)
170
+ } else {
171
+ (source, 1, end - start)
172
+ };
173
+ tree.edit(&InputEdit {
174
+ start_byte: start,
175
+ old_end_byte: start + old_length,
176
+ new_end_byte: start + new_length,
177
+ start_position: Point::new(row, column),
178
+ old_end_position: Point::new(row, column + old_length),
179
+ new_end_position: Point::new(row, column + new_length),
180
+ });
181
+ let incremental =
182
+ parser.parse(next, Some(&tree)).expect("incremental tree");
183
+ let fresh = parser.parse(next, None).expect("fresh tree");
184
+ let mut expected = Vec::new();
185
+ if damage {
186
+ let range = Range {
187
+ start_byte: start,
188
+ end_byte: start + 1,
189
+ start_point: Point::new(row, column),
190
+ end_point: Point::new(row, column + 1),
191
+ };
192
+ expected.push((
193
+ "nonconforming_syntax".to_owned(),
194
+ reason.to_owned(),
195
+ range,
196
+ ));
197
+ if invalid_byte == 0 && reason == "unexpected_command_text" {
198
+ expected.push((
199
+ "nonconforming_syntax".to_owned(),
200
+ "nul_character".to_owned(),
201
+ range,
202
+ ));
203
+ }
204
+ }
205
+ let context =
206
+ format!("{mode}, {name}, byte {invalid_byte:#x}, damage {damage}");
207
+ assert_eq!(issue_signatures(&fresh), expected, "fresh: {context}");
208
+ assert_eq!(
209
+ issue_signatures(&incremental),
210
+ expected,
211
+ "incremental: {context}"
212
+ );
213
+ if !damage {
214
+ assert!(!incremental.root_node().has_error(), "{context}");
215
+ assert_eq!(
216
+ incremental.root_node().to_sexp(),
217
+ fresh.root_node().to_sexp(),
218
+ "{context}"
219
+ );
220
+ }
221
+ tree = incremental;
222
+ }
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ #[test]
229
+ fn invalid_bytes_inside_intervals_and_escapes_remain_visible_through_edits() {
230
+ use tree_sitter::{InputEdit, Point, Range};
231
+
232
+ for (language, mode, interval, count_start) in [
233
+ (super::LANGUAGE, "BRE", &b"/a\\{1,2\\}/p\n"[..], 6),
234
+ (super::LANGUAGE_ERE, "ERE", &b"/a{1,2}/p\n"[..], 5),
235
+ ] {
236
+ let mut parser = tree_sitter::Parser::new();
237
+ parser
238
+ .set_language(&language.into())
239
+ .expect("generated grammar must load");
240
+ for (name, source, start, prefix, reasons) in [
241
+ (
242
+ "malformed interval",
243
+ interval,
244
+ count_start,
245
+ &b"x"[..],
246
+ &[
247
+ ("undefined_syntax", "malformed_interval", 0, 2),
248
+ (
249
+ "invalid_syntax",
250
+ "invalid_regular_expression_character",
251
+ 1,
252
+ 2,
253
+ ),
254
+ ][..],
255
+ ),
256
+ (
257
+ "escaped invalid character",
258
+ &b"/\\*/p\n"[..],
259
+ 2,
260
+ &b""[..],
261
+ &[(
262
+ "invalid_syntax",
263
+ "invalid_regular_expression_character",
264
+ 0,
265
+ 1,
266
+ )][..],
267
+ ),
268
+ ] {
269
+ for invalid_byte in [0, 0xff] {
270
+ let replacement = [prefix, &[invalid_byte]].concat();
271
+ let mut damaged = source.to_vec();
272
+ damaged.splice(start..start + 1, replacement.iter().copied());
273
+ let expected: Vec<_> = reasons
274
+ .iter()
275
+ .map(|&(outcome, reason, first, last)| {
276
+ (
277
+ outcome.to_owned(),
278
+ reason.to_owned(),
279
+ Range {
280
+ start_byte: start + first,
281
+ end_byte: start + last,
282
+ start_point: Point::new(0, start + first),
283
+ end_point: Point::new(0, start + last),
284
+ },
285
+ )
286
+ })
287
+ .collect();
288
+ let mut tree = parser.parse(source, None).expect("initial tree");
289
+ for damage in [true, false, true] {
290
+ let (next, old_length, new_length) = if damage {
291
+ (damaged.as_slice(), 1, replacement.len())
292
+ } else {
293
+ (source, replacement.len(), 1)
294
+ };
295
+ tree.edit(&InputEdit {
296
+ start_byte: start,
297
+ old_end_byte: start + old_length,
298
+ new_end_byte: start + new_length,
299
+ start_position: Point::new(0, start),
300
+ old_end_position: Point::new(0, start + old_length),
301
+ new_end_position: Point::new(0, start + new_length),
302
+ });
303
+ let incremental = parser.parse(next, Some(&tree)).expect("edited tree");
304
+ let fresh = parser.parse(next, None).expect("fresh tree");
305
+ let wanted = if damage { expected.as_slice() } else { &[] };
306
+ let context =
307
+ format!("{mode}, {name}, byte {invalid_byte:#x}, damage {damage}");
308
+ for parsed in [&fresh, &incremental] {
309
+ assert_eq!(issue_signatures(parsed), wanted, "{context}");
310
+ if damage && name == "malformed interval" {
311
+ let mut nodes = vec![parsed.root_node()];
312
+ let mut nested = false;
313
+ while let Some(node) = nodes.pop() {
314
+ if node.kind() == "malformed_interval" {
315
+ nested = node.to_sexp().contains(
316
+ "(syntax_issue (invalid_syntax (invalid_regular_expression_character",
317
+ );
318
+ }
319
+ for index in 0..node.named_child_count() {
320
+ nodes.push(node.named_child(index as u32).unwrap());
321
+ }
322
+ }
323
+ assert!(
324
+ nested,
325
+ "{context}: invalid byte must belong to the interval issue"
326
+ );
327
+ }
328
+ }
329
+ if !damage {
330
+ assert!(!incremental.root_node().has_error(), "{context}");
331
+ assert_eq!(
332
+ incremental.root_node().to_sexp(),
333
+ fresh.root_node().to_sexp(),
334
+ "{context}"
335
+ );
336
+ }
337
+ tree = incremental;
338
+ }
339
+ }
340
+ }
341
+ }
342
+ }
343
+
344
+ #[test]
345
+ fn literal_ranges_remain_stable_when_edits_split_multibyte_delimiters() {
346
+ use tree_sitter::{InputEdit, Point};
347
+
348
+ for (language, mode) in [(super::LANGUAGE, "BRE"), (super::LANGUAGE_ERE, "ERE")] {
349
+ let mut parser = tree_sitter::Parser::new();
350
+ parser
351
+ .set_language(&language.into())
352
+ .expect("generated grammar must load");
353
+ for (source, edited, positions, literal, expected) in [
354
+ (
355
+ "séaébé",
356
+ "séaébèé",
357
+ [8, 9, 11],
358
+ "replacement_literal",
359
+ &[(6, 9)][..],
360
+ ),
361
+ (
362
+ "yéaébé",
363
+ "yéaébèé",
364
+ [8, 9, 11],
365
+ "translation_literal",
366
+ &[(3, 4), (6, 9)][..],
367
+ ),
368
+ (
369
+ "s🙂a🙂b🙂",
370
+ "s🙂a🙂b🙁🙂",
371
+ [14, 15, 19],
372
+ "replacement_literal",
373
+ &[(10, 15)][..],
374
+ ),
375
+ (
376
+ "y🙂a🙂b🙂",
377
+ "y🙂a🙂b🙁🙂",
378
+ [14, 15, 19],
379
+ "translation_literal",
380
+ &[(5, 6), (10, 15)][..],
381
+ ),
382
+ ] {
383
+ let [start, old_end, new_end] = positions;
384
+ assert_eq!(&source.as_bytes()[..start], &edited.as_bytes()[..start]);
385
+ assert_eq!(&source.as_bytes()[old_end..], &edited.as_bytes()[new_end..]);
386
+ let mut tree = parser.parse(source, None).expect("initial tree");
387
+ assert!(!tree.root_node().has_error());
388
+ assert!(issue_signatures(&tree).is_empty());
389
+ tree.edit(&InputEdit {
390
+ start_byte: start,
391
+ old_end_byte: old_end,
392
+ new_end_byte: new_end,
393
+ start_position: Point::new(0, start),
394
+ old_end_position: Point::new(0, old_end),
395
+ new_end_position: Point::new(0, new_end),
396
+ });
397
+ let incremental = parser.parse(edited, Some(&tree)).expect("edited tree");
398
+ let fresh = parser.parse(edited, None).expect("fresh tree");
399
+ for (parse_kind, parsed) in [("fresh", &fresh), ("incremental", &incremental)] {
400
+ let context = format!("{mode}, {parse_kind}, {source:?} -> {edited:?}");
401
+ assert!(!parsed.root_node().has_error(), "{context}");
402
+ assert!(issue_signatures(parsed).is_empty(), "{context}");
403
+ let mut ranges = Vec::new();
404
+ let mut nodes = vec![parsed.root_node()];
405
+ while let Some(node) = nodes.pop() {
406
+ if node.kind() == literal {
407
+ ranges.push((node.start_byte(), node.end_byte()));
408
+ assert_eq!(node.start_position(), Point::new(0, node.start_byte()));
409
+ assert_eq!(node.end_position(), Point::new(0, node.end_byte()));
410
+ }
411
+ for index in (0..node.named_child_count()).rev() {
412
+ nodes.push(node.named_child(index as u32).unwrap());
413
+ }
414
+ }
415
+ assert_eq!(ranges, expected, "{context}");
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ #[test]
422
+ fn completed_groups_replace_recovery_after_multibyte_delimiter_edits() {
423
+ use tree_sitter::{InputEdit, Point};
424
+
425
+ for (language, source, edited, edit_start, group_kind, boundaries) in [
426
+ (
427
+ super::LANGUAGE,
428
+ r"sé\(é",
429
+ r"sé\(è\)éé",
430
+ 6,
431
+ "nondupl_bre",
432
+ [3, 5, 7, 9],
433
+ ),
434
+ (
435
+ super::LANGUAGE_ERE,
436
+ "sé(é",
437
+ "sé(è)éé",
438
+ 5,
439
+ "ere_expression",
440
+ [3, 4, 6, 7],
441
+ ),
442
+ (
443
+ super::LANGUAGE,
444
+ r"s🙂\(🙂",
445
+ r"s🙂\(🙁\)🙂🙂",
446
+ 10,
447
+ "nondupl_bre",
448
+ [5, 7, 11, 13],
449
+ ),
450
+ (
451
+ super::LANGUAGE_ERE,
452
+ "s🙂(🙂",
453
+ "s🙂(🙁)🙂🙂",
454
+ 9,
455
+ "ere_expression",
456
+ [5, 6, 10, 11],
457
+ ),
458
+ ] {
459
+ let mut parser = tree_sitter::Parser::new();
460
+ parser.set_language(&language.into()).unwrap();
461
+ let mut tree = parser.parse(source, None).unwrap();
462
+ assert!(!issue_signatures(&tree).is_empty());
463
+ assert_eq!(
464
+ &source.as_bytes()[..edit_start],
465
+ &edited.as_bytes()[..edit_start]
466
+ );
467
+ tree.edit(&InputEdit {
468
+ start_byte: edit_start,
469
+ old_end_byte: source.len(),
470
+ new_end_byte: edited.len(),
471
+ start_position: Point::new(0, edit_start),
472
+ old_end_position: Point::new(0, source.len()),
473
+ new_end_position: Point::new(0, edited.len()),
474
+ });
475
+ let incremental = parser.parse(edited, Some(&tree)).unwrap();
476
+ let fresh = parser.parse(edited, None).unwrap();
477
+ for (kind, parsed) in [("fresh", &fresh), ("incremental", &incremental)] {
478
+ let context = format!("{kind}: {source:?} -> {edited:?}");
479
+ assert!(!parsed.root_node().has_error(), "{context}");
480
+ assert!(issue_signatures(parsed).is_empty(), "{context}");
481
+ let mut nodes = vec![parsed.root_node()];
482
+ let mut groups = Vec::new();
483
+ while let Some(node) = nodes.pop() {
484
+ if node.kind() == group_kind && node.child_by_field_name("opening").is_some() {
485
+ groups.push(node);
486
+ }
487
+ for index in (0..node.named_child_count()).rev() {
488
+ nodes.push(node.named_child(index as u32).unwrap());
489
+ }
490
+ }
491
+ assert_eq!(groups.len(), 1, "{context}");
492
+ let [start, content, closing, end] = boundaries;
493
+ let group = groups[0];
494
+ assert_eq!(group.byte_range(), start..end, "{context}");
495
+ for (field, range) in [
496
+ ("opening", start..content),
497
+ ("expression", content..closing),
498
+ ("closing", closing..end),
499
+ ] {
500
+ let child = group.child_by_field_name(field).unwrap();
501
+ assert_eq!(child.byte_range(), range, "{field}: {context}");
502
+ assert_eq!(child.start_position(), Point::new(0, range.start));
503
+ assert_eq!(child.end_position(), Point::new(0, range.end));
504
+ }
505
+ }
506
+ assert_eq!(
507
+ incremental.root_node().to_sexp(),
508
+ fresh.root_node().to_sexp()
509
+ );
510
+ }
511
+ }
512
+
513
+ #[test]
514
+ fn balanced_regex_groups_preserve_depth_across_the_16_bit_boundary() {
515
+ for (language, mode, opening, closing, opening_kind, closing_kind) in [
516
+ (
517
+ super::LANGUAGE,
518
+ "BRE",
519
+ "\\(",
520
+ "\\)",
521
+ "back_open_parenthesis",
522
+ "back_close_parenthesis_token",
523
+ ),
524
+ (
525
+ super::LANGUAGE_ERE,
526
+ "ERE",
527
+ "(",
528
+ ")",
529
+ "open_parenthesis",
530
+ "close_parenthesis_token",
531
+ ),
532
+ ] {
533
+ let mut parser = tree_sitter::Parser::new();
534
+ parser
535
+ .set_language(&language.into())
536
+ .expect("generated grammar must load");
537
+ for depth in [65_535, 65_536] {
538
+ let source = format!("/{}a{}/p\n", opening.repeat(depth), closing.repeat(depth));
539
+ let tree = parser
540
+ .parse(&source, None)
541
+ .expect("parser must return a tree");
542
+ let root = tree.root_node();
543
+ assert_eq!(root.kind(), "script", "{mode}, depth {depth}");
544
+ assert_eq!(root.byte_range(), 0..source.len(), "{mode}, depth {depth}");
545
+ assert!(!root.has_error(), "{mode}, depth {depth}");
546
+
547
+ let mut cursor = root.walk();
548
+ let mut openings = 0;
549
+ let mut closings = 0;
550
+ 'walk: loop {
551
+ let node = cursor.node();
552
+ assert_ne!(node.kind(), "syntax_issue", "{mode}, depth {depth}");
553
+ if node.kind() == opening_kind {
554
+ openings += 1;
555
+ } else if node.kind() == closing_kind {
556
+ closings += 1;
557
+ }
558
+ if cursor.goto_first_child() {
559
+ continue;
560
+ }
561
+ while !cursor.goto_next_sibling() {
562
+ if !cursor.goto_parent() {
563
+ break 'walk;
564
+ }
565
+ }
566
+ }
567
+ assert_eq!(openings, depth, "{mode}, depth {depth}");
568
+ assert_eq!(closings, depth, "{mode}, depth {depth}");
569
+ }
570
+ }
571
+ }
572
+ }