// Topological sort ASG traversal
//
// Copyright (C) 2014-2023 Ryan Specialty, LLC.
//
// This file is part of TAME.
//
// This program 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.
//
// This program 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 this program. If not, see .
//! Topological sort of [`Asg`] with ontological consideration.
//!
//! This toplogical sort is a depth-first search (DFS) that emits nodes in
//! post-order.
//! Intuitively,
//! it emits objects sorted in such a way that they appear before each of
//! their dependencies.
//!
//! The ordering is deterministic between runs on the same graph,
//! but it is only one of potentially many orderings.
//!
//! The only information provided by this sort is a stream of
//! [`ObjectIndex`]es ordered linearly.
//! No information about the edge or source object is provided,
//! nor is information about the length of the current path,
//! since an object may be visited any number of different ways and the
//! caller ought not rely on the particular path taken.
//! Furthermore,
//! an object may be visited any number of times from any number of paths,
//! but only the first visit is emitted,
//! so any additional information would provide an incomplete picture;
//! this sort is _not_ intended to provide information about all paths
//! to a particular object and cannot be used in that way.
//!
//! Cutting Of Cycles
//! =================
//! A _cycle_ is a path that references another object earlier in the path,
//! as if it loops in on itself.
//! Cycles are generally not permitted,
//! as they would require that a value would have to be computed before it
//! could compute itself.
//! This almost certainly represents an error in the program's specification.
//!
//! Cycles are permitted for recursion.
//! More information can be found in [`ObjectRel::can_recurse`].
//!
//! A toplogical ordering is defined only for graphs that do not contain
//! cycles.
//! To order a graph _with_ cycles,
//! the depth-first search performs a _cut_,
//! whereby the edge that would have led to the cycle is omitted,
//! as if cutting a loop of string at the point that it is tied.
//! An example of such a cut can be found in [`ObjectRel::can_recurse`].
//!
//! This is done in two scenarios:
//!
//! 1. An unsupported cycle is an error.
//! A cut is performed as a means of error recovery so that the process
//! may continue and discover more errors before terminating.
//!
//! 2. A cycle representing allowed recursion performs a cut since the
//! path taken thus far already represents a valid ordering.
use super::super::{Asg, ObjectIndex};
use crate::{
asg::{graph::object::DynObjectRel, Object, ObjectIndexResolvedSpan},
diagnose::{Annotate, AnnotatedSpan, Diagnostic},
};
use fixedbitset::FixedBitSet;
use std::{error::Error, fmt::Display, iter::once};
#[cfg(doc)]
use crate::{asg::graph::object::ObjectRel, span::Span};
pub fn topo_sort(
asg: &Asg,
init: impl Iterator>,
) -> TopoPostOrderDfs {
TopoPostOrderDfs::new(asg, init)
}
/// Topological sort implemented as a post-order depth-first search (DFS).
///
/// See the [module-level documentation](self) for important information
/// about this traversal.
pub struct TopoPostOrderDfs<'a> {
/// Reference [`Asg`].
///
/// Holding a reference to the [`Asg`] allows this object to serve
/// conveniently as an iterator.
asg: &'a Asg,
/// DFS stack.
///
/// As objects (nodes/vertices) are visited,
/// its relationships (edge targets) are pushed onto the stack.
/// Each iterator pops a relationship off the stack and visits it.
///
/// The inner [`Result`] serves as a cycle flag set by
/// [`Self::flag_or_cut_cycle`].
/// Computing the proper [`Cycle`] error before placing it on the stack
/// would not only bloat the size of each element of this stack,
/// but also use unnecessary memory on the heap.
/// The proper [`Cycle`] error will be computed when this element is
/// retrieved by [`Self::next_oi`].
///
/// _This may contain duplicate [`ObjectIndex`]es even if the graph
/// contains no cycles;_
/// see [`Self::push_neighbors`] for an explanation.
///
/// The traversal ends once the stack becomes empty.
/// It is expected the stack is initialized with at least one initial
/// object prior to beginning the traversal.
stack: Vec, ObjectIndex