commit ed9027d12c4ad6b2febdccc74fdc5f431ba9023f Author: Albin Chaboissier Date: Thu Mar 5 23:03:21 2026 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e11f367 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,181 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "ntw" +version = "0.1.0" +dependencies = [ + "ntw_flowgraph", + "ntw_flowgraph_macros", + "ringbuf", +] + +[[package]] +name = "ntw_flowgraph" +version = "0.1.0" +dependencies = [ + "ntw_flowgraph_macros", + "petgraph", + "ringbuf", +] + +[[package]] +name = "ntw_flowgraph_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", + "serde", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ringbuf" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c" +dependencies = [ + "crossbeam-utils", + "portable-atomic", + "portable-atomic-util", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..47a9577 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ntw" +version = "0.1.0" +edition = "2024" + +[dependencies] +ntw_flowgraph = {path = "ntw_flowgraph"} +ntw_flowgraph_macros = {path = "ntw_flowgraph_macros"} +ringbuf = "0.4.8" diff --git a/ntw_flowgraph/.gitignore b/ntw_flowgraph/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/ntw_flowgraph/.gitignore @@ -0,0 +1 @@ +/target diff --git a/ntw_flowgraph/Cargo.lock b/ntw_flowgraph/Cargo.lock new file mode 100644 index 0000000..260d5b4 --- /dev/null +++ b/ntw_flowgraph/Cargo.lock @@ -0,0 +1,172 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "ntw_flowgraph" +version = "0.1.0" +dependencies = [ + "ntw_flowgraph_macros", + "petgraph", + "ringbuf", +] + +[[package]] +name = "ntw_flowgraph_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", + "serde", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ringbuf" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c" +dependencies = [ + "crossbeam-utils", + "portable-atomic", + "portable-atomic-util", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" diff --git a/ntw_flowgraph/Cargo.toml b/ntw_flowgraph/Cargo.toml new file mode 100644 index 0000000..b343aa2 --- /dev/null +++ b/ntw_flowgraph/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ntw_flowgraph" +version = "0.1.0" +edition = "2024" + +[dependencies] +petgraph = "0.8.3" +ringbuf = "0.4.8" +ntw_flowgraph_macros = {path = "../ntw_flowgraph_macros/"} diff --git a/ntw_flowgraph/src/graph.rs b/ntw_flowgraph/src/graph.rs new file mode 100644 index 0000000..fdf0653 --- /dev/null +++ b/ntw_flowgraph/src/graph.rs @@ -0,0 +1,86 @@ +use petgraph::graph::DiGraph; +use petgraph::graph::NodeIndex; + +use crate::Block; + +pub struct Graph +{ + blocks: Vec>, +} + +impl Graph +{ + pub fn new() -> Graph + { + Graph { blocks: vec![] } + } + + pub fn add_block(&mut self, block: impl Block + 'static) + { + block.set_block_index(self.blocks.len()); + self.blocks.push(Box::new(block)); + } + + pub fn run(&mut self) + { + // Compute the topo_order + let mut digraph = DiGraph::<(), (), usize>::with_capacity(self.blocks.len(), 1); + + for block in self.blocks.iter() + { + let node = digraph.add_node(()); + + for next in block.get_successors() + { + digraph.add_edge(node, next.into(), ()); + } + } + + let topo_order = petgraph::algo::toposort(&digraph, None) + .expect("Graph cannot be cylic") + .into_iter() + .map(|x| x.index()) + .collect::>(); + + // Dumbass round robin + loop + { + let mut one_ready = false; + for x in topo_order.iter() + { + let block = &mut self.blocks[*x]; + if block.ready() + { + block.work(); + one_ready = true; + } + } + + if !one_ready + { + break; + } + } + } + + pub fn print_deps(&self) + { + for (i, e) in self.blocks.iter().enumerate() + { + print!("{}: ", i); + for x in e.get_successors() + { + print!("{}, ", x); + } + println!(); + } + } +} + +impl Default for Graph +{ + fn default() -> Self + { + Self::new() + } +} diff --git a/ntw_flowgraph/src/inout.rs b/ntw_flowgraph/src/inout.rs new file mode 100644 index 0000000..f7390df --- /dev/null +++ b/ntw_flowgraph/src/inout.rs @@ -0,0 +1,65 @@ +use ringbuf::HeapRb; +use ringbuf::SharedRb; +use ringbuf::storage::Heap; +use ringbuf::traits::Producer; +use ringbuf::traits::Split; +use ringbuf::wrap::caching::Caching; +use std::cell::Cell; +use std::rc::Rc; +use std::sync::Arc; + +// Represent a block input, of which data is popped +pub struct In +{ + block: Rc>>, + pub rb: Caching>>, false, true>, +} + +// Represent a block input, in which data is pushed +pub struct Out +{ + to: Rc>>, + pub rb: Caching>>, true, false>, +} + +pub struct Stream; + +impl Stream +{ + pub fn make(length: usize) -> (Out, In) + { + let (prod, cons) = HeapRb::::new(length).split(); + let to = Rc::new(Cell::new(None)); + ( + Out { + to: to.clone(), + rb: prod, + }, + In { + block: to.clone(), + rb: cons, + }, + ) + } +} + +impl In +{ + pub fn set_index(&self, index: usize) + { + self.block.set(Some(index)) + } +} + +impl Out +{ + pub fn get_successor(&self) -> Option + { + self.to.get() + } + + pub fn try_push(&mut self, data: T) -> Result<(), T> + { + self.rb.try_push(data) + } +} diff --git a/ntw_flowgraph/src/lib.rs b/ntw_flowgraph/src/lib.rs new file mode 100644 index 0000000..16337b6 --- /dev/null +++ b/ntw_flowgraph/src/lib.rs @@ -0,0 +1,14 @@ +pub mod graph; +pub mod inout; + +pub trait BlockWork +{ + fn work(&mut self); + fn ready(&self) -> bool; +} + +pub trait Block: BlockWork +{ + fn set_block_index(&self, index: usize); + fn get_successors(&self) -> Vec; +} diff --git a/ntw_flowgraph_macros/.gitignore b/ntw_flowgraph_macros/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/ntw_flowgraph_macros/.gitignore @@ -0,0 +1 @@ +/target diff --git a/ntw_flowgraph_macros/Cargo.lock b/ntw_flowgraph_macros/Cargo.lock new file mode 100644 index 0000000..5a50dce --- /dev/null +++ b/ntw_flowgraph_macros/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "streams_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" diff --git a/ntw_flowgraph_macros/Cargo.toml b/ntw_flowgraph_macros/Cargo.toml new file mode 100644 index 0000000..7361208 --- /dev/null +++ b/ntw_flowgraph_macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ntw_flowgraph_macros" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.106" +quote = "1.0.45" +syn = {version = "2.0.117", features = ["extra-traits"]} diff --git a/ntw_flowgraph_macros/src/lib.rs b/ntw_flowgraph_macros/src/lib.rs new file mode 100644 index 0000000..1055f61 --- /dev/null +++ b/ntw_flowgraph_macros/src/lib.rs @@ -0,0 +1,126 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::DeriveInput; +use syn::Ident; +use syn::parse_macro_input; + +struct BlockDerive +{ + input_fields: Vec, + output_fields: Vec, +} + +fn set_block_index_func(ctx: &BlockDerive) -> proc_macro2::TokenStream +{ + let inputs = ctx.input_fields.clone(); + quote! { + fn set_block_index(&self, index: usize) + { + #({ + self.#inputs.set_index(index); + })* + } + } +} + +fn get_successors_func(ctx: &BlockDerive) -> proc_macro2::TokenStream +{ + let outputs = ctx.output_fields.clone(); + quote! { + fn get_successors(&self) -> Vec + { + let mut output = vec![]; + #( + match self.#outputs.get_successor() + { + None => {}, + Some(x) => output.push(x) + } + )* + + output + } + } +} + +#[proc_macro_derive(Block, attributes(input, output))] +pub fn block_derive(item: TokenStream) -> TokenStream +{ + let cloned = item.clone(); + let input = parse_macro_input!(cloned as DeriveInput); + + let data_struct = match input.data + { + syn::Data::Struct(data_struct) => data_struct, + syn::Data::Enum(_) | syn::Data::Union(_) => panic!(), + }; + + let struct_fields = match data_struct.fields + { + syn::Fields::Named(fields_named) => fields_named, + syn::Fields::Unnamed(_) | syn::Fields::Unit => panic!(), + }; + + let input_fields = struct_fields + .named + .iter() + .filter_map(|f| { + if f.ident.is_some() + && f.attrs.iter().any(|attr| { + attr.meta + .path() + .segments + .last() + .is_some_and(|s| s.ident == "input") + }) + { + Some(f.ident.clone().unwrap()) + } + else + { + None + } + }) + .collect::>(); + + let output_fields = struct_fields + .named + .iter() + .filter_map(|f| { + if f.ident.is_some() + && f.attrs.iter().any(|attr| { + attr.meta + .path() + .segments + .last() + .is_some_and(|s| s.ident == "output") + }) + { + Some(f.ident.clone().unwrap()) + } + else + { + None + } + }) + .collect::>(); + + let derive = BlockDerive { + input_fields, + output_fields, + }; + + let set_index_func = set_block_index_func(&derive); + let get_successors_func = get_successors_func(&derive); + let struct_path = input.ident; + + //item + quote! { + impl Block for #struct_path + { + #set_index_func + #get_successors_func + } + } + .into() +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ed3ef91 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,143 @@ +use ntw_flowgraph::Block; +use ntw_flowgraph::BlockWork; +use ntw_flowgraph::graph::Graph; +use ntw_flowgraph::inout::In; +use ntw_flowgraph::inout::Out; +use ntw_flowgraph::inout::Stream; +use ntw_flowgraph_macros::Block; +use ringbuf::traits::Consumer; +use ringbuf::traits::Observer; +use ringbuf::traits::Producer; + +#[derive(Block)] +pub struct VecSource +{ + vector: Vec, + + #[output] + out: Out, +} + +impl BlockWork for VecSource +{ + fn work(&mut self) + { + while let Some(element) = self.vector.pop() + { + match self.out.rb.try_push(element) + { + Ok(()) => + {} + Err(x) => + { + self.vector.push(x); + break; + } + } + } + } + + fn ready(&self) -> bool + { + self.vector.len() > 0 && self.out.rb.vacant_len() > 0 + } +} + +impl VecSource +{ + pub fn new(vector: Vec) -> (VecSource, In) + { + let (out, stream) = Stream::make(16); + (VecSource { vector, out }, stream) + } +} + +#[derive(Block)] +pub struct Adder +{ + #[input] + in_a: In, + + #[input] + in_b: In, + + #[output] + out: Out, +} + +impl BlockWork for Adder +{ + fn work(&mut self) + { + while let Some(a) = self.in_a.rb.try_pop() + && let Some(b) = self.in_b.rb.try_pop() + && self.out.rb.vacant_len() > 0 + { + let _ = self.out.rb.try_push(a + b); + } + } + + fn ready(&self) -> bool + { + self.in_a.rb.occupied_len() > 0 + && self.in_b.rb.occupied_len() > 0 + && self.out.rb.vacant_len() > 0 + } +} + +impl Adder +{ + pub fn new(in_a: In, in_b: In) -> (Adder, In) + { + let (out, stream) = Stream::make(16); + (Adder { in_a, in_b, out }, stream) + } +} + +#[derive(Block)] +pub struct PrintSink +{ + #[input] + stream: In, +} + +impl BlockWork for PrintSink +{ + fn work(&mut self) + { + if let Some(x) = self.stream.rb.try_pop() + { + println!("{x}"); + } + } + + fn ready(&self) -> bool + { + self.stream.rb.occupied_len() > 0 + } +} + +impl PrintSink +{ + pub fn new(stream: In) -> PrintSink + { + PrintSink { stream } + } +} + +fn main() +{ + let (vector_a, a) = VecSource::new((0..15).collect()); + let (vector_b, b) = VecSource::new((0..15).collect()); + let (adder, sum) = Adder::new(a, b); + let printer = PrintSink::new(sum); + + let mut graph = Graph::new(); + + graph.add_block(printer); + graph.add_block(adder); + graph.add_block(vector_a); + graph.add_block(vector_b); + + graph.run(); +}