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.
This commit is contained in:
multisn8 2024-01-12 17:23:17 +01:00
parent 446db2563d
commit 44433bba58
Signed by: multisamplednight
GPG key ID: 6D525AA147CBDAE2
16 changed files with 233 additions and 105 deletions

102
Cargo.lock generated
View file

@ -8,6 +8,20 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 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]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.5" version = "0.6.5"
@ -147,6 +161,15 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "cli"
version = "0.1.0"
dependencies = [
"clap",
"executor",
"ir",
]
[[package]] [[package]]
name = "color_quant" name = "color_quant"
version = "1.1.0" version = "1.1.0"
@ -217,7 +240,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"image", "image",
"rpl", "ir",
] ]
[[package]] [[package]]
@ -264,6 +287,17 @@ dependencies = [
"spin", "spin",
] ]
[[package]]
name = "getrandom"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]] [[package]]
name = "gif" name = "gif"
version = "0.12.0" version = "0.12.0"
@ -308,6 +342,15 @@ dependencies = [
"tiff", "tiff",
] ]
[[package]]
name = "ir"
version = "0.1.0"
dependencies = [
"ahash",
"ron",
"serde",
]
[[package]] [[package]]
name = "jpeg-decoder" name = "jpeg-decoder"
version = "0.3.0" version = "0.3.0"
@ -323,6 +366,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.11" version = "0.4.11"
@ -374,13 +423,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "pl-cli" name = "once_cell"
version = "0.1.0" version = "1.19.0"
dependencies = [ source = "registry+https://github.com/rust-lang/crates.io-index"
"clap", checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
"executor",
"rpl",
]
[[package]] [[package]]
name = "png" name = "png"
@ -454,14 +500,6 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "rpl"
version = "0.1.0"
dependencies = [
"ron",
"serde",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
@ -549,6 +587,18 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "weezl" name = "weezl"
version = "0.1.7" version = "0.1.7"
@ -621,6 +671,26 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]] [[package]]
name = "zune-inflate" name = "zune-inflate"
version = "0.2.54" version = "0.2.54"

View file

@ -1,13 +1,13 @@
[workspace] [workspace]
members = [ members = [
"crates/executor", "crates/executor",
"crates/pl-cli", "crates/ir",
"crates/rpl" "crates/cli",
] ]
resolver = "2" resolver = "2"
[workspace.dependencies] [workspace.dependencies]
clap = { version = "4", features = [ "derive" ] } clap = { version = "4", features = ["derive"] }
# to enable all the lints below, this must be present in a workspace member's Cargo.toml: # to enable all the lints below, this must be present in a workspace member's Cargo.toml:
# [lints] # [lints]

View file

@ -1,14 +1,14 @@
[package] [package]
name = "pl-cli" name = "cli"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
clap = { workspace = true, features = [ "derive" ] } clap = { workspace = true }
rpl = { path = "../rpl" }
executor = { path = "../executor" } executor = { path = "../executor" }
ir = { path = "../ir" }
[lints] [lints]
workspace = true workspace = true

View file

@ -1,7 +1,7 @@
use std::{fs, path::PathBuf}; use std::{fs, path::PathBuf};
use clap::Parser; use clap::Parser;
use executor::{execute_all, Executors}; use executor::Executors;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct Args { pub struct Args {
@ -14,7 +14,7 @@ fn main() {
let f = fs::read_to_string(args.file) let f = fs::read_to_string(args.file)
.expect("reading IR failed — come back to this later handle errors properly"); .expect("reading IR failed — come back to this later handle errors properly");
let pl = rpl::from_ron(&f); let pl = ir::from_ron(&f).expect("handle me properly");
execute_all(pl.0); dbg!(pl);
} }

View file

@ -8,7 +8,7 @@ edition = "2021"
[dependencies] [dependencies]
clap = { workspace = true, features = [ "derive" ] } clap = { workspace = true, features = [ "derive" ] }
image = "0.24" image = "0.24"
rpl = { path = "../rpl" } ir = { path = "../ir" }
[lints] [lints]
workspace = true workspace = true

View file

@ -1,6 +1,6 @@
pub mod read { pub mod read {
use image::{io::Reader as ImageReader, DynamicImage}; 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 { pub fn read(Read { source, .. }: Read) -> DynamicImage {
// TODO: actual error handling // TODO: actual error handling
@ -15,7 +15,7 @@ pub mod read {
pub mod write { pub mod write {
use image::{DynamicImage, ImageFormat}; 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) { pub fn write(Write { target, format }: Write, input_data: &DynamicImage) {
input_data input_data

View file

@ -1,4 +1,4 @@
use rpl::instructions::{Filter, Instruction}; use ir::instruction::{Filter, Kind};
use crate::value::Dynamic; use crate::value::Dynamic;
mod instructions; mod instructions;
@ -6,12 +6,12 @@ mod instructions;
pub struct Executor; pub struct Executor;
impl crate::Executor for Executor { impl crate::Executor for Executor {
fn execute(instruction: Instruction, input: Option<Dynamic>) -> Option<Dynamic> { fn execute(instruction: Kind, input: Option<Dynamic>) -> Option<Dynamic> {
match instruction { match instruction {
Instruction::Read(read_instruction) => { Kind::Read(read_instruction) => {
Some(Dynamic::Image(instructions::read::read(read_instruction))) Some(Dynamic::Image(instructions::read::read(read_instruction)))
} }
Instruction::Write(write_instruction) => { Kind::Write(write_instruction) => {
instructions::write::write( instructions::write::write(
write_instruction, write_instruction,
match input { match input {
@ -21,10 +21,10 @@ impl crate::Executor for Executor {
); );
None None
} }
Instruction::Math(_) => todo!(), Kind::Math(_) => todo!(),
Instruction::Blend(_) => todo!(), Kind::Blend(_) => todo!(),
Instruction::Noise(_) => todo!(), Kind::Noise(_) => todo!(),
Instruction::Filter(filter_instruction) => match filter_instruction { Kind::Filter(filter_instruction) => match filter_instruction {
Filter::Invert => Some(Dynamic::Image(instructions::filters::invert::invert( Filter::Invert => Some(Dynamic::Image(instructions::filters::invert::invert(
match input { match input {
Some(Dynamic::Image(img)) => img, Some(Dynamic::Image(img)) => img,

View file

@ -1,4 +1,4 @@
use rpl::instructions::Instruction; use ir::instruction::Kind;
use value::Dynamic; use value::Dynamic;
mod debug; mod debug;
@ -17,10 +17,10 @@ pub enum Executors {
} }
trait Executor { trait Executor {
fn execute(instruction: Instruction, input: Option<Dynamic>) -> Option<Dynamic>; fn execute(instruction: Kind, input: Option<Dynamic>) -> Option<Dynamic>;
} }
pub fn execute_all(instructions: Vec<Instruction>) { pub fn execute_all(instructions: Vec<Kind>) {
let mut tmp = None; let mut tmp = None;
for instruction in instructions { for instruction in instructions {

View file

@ -1,13 +1,14 @@
[package] [package]
name = "rpl" name = "ir"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
serde = { version = "1.0.193", features = [ "derive" ] } ahash = { version = "0.8.7", features = ["serde"] }
ron = "0.8" ron = "0.8"
serde = { version = "1.0.193", features = ["derive"] }
[lints] [lints]
workspace = true workspace = true

View file

@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize};
pub mod read; pub mod read;
pub mod write; pub mod write;
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Instruction { pub enum Kind {
Read(read::Read), Read(read::Read),
Write(write::Write), Write(write::Write),
Math(Math), Math(Math),
@ -13,7 +13,7 @@ pub enum Instruction {
Filter(Filter), Filter(Filter),
} }
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Math { pub enum Math {
Add, Add,
Subtract, Subtract,
@ -21,7 +21,7 @@ pub enum Math {
Divide, Divide,
} }
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Blend { pub enum Blend {
Normal, Normal,
Multiply, Multiply,
@ -34,14 +34,14 @@ pub enum Blend {
Lighten, Lighten,
} }
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Noise { pub enum Noise {
Perlin, Perlin,
Simplex, Simplex,
Voronoi, Voronoi,
} }
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Filter { pub enum Filter {
Invert, Invert,
} }

View file

@ -1,18 +1,18 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Read { pub struct Read {
pub source: SourceType, pub source: SourceType,
pub format: SourceFormat, pub format: SourceFormat,
} }
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum SourceType { pub enum SourceType {
File(PathBuf), File(PathBuf),
} }
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum SourceFormat { pub enum SourceFormat {
Jpeg, Jpeg,
Png, Png,

View file

@ -1,18 +1,18 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Write { pub struct Write {
pub target: TargetType, pub target: TargetType,
pub format: TargetFormat, pub format: TargetFormat,
} }
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum TargetType { pub enum TargetType {
File(PathBuf), File(PathBuf),
} }
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum TargetFormat { pub enum TargetFormat {
Jpeg, Jpeg,
Png, Png,

108
crates/ir/src/lib.rs Normal file
View file

@ -0,0 +1,108 @@
use std::ops::RangeInclusive;
use serde::{Deserialize, Serialize};
pub mod instruction;
pub type Map<K, V> = ahash::AHashMap<K, V>;
pub type Set<V> = ahash::AHashSet<V>;
/// # Errors
///
/// Returns an error if the parsed source is not a valid graph IR.
pub fn from_ron(source: &str) -> ron::error::SpannedResult<GraphIr> {
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<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>>,
}
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<usize>,
}

View file

@ -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<Instruction>);
#[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
})
])
);
}
}

View file

@ -40,7 +40,7 @@
just nushell just nushell
typst typst-lsp typst typst-lsp
mold mold
cargo-nextest cargo-nextest cargo-watch
]; ];
}) })
]; ];