@@ -39,6 +39,12 @@ pub struct ProjectGraph {
3939 pub dependencies : HashMap < usize , Vec < usize > > ,
4040}
4141
42+ #[ derive( Debug ) ]
43+ pub enum C3Error {
44+ CycleDetected ( Vec < usize > ) ,
45+ InconsistentLinearization { module : usize } ,
46+ }
47+
4248fn parse_and_get_program ( prog_file : & Path ) -> Result < parse:: Program , String > {
4349 let prog_text = std:: fs:: read_to_string ( prog_file) . map_err ( |e| e. to_string ( ) ) ?;
4450 let file = prog_text. into ( ) ;
@@ -51,6 +57,39 @@ fn parse_and_get_program(prog_file: &Path) -> Result<parse::Program, String> {
5157 }
5258}
5359
60+ // Function used by the C3 algorithm.
61+ fn merge ( mut seqs : Vec < Vec < usize > > ) -> Option < Vec < usize > > {
62+ let mut result = Vec :: new ( ) ;
63+
64+ loop {
65+ seqs. retain ( |s| !s. is_empty ( ) ) ;
66+ if seqs. is_empty ( ) {
67+ return Some ( result) ;
68+ }
69+
70+ let mut candidate = None ;
71+
72+ ' outer: for seq in & seqs {
73+ let head = seq[ 0 ] ;
74+
75+ if seqs. iter ( ) . all ( |s| !s[ 1 ..] . contains ( & head) ) {
76+ candidate = Some ( head) ;
77+ break ' outer;
78+ }
79+ }
80+
81+ let head = candidate?;
82+
83+ result. push ( head) ;
84+
85+ for seq in & mut seqs {
86+ if seq. first ( ) == Some ( & head) {
87+ seq. remove ( 0 ) ;
88+ }
89+ }
90+ }
91+ }
92+
5493impl ProjectGraph {
5594 pub fn new ( lib_cfg : & LibConfig , root_program : & parse:: Program ) -> Result < Self , String > {
5695 let mut modules: Vec < Module > = vec ! [ Module {
@@ -87,7 +126,10 @@ impl ProjectGraph {
87126 }
88127
89128 if let Some ( & existing_id) = lookup. get ( & path) {
90- dependencies. entry ( curr_id) . or_default ( ) . push ( existing_id) ;
129+ let deps = dependencies. entry ( curr_id) . or_default ( ) ;
130+ if !deps. contains ( & existing_id) {
131+ deps. push ( existing_id) ;
132+ }
91133 continue ;
92134 }
93135
@@ -110,4 +152,54 @@ impl ProjectGraph {
110152 dependencies,
111153 } )
112154 }
155+
156+ pub fn c3_linearize ( & self ) -> Result < Vec < usize > , C3Error > {
157+ self . linearize_module ( 0 )
158+ }
159+
160+ fn linearize_module ( & self , root : usize ) -> Result < Vec < usize > , C3Error > {
161+ let mut memo = HashMap :: < usize , Vec < usize > > :: new ( ) ;
162+ let mut visiting = Vec :: < usize > :: new ( ) ;
163+
164+ self . linearize_rec ( root, & mut memo, & mut visiting)
165+ }
166+
167+ fn linearize_rec (
168+ & self ,
169+ module : usize ,
170+ memo : & mut HashMap < usize , Vec < usize > > ,
171+ visiting : & mut Vec < usize > ,
172+ ) -> Result < Vec < usize > , C3Error > {
173+ if let Some ( result) = memo. get ( & module) {
174+ return Ok ( result. clone ( ) ) ;
175+ }
176+
177+ if visiting. contains ( & module) {
178+ let cycle_start = visiting. iter ( ) . position ( |m| * m == module) . unwrap ( ) ;
179+ return Err ( C3Error :: CycleDetected ( visiting[ cycle_start..] . to_vec ( ) ) ) ;
180+ }
181+
182+ visiting. push ( module) ;
183+
184+ let parents = self . dependencies . get ( & module) . cloned ( ) . unwrap_or_default ( ) ;
185+
186+ let mut seqs: Vec < Vec < usize > > = Vec :: new ( ) ;
187+
188+ for parent in & parents {
189+ let lin = self . linearize_rec ( * parent, memo, visiting) ?;
190+ seqs. push ( lin) ;
191+ }
192+
193+ seqs. push ( parents. clone ( ) ) ;
194+
195+ let mut result = vec ! [ module] ;
196+ let merged = merge ( seqs) . ok_or ( C3Error :: InconsistentLinearization { module } ) ?;
197+
198+ result. extend ( merged) ;
199+
200+ visiting. pop ( ) ;
201+ memo. insert ( module, result. clone ( ) ) ;
202+
203+ Ok ( result)
204+ }
113205}
0 commit comments