@@ -38,7 +38,8 @@ fn parse_query_node(tokens: &mut Tokens) -> Result<TokenStream> {
3838 }
3939}
4040
41- /// Parse a query atom: `(kind fields...)` or `(kind fields... bare_children...)`.
41+ /// Parse a query atom: a parenthesized node, a bare `_` (any node), or a
42+ /// bare string literal (unnamed token).
4243/// Does not handle `@capture` — that's handled by the caller as a postfix.
4344fn parse_query_atom ( tokens : & mut Tokens ) -> Result < TokenStream > {
4445 match tokens. peek ( ) {
@@ -58,9 +59,17 @@ fn parse_query_atom(tokens: &mut Tokens) -> Result<TokenStream> {
5859 }
5960 Ok ( result)
6061 }
62+ Some ( TokenTree :: Ident ( id) ) if * id == "_" => {
63+ tokens. next ( ) ;
64+ Ok ( quote ! { yeast:: query:: QueryNode :: Any { match_unnamed: true } } )
65+ }
66+ Some ( TokenTree :: Literal ( _) ) => {
67+ let lit = expect_literal ( tokens) ?;
68+ Ok ( quote ! { yeast:: query:: QueryNode :: UnnamedNode { kind: #lit } } )
69+ }
6170 Some ( tok) => Err ( syn:: Error :: new_spanned (
6271 tok. clone ( ) ,
63- "expected `(` in query; use `(_) @name` to capture a wildcard " ,
72+ "expected `(`, `_`, or string literal in query " ,
6473 ) ) ,
6574 }
6675}
@@ -74,7 +83,7 @@ fn parse_query_node_inner(tokens: &mut Tokens) -> Result<TokenStream> {
7483 ) ) ,
7584 Some ( TokenTree :: Ident ( id) ) if * id == "_" => {
7685 tokens. next ( ) ;
77- Ok ( quote ! { yeast:: query:: QueryNode :: Any ( ) } )
86+ Ok ( quote ! { yeast:: query:: QueryNode :: Any { match_unnamed : false } } )
7887 }
7988 Some ( TokenTree :: Literal ( _) ) => {
8089 let lit = expect_literal ( tokens) ?;
@@ -98,11 +107,14 @@ fn parse_query_node_inner(tokens: &mut Tokens) -> Result<TokenStream> {
98107 }
99108}
100109
101- /// Parse zero or more field specifications and trailing bare patterns.
102- /// Named fields: `name: pattern` or `name*: (list...)`.
103- /// Bare patterns (no field name) become implicit `child` field entries.
110+ /// Parse zero or more field specifications and bare patterns.
111+ /// Named fields: `name: pattern`. Bare patterns (no field name) become
112+ /// implicit `child` field entries. Named fields and bare patterns may
113+ /// appear in any order; bare patterns are accumulated and emitted as a
114+ /// single `("child", ...)` entry.
104115fn parse_query_fields ( tokens : & mut Tokens ) -> Result < Vec < TokenStream > > {
105116 let mut fields = Vec :: new ( ) ;
117+ let mut bare_children: Vec < TokenStream > = Vec :: new ( ) ;
106118 while tokens. peek ( ) . is_some ( ) {
107119 if peek_is_field ( tokens) {
108120 let field_name = expect_ident ( tokens, "expected field name" ) ?;
@@ -115,16 +127,21 @@ fn parse_query_fields(tokens: &mut Tokens) -> Result<Vec<TokenStream>> {
115127 ( #field_str, vec![ yeast:: query:: QueryListElem :: SingleNode ( #child) ] )
116128 } ) ;
117129 } else {
118- // Bare patterns — collect as implicit `child` field
130+ // Bare patterns — accumulate into the implicit `child` field.
131+ // We don't break here, so we can interleave with named fields.
119132 let elems = parse_query_list ( tokens) ?;
120- if !elems. is_empty ( ) {
121- fields. push ( quote ! {
122- ( "child" , vec![ #( #elems) , * ] )
123- } ) ;
133+ if elems. is_empty ( ) {
134+ // Nothing more we can parse at this level.
135+ break ;
124136 }
125- break ;
137+ bare_children . extend ( elems ) ;
126138 }
127139 }
140+ if !bare_children. is_empty ( ) {
141+ fields. push ( quote ! {
142+ ( "child" , vec![ #( #bare_children) , * ] )
143+ } ) ;
144+ }
128145 Ok ( fields)
129146}
130147
@@ -178,10 +195,11 @@ fn parse_query_list(tokens: &mut Tokens) -> Result<Vec<TokenStream>> {
178195 continue ;
179196 }
180197
181- // Check for string literal (unnamed node)
198+ // Check for string literal (unnamed node), optionally followed by @capture
182199 if peek_is_literal ( tokens) {
183200 let lit = expect_literal ( tokens) ?;
184201 let node = quote ! { yeast:: query:: QueryNode :: UnnamedNode { kind: #lit } } ;
202+ let node = maybe_wrap_capture ( tokens, node) ?;
185203 let elem = maybe_wrap_repetition (
186204 tokens,
187205 quote ! {
@@ -192,10 +210,12 @@ fn parse_query_list(tokens: &mut Tokens) -> Result<Vec<TokenStream>> {
192210 continue ;
193211 }
194212
195- // Check for bare _ (wildcard), possibly followed by @capture
213+ // Check for bare `_` (any node, named or unnamed), possibly followed by @capture.
214+ // Distinct from `(_)` which only matches named nodes — this matches
215+ // tree-sitter query semantics.
196216 if peek_is_underscore ( tokens) {
197217 tokens. next ( ) ;
198- let node = quote ! { yeast:: query:: QueryNode :: Any ( ) } ;
218+ let node = quote ! { yeast:: query:: QueryNode :: Any { match_unnamed : true } } ;
199219 let node = maybe_wrap_capture ( tokens, node) ?;
200220 let elem = maybe_wrap_repetition (
201221 tokens,
0 commit comments