From ee675de2024e0547c1ceaab3065e5e1107d22bce Mon Sep 17 00:00:00 2001 From: MultisampledNight Date: Thu, 18 Jan 2024 21:39:19 +0100 Subject: [PATCH] feat: add semi-human-readable-and-writable-ir --- crates/ir/src/lib.rs | 99 +++++++++++++++++++++++++++++++++++++++++--- testfiles/bare.ron | 21 ++++++---- testfiles/invert.ron | 22 ++++++---- 3 files changed, 119 insertions(+), 23 deletions(-) diff --git a/crates/ir/src/lib.rs b/crates/ir/src/lib.rs index 790221d..d2160af 100644 --- a/crates/ir/src/lib.rs +++ b/crates/ir/src/lib.rs @@ -7,11 +7,19 @@ pub mod instruction; pub type Map = ahash::AHashMap; pub type Set = ahash::AHashSet; +/// 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 graph IR. +/// Returns an error if the parsed source is not a valid human-readable graph IR. pub fn from_ron(source: &str) -> ron::error::SpannedResult { - ron::from_str(source) + let human_repr: SemiHumanGraphIr = ron::from_str(source)?; + Ok(human_repr.into()) } /// The toplevel representation of a whole pipeline. @@ -68,10 +76,11 @@ pub mod id { /// 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); + pub struct Instruction(pub(super) Span); impl Instruction { /// Where this instruction is written down. + #[must_use] pub fn span(&self) -> &Span { &self.0 } @@ -81,20 +90,20 @@ pub mod id { /// /// An **instruction** cannot run if any of these are not connected. #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] - pub struct Input(Socket); + pub struct Input(pub(super) 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); + pub struct Output(pub(super) 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, + pub idx: SocketIdx, } /// Where a [`Socket`] is on an **instruction**. @@ -106,3 +115,81 @@ pub mod id { pub struct Span { range: RangeInclusive, } + +// TODO: this desperately belongs into its own file, also id should get its own file +/// Semi-human-{read,writ}able [`GraphIr`] with far less useful types. +/// +/// **Do not use this if you want to programatically construct IR.** +/// Instead, directly use [`GraphIr`]. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct SemiHumanGraphIr { + /// See [`GraphIr::instructions`], just that a simple number is used for the ID instead + /// of a proper span. + instructions: Map, + /// See [`GraphIr::edges`]. RON wants you to type the set as a list. + edges: Map>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] +pub struct SemiHumanSocket { + /// ID of the instruction this socket is on. + on: usize, + idx: u16, +} + +impl From for id::Socket { + fn from(source: SemiHumanSocket) -> Self { + Self { + belongs_to: (id::Instruction(Span { + range: source.on..=source.on, + })), + idx: id::SocketIdx(source.idx), + } + } +} + +impl From for GraphIr { + fn from(source: SemiHumanGraphIr) -> Self { + let edges = source.edges.clone(); + Self { + instructions: source + .instructions + .into_iter() + .map(|(id, kind)| (id::Instruction(Span { range: id..=id }), kind)) + .collect(), + edges: type_edges(source.edges), + // same as above, but also reverse the mapping + rev_edges: reverse_and_type_edges(edges), + } + } +} + +fn type_edges( + edges: Map>, +) -> Map> { + edges + .into_iter() + .map(|(output, inputs)| { + let output = id::Output(output.into()); + let inputs = inputs.into_iter().map(Into::into).map(id::Input).collect(); + (output, inputs) + }) + .collect() +} + +fn reverse_and_type_edges( + edges: Map>, +) -> Map> { + edges + .into_iter() + .fold(Map::new(), |mut rev_edges, (output, inputs)| { + let output = id::Output(output.into()); + + for input in inputs { + let input = id::Input(input.into()); + rev_edges.entry(input).or_default().insert(output.clone()); + } + + rev_edges + }) +} diff --git a/testfiles/bare.ron b/testfiles/bare.ron index a9c97c8..f2a9621 100644 --- a/testfiles/bare.ron +++ b/testfiles/bare.ron @@ -1,12 +1,17 @@ ( - [ - Read(( - source: File("testfiles/juan.png"), - format: Jpeg + instructions: { + 0: Read(( + source: File("testfiles/juan.jpg"), + format: Jpeg, )), - Write(( + 1: Write(( target: File("testfiles/out.png"), - format: Png - )) - ] + format: Png, + )), + }, + edges: { + (on: 0, idx: 0): [ + (on: 1, idx: 0), + ], + }, ) diff --git a/testfiles/invert.ron b/testfiles/invert.ron index 80179f1..3f1f48c 100644 --- a/testfiles/invert.ron +++ b/testfiles/invert.ron @@ -1,13 +1,17 @@ ( - [ - Read(( + instructions: { + 0: Read(( source: File("testfiles/juan.jpg"), - format: Jpeg + format: Jpeg, )), - Filter(Invert), - Write(( - target: File("testfiles/inverted.jpg"), - format: Jpeg - )) - ] + 1: Filter(Invert), + 2: Write(( + target: File("testfiles/inverted.png"), + format: Png, + )), + }, + edges: { + (on: 0, idx: 0): [(on: 1, idx: 0)], + (on: 1, idx: 0): [(on: 2, idx: 0)], + }, )