feat: add semi-human-readable-and-writable-ir

This commit is contained in:
multisn8 2024-01-18 21:39:19 +01:00
parent 3cbe379da3
commit a5e6a41400
Signed by: multisamplednight
GPG key ID: C81EF9B053977241
3 changed files with 119 additions and 23 deletions

View file

@ -7,11 +7,19 @@ pub mod instruction;
pub type Map<K, V> = ahash::AHashMap<K, V>; pub type Map<K, V> = ahash::AHashMap<K, V>;
pub type Set<V> = ahash::AHashSet<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 /// # 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<GraphIr> { pub fn from_ron(source: &str) -> ron::error::SpannedResult<GraphIr> {
ron::from_str(source) let human_repr: SemiHumanGraphIr = ron::from_str(source)?;
Ok(human_repr.into())
} }
/// The toplevel representation of a whole pipeline. /// The toplevel representation of a whole pipeline.
@ -68,10 +76,11 @@ pub mod id {
/// It does **not** contain what kind of instruction this is. /// It does **not** contain what kind of instruction this is.
/// Refer to [`crate::instruction::Kind`] for this instead. /// Refer to [`crate::instruction::Kind`] for this instead.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct Instruction(Span); pub struct Instruction(pub(super) Span);
impl Instruction { impl Instruction {
/// Where this instruction is written down. /// Where this instruction is written down.
#[must_use]
pub fn span(&self) -> &Span { pub fn span(&self) -> &Span {
&self.0 &self.0
} }
@ -81,20 +90,20 @@ pub mod id {
/// ///
/// An **instruction** cannot run if any of these are not connected. /// An **instruction** cannot run if any of these are not connected.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] #[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. /// On an **instruction**, returns outgoing data to be fed to [`Input`]s.
/// ///
/// In contrast to [`Input`]s, [`Output`]s may be used or unused. /// In contrast to [`Input`]s, [`Output`]s may be used or unused.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] #[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**, /// An unspecified socket on a specific **instruction**,
/// and where it is on that **instruction**. /// and where it is on that **instruction**.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct Socket { pub struct Socket {
pub belongs_to: Instruction, pub belongs_to: Instruction,
pub index: SocketIdx, pub idx: SocketIdx,
} }
/// Where a [`Socket`] is on an **instruction**. /// Where a [`Socket`] is on an **instruction**.
@ -106,3 +115,81 @@ pub mod id {
pub struct Span { pub struct Span {
range: RangeInclusive<usize>, range: RangeInclusive<usize>,
} }
// 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<usize, instruction::Kind>,
/// See [`GraphIr::edges`]. RON wants you to type the set as a list.
edges: Map<SemiHumanSocket, Set<SemiHumanSocket>>,
}
#[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<SemiHumanSocket> 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<SemiHumanGraphIr> 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<SemiHumanSocket, Set<SemiHumanSocket>>,
) -> Map<id::Output, Set<id::Input>> {
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<SemiHumanSocket, Set<SemiHumanSocket>>,
) -> Map<id::Input, Set<id::Output>> {
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
})
}

View file

@ -1,12 +1,17 @@
( (
[ instructions: {
Read(( 0: Read((
source: File("testfiles/juan.png"), source: File("testfiles/juan.jpg"),
format: Jpeg format: Jpeg,
)), )),
Write(( 1: Write((
target: File("testfiles/out.png"), target: File("testfiles/out.png"),
format: Png format: Png,
)) )),
] },
edges: {
(on: 0, idx: 0): [
(on: 1, idx: 0),
],
},
) )

View file

@ -1,13 +1,17 @@
( (
[ instructions: {
Read(( 0: Read((
source: File("testfiles/juan.jpg"), source: File("testfiles/juan.jpg"),
format: Jpeg format: Jpeg,
)), )),
Filter(Invert), 1: Filter(Invert),
Write(( 2: Write((
target: File("testfiles/inverted.jpg"), target: File("testfiles/inverted.png"),
format: Jpeg format: Png,
)) )),
] },
edges: {
(on: 0, idx: 0): [(on: 1, idx: 0)],
(on: 1, idx: 0): [(on: 2, idx: 0)],
},
) )