@@ -7,19 +7,6 @@ use crate::{
77 value:: ScalarValue ,
88} ;
99
10- pub struct NoFragmentCycles < ' a > {
11- current_fragment : Option < & ' a str > ,
12- spreads : HashMap < & ' a str , Vec < Spanning < & ' a str > > > ,
13- fragment_order : Vec < & ' a str > ,
14- }
15-
16- struct CycleDetector < ' a > {
17- visited : HashSet < & ' a str > ,
18- spreads : & ' a HashMap < & ' a str , Vec < Spanning < & ' a str > > > ,
19- path_indices : HashMap < & ' a str , usize > ,
20- errors : Vec < RuleError > ,
21- }
22-
2310pub fn factory < ' a > ( ) -> NoFragmentCycles < ' a > {
2411 NoFragmentCycles {
2512 current_fragment : None ,
@@ -28,6 +15,12 @@ pub fn factory<'a>() -> NoFragmentCycles<'a> {
2815 }
2916}
3017
18+ pub struct NoFragmentCycles < ' a > {
19+ current_fragment : Option < & ' a str > ,
20+ spreads : HashMap < & ' a str , Vec < Spanning < & ' a str > > > ,
21+ fragment_order : Vec < & ' a str > ,
22+ }
23+
3124impl < ' a , S > Visitor < ' a , S > for NoFragmentCycles < ' a >
3225where
3326 S : ScalarValue ,
@@ -38,14 +31,12 @@ where
3831 let mut detector = CycleDetector {
3932 visited : HashSet :: new ( ) ,
4033 spreads : & self . spreads ,
41- path_indices : HashMap :: new ( ) ,
4234 errors : Vec :: new ( ) ,
4335 } ;
4436
4537 for frag in & self . fragment_order {
4638 if !detector. visited . contains ( frag) {
47- let mut path = Vec :: new ( ) ;
48- detector. detect_from ( frag, & mut path) ;
39+ detector. detect_from ( frag) ;
4940 }
5041 }
5142
@@ -91,19 +82,46 @@ where
9182 }
9283}
9384
85+ type CycleDetectorState < ' a > = ( & ' a str , Vec < & ' a Spanning < & ' a str > > , HashMap < & ' a str , usize > ) ;
86+
87+ struct CycleDetector < ' a > {
88+ visited : HashSet < & ' a str > ,
89+ spreads : & ' a HashMap < & ' a str , Vec < Spanning < & ' a str > > > ,
90+ errors : Vec < RuleError > ,
91+ }
92+
9493impl < ' a > CycleDetector < ' a > {
95- fn detect_from ( & mut self , from : & ' a str , path : & mut Vec < & ' a Spanning < & ' a str > > ) {
94+ fn detect_from ( & mut self , from : & ' a str ) {
95+ let mut to_visit = Vec :: new ( ) ;
96+ to_visit. push ( ( from, Vec :: new ( ) , HashMap :: new ( ) ) ) ;
97+
98+ while let Some ( ( from, path, path_indices) ) = to_visit. pop ( ) {
99+ to_visit. extend ( self . detect_from_inner ( from, path, path_indices) ) ;
100+ }
101+ }
102+
103+ /// This function should be called only inside [`Self::detect_from()`], as
104+ /// it's a recursive function using heap instead of a stack. So, instead of
105+ /// the recursive call, we return a [`Vec`] that is visited inside
106+ /// [`Self::detect_from()`].
107+ fn detect_from_inner (
108+ & mut self ,
109+ from : & ' a str ,
110+ path : Vec < & ' a Spanning < & ' a str > > ,
111+ mut path_indices : HashMap < & ' a str , usize > ,
112+ ) -> Vec < CycleDetectorState < ' a > > {
96113 self . visited . insert ( from) ;
97114
98115 if !self . spreads . contains_key ( from) {
99- return ;
116+ return Vec :: new ( ) ;
100117 }
101118
102- self . path_indices . insert ( from, path. len ( ) ) ;
119+ path_indices. insert ( from, path. len ( ) ) ;
103120
121+ let mut to_visit = Vec :: new ( ) ;
104122 for node in & self . spreads [ from] {
105- let name = & node. item ;
106- let index = self . path_indices . get ( name) . cloned ( ) ;
123+ let name = node. item ;
124+ let index = path_indices. get ( name) . cloned ( ) ;
107125
108126 if let Some ( index) = index {
109127 let err_pos = if index < path. len ( ) {
@@ -114,14 +132,14 @@ impl<'a> CycleDetector<'a> {
114132
115133 self . errors
116134 . push ( RuleError :: new ( & error_message ( name) , & [ err_pos. start ] ) ) ;
117- } else if !self . visited . contains ( name) {
135+ } else {
136+ let mut path = path. clone ( ) ;
118137 path. push ( node) ;
119- self . detect_from ( name, path) ;
120- path. pop ( ) ;
138+ to_visit. push ( ( name, path, path_indices. clone ( ) ) ) ;
121139 }
122140 }
123141
124- self . path_indices . remove ( from ) ;
142+ to_visit
125143 }
126144}
127145
0 commit comments