diff --git a/dsl/src/dsl_provide.rs b/dsl/src/dsl_provide.rs index c22e5d6..f98130b 100644 --- a/dsl/src/dsl_provide.rs +++ b/dsl/src/dsl_provide.rs @@ -1,55 +1,71 @@ use crate::*; -/// Map EDN tokens to parameters of a given type for a given context -/// TODO: Replace both [Context] and [TryFromDsl] with this trait -/// which returns a [Result]. -pub trait Dsl: Sized { - fn take <'state, 'source> (_: &'state Self, _: &mut TokenIter<'source>) -> Perhaps { - Ok(None) +/// Implement the [Dsl] trait, which boils down to +/// specifying two types and providing an expression. +#[macro_export] macro_rules! dsl { + ($T:ty: |$self:ident:$S:ty, $iter:ident|$expr:expr) => { + impl ::tengri::dsl::Dsl<$T> for $S { + fn take <'state, 'source> ( + &'state $self, $iter: &mut ::tengri::dsl::TokenIter<'source>, + ) -> ::tengri::Perhaps<$T> { + $expr + } + } + } +} + +/// Maps a sequencer of EDN tokens to parameters of supported types +/// for a given context. +pub trait Dsl: Sized { + fn take <'state, 'source> (&'state self, _: &mut TokenIter<'source>) -> Perhaps { + unimplemented!() } fn take_or_fail <'state, 'source> ( - state: &'state Self, iter: &mut TokenIter<'source> - ) -> Usually { - if let Some(value) = Self::take(state, iter)? { + &'state self, + token: &mut TokenIter<'source>, + error: impl Into> + ) -> Usually { + if let Some(value) = Dsl::::take(self, token)? { Ok(value) } else { - Result::Err("not found".into()) // TODO add info and error type + Result::Err(error.into()) } } } -/// Map EDN tokens to parameters of a given type for a given context -pub trait Context<'state, U>: Sized { - fn get <'source> (&'state self, _iter: &mut TokenIter<'source>) -> Option { - None +pub trait FromDsl<'state, State>: Sized { + fn take_from <'source: 'state> (state: &'state State, _token: &mut TokenIter<'source>) + -> Perhaps + { + unimplemented!() } -} - -impl<'state, T: Context<'state, U>, U> Context<'state, U> for &T { - fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option { - (*self).get(iter) - } -} - -impl<'state, T: Context<'state, U>, U> Context<'state, U> for Option { - fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option { - self.as_ref().map(|s|s.get(iter)).flatten() - } -} - -pub trait TryFromDsl<'state, T>: Sized { - fn try_from_expr <'source: 'state> ( - _state: &'state T, _iter: &mut TokenIter<'source> - ) -> Option { - None - } - fn try_from_atom <'source: 'state> ( - state: &'state T, value: Value<'source> - ) -> Option { - if let Exp(0, mut iter) = value { - return Self::try_from_expr(state, &mut iter) + fn take_from_or_fail <'source: 'state> ( + state: &'state State, + token: &mut TokenIter<'source>, + error: impl Into> + ) -> Usually { + if let Some(value) = FromDsl::::take_from(state, token)? { + Ok(value) + } else { + Result::Err(error.into()) } - None } } +//impl, U> Dsl for &T { + //fn take <'state, 'source> (&'state self, iter: &mut TokenIter<'source>) -> Perhaps { + //(*self).take(iter) + //} +//} + +//impl, U> Dsl for Option { + //fn take <'state, 'source> (&'state self, iter: &mut TokenIter<'source>) -> Perhaps { + //Ok(self.as_ref().map(|s|s.take(iter)).transpose()?.flatten()) + //} +//} + +impl<'state, X, Y> Dsl for Y where Y: FromDsl<'state, X> { +} + +//impl> Dsl for T { +//} diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs index 4575e24..dfcbd72 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -1,25 +1,18 @@ use crate::*; use std::marker::PhantomData; -/// A [Command] that can be constructed from a [Token]. -pub trait DslCommand<'state, C>: TryFromDsl<'state, C> + Command {} - -impl<'state, C, T: TryFromDsl<'state, C> + Command> DslCommand<'state, C> for T {} - /// [Input] state that can be matched against a [Value]. -pub trait DslInput: Input { - fn matches_dsl (&self, token: &str) -> bool; -} +pub trait DslInput: Input { fn matches_dsl (&self, token: &str) -> bool; } /// A pre-configured mapping of input events to commands. -pub trait KeyMap<'state, S, C: DslCommand<'state, S>, I: DslInput> { +pub trait KeyMap, C: Command, I: DslInput> { /// Try to find a command that matches the current input event. - fn command (&'state self, state: &'state S, input: &'state I) -> Option; + fn keybind_resolve (&self, state: &S, input: &I) -> Perhaps; } /// A [SourceIter] can be a [KeyMap]. -impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> for SourceIter<'state> { - fn command (&'state self, state: &'state S, input: &'state I) -> Option { +impl<'source, S: Dsl, C: Command, I: DslInput> KeyMap for SourceIter<'source> { + fn keybind_resolve (&self, state: &S, input: &I) -> Perhaps { let mut iter = self.clone(); while let Some((token, rest)) = iter.next() { iter = rest; @@ -29,8 +22,8 @@ impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> f match exp_iter.next() { Some(Token { value: Value::Sym(binding), .. }) => { if input.matches_dsl(binding) { - if let Some(command) = C::try_from_expr(state, &mut exp_iter) { - return Some(command) + if let Some(command) = Dsl::::take(state, &mut exp_iter)? { + return Ok(Some(command)) } } }, @@ -40,13 +33,13 @@ impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> f _ => panic!("invalid config (expected expression)") } } - None + Ok(None) } } /// A [TokenIter] can be a [KeyMap]. -impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> for TokenIter<'state> { - fn command (&'state self, state: &'state S, input: &'state I) -> Option { +impl<'source, S: Dsl, C: Command, I: DslInput> KeyMap for TokenIter<'source> { + fn keybind_resolve (&self, state: &S, input: &I) -> Perhaps { let mut iter = self.clone(); while let Some(next) = iter.next() { match next { @@ -55,8 +48,8 @@ impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> f match e.next() { Some(Token { value: Value::Sym(binding), .. }) => { if input.matches_dsl(binding) { - if let Some(command) = C::try_from_expr(state, &mut e) { - return Some(command) + if let Some(command) = Dsl::::take(state, &mut e)? { + return Ok(Some(command)) } } }, @@ -66,44 +59,29 @@ impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> f _ => panic!("invalid config (expected expression, got: {next:?})") } } - None + Ok(None) } } -pub type InputLayerCond<'state, S> = Boxbool + Send + Sync + 'state>; +pub type InputLayerCond = BoxUsually + Send + Sync>; /// A collection of pre-configured mappings of input events to commands, /// which may be made available subject to given conditions. -pub struct InputMap<'state, S, C, I, M> -where - C: DslCommand<'state, S>, - I: DslInput, - M: KeyMap<'state, S, C, I> + Send + Sync -{ - __: &'state PhantomData<(S, C, I)>, - pub layers: Vec<(InputLayerCond<'state, S>, M)>, +pub struct InputMap +where S: Dsl, C: Command, I: DslInput, M: KeyMap + Send + Sync { + __: PhantomData<(S, C, I)>, + pub layers: Vec<(InputLayerCond, M)>, } -impl<'state, S, C, I, M> Default for InputMap<'state, S, C, I, M> -where - C: DslCommand<'state, S>, - I: DslInput, - M: KeyMap<'state, S, C, I> + Send + Sync -{ +impl Default for InputMap +where S: Dsl, C: Command, I: DslInput, M: KeyMap + Send + Sync { fn default () -> Self { - Self { - __: &PhantomData, - layers: vec![] - } + Self { __: PhantomData, layers: vec![] } } } -impl<'state, S, C, I, M> InputMap<'state, S, C, I, M> -where - C: DslCommand<'state, S>, - I: DslInput, - M: KeyMap<'state, S, C, I> + Send + Sync -{ +impl InputMap +where S: Dsl + 'static, C: Command, I: DslInput, M: KeyMap + Send + Sync { pub fn new (keymap: M) -> Self { Self::default().layer(keymap) } @@ -112,46 +90,38 @@ where self } pub fn add_layer (&mut self, keymap: M) -> &mut Self { - self.add_layer_if(Box::new(|_|true), keymap); + self.add_layer_if(Box::new(|_|Ok(true)), keymap); self } - pub fn layer_if (mut self, condition: InputLayerCond<'state, S>, keymap: M) -> Self { + pub fn layer_if (mut self, condition: InputLayerCond, keymap: M) -> Self { self.add_layer_if(condition, keymap); self } - pub fn add_layer_if (&mut self, condition: InputLayerCond<'state, S>, keymap: M) -> &mut Self { + pub fn add_layer_if (&mut self, condition: InputLayerCond, keymap: M) -> &mut Self { self.layers.push((Box::new(condition), keymap)); self } } -impl<'state, S, C, I, M> std::fmt::Debug for InputMap<'state, S, C, I, M> -where - C: DslCommand<'state, S>, - I: DslInput, - M: KeyMap<'state, S, C, I> + Send + Sync -{ +impl std::fmt::Debug for InputMap +where S: Dsl, C: Command, I: DslInput, M: KeyMap + Send + Sync { fn fmt (&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { write!(f, "[InputMap: {} layer(s)]", self.layers.len()) } } /// An [InputMap] can be a [KeyMap]. -impl<'state, S, C, I, M> KeyMap<'state, S, C, I> for InputMap<'state, S, C, I, M> -where - C: DslCommand<'state, S>, - I: DslInput, - M: KeyMap<'state, S, C, I> + Send + Sync -{ - fn command (&'state self, state: &'state S, input: &'state I) -> Option { +impl KeyMap for InputMap +where S: Dsl, C: Command, I: DslInput, M: KeyMap + Send + Sync { + fn keybind_resolve (&self, state: &S, input: &I) -> Perhaps { for (condition, keymap) in self.layers.iter() { - if !condition(state) { + if !condition(state)? { continue } - if let Some(command) = keymap.command(state, input) { - return Some(command) + if let Some(command) = keymap.keybind_resolve(state, input)? { + return Ok(Some(command)) } } - None + Ok(None) } } diff --git a/output/src/ops/align.rs b/output/src/ops/align.rs index d4c7fb3..a35659b 100644 --- a/output/src/ops/align.rs +++ b/output/src/ops/align.rs @@ -36,38 +36,42 @@ pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } pub struct Align(Alignment, A); #[cfg(feature = "dsl")] -try_from_expr!(<'source, 'state, E>: Align>: |state, iter|{ - if let Some(Token { value: Value::Key(key), .. }) = iter.peek() { - match key { - "align/c"|"align/x"|"align/y"| - "align/n"|"align/s"|"align/e"|"align/w"| - "align/nw"|"align/sw"|"align/ne"|"align/se" => { - let _ = iter.next().unwrap(); - let content = iter.next().expect("no content specified"); - let content = if let Some(content) = state.get_content(&content.value) { - content - } else { - panic!("no content corresponding to {:?}", &content); - }; - return Some(match key { - "align/c" => Self::c(content), - "align/x" => Self::x(content), - "align/y" => Self::y(content), - "align/n" => Self::n(content), - "align/s" => Self::s(content), - "align/e" => Self::e(content), - "align/w" => Self::w(content), - "align/nw" => Self::nw(content), - "align/ne" => Self::ne(content), - "align/sw" => Self::sw(content), - "align/se" => Self::se(content), - _ => unreachable!() - }) - }, - _ => return None +impl<'state, E: Output + 'state, T: ViewContext<'state, E>> +FromDsl<'state, T> for Align> { + fn take_from <'source: 'state> (state: &'state T, iter: &mut TokenIter<'source>) -> Perhaps { + if let Some(Token { value: Value::Key(key), .. }) = iter.peek() { + match key { + "align/c"|"align/x"|"align/y"| + "align/n"|"align/s"|"align/e"|"align/w"| + "align/nw"|"align/sw"|"align/ne"|"align/se" => { + let _ = iter.next().unwrap(); + let content = if let Some(content) = state.get_content(&mut iter.clone())? { + content + } else { + panic!("no content corresponding to {:?}", &iter); + }; + return Ok(Some(match key { + "align/c" => Self::c(content), + "align/x" => Self::x(content), + "align/y" => Self::y(content), + "align/n" => Self::n(content), + "align/s" => Self::s(content), + "align/e" => Self::e(content), + "align/w" => Self::w(content), + "align/nw" => Self::nw(content), + "align/ne" => Self::ne(content), + "align/sw" => Self::sw(content), + "align/se" => Self::se(content), + _ => unreachable!() + })) + }, + _ => return Ok(None) + } + } else { + Ok(None) } } -}); +} impl Align { #[inline] pub const fn c (a: A) -> Self { Self(Alignment::Center, a) } diff --git a/output/src/ops/bsp.rs b/output/src/ops/bsp.rs index 300d244..2819bbf 100644 --- a/output/src/ops/bsp.rs +++ b/output/src/ops/bsp.rs @@ -21,30 +21,40 @@ impl, B: Content> Content for Bsp { } } #[cfg(feature = "dsl")] -try_from_expr!(<'source, 'state, E>: Bsp, RenderBox<'state, E>>: |state, iter| { - if let Some(Token { value: Value::Key(key), .. }) = iter.peek() { - match key { - "bsp/n"|"bsp/s"|"bsp/e"|"bsp/w"|"bsp/a"|"bsp/b" => { - let original = iter.clone(); - let _ = iter.next().unwrap(); - let c1 = iter.next().unwrap_or_else(||panic!("no content1 specified: {original:?}")); - let c2 = iter.next().unwrap_or_else(||panic!("no content2 specified: {original:?}")); - let c1 = state.get_content(&c1.value).expect("no content1 provided"); - let c2 = state.get_content(&c2.value).expect("no content2 provided"); - return Some(match key { - "bsp/n" => Self::n(c1, c2), - "bsp/s" => Self::s(c1, c2), - "bsp/e" => Self::e(c1, c2), - "bsp/w" => Self::w(c1, c2), - "bsp/a" => Self::a(c1, c2), - "bsp/b" => Self::b(c1, c2), - _ => unreachable!(), - }) - }, - _ => return None - } +impl<'state, E: Output + 'state, T: ViewContext<'state, E>> +FromDsl<'state, T> for Bsp, RenderBox<'state, E>> { + fn take_from <'source: 'state> (state: &'state T, iter: &mut TokenIter<'source>) -> Perhaps { + Ok(if let Some(Token { + value: Value::Key("bsp/n"|"bsp/s"|"bsp/e"|"bsp/w"|"bsp/a"|"bsp/b"), + .. + }) = iter.peek() { + let base = iter.clone(); + return Ok(Some(match iter.next() { + Some(Token { value: Value::Key("bsp/n"), .. }) => + Self::n(state.get_content_or_fail(iter)?, + state.get_content_or_fail(iter)?), + Some(Token { value: Value::Key("bsp/s"), .. }) => + Self::s(state.get_content_or_fail(iter)?, + state.get_content_or_fail(iter)?), + Some(Token { value: Value::Key("bsp/e"), .. }) => + Self::e(state.get_content_or_fail(iter)?, + state.get_content_or_fail(iter)?), + Some(Token { value: Value::Key("bsp/w"), .. }) => + Self::w(state.get_content_or_fail(iter)?, + state.get_content_or_fail(iter)?), + Some(Token { value: Value::Key("bsp/a"), .. }) => + Self::a(state.get_content_or_fail(iter)?, + state.get_content_or_fail(iter)?), + Some(Token { value: Value::Key("bsp/b"), .. }) => + Self::b(state.get_content_or_fail(iter)?, + state.get_content_or_fail(iter)?), + _ => unreachable!(), + })) + } else { + None + }) } -}); +} impl Bsp { #[inline] pub const fn n (a: A, b: B) -> Self { Self(North, a, b) } #[inline] pub const fn s (a: A, b: B) -> Self { Self(South, a, b) } diff --git a/output/src/ops/cond.rs b/output/src/ops/cond.rs index 578210e..cfdf2fd 100644 --- a/output/src/ops/cond.rs +++ b/output/src/ops/cond.rs @@ -19,35 +19,41 @@ impl Either { } #[cfg(feature = "dsl")] -try_from_expr!(<'source, 'state, E>: When>: |state, iter| { - if let Some(Token { value: Value::Key("when"), .. }) = iter.peek() { - let _ = iter.next().unwrap(); - let content = iter.next().expect("no content specified").value; - return Some(Self( - state.get(&mut iter) - .unwrap_or_else(||panic!("cond: no condition: {iter:?}")), - state.get_content(&content) - .unwrap_or_else(||panic!("cond: no content for {:?}: {iter:?}", &content)) - )) +impl<'state, E: Output + 'state, T: ViewContext<'state, E>> +FromDsl<'state, T> for When> { + fn take_from <'source: 'state> (state: &'state T, iter: &mut TokenIter<'source>) -> Perhaps { + Ok(if let Some(Token { + value: Value::Key("when"), + .. + }) = iter.peek() { + let base = iter.clone(); + return Ok(Some(Self( + state.take(iter)?.unwrap_or_else(||panic!("cond: no condition: {base:?}")), + state.get_content_or_fail(iter)? + ))) + } else { + None + }) } -}); +} #[cfg(feature = "dsl")] -try_from_expr!(<'source, 'state, E>: Either, RenderBox<'state, E>>: |state, iter| { - if let Some(Token { value: Value::Key("either"), .. }) = iter.peek() { - let base = iter.clone(); - let _ = iter.next().unwrap(); - //panic!("{iter:?}"); - return Some(Self( - state.get(&mut iter) - .unwrap_or_else(||panic!("either: no condition: {base:?}")), - state.get_content(&iter.next().expect("no content specified").value) - .unwrap_or_else(||panic!("either: no content 1: {base:?}")), - state.get_content(&iter.next().expect("no alternate specified").value) - .unwrap_or_else(||panic!("either: no content 2: {base:?}")), - )) +impl<'state, E: Output + 'state, T: ViewContext<'state, E>> +FromDsl<'state, T> for Either, RenderBox<'state, E>> { + fn take_from <'source: 'state> (state: &'state T, iter: &mut TokenIter<'source>) -> Perhaps { + if let Some(Token { value: Value::Key("either"), .. }) = iter.peek() { + let base = iter.clone(); + let _ = iter.next().unwrap(); + //panic!("{iter:?}"); + return Ok(Some(Self( + state.take(iter)?.unwrap_or_else(||panic!("either: no condition: {base:?}")), + state.get_content_or_fail(iter)?, + state.get_content_or_fail(iter)?, + ))) + } + Ok(None) } -}); +} impl> Content for When { fn layout (&self, to: E::Area) -> E::Area { diff --git a/output/src/ops/transform.rs b/output/src/ops/transform.rs index 8cb93ff..80d50ce 100644 --- a/output/src/ops/transform.rs +++ b/output/src/ops/transform.rs @@ -39,30 +39,22 @@ macro_rules! transform_xy { } } #[cfg(feature = "dsl")] - impl<'state, E: Output + 'state, T: ViewContext<'state, E>> TryFromDsl<'state, T> - for $Enum> { - fn try_from_expr <'source: 'state> (state: &'state T, iter: &mut TokenIter<'source>) - -> Option - { - let mut iter = iter.clone(); + impl<'state, E: Output + 'state, T: ViewContext<'state, E>> + FromDsl<'state, T> for $Enum> { + fn take_from <'source: 'state> (state: &'state T, iter: &mut TokenIter<'source>) -> Perhaps { if let Some(Token { value: Value::Key(k), .. }) = iter.peek() { - if k == $x || k == $y || k == $xy { - let _ = iter.next().unwrap(); - let token = iter.next().expect("no content specified"); - let content = if let Some(content) = state.get_content(&token.value) { - content - } else { - panic!("no content corresponding to {:?}", &token.value); - }; - return Some(match k { - $x => Self::x(content), - $y => Self::y(content), - $xy => Self::xy(content), - _ => unreachable!() - }) - } + let mut base = iter.clone(); + return Ok(Some(match iter.next() { + Some(Token{value:Value::Key($x),..}) => + Self::x(state.get_content_or_fail(iter)?), + Some(Token{value:Value::Key($y),..}) => + Self::y(state.get_content_or_fail(iter)?), + Some(Token{value:Value::Key($xy),..}) => + Self::xy(state.get_content_or_fail(iter)?), + _ => unreachable!() + })) } - None + Ok(None) } } impl> Content for $Enum { @@ -92,31 +84,30 @@ macro_rules! transform_xy_unit { #[inline] pub const fn xy (x: U, y: U, item: T) -> Self { Self::XY(x, y, item) } } #[cfg(feature = "dsl")] - impl<'state, E: Output + 'state, T: ViewContext<'state, E>> TryFromDsl<'state, T> - for $Enum> { - fn try_from_expr <'source: 'state> (state: &'state T, iter: &mut TokenIter<'source>) -> Option { - let mut iter = iter.clone(); - if let Some(Token { value: Value::Key(k), .. }) = iter.peek() { - if k == $x || k == $y { - let _ = iter.next().unwrap(); - let u = state.get(&mut iter).expect("no unit specified"); - let c = state.get_content(&iter.next().expect("no content specified").value) - .expect("no content provided"); - return Some(match k { - $x => Self::x(u, c), - $y => Self::y(u, c), - _ => unreachable!(), - }) - } else if k == $xy { - let _ = iter.next().unwrap(); - let u = state.get(&mut iter).expect("no unit specified"); - let v = state.get(&mut iter).expect("no unit specified"); - let c = state.get_content(&iter.next().expect("no content specified").value) - .expect("no content provided"); - return Some(Self::xy(u, v, c)) - } - } - None + impl<'state, E: Output + 'state, T: ViewContext<'state, E>> + FromDsl<'state, T> for $Enum> { + fn take_from <'source: 'state> (state: &'state T, iter: &mut TokenIter<'source>) -> Perhaps { + Ok(if let Some(Token { value: Value::Key($x|$y|$xy), .. }) = iter.peek() { + let mut base = iter.clone(); + Some(match iter.next() { + Some(Token { value: Value::Key($x), .. }) => Self::x( + state.take_or_fail(iter, "no unit specified")?, + state.get_content_or_fail(iter)?, + ), + Some(Token { value: Value::Key($y), .. }) => Self::y( + state.take_or_fail(iter, "no unit specified")?, + state.get_content_or_fail(iter)?, + ), + Some(Token { value: Value::Key($x), .. }) => Self::xy( + state.take_or_fail(iter, "no unit specified")?, + state.take_or_fail(iter, "no unit specified")?, + state.get_content_or_fail(iter)? + ), + _ => unreachable!(), + }) + } else { + None + }) } } impl> Content for $Enum { diff --git a/output/src/space/measure.rs b/output/src/space/measure.rs index a894b60..9ed5c2c 100644 --- a/output/src/space/measure.rs +++ b/output/src/space/measure.rs @@ -85,7 +85,7 @@ impl Measure { pub fn format (&self) -> Arc { format!("{}x{}", self.w(), self.h()).into() } - pub fn of > (&self, item: T) -> Bsp, T> { + pub fn of > (&self, item: T) -> Bsp, T> { Bsp::b(Fill::xy(self), item) } } diff --git a/output/src/view.rs b/output/src/view.rs index 484194d..8f40cc2 100644 --- a/output/src/view.rs +++ b/output/src/view.rs @@ -1,114 +1,63 @@ use crate::*; -#[macro_export] macro_rules! view { - ($Output:ty: |$self:ident: $State:ty| $expr:expr; { - $($sym:literal => $body:expr),* $(,)? - }) => { - impl Content<$Output> for $State { - fn content (&$self) -> impl Render<$Output> { $expr } - } - impl<'a> ViewContext<'a, $Output> for $State { - fn get_content_sym (&'a $self, value: &Value<'a>) -> Option> { - if let Value::Sym(s) = value { - match *s { - $($sym => Some($body.boxed()),)* - _ => None - } - } else { - panic!("expected content, got: {value:?}") - } - } - } - } -} - -// An ephemeral wrapper around view state and view description, -// that is meant to be constructed and returned from [Content::content]. #[cfg(feature = "dsl")] -pub struct View<'a, T>( - pub &'a T, - pub TokenIter<'a> -); - -#[cfg(feature = "dsl")] -impl<'a, O: Output + 'a, T: ViewContext<'a, O>> Content for View<'a, T> { - fn content (&self) -> impl Render { - let mut iter = self.1.clone(); - while let Some(Token { value, .. }) = iter.next() { - if let Some(content) = self.0.get_content(&value) { - return Some(content) - } +#[macro_export] macro_rules! try_delegate { + ($s:ident, $dsl:expr, $T:ty) => { + let value: Option<$T> = FromDsl::take_from($s, $dsl)?; + if let Some(value) = value { + return Ok(Some(value.boxed())) } - return None } } // Provides components to the view. #[cfg(feature = "dsl")] pub trait ViewContext<'state, E: Output + 'state>: Send + Sync - + Context<'state, bool> - + Context<'state, usize> - + Context<'state, E::Unit> + + Dsl + + Dsl + + Dsl { - fn get_content <'source: 'state> (&'state self, value: &Value<'source>) -> Option> { - match value { - Value::Sym(_) => self.get_content_sym(value), - Value::Exp(_, _) => self.get_content_exp(value), - _ => panic!("only :symbols and (expressions) accepted here, got: {value:?}") - } - } - fn get_content_sym <'source: 'state> (&'state self, value: &Value<'source>) - -> Option>; - fn get_content_exp <'source: 'state> (&'state self, value: &Value<'source>) - -> Option> + fn get_content_or_fail <'source: 'state> (&'state self, iter: &mut TokenIter<'source>) + -> Usually> { - try_delegate!(self, *value, When::>); - try_delegate!(self, *value, Either::, RenderBox<'state, E>>); - try_delegate!(self, *value, Align::>); - try_delegate!(self, *value, Bsp::, RenderBox<'state, E>>); - try_delegate!(self, *value, Fill::>); - try_delegate!(self, *value, Fixed::<_, RenderBox<'state, E>>); - try_delegate!(self, *value, Min::<_, RenderBox<'state, E>>); - try_delegate!(self, *value, Max::<_, RenderBox<'state, E>>); - try_delegate!(self, *value, Shrink::<_, RenderBox<'state, E>>); - try_delegate!(self, *value, Expand::<_, RenderBox<'state, E>>); - try_delegate!(self, *value, Push::<_, RenderBox<'state, E>>); - try_delegate!(self, *value, Pull::<_, RenderBox<'state, E>>); - try_delegate!(self, *value, Margin::<_, RenderBox<'state, E>>); - try_delegate!(self, *value, Padding::<_, RenderBox<'state, E>>); - None - } -} - -#[cfg(feature = "dsl")] -#[macro_export] macro_rules! try_delegate { - ($s:ident, $dsl:expr, $T:ty) => { - if let Some(value) = <$T>::try_from_atom($s, $dsl) { - return Some(value.boxed()) + let base = iter.clone(); + if let Some(content) = self.get_content(iter)? { + Ok(content) + } else { + Err(format!("not found: {iter:?}").into()) } } -} - -#[cfg(feature = "dsl")] -#[macro_export] macro_rules! try_from_expr { - (< - $lt_source:lifetime, - $lt_state:lifetime, - $Output:ident - >: $Struct:ty: |$state:ident, $iter:ident|$body:expr) => { - impl< - $lt_state, - $Output: Output + $lt_state, - T: ViewContext<$lt_state, $Output> - > TryFromDsl<$lt_state, T> for $Struct { - fn try_from_expr <$lt_source: $lt_state> ( - $state: &$lt_state T, - $iter: &mut TokenIter<$lt_source> - ) -> Option { - let mut $iter = $iter.clone(); - $body; - None - } + fn get_content <'source: 'state> (&'state self, iter: &mut TokenIter<'source>) + -> Perhaps> + { + match iter.peek() { + Some(Token { value: Value::Sym(_), .. }) => + self.get_content_sym(iter), + Some(Token { value: Value::Exp(_, _), .. }) => + self.get_content_exp(iter), + None => Ok(None), + _ => panic!("only :symbols and (expressions) accepted here") } } + fn get_content_sym <'source: 'state> (&'state self, iter: &mut TokenIter<'source>) + -> Perhaps>; + fn get_content_exp <'source: 'state> (&'state self, iter: &mut TokenIter<'source>) + -> Perhaps> + { + try_delegate!(self, iter, When::>); + try_delegate!(self, iter, Either::, RenderBox<'state, E>>); + try_delegate!(self, iter, Align::>); + try_delegate!(self, iter, Bsp::, RenderBox<'state, E>>); + try_delegate!(self, iter, Fill::>); + try_delegate!(self, iter, Fixed::<_, RenderBox<'state, E>>); + try_delegate!(self, iter, Min::<_, RenderBox<'state, E>>); + try_delegate!(self, iter, Max::<_, RenderBox<'state, E>>); + try_delegate!(self, iter, Shrink::<_, RenderBox<'state, E>>); + try_delegate!(self, iter, Expand::<_, RenderBox<'state, E>>); + try_delegate!(self, iter, Push::<_, RenderBox<'state, E>>); + try_delegate!(self, iter, Pull::<_, RenderBox<'state, E>>); + try_delegate!(self, iter, Margin::<_, RenderBox<'state, E>>); + try_delegate!(self, iter, Padding::<_, RenderBox<'state, E>>); + Ok(None) + } } diff --git a/proc/src/proc_command.rs b/proc/src/proc_command.rs index 123b11b..27f68ff 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -52,49 +52,111 @@ impl CommandImpl { impl ToTokens for CommandDef { fn to_tokens (&self, out: &mut TokenStream2) { - let Self(CommandMeta(target), CommandImpl(block, exposed)) = self; - let enumeration = &block.self_ty; - let variants = exposed.values().map(|x|x.to_enum_variant_def()); - let matchers = exposed.values().map(CommandArm::to_matcher); - let implementations = exposed.values().map(CommandArm::to_implementation); + let Self(CommandMeta(state), CommandImpl(block, exposed)) = self; + + let command_enum = &block.self_ty; + + let variants = exposed.values().map(|arm|{ + let mut out = TokenStream2::new(); + out.append(arm.to_enum_variant_ident()); + //let ident = &arm.0; + if arm.has_args() { + out.append(Group::new(Delimiter::Brace, { + let mut out = TokenStream2::new(); + for (arg, ty) in arm.args() { + write_quote_to(&mut out, quote! { #arg : #ty , }); + } + out + })); + } + out.append(Punct::new(',', Alone)); + out + }); + + let matchers = exposed.values().map(|arm|{ + let key = LitStr::new(&arm.to_key(), Span::call_site()); + let variant = { + let mut out = TokenStream2::new(); + out.append(arm.to_enum_variant_ident()); + let ident = &arm.0; + if arm.has_args() { + out.append(Group::new(Delimiter::Brace, { + let mut out = TokenStream2::new(); + for (arg, ty) in arm.args() { + write_quote_to(&mut out, quote! { + #arg: Dsl::take_or_fail(self, words)?, + }); + } + out + })); + } + out + }; + write_quote(quote! { + Some(::tengri::dsl::Token { value: ::tengri::dsl::Value::Key(#key), .. }) => { + let mut words = words.clone(); + Some(#command_enum::#variant) + }, + //Some(::tengri::dsl::Token { + //value: ::tengri::dsl::Value::Key(#key), .. + //}) => { + //let mut iter = iter.clone(); Some(#command_enum::#variant) + //}, + }) + }); + + let implementations = exposed.values().map(|arm|{ + let ident = &arm.0; + let variant = { + let mut out = TokenStream2::new(); + out.append(arm.to_enum_variant_ident()); + //let ident = &arm.0; + if arm.has_args() { + out.append(Group::new(Delimiter::Brace, { + let mut out = TokenStream2::new(); + for (arg, _ty) in arm.args() { + write_quote_to(&mut out, quote! { #arg , }); + } + out + })); + } + out + }; + let give_rest = write_quote(quote! { /*TODO*/ }); + let give_args = arm.args() + .map(|(arg, _ty)|write_quote(quote! { #arg, })) + .collect::>(); + write_quote(quote! { + #command_enum::#variant => + #command_enum::#ident(state, #(#give_args)* #give_rest), + }) + }); + write_quote_to(out, quote! { /// Generated by [tengri_proc]. - #[derive(Clone, Debug)] - pub enum #enumeration { - #(#variants)* - } + #[derive(Clone, Debug)] pub enum #command_enum { #(#variants)* } + /// Not generated by [tengri_proc]. #block /// Generated by [tengri_proc]. - impl<'state> ::tengri::dsl::TryFromDsl<'state, #target> for #enumeration { - fn try_from_expr <'source: 'state> ( - state: &'state #target, iter: &mut ::tengri::dsl::TokenIter<'source> - ) -> Option { - let mut iter = iter.clone(); - let token = iter.next(); - match token { - #(#matchers)* - _ => None - } + impl ::tengri::input::Command<#state> for #command_enum { + fn execute (self, state: &mut #state) -> Perhaps { + match self { #(#implementations)* } } } /// Generated by [tengri_proc]. - impl<'state> ::tengri::dsl::Context<'state, #enumeration> for #target { - fn get <'source> (&self, iter: &mut ::tengri::dsl::TokenIter<'source>) - -> Option<#enumeration> + impl ::tengri::dsl::Dsl<#command_enum> for #state { + fn take <'source, 'state> ( + &'state self, words: &mut TokenIter<'source> + ) + -> ::tengri::Perhaps<#command_enum> { - use ::tengri::dsl::TryFromDsl; - #enumeration::try_from_expr(self, iter) - } - } - /// Generated by [tengri_proc]. - impl ::tengri::input::Command<#target> for #enumeration { - fn execute (self, state: &mut #target) -> Perhaps { - match self { - #(#implementations)* - } + let mut words = words.clone(); + let token = words.next(); + todo!()//Ok(match token { #(#matchers)* _ => None }) } } }); + //if exposed.len() > 0 { //panic!("{:#?}", block.self_ty); //if let Type::Path(ref path) = *block.self_ty { @@ -146,61 +208,4 @@ impl CommandArm { out.append(Punct::new(',', Alone)); out } - fn to_enum_variant_bind (&self) -> TokenStream2 { - let mut out = TokenStream2::new(); - out.append(self.to_enum_variant_ident()); - let ident = &self.0; - if self.has_args() { - out.append(Group::new(Delimiter::Brace, { - let mut out = TokenStream2::new(); - for (arg, ty) in self.args() { - //let take_err = LitStr::new(&format!("{}: missing argument \"{}\" ({})", - //quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site()); - let give_err = format!("{}: missing value for \"{}\" ({}): {{:#?}}", - quote!{#ident}, quote!{#arg}, quote!{#ty}); - let give_err = LitStr::new(&give_err, Span::call_site()); - write_quote_to(&mut out, quote! { - #arg: ::tengri::dsl::Context::get(state, &mut iter) - .unwrap_or_else(||panic!(#give_err, token)), - }); - } - out - })); - } - out - } - fn to_enum_variant_unbind (&self) -> TokenStream2 { - let mut out = TokenStream2::new(); - out.append(self.to_enum_variant_ident()); - //let ident = &self.0; - if self.has_args() { - out.append(Group::new(Delimiter::Brace, { - let mut out = TokenStream2::new(); - for (arg, _ty) in self.args() { - write_quote_to(&mut out, quote! { #arg , }); - } - out - })); - } - out - } - fn to_matcher (&self) -> TokenStream2 { - let key = LitStr::new(&self.to_key(), Span::call_site()); - let variant = self.to_enum_variant_bind(); - let pattern = quote! { - Some(::tengri::dsl::Token { value: ::tengri::dsl::Value::Key(#key), .. }) - }; - write_quote(quote! { - #pattern => { let mut iter = iter.clone(); Some(Self::#variant) }, - }) - } - fn to_implementation (&self) -> TokenStream2 { - let ident = &self.0; - let variant = self.to_enum_variant_unbind(); - let give_rest = write_quote(quote! { /*TODO*/ }); - let give_args = self.args() - .map(|(arg, _ty)|write_quote(quote! { #arg, })) - .collect::>(); - write_quote(quote! { Self::#variant => Self::#ident(state, #(#give_args)* #give_rest), }) - } } diff --git a/proc/src/proc_expose.rs b/proc/src/proc_expose.rs index 4a11347..d6f23a8 100644 --- a/proc/src/proc_expose.rs +++ b/proc/src/proc_expose.rs @@ -85,15 +85,15 @@ impl ToTokens for ExposeImpl { let values = variants.iter().map(ExposeArm::from); write_quote_to(out, quote! { /// Generated by [tengri_proc]. - impl<'state> ::tengri::dsl::Context<'state, #t> for #target { - fn get <'source> ( - &self, iter: &mut ::tengri::dsl::TokenIter<'source> - ) -> Option<#t> { - Some(match iter.next().map(|x|x.value) { + impl ::tengri::dsl::Dsl<#t> for #target { + fn take <'state, 'source> ( + &'state self, iter: &mut ::tengri::dsl::TokenIter<'source> + ) -> Perhaps<#t> { + Ok(Some(match iter.next().map(|x|x.value) { #predefined #(#values,)* - _ => return None - }) + _ => return Ok(None) + })) } } }); diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 7fdd1fd..48af79f 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -44,7 +44,7 @@ impl Parse for ViewImpl { impl ToTokens for ViewDef { fn to_tokens (&self, out: &mut TokenStream2) { let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self; - let ident = &block.self_ty; + let view = &block.self_ty; let mut available = vec![]; let exposed: Vec<_> = exposed.iter().map(|(k,v)|{ available.push(k.clone()); @@ -52,23 +52,24 @@ impl ToTokens for ViewDef { }).collect(); let available: String = available.join(", "); let error_msg = LitStr::new( - &format!("expected Sym(content), got: {{value:?}}, available: {available}"), + &format!("expected Sym(content), got: {{token:?}}, available: {available}"), Span::call_site() ); for token in quote! { #block /// Generated by [tengri_proc]. - impl ::tengri::output::Content<#output> for #ident { + impl ::tengri::output::Content<#output> for #view { fn content (&self) -> impl Render<#output> { - self.size.of(::tengri::output::View(self, self.config.view)) + self.view() } } /// Generated by [tengri_proc]. - impl<'state> ::tengri::output::ViewContext<'state, #output> for #ident { - fn get_content_sym <'source: 'state> (&'state self, value: &Value<'source>) - -> Option> - { - match value { #(#exposed)* _ => panic!(#error_msg) } + impl<'state> ::tengri::dsl::FromDsl<'state, #view> for ::tengri::output::RenderBox<'state, #output> { + fn take_from <'source: 'state> ( + state: &'state #view, + token: &mut ::tengri::dsl::TokenIter<'source> + ) -> Perhaps { + Ok(match token.peek() { #(#exposed)* _ => None }) } } } { @@ -80,21 +81,44 @@ impl ToTokens for ViewDef { impl ToTokens for ViewArm { fn to_tokens (&self, out: &mut TokenStream2) { let Self(key, value) = self; - out.append(Punct::new(':', Joint)); - out.append(Punct::new(':', Alone)); - out.append(Ident::new("tengri", Span::call_site())); - out.append(Punct::new(':', Joint)); - out.append(Punct::new(':', Alone)); - out.append(Ident::new("dsl", Span::call_site())); - out.append(Punct::new(':', Joint)); - out.append(Punct::new(':', Alone)); - out.append(Ident::new("Value", Span::call_site())); - out.append(Punct::new(':', Joint)); - out.append(Punct::new(':', Alone)); - out.append(Ident::new("Sym", Span::call_site())); + out.append(Ident::new("Some", Span::call_site())); out.append(Group::new(Delimiter::Parenthesis, { let mut out = TokenStream2::new(); - out.append(LitStr::new(key, Span::call_site()).token()); + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("tengri", Span::call_site())); + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("dsl", Span::call_site())); + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("Token", Span::call_site())); + out.append(Group::new(Delimiter::Brace, { + let mut out = TokenStream2::new(); + out.append(Ident::new("value", Span::call_site())); + out.append(Punct::new(':', Alone)); + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("tengri", Span::call_site())); + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("dsl", Span::call_site())); + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("Value", Span::call_site())); + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("Sym", Span::call_site())); + out.append(Group::new(Delimiter::Parenthesis, { + let mut out = TokenStream2::new(); + out.append(LitStr::new(key, Span::call_site()).token()); + out + })); + out.append(Punct::new(',', Alone)); + out.append(Punct::new('.', Joint)); + out.append(Punct::new('.', Alone)); + out + })); out })); out.append(Punct::new('=', Joint)); @@ -102,7 +126,7 @@ impl ToTokens for ViewArm { out.append(Ident::new("Some", Span::call_site())); out.append(Group::new(Delimiter::Parenthesis, { let mut out = TokenStream2::new(); - out.append(Ident::new("self", Span::call_site())); + out.append(Ident::new("state", Span::call_site())); out.append(Punct::new('.', Alone)); out.append(value.clone()); out.append(Group::new(Delimiter::Parenthesis, TokenStream2::new())); diff --git a/tengri/src/lib.rs b/tengri/src/lib.rs index 25d7b27..d451e70 100644 --- a/tengri/src/lib.rs +++ b/tengri/src/lib.rs @@ -7,7 +7,7 @@ pub use ::tengri_core::*; #[cfg(test)] extern crate tengri_proc; #[cfg(test)] #[test] fn test_subcommand () -> Usually<()> { use crate::input::{Command, InputMap, KeyMap, Handle, handle}; - use crate::dsl::{TryFromDsl, TokenIter}; + use crate::dsl::TokenIter; use crate::tui::TuiIn; use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}; //use crate::input::*; @@ -82,7 +82,7 @@ pub use ::tengri_core::*; //FIXME: //#[cfg(test)] #[test] fn test_dsl_context () { - //use crate::dsl::{Context, Value}; + //use crate::dsl::{Dsl, Value}; //struct Test; //#[tengri_proc::expose] @@ -91,10 +91,10 @@ pub use ::tengri_core::*; //true //} //} - //assert_eq!(Context::get(&Test, &Value::Sym(":false")), Some(false)); - //assert_eq!(Context::get(&Test, &Value::Sym(":true")), Some(true)); - //assert_eq!(Context::get(&Test, &Value::Sym(":some-bool")), Some(true)); - //assert_eq!(Context::get(&Test, &Value::Sym(":missing-bool")), None); - //assert_eq!(Context::get(&Test, &Value::Num(0)), Some(false)); - //assert_eq!(Context::get(&Test, &Value::Num(1)), Some(true)); + //assert_eq!(Dsl::get(&Test, &Value::Sym(":false")), Some(false)); + //assert_eq!(Dsl::get(&Test, &Value::Sym(":true")), Some(true)); + //assert_eq!(Dsl::get(&Test, &Value::Sym(":some-bool")), Some(true)); + //assert_eq!(Dsl::get(&Test, &Value::Sym(":missing-bool")), None); + //assert_eq!(Dsl::get(&Test, &Value::Num(0)), Some(false)); + //assert_eq!(Dsl::get(&Test, &Value::Num(1)), Some(true)); //} diff --git a/tui/src/tui_content.rs b/tui/src/tui_content.rs index 38960b9..0d563fd 100644 --- a/tui/src/tui_content.rs +++ b/tui/src/tui_content.rs @@ -21,6 +21,30 @@ impl> Content for std::sync::Arc { } } +impl> Content for Result> { + fn content (&self) -> impl Render { + Bsp::a(self.as_ref().ok(), self.as_ref().err() + .map(|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string()))) + } +} + +//impl> Render for Result> { + //fn layout (&self, to: [u16;4]) -> [u16;4] { + //match self { + //Ok(content) => content.layout(to), + //Err(e) => [0, 0, to.w(), to.h()] + //} + //} + //fn render (&self, to: &mut TuiOut) { + //match self { + //Ok(content) => content.render(to), + //Err(e) => to.blit(&e.to_string(), 0, 0, Some(Style::default() + //.bg(Color::Rgb(32,32,32)) + //.fg(Color::Rgb(255,255,255)))) + //} + //} +//} + mod tui_border; pub use self::tui_border::*; mod tui_button; pub use self::tui_button::*; mod tui_color; pub use self::tui_color::*;