use std::ops::RangeInclusive; use serde::{Deserialize, Serialize}; pub mod instruction; pub type Map = ahash::AHashMap; pub type Set = ahash::AHashSet; /// # Errors /// /// Returns an error if the parsed source is not a valid graph IR. pub fn from_ron(source: &str) -> ron::error::SpannedResult { ron::from_str(source) } /// 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, /// How the data flows forward. **Dependencies** map to **dependents** here. edges: Map>, /// How the data flows backward. **Dependents** map to **dependencies** here. rev_edges: Map>, } pub mod id { use serde::{Deserialize, Serialize}; use crate::Span; /// One specific instruction, and where it is found in code. /// /// It does **not** contain what kind of instruction this is. /// Refer to [`crate::instruction::Kind`] for this instead. #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct Instruction(Span); impl Instruction { /// Where this instruction is written down. pub fn span(&self) -> &Span { &self.0 } } /// On an **instruction**, accepts incoming data. /// /// An **instruction** cannot run if any of these are not connected. #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct Input(Socket); /// On an **instruction**, returns outgoing data to be fed to [`Input`]s. /// /// In contrast to [`Input`]s, [`Output`]s may be used or unused. #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct Output(Socket); /// An unspecified socket on a specific **instruction**, /// and where it is on that **instruction**. #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct Socket { pub belongs_to: Instruction, pub index: SocketIdx, } /// Where a [`Socket`] is on an **instruction**. #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct SocketIdx(pub u16); } #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct Span { range: RangeInclusive, }