Skip to content

Commit ddd1bd8

Browse files
committed
yeast: Test query choice behavior
1 parent c9c0bfd commit ddd1bd8

1 file changed

Lines changed: 221 additions & 0 deletions

File tree

‎shared/yeast/tests/test.rs‎

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,96 @@ fn test_query_no_match() {
415415
assert!(!matched);
416416
}
417417

418+
#[test]
419+
fn test_query_choice_checks_presence() {
420+
for input in ["x = 1", "x = y"] {
421+
let ast = parse_input(input);
422+
let mut cursor = AstCursor::new(&ast);
423+
cursor.goto_first_child();
424+
let assignment_id = cursor.node_id();
425+
426+
let query = yeast::query!(
427+
(assignment
428+
right: [
429+
(integer)
430+
(identifier)
431+
]
432+
)
433+
);
434+
let mut captures = yeast::captures::Captures::new();
435+
assert!(
436+
query.do_match(&ast, assignment_id, &mut captures).unwrap(),
437+
"query choice should match {input}"
438+
);
439+
}
440+
}
441+
442+
#[test]
443+
fn test_repeated_query_choice_accumulates_present_captures() {
444+
let ast = parse_input("[x, 1, y]");
445+
446+
let mut cursor = AstCursor::new(&ast);
447+
cursor.goto_first_child();
448+
let array_id = cursor.node_id();
449+
450+
let query = yeast::query!(
451+
(array
452+
[
453+
(identifier) @names
454+
(integer)
455+
]*
456+
)
457+
);
458+
let mut captures = yeast::captures::Captures::new();
459+
assert!(query.do_match(&ast, array_id, &mut captures).unwrap());
460+
assert_eq!(captures.get_all("names").len(), 2);
461+
}
462+
463+
#[test]
464+
fn test_query_choice_accepts_quantified_alternative() {
465+
let ast = parse_input("[x, y]");
466+
467+
let mut cursor = AstCursor::new(&ast);
468+
cursor.goto_first_child();
469+
let array_id = cursor.node_id();
470+
471+
let query = yeast::query!(
472+
(array
473+
[
474+
(identifier)+ @names
475+
(integer)
476+
]
477+
)
478+
);
479+
let mut captures = yeast::captures::Captures::new();
480+
assert!(query.do_match(&ast, array_id, &mut captures).unwrap());
481+
assert_eq!(captures.get_all("names").len(), 2);
482+
}
483+
484+
#[test]
485+
fn test_query_sequence_can_start_with_choice() {
486+
let ast = parse_input("[x, 1]");
487+
488+
let mut cursor = AstCursor::new(&ast);
489+
cursor.goto_first_child();
490+
let array_id = cursor.node_id();
491+
492+
let query = yeast::query!(
493+
(array
494+
(
495+
[
496+
(identifier) @value
497+
(integer) @value
498+
]
499+
","
500+
)
501+
)
502+
);
503+
let mut captures = yeast::captures::Captures::new();
504+
assert!(query.do_match(&ast, array_id, &mut captures).unwrap());
505+
assert_eq!(captures.get_all("value").len(), 1);
506+
}
507+
418508
#[test]
419509
fn test_query_skips_extras_in_positional_match() {
420510
// Regression test: positional wildcards `(_)` must not bind to
@@ -1673,6 +1763,137 @@ fn test_raw_capture_marker_explicit_translate() {
16731763
);
16741764
}
16751765

1766+
#[test]
1767+
fn test_query_choice_makes_branch_specific_captures_optional() {
1768+
fn rules() -> Vec<Rule> {
1769+
vec![
1770+
yeast::rule!(
1771+
(program (_)* @stmts)
1772+
=>
1773+
(program stmt: {stmts})
1774+
),
1775+
yeast::rule!(
1776+
(assignment
1777+
left: (_) @left
1778+
right: [
1779+
(integer) @number
1780+
(identifier) @@name
1781+
])
1782+
=>
1783+
assignment {
1784+
let right = match (number, name) {
1785+
(Some(number), None) => number,
1786+
(None, Some(name)) => {
1787+
let spelling = ctx.source_text(name);
1788+
tree!((identifier #{spelling}))
1789+
}
1790+
_ => return Err("exactly one alternative should match".to_string()),
1791+
};
1792+
tree!((assignment left: {left} right: {right}))
1793+
}
1794+
),
1795+
yeast::rule!((identifier) => (identifier "ID")),
1796+
yeast::rule!((integer) => (integer "INT")),
1797+
]
1798+
}
1799+
1800+
let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into();
1801+
let schema =
1802+
yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap();
1803+
let phases = vec![Phase::new("translate", rules())];
1804+
let runner: Runner = Runner::with_schema(lang, &schema, &phases);
1805+
1806+
for (input, expected_right) in [("x = 1", "integer \"INT\""), ("x = y", "identifier \"y\"")] {
1807+
let ast = runner.run(input).unwrap();
1808+
let dump = dump_ast(&ast, ast.get_root(), input);
1809+
assert!(
1810+
dump.contains(expected_right),
1811+
"unexpected dump for {input}: {dump}"
1812+
);
1813+
}
1814+
}
1815+
1816+
#[test]
1817+
fn test_query_choice_keeps_shared_capture_required() {
1818+
fn rules() -> Vec<Rule> {
1819+
vec![
1820+
yeast::rule!(
1821+
(program (_)* @stmts)
1822+
=>
1823+
(program stmt: {stmts})
1824+
),
1825+
yeast::rule!(
1826+
(assignment
1827+
left: (_) @left
1828+
right: [
1829+
(integer) @value
1830+
(identifier) @value
1831+
])
1832+
=>
1833+
assignment {
1834+
let value: yeast::OutputId = value;
1835+
tree!((assignment left: {left} right: {value}))
1836+
}
1837+
),
1838+
yeast::rule!((identifier) => (identifier "ID")),
1839+
yeast::rule!((integer) => (integer "INT")),
1840+
]
1841+
}
1842+
1843+
let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into();
1844+
let schema =
1845+
yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap();
1846+
let phases = vec![Phase::new("translate", rules())];
1847+
let runner: Runner = Runner::with_schema(lang, &schema, &phases);
1848+
1849+
for input in ["x = 1", "x = y"] {
1850+
runner.run(input).unwrap();
1851+
}
1852+
}
1853+
1854+
#[test]
1855+
fn test_query_choice_promotes_mixed_multiplicity_to_repeated() {
1856+
let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into();
1857+
let schema =
1858+
yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap();
1859+
let rules: Vec<Rule> = vec![
1860+
yeast::rule!(
1861+
(program (_)* @stmts)
1862+
=>
1863+
(program stmt: {stmts})
1864+
),
1865+
yeast::rule!(
1866+
(assignment
1867+
left: [
1868+
(identifier) @values
1869+
(left_assignment_list (identifier)* @values)
1870+
]
1871+
right: (_) @right)
1872+
=>
1873+
assignment {
1874+
let values: Vec<yeast::OutputId> = values;
1875+
tree!((assignment
1876+
left: (left_assignment_list item: {values})
1877+
right: {right}))
1878+
}
1879+
),
1880+
yeast::rule!((identifier) => (identifier "ID")),
1881+
yeast::rule!((integer) => (integer "INT")),
1882+
];
1883+
let phases = vec![Phase::new("translate", rules)];
1884+
let runner: Runner = Runner::with_schema(lang, &schema, &phases);
1885+
1886+
for (input, expected_count) in [("x = 1", 1), ("x, y = 1", 2)] {
1887+
let ast = runner.run(input).unwrap();
1888+
let dump = dump_ast(&ast, ast.get_root(), input);
1889+
assert_eq!(
1890+
dump.matches("identifier \"ID\"").count(),
1891+
expected_count,
1892+
"unexpected dump for {input}: {dump}"
1893+
);
1894+
}
1895+
}
1896+
16761897
// ---- Cursor tests ----
16771898

16781899
#[test]

0 commit comments

Comments
 (0)