Yeast: Fix remaining review issues (#8-#10)

#8: Reject * after non-capture template groups with a compile error.
Previously (foo (bar)*) silently dropped the *, behaving like (bar).

#9: Verify inner token streams are exhausted after parsing query nodes.
Unconsumed tokens inside a parenthesized group now produce a compile
error. Fixed a test using the old redundant (pattern)* syntax inside
a field*: group.

#10: Use ast.get_root() instead of hardcoded 0 for the root node id
in apply_rules calls.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Taus
2026-05-01 15:28:12 +00:00
parent 25155298c7
commit f5a41c09cb
3 changed files with 14 additions and 6 deletions

View File

@@ -46,7 +46,11 @@ fn parse_query_atom(tokens: &mut Tokens) -> Result<TokenStream> {
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => {
let group = expect_group(tokens, Delimiter::Parenthesis)?;
let mut inner = group.stream().into_iter().peekable();
parse_query_node_inner(&mut inner)
let result = parse_query_node_inner(&mut inner)?;
if let Some(tok) = inner.next() {
return Err(syn::Error::new_spanned(tok, "unexpected token in query node"));
}
Ok(result)
}
Some(tok) => Err(syn::Error::new_spanned(
tok.clone(),
@@ -348,8 +352,10 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result<TokenStre
__children.extend(#ctx.capture_all(#name_str));
});
} else {
let node = parse_direct_node_inner(&mut inner, ctx)?;
child_stmts.push(quote! { __children.push(#node); });
return Err(syn::Error::new(
Span::call_site(),
"* after a non-capture group is not supported in tree templates; use (@name)* to splice a repeated capture",
));
}
has_children = true;
continue;

View File

@@ -610,7 +610,8 @@ impl Runner {
pub fn run_from_tree(&self, tree: &tree_sitter::Tree) -> Result<Ast, String> {
let fresh = tree_builder::FreshScope::new();
let mut ast = Ast::from_tree(self.language.clone(), tree);
let res = apply_rules(&self.rules, &mut ast, 0, &fresh)?;
let root = ast.get_root();
let res = apply_rules(&self.rules, &mut ast, root, &fresh)?;
if res.len() != 1 {
return Err(format!("Expected exactly one result node, got {}", res.len()));
}
@@ -626,7 +627,8 @@ impl Runner {
let tree = parser.parse(input, None)
.ok_or_else(|| "Failed to parse input".to_string())?;
let mut ast = Ast::from_tree(self.language.clone(), &tree);
let res = apply_rules(&self.rules, &mut ast, 0, &fresh)?;
let root = ast.get_root();
let res = apply_rules(&self.rules, &mut ast, root, &fresh)?;
if res.len() != 1 {
return Err(format!("Expected exactly one result node, got {}", res.len()));
}

View File

@@ -200,7 +200,7 @@ mod tests {
let query9: QueryNode = yeast::query!(
(assignment
left: (element_reference
object*: ((_) @obj)*
object*: ((_) @obj)
(_) @index
)
right: (_) @rhs