use crate::{
Edge,
node::{self, Node},
};
#[doc(inline)]
pub use codegen::{eval_fn_body, pull_eval_fn_name, push_eval_fn_name};
#[doc(inline)]
pub use error::ModuleError;
#[doc(inline)]
pub use flow::{Block, Flow, FlowGraph, NodeConf, NodeConns, flow_graph};
use meta::MetaTree;
#[doc(inline)]
pub use meta::{EdgeKind, Meta, MetaGraph};
use petgraph::visit::{
Data, Dfs, EdgeRef, GraphBase, GraphRef, IntoEdgesDirected, IntoNeighbors, IntoNodeReferences,
NodeIndexable, Topo, Visitable, Walker,
};
pub(crate) use rosetree::RoseTree;
use std::{collections::HashSet, hash::Hash};
use steel::parser::ast::ExprKind;
mod codegen;
pub mod error;
mod flow;
mod meta;
mod rosetree;
pub trait Edges {
fn edges(&self) -> impl Iterator<Item = Edge>;
}
impl Edges for Edge {
fn edges(&self) -> impl Iterator<Item = Edge> {
std::iter::once(*self)
}
}
impl Edges for Vec<Edge> {
fn edges(&self) -> impl Iterator<Item = Edge> {
self.iter().copied()
}
}
fn eval_neighbors<G>(
g: G,
n: G::NodeId,
conns: &node::Conns,
src_conn: impl Fn(&Edge) -> usize,
) -> HashSet<G::NodeId>
where
G: IntoEdgesDirected,
G::EdgeWeight: Edges,
G::NodeId: Eq + Hash,
{
let mut set = HashSet::new();
for e_ref in g.edges_directed(n, petgraph::Outgoing) {
for edge in e_ref.weight().edges() {
let conn_ix = src_conn(&edge);
let include = conns.get(conn_ix).unwrap();
if include {
set.insert(e_ref.target());
}
}
}
set
}
fn push_eval_neighbors<G>(g: G, n: G::NodeId, ev: &node::Conns) -> HashSet<G::NodeId>
where
G: IntoEdgesDirected,
G::EdgeWeight: Edges,
G::NodeId: Eq + Hash,
{
eval_neighbors(g, n, ev, |edge| edge.output.0 as usize)
}
fn pull_eval_neighbors<G>(g: G, n: G::NodeId, ev: &node::Conns) -> HashSet<G::NodeId>
where
G: IntoEdgesDirected,
G::EdgeWeight: Edges,
G::NodeId: Eq + Hash,
{
let rev_g = petgraph::visit::Reversed(g);
eval_neighbors(rev_g, n, ev, |edge| edge.input.0 as usize)
}
fn reachable<G>(
g: G,
src: G::NodeId,
src_neighbors: &HashSet<G::NodeId>,
) -> impl Iterator<Item = G::NodeId>
where
G: IntoEdgesDirected + Visitable,
G::NodeId: Eq + Hash,
{
#[derive(Clone, Copy)]
struct EvalFilter<'a, G: GraphBase> {
g: G,
src: G::NodeId,
src_neighbors: &'a HashSet<G::NodeId>,
}
struct EvalFilterNeighbors<'a, I: Iterator> {
neighbors: I,
is_src: bool,
src_neighbors: &'a HashSet<I::Item>,
}
impl<'a, G> GraphBase for EvalFilter<'a, G>
where
G: GraphBase,
{
type NodeId = G::NodeId;
type EdgeId = G::EdgeId;
}
impl<'a, G: GraphRef> GraphRef for EvalFilter<'a, G> {}
impl<'a, G> Visitable for EvalFilter<'a, G>
where
G: GraphRef + Visitable,
{
type Map = G::Map;
fn visit_map(&self) -> Self::Map {
self.g.visit_map()
}
fn reset_map(&self, map: &mut Self::Map) {
self.g.reset_map(map);
}
}
impl<'a, I> Iterator for EvalFilterNeighbors<'a, I>
where
I: Iterator,
I::Item: Eq + Hash,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
while let Some(n) = self.neighbors.next() {
if !self.is_src || self.src_neighbors.contains(&n) {
return Some(n);
}
}
None
}
}
impl<'a, G> IntoNeighbors for EvalFilter<'a, G>
where
G: IntoNeighbors,
G::NodeId: Eq + Hash,
{
type Neighbors = EvalFilterNeighbors<'a, G::Neighbors>;
fn neighbors(self, a: Self::NodeId) -> Self::Neighbors {
let neighbors = self.g.neighbors(a);
EvalFilterNeighbors {
neighbors,
is_src: self.src == a,
src_neighbors: &self.src_neighbors,
}
}
}
let g = EvalFilter {
g,
src,
src_neighbors,
};
Dfs::new(g, src).iter(g)
}
fn push_reachable<G>(
g: G,
n: G::NodeId,
nbs: &HashSet<G::NodeId>,
) -> impl Iterator<Item = G::NodeId>
where
G: IntoEdgesDirected + Visitable,
G::NodeId: Eq + Hash,
{
reachable(g, n, nbs)
}
fn pull_reachable<G>(
g: G,
n: G::NodeId,
nbs: &HashSet<G::NodeId>,
) -> impl Iterator<Item = G::NodeId>
where
G: IntoEdgesDirected + Visitable,
G::NodeId: Eq + Hash,
{
let rev_g = petgraph::visit::Reversed(g);
reachable(rev_g, n, nbs)
}
pub(crate) fn eval_order<G, A, B>(g: G, push: A, pull: B) -> impl Iterator<Item = G::NodeId>
where
G: IntoEdgesDirected + IntoNodeReferences + Visitable,
G::EdgeWeight: Edges,
G::NodeId: Eq + Hash,
A: IntoIterator<Item = (G::NodeId, node::Conns)>,
B: IntoIterator<Item = (G::NodeId, node::Conns)>,
{
let mut reachable = HashSet::new();
reachable.extend(push.into_iter().flat_map(|(n, conns)| {
let ps = push_eval_neighbors(g, n, &conns);
push_reachable(g, n, &ps).collect::<Vec<_>>()
}));
reachable.extend(pull.into_iter().flat_map(|(n, conns)| {
let pl = pull_eval_neighbors(g, n, &conns);
pull_reachable(g, n, &pl).collect::<Vec<_>>()
}));
Topo::new(g).iter(g).filter(move |n| reachable.contains(&n))
}
pub fn module<'a, G>(get_node: node::GetNode<'a>, g: G) -> Result<Vec<ExprKind>, ModuleError>
where
G: Data<EdgeWeight = Edge> + IntoEdgesDirected + IntoNodeReferences + NodeIndexable + Visitable,
G::NodeWeight: Node,
{
let mut meta_tree = MetaTree::default();
crate::graph::visit(get_node, g, &[], &mut meta_tree);
if !meta_tree.errors.is_empty() {
return Err(error::MetaErrors(meta_tree.errors).into());
}
let flow_tree = meta_tree
.tree
.try_map_ref(&mut |meta| Flow::from_meta(meta).map(|flow| (meta, flow)))?;
let node_confs_tree = flow_tree.map_ref(&mut |(_, flow)| codegen::unique_node_confs(flow));
let node_fns = codegen::node_fns(get_node, g, &node_confs_tree)?;
let eval_fns = codegen::eval_fns(&flow_tree)?;
Ok(node_fns.into_iter().chain(eval_fns).collect())
}