summaryrefslogtreecommitdiff
path: root/src/resolver.rs
blob: d02d80f8eb53edad086ad1aa97d3250ba2935800 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
use crate::*;

use log::{info, error};

type PushFn = fn(&mut String, &SourceFile);


/// Resolve undeclared symbols in a source unit with definitions from other units.
pub struct Resolver {
    /// Definitions from all included source units.
    pub definitions: Vec<TrackedDefinition>,
    /// All resolved references in all included source units.
    pub resolved: Vec<TrackedReference>,
    /// All unresolved references in all included source units.
    pub unresolved: Vec<TrackedSymbol>,
    /// All redefined (duplicate) definitions in all included source units.
    pub redefinitions: Vec<TrackedRedefinition>,
    /// All included source units.
    pub source_units: Vec<HeirarchicalSourceUnit>,
    /// The 'source_units' indices of the root source units.
    pub root_unit_ids: Vec<usize>,
    /// Source units that can be included later to resolve symbols.
    pub library_source_units: Vec<SourceUnit>,
}


impl Resolver {
    pub fn new(source_unit: SourceUnit) -> Self {
        let mut new = Self {
            definitions: Vec::new(),
            resolved: Vec::new(),
            unresolved: Vec::new(),
            redefinitions: Vec::new(),
            source_units: Vec::new(),
            root_unit_ids: Vec::new(),
            library_source_units: Vec::new(),
        };
        new.include_source_unit(source_unit, None);
        return new;
    }

    pub fn include_source_unit(&mut self, mut source_unit: SourceUnit, parent_id: Option<usize>) {
        let source_id = self.source_units.len();
        use std::mem::take;

        info!("Including source unit at {:?}", source_unit.main.path);
        if let Some(symbols) = &mut source_unit.main.symbols {
            self.include_symbols(take(symbols), source_id, SourceRole::Main);
        }
        if let Some(head) = &mut source_unit.head {
            if let Some(symbols) = &mut head.symbols {
                self.include_symbols(take(symbols), source_id, SourceRole::Head);
            }
        }
        if let Some(tail) = &mut source_unit.tail {
            if let Some(symbols) = &mut tail.symbols {
                self.include_symbols(take(symbols), source_id, SourceRole::Tail);
            }
        }

        match parent_id {
            Some(parent_id) => match self.source_units.get_mut(parent_id) {
                Some(parent) => parent.child_ids.push(source_id),
                None => error!("Could not find parent (#{parent_id}) of source unit #{source_id}"),
            }
            None => self.root_unit_ids.push(source_id),
        }
        self.source_units.push(
            HeirarchicalSourceUnit {
                source_unit,
                child_ids: Vec::new(),
                parent_ids: Vec::new(),
            }
        );
    }

    fn include_symbols(&mut self, symbols: Vec<Symbol>, source_id: usize, source_role: SourceRole) {
        for symbol in symbols {
            match symbol.role {
                SymbolRole::Definition(_) => {
                    // Check if the symbol has already been defined.
                    let equal = |d: &TrackedDefinition| { &d.tracked.symbol == &symbol };
                    if let Some(original) = self.definitions.iter().position(equal) {
                        let tracked = TrackedSymbol { symbol, source_id, source_role };
                        let definition = original;
                        self.redefinitions.push(TrackedRedefinition { tracked, definition });
                    } else {
                        // Resolve all unresolved references that match this symbol.
                        let defines = |r: &mut TrackedSymbol| symbol.defines(&r.symbol);
                        let definition = self.definitions.len();
                        let mut references = Vec::new();
                        for tracked in self.unresolved.extract_if(.., defines) {
                            references.push(self.resolved.len());
                            self.resolved.push(TrackedReference { tracked, definition });
                        }
                        let tracked = TrackedSymbol { symbol, source_id, source_role };
                        self.definitions.push(TrackedDefinition { tracked, references });
                    }
                }
                SymbolRole::Reference => {
                    let tracked = TrackedSymbol { symbol, source_id, source_role };
                    let defines = |d: &TrackedDefinition| d.tracked.symbol.defines(&tracked.symbol);
                    match self.definitions.iter().position(defines) {
                        Some(index) => {
                            let definition = &mut self.definitions[index];
                            definition.references.push(self.resolved.len());
                            self.resolved.push(TrackedReference { tracked, definition: index })
                        }
                        None => self.unresolved.push(tracked),
                    }
                }
            }
        }
    }

    /// Add a set of source units that might contain definitions for unresolved symbols.
    pub fn add_library_source_units(&mut self, source_units: Vec<SourceUnit>) {
        for source_unit in source_units {
            // Discard source units that have already been included.
            let in_included = self.source_units.iter().any(|s| s.source_unit == source_unit);
            let in_library = self.library_source_units.iter().any(|s| *s == source_unit);
            if !in_included && !in_library {
                self.library_source_units.push(source_unit);
            }
        }
    }

    /// Attempt to resolve unresolved references with library source units.
    pub fn resolve(&mut self) {
        // Repeatedly test if each library source unit resolves an unresolved
        // symbol, breaking the loop when no new resolutions are found.
        'outer: loop {
            for (i, source_unit) in self.library_source_units.iter().enumerate() {
                if let Some(id) = self.unit_resolved_by_unit(&source_unit) {
                    let source_unit = self.library_source_units.remove(i);
                    self.include_source_unit(source_unit, Some(id));
                    continue 'outer;
                }
            }
            break;
        }
    }

    /// Returns true if all references in all included source units have been
    /// resolved.
    pub fn error(&self) -> Option<ResolverError> {
        match self.unresolved.is_empty() && self.redefinitions.is_empty() {
            true => None,
            false => Some(ResolverError { resolver: self })
        }
    }

    /// Return a type that can print all symbol definitions and references.
    pub fn symbols(&self) -> SourceSymbols {
        SourceSymbols { resolver: self }
    }

    /// Return a type that can print all unused symbol definitions.
    pub fn unused(&self) -> UnusedSymbols {
        UnusedSymbols { resolver: self }
    }

    /// Return a type that can print the structure of the source tree.
    pub fn hierarchy(&self) -> SourceHierarchy {
        SourceHierarchy { resolver: self }
    }

    /// Return the ID of a source unit that contains an unresolved reference
    /// to a symbol defined by this unit.
    fn unit_resolved_by_unit(&self, source_unit: &SourceUnit) -> Option<usize> {
        macro_rules! return_resolved_unit {
            ($file:expr) => {
                if let Some(symbols) = &$file.symbols {
                    if let Some(id) = self.unit_resolved_by_symbol(&symbols) {
                        return Some(id);
                    }
                }
            };
        }
        return_resolved_unit!(&source_unit.main);
        if let Some(head) = &source_unit.head { return_resolved_unit!(&head) }
        if let Some(tail) = &source_unit.tail { return_resolved_unit!(&tail) }
        return None;
    }

    /// Returns the ID of a source unit that contains an unresolved reference
    /// to a symbol defined by one of these symbols.
    fn unit_resolved_by_symbol(&self, symbols: &[Symbol]) -> Option<usize> {
        for symbol in symbols {
            if let SymbolRole::Definition(_) = symbol.role {
                for unresolved in &self.unresolved {
                    if unresolved.symbol.name == symbol.name {
                        return Some(unresolved.source_id);
                    }
                }
            }
        }
        return None;
    }

    /// Populate the .parent_ids field of every source unit. The parents of
    /// each source unit are the units that define a symbol referenced by the
    /// unit, where the definition type is MustPrecedeReference.
    pub fn calculate_hierarchy(&mut self) {
        // Clear the .parent_ids field of every source unit.
        for source_unit in &mut self.source_units {
            source_unit.parent_ids.clear();
        }
        // Populate the .parent_ids field of every source unit.
        for reference in &self.resolved {
            let predicate = |d: &&TrackedDefinition| d.tracked.symbol.name == reference.tracked.symbol.name;
            if let Some(definition) = self.definitions.iter().find(predicate) {
                // A unit cannot be its own parent.
                let is_self = reference.tracked.source_id == definition.tracked.source_id;
                let must_precede = SymbolRole::Definition(DefinitionType::MustPrecedeReference);
                if  is_self || definition.tracked.symbol.role != must_precede { continue; }
                let referencing_unit = &mut self.source_units[reference.tracked.source_id];
                referencing_unit.parent_ids.push(definition.tracked.source_id);
            };
        }
    }

    /// Concatenate all included source units into one string.
    /// If the source unit dependency graph contains a cycle, the IDs of the
    /// source units involved in the cycle will be returned.
    pub fn get_merged_source_code(&self, push: PushFn) -> Result<String, MergeError> {
        // The ID of each source unit will come after the IDs of all
        // parents of that unit.
        let head_order = {
            let mut included_source_ids = Vec::new();
            let mut remaining_source_ids = (0..self.source_units.len()).collect::<Vec<_>>();

            'restart: while !remaining_source_ids.is_empty() {
                // Iterate over source units, most-recently-included first.
                'next: for (i, id) in remaining_source_ids.iter().enumerate() {
                    let unit = &self.source_units[*id];
                    for parent_id in &unit.parent_ids {
                        // Skip this unit if a parent hasn't yet been included.
                        if !included_source_ids.contains(parent_id) {
                            continue 'next;
                        }
                    }
                    // Include this unit, then check remaining units from the start.
                    included_source_ids.push(*id);
                    remaining_source_ids.remove(i);
                    continue 'restart;
                }
                // All remaining source units depend on at least one remaining
                // source unit, indicating a dependency cycle.
                return Err(MergeError {
                    resolver: self,
                    cyclic_unit_ids: remaining_source_ids,
                });
            }
            included_source_ids
        };
        let mut source_code = String::new();

        // Push head source code in calculated parent-preceding order.
        for id in &head_order {
            let source_unit = &self.source_units[*id];
            if let Some(head) = &source_unit.source_unit.head {
                push(&mut source_code, head);
            }
        }
        // Push main source code in source-added order.
        // The root unit will be pushed first.
        for source_unit in self.source_units.iter() {
            let main = &source_unit.source_unit.main;
            push(&mut source_code, &main);
        }
        // Push tail source code in reverse source-added order.
        // The root unit will be pushed last.
        for source_unit in self.source_units.iter().rev() {
            if let Some(tail) = &source_unit.source_unit.tail {
                push(&mut source_code, tail);
            }
        }
        return Ok(source_code);
    }
}


/// A source unit tracked with pointers to parents and dependents.
pub struct HeirarchicalSourceUnit {
    pub source_unit: SourceUnit,
    /// Pointers to source units that resolve references this unit.
    pub child_ids: Vec<usize>,
    /// Pointers to source units that must be included before this unit.
    pub parent_ids: Vec<usize>,
}

pub struct TrackedDefinition {
    pub tracked: TrackedSymbol,
    /// Points to resolved references in `resolved`.
    pub references: Vec<usize>,
}

pub struct TrackedRedefinition {
    pub tracked: TrackedSymbol,
    /// Points to existing definition in `definitions`.
    pub definition: usize,
}

pub struct TrackedReference {
    pub tracked: TrackedSymbol,
    /// Points to resolving definition in `definitions`.
    pub definition: usize,
}

pub struct TrackedSymbol {
    pub symbol: Symbol,
    pub source_id: usize,
    pub source_role: SourceRole,
}

impl TrackedSymbol {
    pub fn context<'a>(&'a self, resolver: &'a Resolver) -> Context<'a> {
        let source_unit = &resolver.source_units[self.source_id].source_unit;
        let source_code = match self.source_role {
            SourceRole::Main => source_unit.main.source_code.as_str(),
            SourceRole::Head => match &source_unit.head {
                Some(head) => head.source_code.as_str(),
                None => unreachable!("Failed to find source code of head file"),
            }
            SourceRole::Tail => match &source_unit.tail {
                Some(tail) => tail.source_code.as_str(),
                None => unreachable!("Failed to find source code of tail file"),
            }
        };
        Context { source_code, source: &self.symbol.source }
    }
}

impl PartialEq for TrackedSymbol {
    fn eq(&self, other: &TrackedSymbol) -> bool {
        self.symbol.name.eq(&other.symbol.name)
    }
}

#[derive(Clone, Copy, Debug)]
pub enum SourceRole {
    Main,
    Head,
    Tail,
}