| 1 | use crate::ast::{Id, Span}; | 
| 2 | use anyhow::Result; | 
|---|
| 3 | use indexmap::IndexMap; | 
|---|
| 4 | use std::collections::BinaryHeap; | 
|---|
| 5 | use std::fmt; | 
|---|
| 6 | use std::mem; | 
|---|
| 7 |  | 
|---|
| 8 | #[ derive(Default, Clone)] | 
|---|
| 9 | struct State { | 
|---|
| 10 | /// Number of outbound edges from this node which have still not been | 
|---|
| 11 | /// processed into the topological ordering. | 
|---|
| 12 | outbound_remaining: usize, | 
|---|
| 13 |  | 
|---|
| 14 | /// Indices of nodes that depend on this one, used when this node is added | 
|---|
| 15 | /// to the binary heap to decrement `outbound_remaining`. | 
|---|
| 16 | reverse_deps: Vec<usize>, | 
|---|
| 17 | } | 
|---|
| 18 |  | 
|---|
| 19 | /// Performs a topological sort of the `deps` provided, returning the order in | 
|---|
| 20 | /// which to visit the nodes in reverse-dep order. | 
|---|
| 21 | /// | 
|---|
| 22 | /// This sort goes one level further as well to produce a stable ordering | 
|---|
| 23 | /// regardless of the input edges so long as the structure of the graph has | 
|---|
| 24 | /// changed. Notably the nodes are sorted, by name, in the output in addition to | 
|---|
| 25 | /// being sorted in dependency order. This is done to assist with round-tripping | 
|---|
| 26 | /// documents where new edges are discovered during world elaboration that | 
|---|
| 27 | /// doesn't change the dependency graph but can change the dependency listings | 
|---|
| 28 | /// between serializations. | 
|---|
| 29 | /// | 
|---|
| 30 | /// The algorithm chosen here to do this is: | 
|---|
| 31 | /// | 
|---|
| 32 | /// * Build some metadata about all nodes including their count of outbound | 
|---|
| 33 | ///   edges remaining to be added to the order and a reverse dependency list. | 
|---|
| 34 | /// * Collect all nodes with 0 outbound edges into a binary heap. | 
|---|
| 35 | /// * Pop from the binary heap and decrement outbound edges that depend on | 
|---|
| 36 | ///   this node. | 
|---|
| 37 | /// * Iterate until the dependency ordering is the same size as the dependency | 
|---|
| 38 | ///   array. | 
|---|
| 39 | /// | 
|---|
| 40 | /// This sort will also detect when dependencies are missing or when cycles are | 
|---|
| 41 | /// present and return an error. | 
|---|
| 42 | pub fn toposort<'a>( | 
|---|
| 43 | kind: &str, | 
|---|
| 44 | deps: &IndexMap<&'a str, Vec<Id<'a>>>, | 
|---|
| 45 | ) -> Result<Vec<&'a str>, Error> { | 
|---|
| 46 | // Initialize a `State` per-node with the number of outbound edges and | 
|---|
| 47 | // additionally filling out the `reverse_deps` array. | 
|---|
| 48 | let mut states = vec![State::default(); deps.len()]; | 
|---|
| 49 | for (i, (_, edges)) in deps.iter().enumerate() { | 
|---|
| 50 | states[i].outbound_remaining = edges.len(); | 
|---|
| 51 | for edge in edges { | 
|---|
| 52 | let (j, _, _) = deps | 
|---|
| 53 | .get_full(edge.name) | 
|---|
| 54 | .ok_or_else(|| Error::NonexistentDep { | 
|---|
| 55 | span: edge.span, | 
|---|
| 56 | name: edge.name.to_string(), | 
|---|
| 57 | kind: kind.to_string(), | 
|---|
| 58 | highlighted: None, | 
|---|
| 59 | })?; | 
|---|
| 60 | states[j].reverse_deps.push(i); | 
|---|
| 61 | } | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|
| 64 | let mut order = Vec::new(); | 
|---|
| 65 | let mut heap = BinaryHeap::new(); | 
|---|
| 66 |  | 
|---|
| 67 | // Seed the `heap` with edges that have no outbound edges | 
|---|
| 68 | // | 
|---|
| 69 | // The heap here is keyed by `(usize, &str, usize)` where the first `usize` | 
|---|
| 70 | // is unique which is what determines the order of the heap. The other two | 
|---|
| 71 | // fields are metadata used when pulling from the heap. The first `usize` is | 
|---|
| 72 | // the index of the item within the original dependency map which should | 
|---|
| 73 | // reflect the original source order of the item. Note that this is stored | 
|---|
| 74 | // in reverse order to ensure that when there are multiple items in the heap | 
|---|
| 75 | // the first item in the original order is popped first. | 
|---|
| 76 | for (i, dep) in deps.keys().enumerate() { | 
|---|
| 77 | if states[i].outbound_remaining == 0 { | 
|---|
| 78 | heap.push((deps.len() - i, *dep, i)); | 
|---|
| 79 | } | 
|---|
| 80 | } | 
|---|
| 81 |  | 
|---|
| 82 | // Drain the binary heap which represents all nodes that have had all their | 
|---|
| 83 | // dependencies processed. Iteratively add to the heap as well as nodes are | 
|---|
| 84 | // removed. | 
|---|
| 85 | while let Some((_order, node, i)) = heap.pop() { | 
|---|
| 86 | order.push(node); | 
|---|
| 87 | for i in mem::take(&mut states[i].reverse_deps) { | 
|---|
| 88 | states[i].outbound_remaining -= 1; | 
|---|
| 89 | if states[i].outbound_remaining == 0 { | 
|---|
| 90 | let (dep, _) = deps.get_index(i).unwrap(); | 
|---|
| 91 | heap.push((deps.len() - i, *dep, i)); | 
|---|
| 92 | } | 
|---|
| 93 | } | 
|---|
| 94 | } | 
|---|
| 95 |  | 
|---|
| 96 | // If all nodes are present in order then a topological ordering was | 
|---|
| 97 | // achieved and it can be returned. | 
|---|
| 98 | if order.len() == deps.len() { | 
|---|
| 99 | return Ok(order); | 
|---|
| 100 | } | 
|---|
| 101 |  | 
|---|
| 102 | // ... otherwise there are still dependencies with remaining edges which | 
|---|
| 103 | // means that a cycle must be present, so find the cycle and report the | 
|---|
| 104 | // error. | 
|---|
| 105 | for (i, state) in states.iter().enumerate() { | 
|---|
| 106 | if state.outbound_remaining == 0 { | 
|---|
| 107 | continue; | 
|---|
| 108 | } | 
|---|
| 109 | let (_, edges) = deps.get_index(i).unwrap(); | 
|---|
| 110 | for dep in edges { | 
|---|
| 111 | let (j, _, _) = deps.get_full(dep.name).unwrap(); | 
|---|
| 112 | if states[j].outbound_remaining == 0 { | 
|---|
| 113 | continue; | 
|---|
| 114 | } | 
|---|
| 115 | return Err(Error::Cycle { | 
|---|
| 116 | span: dep.span, | 
|---|
| 117 | name: dep.name.to_string(), | 
|---|
| 118 | kind: kind.to_string(), | 
|---|
| 119 | highlighted: None, | 
|---|
| 120 | }); | 
|---|
| 121 | } | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | unreachable!() | 
|---|
| 125 | } | 
|---|
| 126 |  | 
|---|
| 127 | #[ derive(Debug)] | 
|---|
| 128 | pub enum Error { | 
|---|
| 129 | NonexistentDep { | 
|---|
| 130 | span: Span, | 
|---|
| 131 | name: String, | 
|---|
| 132 | kind: String, | 
|---|
| 133 | highlighted: Option<String>, | 
|---|
| 134 | }, | 
|---|
| 135 | Cycle { | 
|---|
| 136 | span: Span, | 
|---|
| 137 | name: String, | 
|---|
| 138 | kind: String, | 
|---|
| 139 | highlighted: Option<String>, | 
|---|
| 140 | }, | 
|---|
| 141 | } | 
|---|
| 142 |  | 
|---|
| 143 | impl Error { | 
|---|
| 144 | pub(crate) fn highlighted(&self) -> Option<&str> { | 
|---|
| 145 | match self { | 
|---|
| 146 | Error::NonexistentDep { highlighted: &Option, .. } | Error::Cycle { highlighted: &Option, .. } => { | 
|---|
| 147 | highlighted.as_deref() | 
|---|
| 148 | } | 
|---|
| 149 | } | 
|---|
| 150 | } | 
|---|
| 151 | pub(crate) fn set_highlighted(&mut self, string: String) { | 
|---|
| 152 | match self { | 
|---|
| 153 | Error::NonexistentDep { highlighted: &mut Option, .. } | Error::Cycle { highlighted: &mut Option, .. } => { | 
|---|
| 154 | *highlighted = Some(string); | 
|---|
| 155 | } | 
|---|
| 156 | } | 
|---|
| 157 | } | 
|---|
| 158 | } | 
|---|
| 159 |  | 
|---|
| 160 | impl fmt::Display for Error { | 
|---|
| 161 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
|---|
| 162 | if let Some(s: &str) = self.highlighted() { | 
|---|
| 163 | return f.write_str(data:s); | 
|---|
| 164 | } | 
|---|
| 165 | match self { | 
|---|
| 166 | Error::NonexistentDep { kind: &String, name: &String, .. } => { | 
|---|
| 167 | write!(f, "{kind}  `{name} ` does not exist") | 
|---|
| 168 | } | 
|---|
| 169 | Error::Cycle { kind: &String, name: &String, .. } => { | 
|---|
| 170 | write!(f, "{kind}  `{name} ` depends on itself") | 
|---|
| 171 | } | 
|---|
| 172 | } | 
|---|
| 173 | } | 
|---|
| 174 | } | 
|---|
| 175 |  | 
|---|
| 176 | impl std::error::Error for Error {} | 
|---|
| 177 |  | 
|---|
| 178 | #[ cfg(test)] | 
|---|
| 179 | mod tests { | 
|---|
| 180 | use super::*; | 
|---|
| 181 |  | 
|---|
| 182 | fn id(name: &str) -> Id<'_> { | 
|---|
| 183 | Id { | 
|---|
| 184 | name, | 
|---|
| 185 | span: Span { start: 0, end: 0 }, | 
|---|
| 186 | } | 
|---|
| 187 | } | 
|---|
| 188 |  | 
|---|
| 189 | #[ test] | 
|---|
| 190 | fn smoke() { | 
|---|
| 191 | let empty: Vec<&str> = Vec::new(); | 
|---|
| 192 | assert_eq!(toposort( "", &IndexMap::new()).unwrap(), empty); | 
|---|
| 193 |  | 
|---|
| 194 | let mut nonexistent = IndexMap::new(); | 
|---|
| 195 | nonexistent.insert( "a", vec![id( "b")]); | 
|---|
| 196 | assert!(matches!( | 
|---|
| 197 | toposort( "", &nonexistent), | 
|---|
| 198 | Err(Error::NonexistentDep { .. }) | 
|---|
| 199 | )); | 
|---|
| 200 |  | 
|---|
| 201 | let mut one = IndexMap::new(); | 
|---|
| 202 | one.insert( "a", vec![]); | 
|---|
| 203 | assert_eq!(toposort( "", &one).unwrap(), [ "a"]); | 
|---|
| 204 |  | 
|---|
| 205 | let mut two = IndexMap::new(); | 
|---|
| 206 | two.insert( "a", vec![]); | 
|---|
| 207 | two.insert( "b", vec![id( "a")]); | 
|---|
| 208 | assert_eq!(toposort( "", &two).unwrap(), [ "a", "b"]); | 
|---|
| 209 |  | 
|---|
| 210 | let mut two = IndexMap::new(); | 
|---|
| 211 | two.insert( "a", vec![id( "b")]); | 
|---|
| 212 | two.insert( "b", vec![]); | 
|---|
| 213 | assert_eq!(toposort( "", &two).unwrap(), [ "b", "a"]); | 
|---|
| 214 | } | 
|---|
| 215 |  | 
|---|
| 216 | #[ test] | 
|---|
| 217 | fn cycles() { | 
|---|
| 218 | let mut cycle = IndexMap::new(); | 
|---|
| 219 | cycle.insert( "a", vec![id( "a")]); | 
|---|
| 220 | assert!(matches!(toposort( "", &cycle), Err(Error::Cycle { .. }))); | 
|---|
| 221 |  | 
|---|
| 222 | let mut cycle = IndexMap::new(); | 
|---|
| 223 | cycle.insert( "a", vec![id( "b")]); | 
|---|
| 224 | cycle.insert( "b", vec![id( "c")]); | 
|---|
| 225 | cycle.insert( "c", vec![id( "a")]); | 
|---|
| 226 | assert!(matches!(toposort( "", &cycle), Err(Error::Cycle { .. }))); | 
|---|
| 227 | } | 
|---|
| 228 |  | 
|---|
| 229 | #[ test] | 
|---|
| 230 | fn depend_twice() { | 
|---|
| 231 | let mut two = IndexMap::new(); | 
|---|
| 232 | two.insert( "b", vec![id( "a"), id( "a")]); | 
|---|
| 233 | two.insert( "a", vec![]); | 
|---|
| 234 | assert_eq!(toposort( "", &two).unwrap(), [ "a", "b"]); | 
|---|
| 235 | } | 
|---|
| 236 |  | 
|---|
| 237 | #[ test] | 
|---|
| 238 | fn preserve_order() { | 
|---|
| 239 | let mut order = IndexMap::new(); | 
|---|
| 240 | order.insert( "a", vec![]); | 
|---|
| 241 | order.insert( "b", vec![]); | 
|---|
| 242 | assert_eq!(toposort( "", &order).unwrap(), [ "a", "b"]); | 
|---|
| 243 |  | 
|---|
| 244 | let mut order = IndexMap::new(); | 
|---|
| 245 | order.insert( "b", vec![]); | 
|---|
| 246 | order.insert( "a", vec![]); | 
|---|
| 247 | assert_eq!(toposort( "", &order).unwrap(), [ "b", "a"]); | 
|---|
| 248 | } | 
|---|
| 249 | } | 
|---|
| 250 |  | 
|---|