From fcf7e909ee98b07562d805f0ce9bd436172b139e Mon Sep 17 00:00:00 2001 From: MultisampledNight Date: Fri, 12 Jan 2024 17:23:17 +0100 Subject: [PATCH] feat(ir): replace Rpl with GraphIr Semi-broken as atm the CLI just does nothing except printing the parsed IR, instead of actually executing it. --- Cargo.lock | 117 +++++++++--------- Cargo.toml | 6 +- crates/executor/Cargo.toml | 2 +- crates/executor/src/debug/instructions/mod.rs | 4 +- crates/executor/src/debug/mod.rs | 16 +-- crates/executor/src/lib.rs | 6 +- crates/{rpl => ir}/Cargo.toml | 5 +- .../src/instruction}/mod.rs | 12 +- .../src/instruction}/read.rs | 6 +- .../src/instruction}/write.rs | 6 +- crates/ir/src/lib.rs | 108 ++++++++++++++++ crates/{rpl => ir}/src/value/mod.rs | 0 crates/rpl/src/lib.rs | 51 -------- flake.nix | 2 +- 14 files changed, 198 insertions(+), 143 deletions(-) rename crates/{rpl => ir}/Cargo.toml (62%) rename crates/{rpl/src/instructions => ir/src/instruction}/mod.rs (59%) rename crates/{rpl/src/instructions => ir/src/instruction}/read.rs (54%) rename crates/{rpl/src/instructions => ir/src/instruction}/write.rs (55%) create mode 100644 crates/ir/src/lib.rs rename crates/{rpl => ir}/src/value/mod.rs (100%) delete mode 100644 crates/rpl/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 091f8a8..733ec12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,20 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + [[package]] name = "anstream" version = "0.6.5" @@ -171,6 +185,15 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "cli" +version = "0.1.0" +dependencies = [ + "clap", + "executor", + "ir", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -271,7 +294,7 @@ version = "0.1.0" dependencies = [ "clap", "image", - "rpl", + "ir", ] [[package]] @@ -374,10 +397,13 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +name = "ir" +version = "0.1.0" +dependencies = [ + "ahash", + "ron", + "serde", +] [[package]] name = "jpeg-decoder" @@ -400,17 +426,6 @@ version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" -dependencies = [ - "bitflags 2.4.1", - "libc", - "redox_syscall", -] - [[package]] name = "lock_api" version = "0.4.11" @@ -462,34 +477,10 @@ dependencies = [ ] [[package]] -name = "num_threads" -version = "0.1.6" +name = "once_cell" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "owo-colors" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" - -[[package]] -name = "pl-cli" -version = "0.1.0" -dependencies = [ - "clap", - "executor", - "rpl", -] +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "png" @@ -589,20 +580,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "rpl" -version = "0.1.0" -dependencies = [ - "ron", - "serde", -] - -[[package]] -name = "ryu" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" - [[package]] name = "scopeguard" version = "1.2.0" @@ -747,6 +724,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -892,10 +875,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] -name = "yansi" -version = "0.5.1" +name = "zerocopy" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zune-inflate" diff --git a/Cargo.toml b/Cargo.toml index c6acf53..74dd3e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,13 +2,13 @@ members = [ "crates/app", "crates/executor", - "crates/rpl" + "crates/ir", + "crates/cli", ] resolver = "2" [workspace.dependencies] -clap = { version = "4", features = [ "derive" ] } -serde = { version = "1.0", features = [ "derive" ] } +clap = { version = "4", features = ["derive"] } # to enable all the lints below, this must be present in a workspace member's Cargo.toml: # [lints] diff --git a/crates/executor/Cargo.toml b/crates/executor/Cargo.toml index 49d9685..711b9b9 100644 --- a/crates/executor/Cargo.toml +++ b/crates/executor/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] clap = { workspace = true, features = [ "derive" ] } image = "0.24" -rpl = { path = "../rpl" } +ir = { path = "../ir" } [lints] workspace = true diff --git a/crates/executor/src/debug/instructions/mod.rs b/crates/executor/src/debug/instructions/mod.rs index 7fa5531..b78c7b5 100644 --- a/crates/executor/src/debug/instructions/mod.rs +++ b/crates/executor/src/debug/instructions/mod.rs @@ -1,6 +1,6 @@ pub mod read { use image::{io::Reader as ImageReader, DynamicImage}; - use rpl::instructions::read::{Read, SourceType}; + use ir::instruction::read::{Read, SourceType}; pub fn read(Read { source, .. }: Read) -> DynamicImage { // TODO: actual error handling @@ -15,7 +15,7 @@ pub mod read { pub mod write { use image::{DynamicImage, ImageFormat}; - use rpl::instructions::write::{TargetFormat, TargetType, Write}; + use ir::instruction::write::{TargetFormat, TargetType, Write}; pub fn write(Write { target, format }: Write, input_data: &DynamicImage) { input_data diff --git a/crates/executor/src/debug/mod.rs b/crates/executor/src/debug/mod.rs index b7524d3..b9cd743 100644 --- a/crates/executor/src/debug/mod.rs +++ b/crates/executor/src/debug/mod.rs @@ -1,4 +1,4 @@ -use rpl::instructions::{Filter, Instruction}; +use ir::instruction::{Filter, Kind}; use crate::value::Dynamic; mod instructions; @@ -6,12 +6,12 @@ mod instructions; pub struct Executor; impl crate::Executor for Executor { - fn execute(instruction: Instruction, input: Option) -> Option { + fn execute(instruction: Kind, input: Option) -> Option { match instruction { - Instruction::Read(read_instruction) => { + Kind::Read(read_instruction) => { Some(Dynamic::Image(instructions::read::read(read_instruction))) } - Instruction::Write(write_instruction) => { + Kind::Write(write_instruction) => { instructions::write::write( write_instruction, match input { @@ -21,10 +21,10 @@ impl crate::Executor for Executor { ); None } - Instruction::Math(_) => todo!(), - Instruction::Blend(_) => todo!(), - Instruction::Noise(_) => todo!(), - Instruction::Filter(filter_instruction) => match filter_instruction { + Kind::Math(_) => todo!(), + Kind::Blend(_) => todo!(), + Kind::Noise(_) => todo!(), + Kind::Filter(filter_instruction) => match filter_instruction { Filter::Invert => Some(Dynamic::Image(instructions::filters::invert::invert( match input { Some(Dynamic::Image(img)) => img, diff --git a/crates/executor/src/lib.rs b/crates/executor/src/lib.rs index 9e4da44..639947e 100644 --- a/crates/executor/src/lib.rs +++ b/crates/executor/src/lib.rs @@ -1,4 +1,4 @@ -use rpl::instructions::Instruction; +use ir::instruction::Kind; use value::Dynamic; mod debug; @@ -17,10 +17,10 @@ pub enum Executors { } trait Executor { - fn execute(instruction: Instruction, input: Option) -> Option; + fn execute(instruction: Kind, input: Option) -> Option; } -pub fn execute_all(instructions: Vec) { +pub fn execute_all(instructions: Vec) { let mut tmp = None; for instruction in instructions { diff --git a/crates/rpl/Cargo.toml b/crates/ir/Cargo.toml similarity index 62% rename from crates/rpl/Cargo.toml rename to crates/ir/Cargo.toml index 7cfd2ce..1bb10f9 100644 --- a/crates/rpl/Cargo.toml +++ b/crates/ir/Cargo.toml @@ -1,13 +1,14 @@ [package] -name = "rpl" +name = "ir" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -serde = { workspace = true, features = [ "derive" ] } +ahash = { version = "0.8.7", features = ["serde"] } ron = "0.8" +serde = { version = "1.0.193", features = ["derive"] } [lints] workspace = true diff --git a/crates/rpl/src/instructions/mod.rs b/crates/ir/src/instruction/mod.rs similarity index 59% rename from crates/rpl/src/instructions/mod.rs rename to crates/ir/src/instruction/mod.rs index 76ecc22..adb46e6 100644 --- a/crates/rpl/src/instructions/mod.rs +++ b/crates/ir/src/instruction/mod.rs @@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize}; pub mod read; pub mod write; -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] -pub enum Instruction { +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum Kind { Read(read::Read), Write(write::Write), Math(Math), @@ -13,7 +13,7 @@ pub enum Instruction { Filter(Filter), } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Math { Add, Subtract, @@ -21,7 +21,7 @@ pub enum Math { Divide, } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Blend { Normal, Multiply, @@ -34,14 +34,14 @@ pub enum Blend { Lighten, } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Noise { Perlin, Simplex, Voronoi, } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Filter { Invert, } diff --git a/crates/rpl/src/instructions/read.rs b/crates/ir/src/instruction/read.rs similarity index 54% rename from crates/rpl/src/instructions/read.rs rename to crates/ir/src/instruction/read.rs index 6ccd4d3..7f68524 100644 --- a/crates/rpl/src/instructions/read.rs +++ b/crates/ir/src/instruction/read.rs @@ -1,18 +1,18 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Read { pub source: SourceType, pub format: SourceFormat, } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum SourceType { File(PathBuf), } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum SourceFormat { Jpeg, Png, diff --git a/crates/rpl/src/instructions/write.rs b/crates/ir/src/instruction/write.rs similarity index 55% rename from crates/rpl/src/instructions/write.rs rename to crates/ir/src/instruction/write.rs index d5ea207..973fab8 100644 --- a/crates/rpl/src/instructions/write.rs +++ b/crates/ir/src/instruction/write.rs @@ -1,18 +1,18 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Write { pub target: TargetType, pub format: TargetFormat, } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum TargetType { File(PathBuf), } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum TargetFormat { Jpeg, Png, diff --git a/crates/ir/src/lib.rs b/crates/ir/src/lib.rs new file mode 100644 index 0000000..790221d --- /dev/null +++ b/crates/ir/src/lib.rs @@ -0,0 +1,108 @@ +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, +} diff --git a/crates/rpl/src/value/mod.rs b/crates/ir/src/value/mod.rs similarity index 100% rename from crates/rpl/src/value/mod.rs rename to crates/ir/src/value/mod.rs diff --git a/crates/rpl/src/lib.rs b/crates/rpl/src/lib.rs deleted file mode 100644 index 657b07d..0000000 --- a/crates/rpl/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -use instructions::Instruction; -use serde::{Deserialize, Serialize}; - -pub mod instructions; - -/// # Panics -/// -/// Panics if deserialization fails. lol. -#[must_use] -pub fn from_ron(raw: &str) -> Rpl { - ron::from_str(raw).expect("come back later and handle me correctly") -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] -pub struct Rpl(pub Vec); - -#[cfg(test)] -mod tests { - use super::*; - use crate::instructions::{ - read::{SourceFormat, SourceType}, - write::{TargetFormat, TargetType}, - Math, - }; - - #[test] - fn test_simple_deserialize() { - const TEST_DATA: &str = concat!( - "([", - "Read( (source: File(\"~/example/file.png\"), format: Png) ),", - "Math(Add),", - "Write(( target: File(\"~/example/out.jpg\"), format: Jpeg)),", - "])", - ); - - assert_eq!( - from_ron(TEST_DATA), - Rpl(vec![ - Instruction::Read(instructions::read::Read { - source: SourceType::File("~/example/file.png".into()), - format: SourceFormat::Png - }), - Instruction::Math(Math::Add), - Instruction::Write(instructions::write::Write { - target: TargetType::File("~/example/out.jpg".into()), - format: TargetFormat::Jpeg - }) - ]) - ); - } -} diff --git a/flake.nix b/flake.nix index aa617a3..b0d5bde 100644 --- a/flake.nix +++ b/flake.nix @@ -40,7 +40,7 @@ just nushell typst typst-lsp mold - cargo-nextest + cargo-nextest cargo-watch ]; }) ];