@@ -16,20 +16,42 @@ pub enum QueryNode {
1616 UnnamedNode {
1717 kind : & ' static str ,
1818 } ,
19+ Choice {
20+ alternatives : Vec < QueryNode > ,
21+ } ,
1922 Capture {
2023 capture : & ' static str ,
2124 node : Box < QueryNode > ,
2225 } ,
2326}
2427
2528impl QueryNode {
26- /// Returns the root node kind this query matches, if it's specific.
27- /// Returns None for wildcards (Any) and captures wrapping wildcards.
29+ /// Returns the root node kind this query matches when it has exactly one.
30+ /// Choices whose alternatives share a root kind return that kind; wildcards
31+ /// and choices spanning multiple root kinds return `None`.
2832 pub fn root_kind ( & self ) -> Option < & ' static str > {
33+ let kinds = self . root_kinds ( ) ?;
34+ ( kinds. len ( ) == 1 ) . then_some ( kinds[ 0 ] )
35+ }
36+
37+ /// Returns all root node kinds this query can match when they are known.
38+ /// Returns `None` for wildcards and choices containing a wildcard.
39+ pub ( crate ) fn root_kinds ( & self ) -> Option < Vec < & ' static str > > {
2940 match self {
30- QueryNode :: Node { kind, .. } => Some ( kind) ,
31- QueryNode :: UnnamedNode { kind } => Some ( kind) ,
32- QueryNode :: Capture { node, .. } => node. root_kind ( ) ,
41+ QueryNode :: Node { kind, .. } => Some ( vec ! [ kind] ) ,
42+ QueryNode :: UnnamedNode { kind } => Some ( vec ! [ kind] ) ,
43+ QueryNode :: Choice { alternatives } => {
44+ let mut kinds = Vec :: new ( ) ;
45+ for alternative in alternatives {
46+ for kind in alternative. root_kinds ( ) ? {
47+ if !kinds. contains ( & kind) {
48+ kinds. push ( kind) ;
49+ }
50+ }
51+ }
52+ Some ( kinds)
53+ }
54+ QueryNode :: Capture { node, .. } => node. root_kinds ( ) ,
3355 QueryNode :: Any { .. } => None ,
3456 }
3557 }
@@ -41,6 +63,9 @@ pub enum QueryListElem {
4163 children : Vec < QueryListElem > ,
4264 rep : Rep ,
4365 } ,
66+ Choice {
67+ alternatives : Vec < Vec < QueryListElem > > ,
68+ } ,
4469 SingleNode ( QueryNode ) ,
4570}
4671
@@ -60,6 +85,9 @@ impl QueryNode {
6085 QueryNode :: Any { match_unnamed } => !match_unnamed,
6186 QueryNode :: Node { .. } => true ,
6287 QueryNode :: UnnamedNode { .. } => false ,
88+ QueryNode :: Choice { alternatives } => {
89+ alternatives. iter ( ) . all ( QueryNode :: matches_named_only)
90+ }
6391 QueryNode :: Capture { node, .. } => node. matches_named_only ( ) ,
6492 }
6593 }
@@ -105,6 +133,14 @@ impl QueryNode {
105133 . ok_or_else ( || format ! ( "unnamed Node kind {kind} not found in language" ) ) ?;
106134 Ok ( node. kind == target_kind)
107135 }
136+ QueryNode :: Choice { alternatives } => {
137+ if let Some ( captures) = match_choice ( alternatives, ast, node) ? {
138+ matches. merge ( & captures) ;
139+ Ok ( true )
140+ } else {
141+ Ok ( false )
142+ }
143+ }
108144 QueryNode :: Capture {
109145 capture,
110146 node : sub_query,
@@ -119,6 +155,20 @@ impl QueryNode {
119155 }
120156}
121157
158+ fn match_choice (
159+ alternatives : & [ QueryNode ] ,
160+ ast : & Ast ,
161+ node : Id ,
162+ ) -> Result < Option < Captures > , String > {
163+ for alternative in alternatives {
164+ let mut captures = Captures :: new ( ) ;
165+ if alternative. do_match ( ast, node, & mut captures) ? {
166+ return Ok ( Some ( captures) ) ;
167+ }
168+ }
169+ Ok ( None )
170+ }
171+
122172fn match_children < ' a > (
123173 child_matchers : impl Iterator < Item = & ' a QueryListElem > ,
124174 ast : & Ast ,
@@ -177,6 +227,24 @@ impl QueryListElem {
177227 Ok ( true )
178228 }
179229 }
230+ QueryListElem :: Choice { alternatives } => {
231+ let start = remaining_children. clone ( ) ;
232+ for alternative in alternatives {
233+ let mut candidate_children = start. clone ( ) ;
234+ let mut candidate_captures = Captures :: new ( ) ;
235+ if match_children (
236+ alternative. iter ( ) ,
237+ ast,
238+ & mut candidate_children,
239+ & mut candidate_captures,
240+ ) ? {
241+ * remaining_children = candidate_children;
242+ matches. merge ( & candidate_captures) ;
243+ return Ok ( true ) ;
244+ }
245+ }
246+ Ok ( false )
247+ }
180248 QueryListElem :: SingleNode ( sub_query) => {
181249 // Forward-scan semantics: advance through the iterator until
182250 // we find a child that matches `sub_query`. Skip ahead past
0 commit comments