From 600d0b3acab8036d0366131defedf198bb316896 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 4 Jan 2025 08:49:38 +0100 Subject: [PATCH] wip: try to get a simplified parser going --- Cargo.lock | 10 +- Cargo.toml | 6 +- README.md | 6 + edn/Cargo.lock | 92 ++++++++++ edn/Cargo.toml | 8 + edn/src/edn_layout.rs | 118 +++++++++++++ edn/src/edn_lib.rs | 296 +++++++++++++++++++++++++++++++++ edn/src/example.edn | 12 ++ edn/src/lib.rs | 2 + engine/Cargo.toml | 1 - engine/src/edn.rs | 35 ---- engine/src/lib.rs | 4 +- layout/src/layout_edn.rs | 47 ------ layout/src/lib.rs | 4 +- src/groovebox/groovebox.edn | 12 ++ src/groovebox/groovebox_tui.rs | 148 ++++++++++++----- src/lib.rs | 8 +- 17 files changed, 676 insertions(+), 133 deletions(-) create mode 100644 edn/Cargo.lock create mode 100644 edn/Cargo.toml create mode 100644 edn/src/edn_layout.rs create mode 100644 edn/src/edn_lib.rs create mode 100644 edn/src/example.edn create mode 100644 edn/src/lib.rs delete mode 100644 layout/src/layout_edn.rs create mode 100644 src/groovebox/groovebox.edn diff --git a/Cargo.lock b/Cargo.lock index 762b7603..50887011 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1369,7 +1369,6 @@ dependencies = [ "atomic_float", "backtrace", "clap", - "clojure-reader", "jack", "livi", "midly", @@ -1378,18 +1377,25 @@ dependencies = [ "quanta", "rand", "symphonia", + "tek_edn", "tek_layout", "toml", "uuid", "wavers", ] +[[package]] +name = "tek_edn" +version = "0.1.0" +dependencies = [ + "clojure-reader", +] + [[package]] name = "tek_engine" version = "0.2.0" dependencies = [ "better-panic", - "clojure-reader", "crossterm", "ratatui", ] diff --git a/Cargo.toml b/Cargo.toml index 3fcd44a1..73cd11b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,11 +5,11 @@ version = "0.2.0" [dependencies] tek_layout = { path = "./layout" } +tek_edn = { optional = true, path = "./edn" } atomic_float = "1.0.0" backtrace = "0.3.72" clap = { version = "4.5.4", features = [ "derive" ] } -clojure-reader = "0.3.0" jack = { path = "./rust-jack" } livi = "0.7.4" midly = "0.5" @@ -27,6 +27,10 @@ wavers = "1.4.3" #vst3 = "0.1.0" #winit = { version = "0.30.4", features = [ "x11" ] } +[features] +default = ["edn"] +edn = ["tek_edn"] + [[bin]] name = "tek_arranger" path = "bin/cli_arranger.rs" diff --git a/README.md b/README.md index 40708f51..61cf10fe 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,12 @@ from there, use the commands in the `Justfile`, e.g.: just arranger ``` +note that `tek > 0.2.0-rc.7` will require rust nightly +for the unstable features `type_alias_impl_trait` and +`impl_trait_in_assoc_type`. make some noise for lucky +[**rust rfc2515**](https://github.com/rust-lang/rust/issues/63063) +if you want to see this buildable with stable/beta. + ## design goals ### lightweight diff --git a/edn/Cargo.lock b/edn/Cargo.lock new file mode 100644 index 00000000..c0add748 --- /dev/null +++ b/edn/Cargo.lock @@ -0,0 +1,92 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "clojure-reader" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edf141eea627c101a97509266bc9f6ba8cd408618f5e2ac4a0cb6b64b1d4ea8" +dependencies = [ + "ordered-float", +] + +[[package]] +name = "const_panic" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53857514f72ee4a2b583de67401e3ff63a5472ca4acf289d09a9ea7636dfec17" + +[[package]] +name = "konst" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9" +dependencies = [ + "const_panic", + "konst_kernel", + "konst_proc_macros", + "typewit", +] + +[[package]] +name = "konst_kernel" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c" +dependencies = [ + "typewit", +] + +[[package]] +name = "konst_proc_macros" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00af7901ba50898c9e545c24d5c580c96a982298134e8037d8978b6594782c07" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "tek_edn" +version = "0.1.0" +dependencies = [ + "clojure-reader", + "konst", +] + +[[package]] +name = "typewit" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb77c29baba9e4d3a6182d51fa75e3215c7fd1dab8f4ea9d107c716878e55fc0" +dependencies = [ + "typewit_proc_macros", +] + +[[package]] +name = "typewit_proc_macros" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" diff --git a/edn/Cargo.toml b/edn/Cargo.toml new file mode 100644 index 00000000..1ee74954 --- /dev/null +++ b/edn/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "tek_edn" +edition = "2021" +version = "0.1.0" + +[dependencies] +clojure-reader = "0.3.0" +konst = "0.3.16" diff --git a/edn/src/edn_layout.rs b/edn/src/edn_layout.rs new file mode 100644 index 00000000..3773374b --- /dev/null +++ b/edn/src/edn_layout.rs @@ -0,0 +1,118 @@ +use crate::*; + +#[cfg(test)] #[test] fn test_edn_layout () -> Result<(), ParseError> { + let source = include_str!("example.edn"); + let layout = Item::read_all(source)?; + panic!("{layout:?}"); + let content = EdnLayout::from(&layout); + Ok(()) +} + +impl<'a> From<&'a [Item]> for EdnLayout<'a> { + fn from (items: &'a [Item]) -> Self { + Self(items) + } +} + +pub struct EdnLayout<'a, E>(&'a [Item]); +impl<'a, E> EdnLayout<'a, E> { + fn get_content (&self) -> impl Content { + } +} + +//pub struct EdnContent<'a, T>(T, &'a [Item]); + +#[macro_export] macro_rules! edn_ns { + ($name:literal = $Host:ident<$E:ident: $Engine:path> |$args:ident| { $( + ($fn:literal + $Struct:ident + $(<$($G:ident$(: $Generic:ty)?),+>)? + $(::$Variant:ident)? + ($($arg:expr),*)) + )* }) => { + //pub trait $Host<$E: Engine> { + //fn read_one <'e> (edn: &[Edn<'e>]) -> impl Content<$E> { + //if let Some(Edn::Symbol(name)) = edn.get(0) { + //match *name { + //$( + //$fn => $Struct$(::$Variant)?($($arg),+), + //)* + //_ => {} + //} + //} else { + //panic!("invalid edn") + //} + //} + //} + }; +} + +edn_ns! { + + "when" = When(args[0].into(), args[1].into()), + + "either" = Either(args[0].into(), args[1].into(), args[2].into()), + + "map" = Map(args[0].into(), args[1].into()), + + "fill" = edn_ns! { + "x" = Fixed::X(args[0].into(), args[1].into()), + "y" = Fixed::Y(args[0].into(), args[1].into()), + "xy" = Fixed::XY(args[0].into(), args[1].into(), args[2].into()), + }, + + "fixed" = edn_ns! { + "x" = Fixed::X(args[0].into(), args[1].into()), + "y" = Fixed::Y(args[0].into(), args[1].into()), + "xy" = Fixed::XY(args[0].into(), args[1].into(), args[2].into()), + }, + + "shrink" = edn_ns! { + "x" = Shrink::X(args[0].into(), args[1].into()), + "y" = Shrink::Y(args[0].into(), args[1].into()), + "xy" = Shrink::XY(args[0].into(), args[1].into(), args[2].into()), + }, + + "expand" = edn_ns! { + "x" = Expand::X(args[0].into(), args[1].into()), + "y" = Expand::Y(args[0].into(), args[1].into()), + "xy" = Expand::XY(args[0].into(), args[1].into(), args[2].into()), + }, + + "min" = edn_ns! { + "x" = Min::X(args[0].into(), args[1].into()), + "y" = Min::Y(args[0].into(), args[1].into()), + "xy" = Min::XY(args[0].into(), args[1].into(), args[2].into()), + }, + + "max" = edn_ns! { + "x" = Max::X(args[0].into(), args[1].into()), + "y" = Max::Y(args[0].into(), args[1].into()), + "xy" = Max::XY(args[0].into(), args[1].into(), args[2].into()), + }, + + "push" = edn_ns! { + "x" = Push::X(args[0].into(), args[1].into()), + "y" = Push::Y(args[0].into(), args[1].into()), + "xy" = Push::XY(args[0].into(), args[1].into(), args[2].into()), + }, + + "pull" = edn_ns! { + "x" = Pull::X(args[0].into(), args[1].into()), + "y" = Pull::Y(args[0].into(), args[1].into()), + "xy" = Pull::XY(args[0].into(), args[1].into(), args[2].into()), + }, + + "margin" = edn_ns! { + "x" = Margin::X(args[0].into(), args[1].into()), + "y" = Margin::Y(args[0].into(), args[1].into()), + "xy" = Margin::XY(args[0].into(), args[1].into(), args[2].into()), + }, + + "padding" = edn_ns! { + "x" = Padding::X(args[0].into(), args[1].into()), + "y" = Padding::Y(args[0].into(), args[1].into()), + "xy" = Padding::XY(args[0].into(), args[1].into(), args[2].into()), + }, + +} diff --git a/edn/src/edn_lib.rs b/edn/src/edn_lib.rs new file mode 100644 index 00000000..92f14b92 --- /dev/null +++ b/edn/src/edn_lib.rs @@ -0,0 +1,296 @@ +use std::sync::{Arc, RwLock}; +use std::collections::BTreeMap; +pub use clojure_reader::edn::Edn; + +#[cfg(test)] #[test] fn test_edn () -> Result<(), ParseError> { + use Item::*; + assert_eq!(Item::read_all("")?, + vec![]); + assert_eq!(Item::read_all(" ")?, + vec![]); + assert_eq!(Item::read_all("1234")?, + vec![Num(1234)]); + assert_eq!(Item::read_all("1234 5 67")?, + vec![Num(1234), Num(5), Num(67)]); + assert_eq!(Item::read_all("foo/bar")?, + vec![Key("foo/bar".into())]); + assert_eq!(Item::read_all(":symbol")?, + vec![Sym(":symbol".into())]); + assert_eq!(Item::read_all(" foo/bar :baz 456")?, + vec![Key("foo/bar".into()), Sym(":baz".into()), Num(456)]); + assert_eq!(Item::read_all(" (foo/bar :baz 456) ")?, + vec![Exp(vec![Key("foo/bar".into()), Sym(":baz".into()), Num(456)])]); + Ok(()) +} + +fn number (digits: &str) -> usize { + let mut value = 0; + for c in digits.chars() { + value = 10 * value + digit(c); + } + value +} + +const fn digit (c: char) -> usize { + match c { '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, + '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, _ => unreachable!() } +} + +#[derive(Debug)] +pub enum ParseError { + Empty, + Unexpected(char), + Incomplete +} + +#[derive(Debug, Clone, Default, PartialEq)] +pub enum Item { + #[default] Nil, + Num(usize), + Sym(String), + Key(String), + Exp(Vec), +} + +impl Item { + pub fn read_all <'a> (mut source: &'a str) -> Result, ParseError> { + let mut items = vec![]; + loop { + if source.len() == 0 { + break + } + let (remaining, token) = Token::chomp(source)?; + match Item::read(token)? { Item::Nil => {}, item => items.push(item) }; + source = remaining + } + Ok(items) + } + pub fn read <'a> (token: Token<'a>) -> Result { + use Token::*; + Ok(match token { + Nil => Item::Nil, + Num(chars, index, length) => + Self::Num(number(&chars[index..index+length])), + Sym(chars, index, length) => + Self::Sym(chars[index..index+length].to_string()), + Key(chars, index, length) => + Self::Key(chars[index..index+length].to_string()), + Exp(chars, index, length, 0) => + Self::Exp(Self::read_all(&chars[index+1..(index+length).saturating_sub(1)])?), + _ => panic!("unclosed delimiter") + }) + } +} + + +#[derive(Debug, Copy, Clone, Default, PartialEq)] +pub enum Token<'a> { + #[default] Nil, + Num(&'a str, usize, usize), + Sym(&'a str, usize, usize), + Key(&'a str, usize, usize), + Exp(&'a str, usize, usize, usize), +} + +impl<'a> Token<'a> { + fn chomp (source: &'a str) -> Result<(&'a str, Self), ParseError> { + use Token::*; + let mut state = Self::default(); + for (index, c) in source.char_indices() { + state = match state { + // must begin expression + Nil => match c { + ' '|'\n'|'\r'|'\t' => Nil, + '(' => Exp(source, index, 1, 1), + ':' => Sym(source, index, 1), + '0'..='9' => Num(source, index, 1), + 'a'..='z' => Key(source, index, 1), + _ => return Err(ParseError::Unexpected(c)) + }, + Num(_, _, 0) => unreachable!(), + Sym(_, _, 0) => unreachable!(), + Key(_, _, 0) => unreachable!(), + Num(source, index, length) => match c { + '0'..='9' => Num(source, index, length + 1), + ' '|'\n'|'\r'|'\t' => return Ok((&source[index+length..], Num(source, index, length))), + _ => return Err(ParseError::Unexpected(c)) + }, + Sym(source, index, length) => match c { + 'a'..='z'|'0'..='9'|'-' => Sym(source, index, length + 1), + ' '|'\n'|'\r'|'\t' => return Ok((&source[index+length..], Sym(source, index, length))), + _ => return Err(ParseError::Unexpected(c)) + }, + Key(source, index, length) => match c { + 'a'..='z'|'0'..='9'|'-'|'/' => Key(source, index, length + 1), + ' '|'\n'|'\r'|'\t' => return Ok((&source[index+length..], Key(source, index, length))), + _ => return Err(ParseError::Unexpected(c)) + }, + Exp(source, index, length, 0) => match c { + ' '|'\n'|'\r'|'\t' => return Ok((&source[index+length..], Exp(source, index, length, 0))), + _ => return Err(ParseError::Unexpected(c)) + }, + Exp(source, index, length, depth) => match c { + ')' => Exp(source, index, length + 1, depth - 1), + '(' => Exp(source, index, length + 1, depth + 1), + _ => Exp(source, index, length + 1, depth) + }, + } + } + Ok(("", state)) + } +} + +//#[derive(Debug, Copy, Clone, Default, PartialEq)] +//pub struct Items<'a>(&'a [Item<'a>]); +//impl<'a> Items<'a> { + //fn iter (&'a self) -> ItemsIterator<'a> { + //ItemsIterator(0, self.0) + //} +//} + +//pub struct ItemsIterator<'a>(usize, &'a [Item<'a>]); +//impl<'a> Iterator for ItemsIterator<'a> { + //type Item = &'a Item<'a>; + //fn next (&mut self) -> Option { + //let item = self.1.get(self.0); + //self.0 += 1; + //item + //} +//} + +/* + +nice but doesn't work without compile time slice concat +(which i guess could be implemeted using an unsafe linked list?) +never done that one before im ny life, might try + +use konst::slice_concat; + +const fn read <'a> ( + chars: impl Iterator +) -> Result, ParseError> { + use Range::*; + let mut state = Range::Nil; + let mut tokens: &[Range<'a>] = &[]; + while let Some(c) = chars.next() { + state = match state { + // must begin expression + Nil => match c { + ' ' => Nil, + '(' => Exp(&[]), + ':' => Sym(&[]), + '1'..'9' => Num(digit(c)), + 'a'..'z' => Key(&[&[c]]), + _ => return Err(ParseError::Unexpected(c)) + }, + Num(b) => match c { + ' ' => return Ok(Num(digit(c))), + '1'..'9' => Num(b*10+digit(c)), + _ => return Err(ParseError::Unexpected(c)) + } + Sym([]) => match c { + 'a'..'z' => Sym(&[c]), + _ => return Err(ParseError::Unexpected(c)) + }, + Sym([b @ ..]) => match c { + ' ' => return Ok(Sym(&b)), + 'a'..'z' | '0'..'9' | '-' => Sym(&[..b, c]), + _ => return Err(ParseError::Unexpected(c)) + } + Key([[b @ ..]]) => match c { + ' ' => return Ok(Key(&[&b])), + '/' => Key(&[&b, &[]]), + 'a'..'z' | '0'..'9' | '-' => Key(&[&[..b, c], &[]]), + _ => return Err(ParseError::Unexpected(c)) + } + Key([s @ .., []]) => match c { + 'a'..'z' => Key(&[..s, &[c]]), + _ => return Err(ParseError::Unexpected(c)) + } + Key([s @ .., [b @ ..]]) => match c { + '/' => Key([..s, &b, &[]]), + 'a'..'z' | '0'..'9' | '-' => Key(&[..s, &[..b, c]]), + _ => return Err(ParseError::Unexpected(c)) + } + // expression must begin with key or symbol + Exp([]) => match c { + ' ' => Exp(&[]), + ')' => return Err(ParseError::Empty), + ':' => Exp(&[Sym(&[':'])]), + c => Exp(&[Key(&[&[c]])]), + }, + + // expression can't begin with number + Exp([Num(num)]) => return Err(ParseError::Unexpected(c)), + + // symbol begins with : and lowercase a-z + Exp([Sym([':'])]) => match c { + 'a'..'z' => Exp(&[Sym(&[':', c])]), + _ => return Err(ParseError::Unexpected(c)), + }, + + // any other char is part of symbol until space or ) + Exp([Sym([':', b @ ..])]) => match c { + ')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil }, + ' ' => Exp(&[Sym(&[':', ..b]), Nil]), + c => Exp(&[Sym(&[':', ..b, c])]), + }, + + // key begins with lowercase a-z + Exp([Key([])]) => match c { + 'a'..'z' => Exp([Key([[c]])]), + _ => return Err(ParseError::Unexpected(c)), + }, + + // any other char is part of key until slash space or ) + Exp([Key([[b @ ..]])]) => match c { + '/' => Exp(&[Key(&[[..b], []])]), + ' ' => Exp(&[Key(&[[..b]]), Nil]), + ')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil }, + c => Exp(&[Key(&[[..b, c]])]) + } + + // slash adds new section to key + Exp([Key([b @ .., []])]) => match c { + '/' => Exp(&[Key(&[[..b], []])]), + ' ' => Exp(&[Key(&[[..b]]), Nil]), + ')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil }, + c => Exp(&[Key(&[[..b, c]])]) + } + + } + } + Ok(state) +} +*/ + + +/// EDN parsing helper. +#[macro_export] macro_rules! edn { + ($edn:ident { $($pat:pat => $expr:expr),* $(,)? }) => { + match $edn { $($pat => $expr),* } + }; + ($edn:ident in $args:ident { $($pat:pat => $expr:expr),* $(,)? }) => { + for $edn in $args { + edn!($edn { $($pat => $expr),* }) + } + }; +} + +pub trait FromEdn: Sized { + const ID: &'static str; + fn from_edn (context: C, expr: &[Edn<'_>]) -> + std::result::Result>; +} + +/// Implements the [FromEdn] trait. +#[macro_export] macro_rules! from_edn { + ($id:expr => |$context:tt:$Context:ty, $args:ident| -> $T:ty $body:block) => { + impl FromEdn<$Context> for $T { + const ID: &'static str = $id; + fn from_edn <'e> ($context: $Context, $args: &[Edn<'e>]) -> Usually { + $body + } + } + } +} diff --git a/edn/src/example.edn b/edn/src/example.edn new file mode 100644 index 00000000..afca85a5 --- /dev/null +++ b/edn/src/example.edn @@ -0,0 +1,12 @@ +(sized + (bsp/s (fill/x (fixed/y 2 (lay + (align/w :input-meter-l) + (align/e :input-meter-r) + (align/x :transport)))) + (bsp/n (row :clip-play :clip-next :clip-edit :edit-stat) + (bsp/n (max/y :sample-h (fill/xy :sample-view)) + (bsp/n (align/w (fixed/y 1 :sample-stat)) + (bsp/n (fixed/x :pool-w :pool-view) + (fill/xy (bsp/e + (fixed/x :samples-w (push/y :samples-y :samples-view)) + :midi-view)))))))) diff --git a/edn/src/lib.rs b/edn/src/lib.rs new file mode 100644 index 00000000..14841351 --- /dev/null +++ b/edn/src/lib.rs @@ -0,0 +1,2 @@ +mod edn_lib; pub use self::edn_lib::*; +mod edn_layout; pub use self::edn_layout::*; diff --git a/engine/Cargo.toml b/engine/Cargo.toml index f9005149..b72a07cb 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -7,4 +7,3 @@ version = "0.2.0" crossterm = "0.28.1" ratatui = { version = "0.29.0", features = [ "unstable-widget-ref", "underline-color" ] } better-panic = "0.3.0" -clojure-reader = "0.3.0" diff --git a/engine/src/edn.rs b/engine/src/edn.rs index 2a84d991..c7b7e813 100644 --- a/engine/src/edn.rs +++ b/engine/src/edn.rs @@ -1,36 +1 @@ use crate::*; - -use std::sync::{Arc, RwLock}; -use std::collections::BTreeMap; - -pub use clojure_reader::edn::Edn; - -/// EDN parsing helper. -#[macro_export] macro_rules! edn { - ($edn:ident { $($pat:pat => $expr:expr),* $(,)? }) => { - match $edn { $($pat => $expr),* } - }; - ($edn:ident in $args:ident { $($pat:pat => $expr:expr),* $(,)? }) => { - for $edn in $args { - edn!($edn { $($pat => $expr),* }) - } - }; -} - -pub trait FromEdn: Sized { - const ID: &'static str; - fn from_edn (context: C, expr: &[Edn<'_>]) -> Usually; -} - -/// Implements the [FromEdn] trait. -#[macro_export] macro_rules! from_edn { - ($id:expr => |$context:tt:$Context:ty, $args:ident| -> $T:ty $body:block) => { - impl FromEdn<$Context> for $T { - const ID: &'static str = $id; - fn from_edn <'e> ($context: $Context, $args: &[Edn<'e>]) -> Usually { - $body - } - } - } -} - diff --git a/engine/src/lib.rs b/engine/src/lib.rs index e792e371..a294d35d 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -1,10 +1,12 @@ +#![feature(associated_type_defaults)] + //mod component; pub use self::component::*; mod engine; pub use self::engine::*; mod input; pub use self::input::*; mod output; pub use self::output::*; pub mod tui; -pub mod edn; +#[cfg(feature = "edn")] pub mod edn; pub use std::error::Error; diff --git a/layout/src/layout_edn.rs b/layout/src/layout_edn.rs deleted file mode 100644 index e466483e..00000000 --- a/layout/src/layout_edn.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::*; -use ::tek_engine::edn::*; - -macro_rules! edn_module { - ($name:literal = $Host:ident<$E:ident: $Engine:path> { $( - (defn $fn:ident - $(<$($G:ident: $Generic:ty),+>)? - $Struct:ident($($arg:ident : $Arg:ty),*)) - )* }) => { - pub trait $Host<$E: Engine> { - pub fn read_one <'e> (edn: &[Edn<'e>]) -> impl Content<$E> { - if let Some(Edn::Symbol(name)) = edn.get(0) { - match name { - $( - stringify!($fn) => - ),* - } - } else { - panic!("invalid edn") - } - } - $( - - )* - } - }; -} - -//edn_module! { - //(host LayoutEdn) - //(name "layout") - - //(defn when > - //When(cond: bool, item: A)) - - //(defn either , B: Content> - //Either(cond: bool, a: A, b: B)) - - //(defn map < - //A: Content, - //B: Content, - //I: Iterator + Send + Sync, - //F: Fn() -> I + Send + Sync, - //G: Fn(A, usize)->B + Send + Sync - //> - //Map(get_iterator: I, callback: G)) -//} diff --git a/layout/src/lib.rs b/layout/src/lib.rs index 56f2a87d..d87dac79 100644 --- a/layout/src/lib.rs +++ b/layout/src/lib.rs @@ -1,8 +1,8 @@ -//mod collection; pub use self::collection::*; +#![feature(type_alias_impl_trait)] +#![feature(impl_trait_in_assoc_type)] mod align; pub use self::align::*; mod direction; pub use self::direction::*; -mod layout_edn; pub use self::edn::*; mod measure; pub use self::measure::*; mod ops; pub use self::ops::*; mod transform_xy; pub use self::transform_xy::*; diff --git a/src/groovebox/groovebox.edn b/src/groovebox/groovebox.edn new file mode 100644 index 00000000..afca85a5 --- /dev/null +++ b/src/groovebox/groovebox.edn @@ -0,0 +1,12 @@ +(sized + (bsp/s (fill/x (fixed/y 2 (lay + (align/w :input-meter-l) + (align/e :input-meter-r) + (align/x :transport)))) + (bsp/n (row :clip-play :clip-next :clip-edit :edit-stat) + (bsp/n (max/y :sample-h (fill/xy :sample-view)) + (bsp/n (align/w (fixed/y 1 :sample-stat)) + (bsp/n (fixed/x :pool-w :pool-view) + (fill/xy (bsp/e + (fixed/x :samples-w (push/y :samples-y :samples-view)) + :midi-view)))))))) diff --git a/src/groovebox/groovebox_tui.rs b/src/groovebox/groovebox_tui.rs index 03acbae1..1b5125fe 100644 --- a/src/groovebox/groovebox_tui.rs +++ b/src/groovebox/groovebox_tui.rs @@ -1,49 +1,111 @@ use crate::*; use super::*; +use std::marker::ConstParamTy; -render!(Tui: (self: Groovebox) => self.size.of( - Bsp::s(self.toolbar_view(), - Bsp::n(self.selector_view(), - Bsp::n(self.sample_view(), - Bsp::n(self.status_view(), - Bsp::w(self.pool_view(), Fill::xy(Bsp::e(self.sampler_view(), &self.editor))))))))); +const GROOVEBOX_EDN: &'static str = include_str!("groovebox.edn"); -impl Groovebox { - fn toolbar_view (&self) -> impl Content + use<'_> { - Fill::x(Fixed::y(2, lay!( - Align::w(Meter("L/", self.sampler.input_meter[0])), - Align::e(Meter("R/", self.sampler.input_meter[1])), - Align::x(TransportView::new(true, &self.player.clock)), - ))) - } - fn selector_view (&self) -> impl Content + use<'_> { - row!( - ClipSelected::play_phrase(&self.player), - ClipSelected::next_phrase(&self.player), - MidiEditClip(&self.editor), - MidiEditStatus(&self.editor), - ) - } - fn sample_view (&self) -> impl Content + use<'_> { - let note_pt = self.editor.note_point(); - let sample_h = if self.compact { 0 } else { 5 }; - Max::y(sample_h, Fill::xy( - SampleViewer::from_sampler(&self.sampler, note_pt))) - } - fn status_view (&self) -> impl Content + use<'_> { - let note_pt = self.editor.note_point(); - Align::w(Fixed::y(1, SamplerStatus(&self.sampler, note_pt))) - } - fn pool_view (&self) -> impl Content + use<'_> { - let w = self.size.w(); - let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; - Fixed::x(if self.compact { 5 } else { pool_w }, - PoolView(self.compact, &self.pool)) - } - fn sampler_view (&self) -> impl Content + use<'_> { - let sampler_w = if self.compact { 4 } else { 11 }; - let sampler_y = if self.compact { 1 } else { 0 }; - Fixed::x(sampler_w, Push::y(sampler_y, Fill::y( - SampleList::new(self.compact, &self.sampler, &self.editor)))) +impl Content for Groovebox { + fn content (&self) -> impl Content { + EdnView::parse(self.edn.as_slice()) } } +//render!(Tui: (self: Groovebox) => self.size.of( + //Bsp::s(self.toolbar_view(), + //Bsp::n(self.selector_view(), + //Bsp::n(self.sample_view(), + //Bsp::n(self.status_view(), + //Bsp::w(self.pool_view(), Fill::xy(Bsp::e(self.sampler_view(), &self.editor))))))))); + +macro_rules! edn_context { + ($Struct:ident |$l:lifetime, $state:ident| { + $($key:literal = $field:ident: $Type:ty => $expr:expr,)* + }) => { + + #[derive(Default)] + pub struct EdnView<$l> { $($field: Option<$Type>),* } + + impl<$l> EdnView<$l> { + pub fn parse <'e> (edn: &[Edn<'e>]) -> impl Fn(&$Struct) + use<'e> { + let imports = Self::imports_all(edn); + move |state| { + let mut context = EdnView::default(); + for import in imports.iter() { + context.import(state, import) + } + } + } + fn imports_all <'e> (edn: &[Edn<'e>]) -> Vec<&'e str> { + let mut imports = vec![]; + for edn in edn.iter() { + for import in Self::imports_one(edn) { + imports.push(import); + } + } + imports + } + fn imports_one <'e> (edn: &Edn<'e>) -> Vec<&'e str> { + match edn { + Edn::Symbol(import) => vec![import], + Edn::List(edn) => Self::imports_all(edn.as_slice()), + _ => vec![], + } + } + pub fn import (&mut self, $state: &$l$Struct, key: &str) { + match key { + $($key => self.$field = Some($expr),)* + _ => {} + } + } + } + } +} + +edn_context!(Groovebox |'a, state| { + ":input-meter-l" = input_meter_l: Meter<'a> => Meter("L/", state.sampler.input_meter[0]), + ":input-meter-r" = input_meter_r: Meter<'a> => Meter("R/", state.sampler.input_meter[1]), + + ":transport" = transport: TransportView<'a> => TransportView::new(true, &state.player.clock), + + ":clip-play" = clip_play: ClipSelected => ClipSelected::play_phrase(&state.player), + ":clip-next" = clip_next: ClipSelected => ClipSelected::next_phrase(&state.player), + ":clip-edit" = clip_edit: MidiEditClip<'a> => MidiEditClip(&state.editor), + + ":edit-stat" = edit_stat: MidiEditStatus<'a> => MidiEditStatus(&state.editor), + + ":sample-h" = sample_h: u16 => if state.compact { 0 } else { 5 }, + ":sample-view" = sample_view: SampleViewer => SampleViewer::from_sampler( + &state.sampler, state.editor.note_point()), + ":sample-stat" = sample_stat: SamplerStatus<'a> => SamplerStatus( + &state.sampler, state.editor.note_point()), + + ":pool-w" = pool_w: u16 => if state.compact { 5 } else { + let w = state.size.w(); + if w > 60 { 20 } else if w > 40 { 15 } else { 10 } }, + ":pool-view" = pool_view: PoolView<'a> => PoolView(state.compact, &state.pool), + + ":samples-w" = samples_w: u16 => if state.compact { 4 } else { 11 }, + ":samples-y" = samples_y: u16 => if state.compact { 1 } else { 0 }, + ":samples-view" = samples_view: SampleList<'a> => SampleList::new( + state.compact, &state.sampler, &state.editor), + + ":midi-view" = midi_view: &'a MidiEditor => &state.editor, +}); + +//impl Groovebox { + //fn status_view (&self) -> impl Content + use<'_> { + //let note_pt = self.editor.note_point(); + //Align::w(Fixed::y(1, )) + //} + //fn pool_view (&self) -> impl Content + use<'_> { + //let w = self.size.w(); + //let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; + //Fixed::x(if self.compact { 5 } else { pool_w }, + //) + //} + //fn sampler_view (&self) -> impl Content + use<'_> { + //let sampler_w = if self.compact { 4 } else { 11 }; + //let sampler_y = if self.compact { 1 } else { 0 }; + //Fixed::x(sampler_w, Push::y(sampler_y, Fill::y( + //SampleList::new(self.compact, &self.sampler, &self.editor)))) + //} +//} diff --git a/src/lib.rs b/src/lib.rs index 84d9c50b..a88f93d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,9 @@ #![allow(unused)] #![allow(clippy::unit_arg)] +#![feature(adt_const_params)] +#![feature(type_alias_impl_trait)] +#![feature(impl_trait_in_assoc_type)] +#![feature(associated_type_defaults)] pub use ::tek_layout; pub use ::tek_layout::tek_engine; @@ -12,7 +16,6 @@ pub(crate) use ::tek_layout::{ Output, Content, Thunk, render, Input, Handle, handle, kexp, kpat, - edn::*, tui::{ Tui, TuiIn, key, ctrl, shift, alt, @@ -33,6 +36,9 @@ pub(crate) use ::tek_layout::{ } }; +pub use ::tek_edn; +pub(crate) use ::tek_edn::*; + pub(crate) use std::cmp::{Ord, Eq, PartialEq}; pub(crate) use std::collections::BTreeMap; pub(crate) use std::error::Error;