iowo/crates/ir/src/lib.rs

78 lines
3.1 KiB
Rust
Raw Normal View History

use std::ops::RangeInclusive;
use serde::{Deserialize, Serialize};
pub mod id;
pub mod instruction;
// feel free to make this public if there's sufficient reason to do so
// otherwise, go use the GraphIr in the module this is written in directly instead!!
mod semi_human;
pub type Map<K, V> = ahash::AHashMap<K, V>;
pub type Set<V> = ahash::AHashSet<V>;
/// Gives you a super well typed graph IR for a given human-readable repr.
///
/// Look at [`SemiHumanGraphIr`] and the test files in the repo at `testfiles/`
/// to see what the RON should look like.
/// No, we don't want you to write out [`GraphIr`] in full by hand.
/// That's something for the machines to do.
///
/// # Errors
///
/// Returns an error if the parsed source is not a valid human-readable graph IR.
pub fn from_ron(source: &str) -> ron::error::SpannedResult<GraphIr> {
let human_repr: semi_human::GraphIr = ron::from_str(source)?;
Ok(human_repr.into())
}
/// The toplevel representation of a whole pipeline.
///
/// Pipelines may not be fully linear. They may branch out and recombine later on.
/// As such, the representation for them which is currently used is a
/// [**D**irected **A**cyclic **G**raph](https://en.wikipedia.org/wiki/Directed_acyclic_graph)
/// .
///
/// For those who are already familiar with graphs, a DAG is one, except that:
///
/// - It is **directed**: Edges have a direction they point to.
/// In this case, edges point from the outputs of streamers to inputs of consumers.
/// - It is **acyclic**: Those directed edges may not form loops.
/// In other words, if one follows edges only in their direction, it must be impossible
/// to come back to an already visited node.
///
/// Here, if an edge points from _A_ to _B_ (`A --> B`),
/// then _A_ is called a **dependency** of _B_,
/// and _B_ is called a **dependent** of _A_.
///
/// The DAG also enables another neat operation:
/// [Topological sorting](https://en.wikipedia.org/wiki/Topological_sorting).
/// This allows to put the entire graph into a linear list,
/// where it's guaranteed that once a vertex is visited,
/// all dependencies of it will have been visited already as well.
///
/// The representation used here in specific is a bit more complicated,
/// since **instructions** directly aren't just connected to one another,
/// but their **sockets** are instead.
///
/// So the vertices of the DAG are the **sockets**
/// (which are either [`id::Input`] or [`id::Output`] depending on the direction),
/// and each **socket** in turn belongs to an **instruction**.
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct GraphIr {
/// "Backbone" storage of all **instruction** IDs to
/// what **kind of instruction** they are.
instructions: Map<id::Instruction, instruction::Kind>,
/// How the data flows forward. **Dependencies** map to **dependents** here.
edges: Map<id::Output, Set<id::Input>>,
/// How the data flows backward. **Dependents** map to **dependencies** here.
rev_edges: Map<id::Input, Set<id::Output>>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct Span {
range: RangeInclusive<usize>,
}