mirror of
https://github.com/AleoHQ/leo.git
synced 2024-11-13 08:47:17 +03:00
Add graph data structure
This commit is contained in:
parent
063cb15d97
commit
9fbcdfc9f3
119
compiler/passes/src/common/graph/mod.rs
Normal file
119
compiler/passes/src/common/graph/mod.rs
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use leo_span::Symbol;
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
/// A node in a graph.
|
||||
pub trait Node: Copy + 'static + Eq + PartialEq + Debug + Hash {}
|
||||
|
||||
impl Node for Symbol {}
|
||||
|
||||
/// Errors in graph operations.
|
||||
#[derive(Debug)]
|
||||
pub enum GraphError {
|
||||
/// A cycle was detected in the graph.
|
||||
CycleDetected,
|
||||
}
|
||||
|
||||
/// A directed graph.
|
||||
#[derive(Debug)]
|
||||
pub struct DiGraph<N: Node> {
|
||||
/// The set of nodes in the graph.
|
||||
nodes: IndexSet<N>,
|
||||
// TODO: Better name.
|
||||
/// The directed edges in the graph.
|
||||
edges: IndexMap<N, IndexSet<N>>,
|
||||
}
|
||||
|
||||
impl<N: Node> DiGraph<N> {
|
||||
/// Initializes a new `CallGraph` from a vector of source nodes.
|
||||
pub fn new(nodes: IndexSet<N>) -> Self {
|
||||
Self {
|
||||
nodes,
|
||||
edges: IndexMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an edge to the call graph.
|
||||
pub fn add_edge(&mut self, from: N, to: N) {
|
||||
// Add `from` and `to` to the set of nodes if they are not already in the set.
|
||||
self.nodes.insert(from);
|
||||
self.nodes.insert(to);
|
||||
|
||||
// Add the edge to the adjacency list.
|
||||
let entry = self.edges.entry(from).or_default();
|
||||
entry.insert(to);
|
||||
}
|
||||
|
||||
/// Returns `true` if the graph contains the given node.
|
||||
pub fn contains_node(&self, node: N) -> bool {
|
||||
self.nodes.contains(&node)
|
||||
}
|
||||
|
||||
/// Detects if there is a cycle in the graph.
|
||||
pub fn topological_sort(&self) -> Result<IndexSet<N>, GraphError> {
|
||||
// The set of nodes that do not need to be visited again.
|
||||
let mut finished: IndexSet<N> = IndexSet::with_capacity(self.nodes.len());
|
||||
// The set of nodes that are on the path to the current node in the search.
|
||||
let mut discovered: IndexSet<N> = IndexSet::new();
|
||||
|
||||
// Perform a depth-first search of the graph, starting from `node`, for each node in the graph.
|
||||
for node in self.nodes.iter() {
|
||||
// If the node has not been explored, explore it.
|
||||
if !discovered.contains(node) //TODO: Can we remove this check? Any nodes added to discovered in `contains_cycle_from` are removed.
|
||||
&& !finished.contains(node)
|
||||
&& self.contains_cycle_from(*node, &mut discovered, &mut finished)
|
||||
{
|
||||
// A cycle was found.
|
||||
return Err(GraphError::CycleDetected);
|
||||
}
|
||||
}
|
||||
// No cycle was found. Return the set of nodes in topological order.
|
||||
Ok(finished)
|
||||
}
|
||||
|
||||
// Detects if there is a cycle in the graph starting from the given node, via a recursive depth-first search.
|
||||
// Nodes are added to to `finished` in topological order.
|
||||
fn contains_cycle_from(&self, node: N, discovered: &mut IndexSet<N>, finished: &mut IndexSet<N>) -> bool {
|
||||
// Add the node to the set of discovered nodes.
|
||||
discovered.insert(node);
|
||||
|
||||
// Check each outgoing edge of the node.
|
||||
if let Some(children) = self.edges.get(&node) {
|
||||
for child in children.iter() {
|
||||
// If the node already been discovered, there is a cycle.
|
||||
if discovered.contains(child) {
|
||||
return true;
|
||||
}
|
||||
// If the node has not been explored, explore it.
|
||||
if !finished.contains(child) && self.contains_cycle_from(*child, discovered, finished) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the node from the set of discovered nodes.
|
||||
discovered.remove(&node);
|
||||
// Add the node to the set of finished nodes.
|
||||
finished.insert(node);
|
||||
|
||||
false
|
||||
}
|
||||
}
|
@ -17,6 +17,9 @@
|
||||
pub mod assigner;
|
||||
pub use assigner::*;
|
||||
|
||||
pub mod graph;
|
||||
pub use graph::*;
|
||||
|
||||
pub mod rename_table;
|
||||
pub use rename_table::*;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user