diff --git a/Cargo.lock b/Cargo.lock index 17d931b..d717471 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,7 +366,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.3+wasi-0.2.4", ] [[package]] @@ -874,9 +874,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "rustc-demangle" @@ -1042,7 +1042,7 @@ dependencies = [ [[package]] name = "tengri" -version = "0.13.0" +version = "0.14.0" dependencies = [ "crossterm 0.29.0", "tengri", @@ -1056,11 +1056,11 @@ dependencies = [ [[package]] name = "tengri_core" -version = "0.13.0" +version = "0.14.0" [[package]] name = "tengri_dsl" -version = "0.13.0" +version = "0.14.0" dependencies = [ "const_panic", "itertools 0.14.0", @@ -1073,7 +1073,7 @@ dependencies = [ [[package]] name = "tengri_input" -version = "0.13.0" +version = "0.14.0" dependencies = [ "tengri_core", "tengri_dsl", @@ -1082,7 +1082,7 @@ dependencies = [ [[package]] name = "tengri_output" -version = "0.13.0" +version = "0.14.0" dependencies = [ "proptest", "proptest-derive", @@ -1094,7 +1094,7 @@ dependencies = [ [[package]] name = "tengri_proc" -version = "0.13.0" +version = "0.14.0" dependencies = [ "heck", "proc-macro2", @@ -1105,7 +1105,7 @@ dependencies = [ [[package]] name = "tengri_tui" -version = "0.13.0" +version = "0.14.0" dependencies = [ "atomic_float", "better-panic", @@ -1146,9 +1146,9 @@ dependencies = [ [[package]] name = "typewit" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97e72ba082eeb9da9dc68ff5a2bf727ef6ce362556e8d29ec1aed3bd05e7d86a" +checksum = "4dd91acc53c592cb800c11c83e8e7ee1d48378d05cfa33b5474f5f80c5b236bf" dependencies = [ "typewit_proc_macros", ] @@ -1223,11 +1223,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.3+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -1473,13 +1473,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "zerocopy" diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index 4372ff4..3859210 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -15,8 +15,8 @@ pub(crate) use ::{ tengri_core::* }; pub(crate) use self::DslError::*; -mod dsl_conv; pub use self::dsl_conv::*; #[cfg(test)] mod dsl_test; +mod dsl_macros; // Trait that designates any string-like as potentially parsable DSL. flex_trait!(Dsl: Debug + Send + Sync + Sized { fn src (&self) -> DslPerhaps<&str> { unreachable!("Dsl::src default impl") } @@ -148,54 +148,6 @@ pub const fn to_digit (c: char) -> Result { _ => return Err(Unexpected(c, None, Some("parse digit"))) }) } -#[macro_export] macro_rules! dsl_type (($T:ident { $($trait:tt)* } { - pub const fn $peek:ident $($_1:tt)?; - pub const fn $peek_only:ident $($_2:tt)?; - pub const fn $seek:ident $($_3:tt)?; - pub const fn $seek_start:ident ($source1:ident) $body1:block - pub const fn $seek_length:ident ($source2:ident) $body2:block -})=>{ - pub trait $T: Dsl { $($trait)* } - impl $T for D {} - pub const fn $seek_start ($source1: &str) -> DslPerhaps $body1 - pub const fn $seek_length ($source2: &str) -> DslPerhaps $body2 - /// Find a start and length corresponding to a syntax token. - pub const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> { - match $seek_start(source) { - Err(e) => Err(e), - Ok(None) => Ok(None), - Ok(Some(start)) => match $seek_length(str_from(source, start)) { - Ok(Some(length)) => Ok(Some((start, length))), - Ok(None) => Ok(None), - Err(e) => Err(e), - }, - } - } - /// Find a slice corrensponding to a syntax token. - pub const fn $peek (source: &str) -> DslPerhaps<&str> { - match $seek(source) { - Err(e) => Err(e), - Ok(None) => Ok(None), - Ok(Some((start, length))) => Ok(Some(str_range(source, start, start + length))), - } - } - /// Find a slice corrensponding to a syntax token - /// but return an error if it isn't the only thing - /// in the source. - pub const fn $peek_only (source: &str) -> DslPerhaps<&str> { - match $seek(source) { - Err(e) => Err(e), - Ok(None) => Ok(None), - Ok(Some((start, length))) => { - if let Err(e) = no_trailing_non_space(source, start + length, Some("peek_only")) { - Err(e) - } else { - Ok(Some(str_range(source, start, start + length))) - } - } - } - } -}); dsl_type!(DslWord { fn word (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(word_peek_only))} @@ -278,85 +230,25 @@ fn ok_flat (x: Option>) -> DslPerhaps { Ok(x.transpose()?.flatten()) } -/// Define a DSL namespace that provides values to words and expressions. -#[macro_export] macro_rules! dsl_ns ( - // Special form for numeric types - (num |$state:ident : $State: ty| $($($num:lifetime)? $Type:ty $(=> { $( - $pat:tt => $body:expr - ),* $(,)? })?;)+) => { - $(dsl_ns!(num |$state: $State| -> $($num)? $Type { $( $($pat => $body),* )? });)+ - }; - // Special form for numeric types - (num |$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { - impl<'t> DslNs<'t, $Type> for $State { - const WORDS: DslNsMap<'t, fn (&$State)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); - const EXPRS: DslNsMap<'t, fn (&$State, &str)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); - fn from_literal (&self, dsl: &D) -> Perhaps<$Type> { - Ok(if let Some(src) = dsl.src()? { - Some(to_number(src)? as $Type) - } else { - None - }) - } - } - }; - // A namespace may resolve one or more types. - (|$state:ident : $State: ty| $($Type:ty $(=> { $( $pat:tt => $body:expr ),* $(,)? })? ;)+) => { - $(dsl_ns!(|$state: $State| -> $Type { $( $($pat => $body),* )? });)+ - }; - // Regular form for single type - (|$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { - impl<'t> DslNs<'t, $Type> for $State { - const WORDS: DslNsMap<'t, fn (&$State)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); - const EXPRS: DslNsMap<'t, fn (&$State, &str)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); - } - }; - // Symbols only. - (@word ($state:ident: $State:ty) -> $Type:ty { - $word:literal => $body:expr - }) => { - ($word, |$state|Ok(Some($body))) - }; - // Expression handlers only. - (@exp ($state:ident: $State:ty) -> $Type:ty { - ($head:literal $(,$arg:ident:$ty:ty)* $(,)*) => $body:expr - }) => { ($head, |$state, tail: &str|{ - $( - let head = tail.head()?.unwrap_or_default(); - let tail = tail.tail()?.unwrap_or_default(); - let $arg: $ty = if let Some(arg) = $state.from(&head)? { - arg - } else { - return Err(format!("{}: missing argument: {} ({}); got: {tail}", - $head, - stringify!($arg), - stringify!($Type), - ).into()) - }; - )* - Ok(Some($body)) - }) }; - // Nothing else in symbols. - (@word ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { - ("", |_|Ok(None)) // FIXME don't emit at all - }; - // Nothing else in expression handlers. - (@exp ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { - ("", |_, _|Ok(None)) // FIXME don't emit at all - }; -); +/// Namespace mapping. +#[derive(Debug)] pub struct DslNsMap<'t, T: Debug + 't>(pub &'t [(&'t str, T)]); +/// Namespace mapping for [DslWord]s. +pub type DslWords<'t, T, U> = DslNsMap<'t, fn (&'t T)->Perhaps>; +/// Namespace mapping for [DslExpr]s. +pub type DslExprs<'t, T, U> = DslNsMap<'t, fn (&'t T, &str)->Perhaps>; +impl<'t, T: Debug + 't> DslNsMap<'t, T> { + /// Populate a namespace with pre-existing values. + pub const fn new (data: &'t [(&'t str, T)]) -> Self { Self(data) /* TODO a search trie */ } +} + pub trait DslNs<'t, T: 't>: 't { /// Known symbols. - const WORDS: DslNsMap<'t, fn (&Self)->Perhaps> = DslNsMap::new(&[]); + const WORDS: DslWords<'t, Self, T> = DslNsMap::new(&[]); /// Known expressions. - const EXPRS: DslNsMap<'t, fn (&Self, &str)->Perhaps> = DslNsMap::new(&[]); + const EXPRS: DslExprs<'t, Self, T> = DslNsMap::new(&[]); /// Resolve an expression or symbol. - fn from (&self, dsl: &D) -> Perhaps { + fn from (&'t self, dsl: &D) -> Perhaps { if let Ok(Some(literal)) = self.from_literal(dsl) { Ok(Some(literal)) } else if let Ok(Some(meaning)) = self.from_word(dsl) { @@ -370,14 +262,19 @@ pub trait DslNs<'t, T: 't>: 't { Ok(None) } /// Resolve a symbol if known. - fn from_word (&self, dsl: &D) -> Perhaps { + fn from_word (&'t self, dsl: &D) -> Perhaps { if let Some(dsl) = dsl.word()? { - for (key, get) in Self::WORDS.0 { if dsl == *key { return get(self) } } + for (key, get) in Self::WORDS.0.iter() { + if dsl == *key { + let value = get(self); + return value + } + } } return Ok(None) } /// Resolve an expression if known. - fn from_expr (&self, dsl: &D) -> Perhaps { + fn from_expr (&'t self, dsl: &D) -> Perhaps { if let Some(expr) = dsl.expr()? { for (key, get) in Self::EXPRS.0.iter() { if Some(*key) == expr.head()? { @@ -389,41 +286,26 @@ pub trait DslNs<'t, T: 't>: 't { } } -/// Namespace mapping. -#[derive(Debug)] -pub struct DslNsMap<'t, T: Debug + 't>(pub &'t [(&'t str, T)]); -impl<'t, T: Debug + 't> DslNsMap<'t, T> { - /// Populate a namespace with pre-existing values. - pub const fn new (data: &'t [(&'t str, T)]) -> Self { Self(data) /* TODO a search trie */ } +#[deprecated] +/// `T` + [Dsl] -> `Self`. +pub trait FromDsl: Sized { + fn from_dsl (state: &T, dsl: &impl Dsl) -> Perhaps; + fn from_dsl_or (state: &T, dsl: &impl Dsl, err: Box) -> Usually { + Self::from_dsl(state, dsl)?.ok_or(err) + } + fn from_dsl_or_else (state: &T, dsl: &impl Dsl, err: impl Fn()->Box) -> Usually { + Self::from_dsl(state, dsl)?.ok_or_else(err) + } } -#[macro_export] macro_rules!dsl_words((|$state:ident|->$Type:ty$({ - $($word:literal => $body:expr),* $(,)? -})?)=>{ - const WORDS: DslNsMap<'t, fn (&Self)->Perhaps<$Type>> = DslNsMap::new(&[$( - $(($word, |$state: &Self|{Ok(Some($body))})),* - )? ]); -}); - -#[macro_export] macro_rules!dsl_exprs((|$state:ident|->$Type:ty$({ - $($name:literal ($($arg:ident:$ty:ty),* $(,)?) => $body:expr),* $(,)? -})?)=>{ - const EXPRS: DslNsMap<'t, fn (&Self, &str)->Perhaps<$Type>> = DslNsMap::new(&[$( - $(($name, |$state: &Self, tail: &str|{ - $( - let head = tail.head()?.unwrap_or_default(); - let tail = tail.tail()?.unwrap_or_default(); - let $arg: $ty = if let Some(arg) = $state.from(&head)? { - arg - } else { - return Err(format!("{}: arg \"{}\" ({}) got: {head} {tail}", - $name, - stringify!($arg), - stringify!($ty), - ).into()) - }; - )* - Ok(Some($body)) - })),* - )? ]); -}); +#[deprecated] +/// `self` + [Dsl] -> `T` +pub trait DslInto { + fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps; + fn dsl_into_or (&self, dsl: &impl Dsl, err: Box) -> Usually { + self.dsl_into(dsl)?.ok_or(err) + } + fn dsl_into_or_else (&self, dsl: &impl Dsl, err: impl Fn()->Box) -> Usually { + self.dsl_into(dsl)?.ok_or_else(err) + } +} diff --git a/dsl/src/dsl_conv.rs b/dsl/src/dsl_conv.rs deleted file mode 100644 index 7bbb91c..0000000 --- a/dsl/src/dsl_conv.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::*; - -// TODO DEPRECATE: - -/// `T` + [Dsl] -> `Self`. -pub trait FromDsl: Sized { - fn from_dsl (state: &T, dsl: &impl Dsl) -> Perhaps; - fn from_dsl_or (state: &T, dsl: &impl Dsl, err: Box) -> Usually { - Self::from_dsl(state, dsl)?.ok_or(err) - } - fn from_dsl_or_else (state: &T, dsl: &impl Dsl, err: impl Fn()->Box) -> Usually { - Self::from_dsl(state, dsl)?.ok_or_else(err) - } -} - -/// `self` + [Dsl] -> `T` -pub trait DslInto { - fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps; - fn dsl_into_or (&self, dsl: &impl Dsl, err: Box) -> Usually { - self.dsl_into(dsl)?.ok_or(err) - } - fn dsl_into_or_else (&self, dsl: &impl Dsl, err: impl Fn()->Box) -> Usually { - self.dsl_into(dsl)?.ok_or_else(err) - } -} - -/// `self` + `T` -> [Dsl] -pub trait DslFrom { - fn dsl_from (&self, val: &T) -> Perhaps; - fn dsl_from_or (&self, val: &T, err: Box) -> Usually { - self.dsl_from(val)?.ok_or(err) - } - fn dsl_from_or_else (&self, val: &T, err: impl Fn()->Box) -> Usually { - self.dsl_from(val)?.ok_or_else(err) - } -} - -/// `self` + `T` + -> [Dsl] -pub trait IntoDsl { - fn into_dsl (&self, state: &T) -> Perhaps; - fn into_dsl_or (&self, state: &T, err: Box) -> Usually { - self.into_dsl(state)?.ok_or(err) - } - fn into_dsl_or_else (&self, state: &T, err: impl Fn()->Box) -> Usually { - self.into_dsl(state)?.ok_or_else(err) - } -} - -//impl<'s, T: FromDsl, U> DslInto<'s, T> for U { - //fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps { - //T::from_dsl(self, dsl) - //} -//} - -//impl, U> IntoDsl for U { - //fn into_dsl (&self, state: &T) -> Perhaps { - //T::dsl_from(state, self) - //} -//} diff --git a/dsl/src/dsl_macros.rs b/dsl/src/dsl_macros.rs new file mode 100644 index 0000000..36d2c36 --- /dev/null +++ b/dsl/src/dsl_macros.rs @@ -0,0 +1,153 @@ +#[macro_export] macro_rules! dsl_type (($T:ident { $($trait:tt)* } { + pub const fn $peek:ident $($_1:tt)?; + pub const fn $peek_only:ident $($_2:tt)?; + pub const fn $seek:ident $($_3:tt)?; + pub const fn $seek_start:ident ($source1:ident) $body1:block + pub const fn $seek_length:ident ($source2:ident) $body2:block +})=>{ + pub trait $T: Dsl { $($trait)* } + impl $T for D {} + pub const fn $seek_start ($source1: &str) -> DslPerhaps $body1 + pub const fn $seek_length ($source2: &str) -> DslPerhaps $body2 + /// Find a start and length corresponding to a syntax token. + pub const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> { + match $seek_start(source) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some(start)) => match $seek_length(str_from(source, start)) { + Ok(Some(length)) => Ok(Some((start, length))), + Ok(None) => Ok(None), + Err(e) => Err(e), + }, + } + } + /// Find a slice corrensponding to a syntax token. + pub const fn $peek (source: &str) -> DslPerhaps<&str> { + match $seek(source) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some((start, length))) => Ok(Some(str_range(source, start, start + length))), + } + } + /// Find a slice corrensponding to a syntax token + /// but return an error if it isn't the only thing + /// in the source. + pub const fn $peek_only (source: &str) -> DslPerhaps<&str> { + match $seek(source) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some((start, length))) => { + if let Err(e) = no_trailing_non_space(source, start + length, Some("peek_only")) { + Err(e) + } else { + Ok(Some(str_range(source, start, start + length))) + } + } + } + } +}); + +#[macro_export] macro_rules!dsl_words( + (|$state:ident|->$Type:ty$({ + $($word:literal => $body:expr),* $(,)? + })?)=>{ + const WORDS: DslWords<'t, Self, $Type> = DslNsMap::new(&[$( + $(($word, move|$state: &Self|{Ok(Some($body))})),* + )? ]); + }); + +#[macro_export] macro_rules!dsl_exprs( + (|$state:ident|->$Type:ty$({ + $($name:literal ($($arg:ident:$ty:ty),* $(,)?) => $body:expr),* $(,)? + })?)=>{ + const EXPRS: DslExprs<'t, Self, $Type> = DslNsMap::new(&[$( + $(($name, |$state: &Self, tail: &str|{ + $( + let head = tail.head()?.unwrap_or_default(); + let tail = tail.tail()?.unwrap_or_default(); + let $arg: $ty = if let Some(arg) = $state.from(&head)? { + arg + } else { + return Err(format!("{}: arg \"{}\" ({}) got: {head} {tail}", + $name, + stringify!($arg), + stringify!($ty), + ).into()) + }; + )* + Ok(Some($body)) + })),* + )? ]); + }); + +/// Define a DSL namespace that provides values to words and expressions. +#[macro_export] macro_rules! dsl_ns ( + // Special form for numeric types + (num |$state:ident : $State: ty| $($($num:lifetime)? $Type:ty $(=> { $( + $pat:tt => $body:expr + ),* $(,)? })?;)+) => { + $(dsl_ns!(num |$state: $State| -> $($num)? $Type { $( $($pat => $body),* )? });)+ + }; + // Special form for numeric types + (num |$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { + impl<'t> DslNs<'t, $Type> for $State { + const WORDS: DslWords<'t, $State, $Type> = DslNsMap::new(&[ + $(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); + const EXPRS: DslExprs<'t, $State, $Type> = DslNsMap::new(&[ + $(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); + fn from_literal (&self, dsl: &D) -> Perhaps<$Type> { + Ok(if let Some(src) = dsl.src()? { + Some(to_number(src)? as $Type) + } else { + None + }) + } + } + }; + // A namespace may resolve one or more types. + (|$state:ident : $State: ty| $($Type:ty $(=> { $( $pat:tt => $body:expr ),* $(,)? })? ;)+) => { + $(dsl_ns!(|$state: $State| -> $Type { $( $($pat => $body),* )? });)+ + }; + // Regular form for single type + (|$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { + impl<'t> DslNs<'t, $Type> for $State { + const WORDS: DslNsMap<'t, fn (&$State)->Perhaps<$Type>> = + DslNsMap::new(&[$(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); + const EXPRS: DslNsMap<'t, fn (&$State, &str)->Perhaps<$Type>> = + DslNsMap::new(&[$(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); + } + }; + // Symbols only. + (@word ($state:ident: $State:ty) -> $Type:ty { + $word:literal => $body:expr + }) => { + ($word, |$state|Ok(Some($body))) + }; + // Expression handlers only. + (@exp ($state:ident: $State:ty) -> $Type:ty { + ($head:literal $(,$arg:ident:$ty:ty)* $(,)*) => $body:expr + }) => { ($head, |$state, tail: &str|{ + $( + let head = tail.head()?.unwrap_or_default(); + let tail = tail.tail()?.unwrap_or_default(); + let $arg: $ty = if let Some(arg) = $state.from(&head)? { + arg + } else { + return Err(format!("{}: missing argument: {} ({}); got: {tail}", + $head, + stringify!($arg), + stringify!($Type), + ).into()) + }; + )* + Ok(Some($body)) + }) }; + // Nothing else in symbols. + (@word ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { + ("", |_|Ok(None)) // FIXME don't emit at all + }; + // Nothing else in expression handlers. + (@exp ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { + ("", |_, _|Ok(None)) // FIXME don't emit at all + }; +); diff --git a/output/src/layout.rs b/output/src/layout.rs index cfa7f37..14f1cef 100644 --- a/output/src/layout.rs +++ b/output/src/layout.rs @@ -771,33 +771,38 @@ impl Memo { ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } } -pub struct Stack { - __: PhantomData, +pub struct Stack<'t, E, F1> { + __: PhantomData<&'t (E, F1)>, direction: Direction, - callback: F + callback: F1 } -impl)->())->()> Stack { - pub fn north (callback: F) -> Self { Self::new(North, callback) } - pub fn south (callback: F) -> Self { Self::new(South, callback) } - pub fn east (callback: F) -> Self { Self::new(East, callback) } - pub fn west (callback: F) -> Self { Self::new(West, callback) } - pub fn above (callback: F) -> Self { Self::new(Above, callback) } - pub fn below (callback: F) -> Self { Self::new(Below, callback) } - pub fn new (direction: Direction, callback: F) -> Self { +impl<'t, E, F1> Stack<'t, E, F1> where + E: Output, + F1: Fn(&mut dyn FnMut(&(dyn Render + 't))) + Send + Sync, +{ + pub fn north (callback: F1) -> Self { Self::new(North, callback) } + pub fn south (callback: F1) -> Self { Self::new(South, callback) } + pub fn east (callback: F1) -> Self { Self::new(East, callback) } + pub fn west (callback: F1) -> Self { Self::new(West, callback) } + pub fn above (callback: F1) -> Self { Self::new(Above, callback) } + pub fn below (callback: F1) -> Self { Self::new(Below, callback) } + pub fn new (direction: Direction, callback: F1) -> Self { Self { direction, callback, __: Default::default(), } } } -impl)->())->()> Content for Stack { +impl<'t, E, F1> Content for Stack<'t, E, F1> where + Self: 't, + E: Output, + F1: Fn(&mut dyn FnMut(&(dyn Render + 't))) + Send + Sync, +{ fn layout (&self, to: E::Area) -> E::Area { - let mut x = to.x(); - let mut y = to.y(); + let Self { direction, callback, .. } = self; + let (mut x, mut y) = (to.x(), to.y()); let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w()); let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h()); - (self.callback)(&mut move |component: &dyn Render|{ + callback(&mut move|component|{ let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh(); - match self.direction { - North | West => { todo!() }, - Above | Below => {}, + match direction { South => { y = y.plus(h); h_used = h_used.plus(h); h_remaining = h_remaining.minus(h); @@ -806,33 +811,36 @@ impl)->())->()> Content for St w_used = w_used.plus(w); w_remaining = w_remaining.minus(w); h_used = h_used.max(h); }, + North | West => { todo!() }, + Above | Below => {}, } }); - match self.direction { + match direction { North | West => { todo!() }, South | East => { [to.x(), to.y(), w_used.into(), h_used.into()].into() }, Above | Below => { [to.x(), to.y(), to.w(), to.h()].into() }, } } fn render (&self, to: &mut E) { - let mut x = to.x(); - let mut y = to.y(); + let Self { direction, callback, .. } = self; + let (mut x, mut y) = (to.x(), to.y()); let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w()); let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h()); - (self.callback)(&mut move |component: &dyn Render|{ + callback(&mut move|component|{ let layout = component.layout([x, y, w_remaining, h_remaining].into()); - match self.direction { - Above | Below => { to.place(layout, component); } + match direction { + South => { + y = y.plus(layout.h()); + h_remaining = h_remaining.minus(layout.h()); + h_used = h_used.plus(layout.h()) }, + East => { + x = x.plus(layout.w()); + w_remaining = w_remaining.minus(layout.w()); + w_used = w_used.plus(layout.h()) }, North | West => { todo!() }, - South => { y = y.plus(layout.h()); - h_remaining = h_remaining.minus(layout.h()); - h_used = h_used.plus(layout.h()); - to.place(layout, component); }, - East => { x = x.plus(layout.w()); - w_remaining = w_remaining.minus(layout.w()); - w_used = w_used.plus(layout.h()); - to.place(layout, component); }, - } + Above | Below => {} + }; + to.place(layout, component); }); } } @@ -881,66 +889,45 @@ impl<'a, E, A, B, I, F, G> Map where } } -impl<'a, E, A, B, I, F> Map>>>, I, F, fn(A, usize)->B> -where - E: Output, - B: Render, - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a -{ - pub const fn east ( - size: E::Unit, - get_iter: F, - get_item: impl Fn(A, usize)->B + Send + Sync - ) -> Map< - E, A, - Push>>, - I, F, - impl Fn(A, usize)->Push>> + Send + Sync - > { - Map { - __: PhantomData, - get_iter, - get_item: move |item: A, index: usize|{ - // FIXME: multiply - let mut push: E::Unit = E::Unit::from(0u16); - for _ in 0..index { - push = push + size; - } - Push::x(push, Align::w(Fixed::x(size, get_item(item, index)))) - } - } - } - - pub const fn south ( - size: E::Unit, - get_iter: F, - get_item: impl Fn(A, usize)->B + Send + Sync - ) -> Map< - E, A, - Push>>, - I, F, - impl Fn(A, usize)->Push>> + Send + Sync +macro_rules! impl_map_direction (($name:ident, $axis:ident, $align:ident)=>{ + impl<'a, E, A, B, I, F> Map< + E, A, Push>>>, I, F, fn(A, usize)->B > where E: Output, B: Render, I: Iterator + Send + Sync + 'a, F: Fn() -> I + Send + Sync + 'a { - Map { - __: PhantomData, - get_iter, - get_item: move |item: A, index: usize|{ - // FIXME: multiply - let mut push: E::Unit = E::Unit::from(0u16); - for _ in 0..index { - push = push + size; + pub const fn $name ( + size: E::Unit, + get_iter: F, + get_item: impl Fn(A, usize)->B + Send + Sync + ) -> Map< + E, A, + Push>>, + I, F, + impl Fn(A, usize)->Push>> + Send + Sync + > { + Map { + __: PhantomData, + get_iter, + get_item: move |item: A, index: usize|{ + // FIXME: multiply + let mut push: E::Unit = E::Unit::from(0u16); + for _ in 0..index { + push = push + size; + } + Push::$axis(push, Align::$align(Fixed::$axis(size, get_item(item, index)))) } - Push::y(push, Align::n(Fixed::y(size, get_item(item, index)))) } } } -} +}); + +impl_map_direction!(east, x, w); +impl_map_direction!(south, y, n); +impl_map_direction!(west, x, e); +impl_map_direction!(north, y, s); impl<'a, E, A, B, I, F, G> Content for Map where E: Output,