@@ -7,19 +7,6 @@ use crate::{
7
7
value:: ScalarValue ,
8
8
} ;
9
9
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
-
23
10
pub fn factory < ' a > ( ) -> NoFragmentCycles < ' a > {
24
11
NoFragmentCycles {
25
12
current_fragment : None ,
@@ -28,6 +15,12 @@ pub fn factory<'a>() -> NoFragmentCycles<'a> {
28
15
}
29
16
}
30
17
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
+
31
24
impl < ' a , S > Visitor < ' a , S > for NoFragmentCycles < ' a >
32
25
where
33
26
S : ScalarValue ,
@@ -38,14 +31,12 @@ where
38
31
let mut detector = CycleDetector {
39
32
visited : HashSet :: new ( ) ,
40
33
spreads : & self . spreads ,
41
- path_indices : HashMap :: new ( ) ,
42
34
errors : Vec :: new ( ) ,
43
35
} ;
44
36
45
37
for frag in & self . fragment_order {
46
38
if !detector. visited . contains ( frag) {
47
- let mut path = Vec :: new ( ) ;
48
- detector. detect_from ( frag, & mut path) ;
39
+ detector. detect_from ( frag) ;
49
40
}
50
41
}
51
42
@@ -91,19 +82,46 @@ where
91
82
}
92
83
}
93
84
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
+
94
93
impl < ' 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 > > {
96
113
self . visited . insert ( from) ;
97
114
98
115
if !self . spreads . contains_key ( from) {
99
- return ;
116
+ return Vec :: new ( ) ;
100
117
}
101
118
102
- self . path_indices . insert ( from, path. len ( ) ) ;
119
+ path_indices. insert ( from, path. len ( ) ) ;
103
120
121
+ let mut to_visit = Vec :: new ( ) ;
104
122
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 ( ) ;
107
125
108
126
if let Some ( index) = index {
109
127
let err_pos = if index < path. len ( ) {
@@ -114,14 +132,14 @@ impl<'a> CycleDetector<'a> {
114
132
115
133
self . errors
116
134
. push ( RuleError :: new ( & error_message ( name) , & [ err_pos. start ] ) ) ;
117
- } else if !self . visited . contains ( name) {
135
+ } else {
136
+ let mut path = path. clone ( ) ;
118
137
path. push ( node) ;
119
- self . detect_from ( name, path) ;
120
- path. pop ( ) ;
138
+ to_visit. push ( ( name, path, path_indices. clone ( ) ) ) ;
121
139
}
122
140
}
123
141
124
- self . path_indices . remove ( from ) ;
142
+ to_visit
125
143
}
126
144
}
127
145
0 commit comments