diff --git a/dsl/src/dsl_parse.rs b/dsl/src/dsl_parse.rs index 63bc567..81a0336 100644 --- a/dsl/src/dsl_parse.rs +++ b/dsl/src/dsl_parse.rs @@ -171,3 +171,112 @@ pub const fn to_digit (c: char) -> DslResult { _ => return Result::Err(Unexpected(c)) }) } + +#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'source> { + pub source: &'source str, + pub start: usize, + pub length: usize, + pub value: Value<'source>, +} + +#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum Value<'source> { + #[default] Nil, + Err(DslError), + Num(usize), + Sym(&'source str), + Key(&'source str), + Str(&'source str), + Exp(usize, TokenIter<'source>), +} + +impl<'source> std::fmt::Display for Value<'source> { + fn fmt (&self, out: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(out, "{}", match self { + Nil => String::new(), + Err(e) => format!("[error: {e}]"), + Num(n) => format!("{n}"), + Sym(s) => format!("{s}"), + Key(s) => format!("{s}"), + Str(s) => format!("{s}"), + Exp(_, e) => format!("{e:?}"), + }) + } +} + +impl<'source> Token<'source> { + pub const fn new ( + source: &'source str, start: usize, length: usize, value: Value<'source> + ) -> Self { + Self { source, start, length, value } + } + pub const fn end (&self) -> usize { + self.start.saturating_add(self.length) + } + pub const fn slice (&'source self) -> &'source str { + self.slice_source(self.source) + } + pub const fn slice_source <'range> (&'source self, source: &'range str) -> &'range str { + str_range(source, self.start, self.end()) + } + pub const fn slice_source_exp <'range> (&'source self, source: &'range str) -> &'range str { + str_range(source, self.start.saturating_add(1), self.end()) + } + pub const fn with_value (self, value: Value<'source>) -> Self { + Self { value, ..self } + } + pub const fn value (&self) -> Value { + self.value + } + pub const fn error (self, error: DslError) -> Self { + Self { value: Value::Err(error), ..self } + } + pub const fn grow (self) -> Self { + Self { length: self.length.saturating_add(1), ..self } + } + pub const fn grow_num (self, m: usize, c: char) -> Self { + match to_digit(c) { + Ok(n) => Self { value: Num(10*m+n), ..self.grow() }, + Result::Err(e) => Self { value: Err(e), ..self.grow() }, + } + } + pub const fn grow_key (self) -> Self { + let token = self.grow(); + token.with_value(Key(token.slice_source(self.source))) + } + pub const fn grow_sym (self) -> Self { + let token = self.grow(); + token.with_value(Sym(token.slice_source(self.source))) + } + pub const fn grow_str (self) -> Self { + let token = self.grow(); + token.with_value(Str(token.slice_source(self.source))) + } + pub const fn grow_exp (self) -> Self { + let token = self.grow(); + if let Exp(depth, _) = token.value { + token.with_value(Exp(depth, TokenIter::new(token.slice_source_exp(self.source)))) + } else { + unreachable!() + } + } + pub const fn grow_in (self) -> Self { + let token = self.grow_exp(); + if let Value::Exp(depth, source) = token.value { + token.with_value(Value::Exp(depth.saturating_add(1), source)) + } else { + unreachable!() + } + } + pub const fn grow_out (self) -> Self { + let token = self.grow_exp(); + if let Value::Exp(depth, source) = token.value { + if depth > 0 { + token.with_value(Value::Exp(depth - 1, source)) + } else { + return self.error(Unexpected(')')) + } + } else { + unreachable!() + } + } +} diff --git a/dsl/src/dsl_provide.rs b/dsl/src/dsl_provide.rs index d9896de..f98130b 100644 --- a/dsl/src/dsl_provide.rs +++ b/dsl/src/dsl_provide.rs @@ -1,27 +1,45 @@ use crate::*; -pub trait Dsl: Sized { - fn take <'state, 'source: 'state> ( - &'state self, - token: &mut TokenIter<'source> - ) - -> Perhaps; - fn take_or_fail <'state, 'source: 'state> ( +/// 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 self, token: &mut TokenIter<'source>, error: impl Into> - ) -> Usually { - if let Some(value) = Dsl::::take(self, token)? { + ) -> Usually { + if let Some(value) = Dsl::::take(self, token)? { Ok(value) } else { Result::Err(error.into()) } } } -pub trait FromDsl<'source, State>: Sized { - fn take_from <'state> (state: &'state State, token: &mut TokenIter<'source>) - -> Perhaps; - fn take_from_or_fail <'state> ( + +pub trait FromDsl<'state, State>: Sized { + fn take_from <'source: 'state> (state: &'state State, _token: &mut TokenIter<'source>) + -> Perhaps + { + unimplemented!() + } + fn take_from_or_fail <'source: 'state> ( state: &'state State, token: &mut TokenIter<'source>, error: impl Into> @@ -33,67 +51,6 @@ pub trait FromDsl<'source, State>: Sized { } } } -//impl, T> FromDsl for T { - //fn take_from <'state, 'source: 'state> (state: &'state State, token: &mut TokenIter<'source>) - //-> Perhaps - //{ - //Dsl::take(state, token) - //} -//} - -/// Implement the [Dsl] trait, which boils down to -/// specifying two types and providing an expression. -#[macro_export] macro_rules! from_dsl { - (@a: $T:ty: |$state:ident, $words:ident|$expr:expr) => { - impl<'source, State: Dsl, A> FromDsl<'source, State> for $T { - fn take_from <'state> ( - $state: &'state State, - $words: &mut TokenIter<'source>, - ) -> Perhaps<$T> { - $expr - } - } - }; - (@ab: $T:ty: |$state:ident, $words:ident|$expr:expr) => { - impl<'source, State: Dsl + Dsl, A, B> FromDsl<'source, State> for $T { - fn take_from <'state> ( - $state: &'state State, - $words: &mut TokenIter<'source>, - ) -> Perhaps<$T> { - $expr - } - } - }; - ($T:ty: |$state:ident:$S:ty, $words:ident|$expr:expr) => { - impl<'source> FromDsl<'source, $S> for $T { - fn take_from <'state> ( - $state: &'state $S, - $words: &mut TokenIter<'source>, - ) -> 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 self, - //token: &mut TokenIter<'source>, - //error: impl Into> - //) -> Usually { - //if let Some(value) = Dsl::::take(self, token)? { - //Ok(value) - //} else { - //Result::Err(error.into()) - //} - //} -//} //impl, U> Dsl for &T { //fn take <'state, 'source> (&'state self, iter: &mut TokenIter<'source>) -> Perhaps { @@ -107,8 +64,8 @@ pub trait FromDsl<'source, State>: Sized { //} //} -//impl<'state, X, Y> Dsl for Y where Y: Dsl<'state, X> { -//} +impl<'state, X, Y> Dsl for Y where Y: FromDsl<'state, X> { +} -//impl> Dsl for T { +//impl> Dsl for T { //} diff --git a/dsl/src/dsl_token.rs b/dsl/src/dsl_token.rs deleted file mode 100644 index d45203b..0000000 --- a/dsl/src/dsl_token.rs +++ /dev/null @@ -1,110 +0,0 @@ -use crate::*; - -#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'source> { - pub source: &'source str, - pub start: usize, - pub length: usize, - pub value: Value<'source>, -} - -#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum Value<'source> { - #[default] Nil, - Err(DslError), - Num(usize), - Sym(&'source str), - Key(&'source str), - Str(&'source str), - Exp(usize, TokenIter<'source>), -} - -impl<'source> std::fmt::Display for Value<'source> { - fn fmt (&self, out: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - write!(out, "{}", match self { - Nil => String::new(), - Err(e) => format!("[error: {e}]"), - Num(n) => format!("{n}"), - Sym(s) => format!("{s}"), - Key(s) => format!("{s}"), - Str(s) => format!("{s}"), - Exp(_, e) => format!("{e:?}"), - }) - } -} - -impl<'source> Token<'source> { - pub const fn new ( - source: &'source str, start: usize, length: usize, value: Value<'source> - ) -> Self { - Self { source, start, length, value } - } - pub const fn end (&self) -> usize { - self.start.saturating_add(self.length) - } - pub const fn slice (&'source self) -> &'source str { - self.slice_source(self.source) - } - pub const fn slice_source <'range> (&'source self, source: &'range str) -> &'range str { - str_range(source, self.start, self.end()) - } - pub const fn slice_source_exp <'range> (&'source self, source: &'range str) -> &'range str { - str_range(source, self.start.saturating_add(1), self.end()) - } - pub const fn with_value (self, value: Value<'source>) -> Self { - Self { value, ..self } - } - pub const fn value (&self) -> Value { - self.value - } - pub const fn error (self, error: DslError) -> Self { - Self { value: Value::Err(error), ..self } - } - pub const fn grow (self) -> Self { - Self { length: self.length.saturating_add(1), ..self } - } - pub const fn grow_num (self, m: usize, c: char) -> Self { - match to_digit(c) { - Ok(n) => Self { value: Num(10*m+n), ..self.grow() }, - Result::Err(e) => Self { value: Err(e), ..self.grow() }, - } - } - pub const fn grow_key (self) -> Self { - let token = self.grow(); - token.with_value(Key(token.slice_source(self.source))) - } - pub const fn grow_sym (self) -> Self { - let token = self.grow(); - token.with_value(Sym(token.slice_source(self.source))) - } - pub const fn grow_str (self) -> Self { - let token = self.grow(); - token.with_value(Str(token.slice_source(self.source))) - } - pub const fn grow_exp (self) -> Self { - let token = self.grow(); - if let Exp(depth, _) = token.value { - token.with_value(Exp(depth, TokenIter::new(token.slice_source_exp(self.source)))) - } else { - unreachable!() - } - } - pub const fn grow_in (self) -> Self { - let token = self.grow_exp(); - if let Value::Exp(depth, source) = token.value { - token.with_value(Value::Exp(depth.saturating_add(1), source)) - } else { - unreachable!() - } - } - pub const fn grow_out (self) -> Self { - let token = self.grow_exp(); - if let Value::Exp(depth, source) = token.value { - if depth > 0 { - token.with_value(Value::Exp(depth - 1, source)) - } else { - return self.error(Unexpected(')')) - } - } else { - unreachable!() - } - } -} diff --git a/dsl/src/lib.rs b/dsl/src/lib.rs index 712eced..9c9b44e 100644 --- a/dsl/src/lib.rs +++ b/dsl/src/lib.rs @@ -46,7 +46,6 @@ pub(crate) use self::Value::*; pub(crate) use self::DslError::*; mod dsl_error; pub use self::dsl_error::*; -mod dsl_token; pub use self::dsl_token::*; mod dsl_parse; pub use self::dsl_parse::*; mod dsl_provide; pub use self::dsl_provide::*; diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs index 2856c89..dfcbd72 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -5,15 +5,13 @@ use std::marker::PhantomData; pub trait DslInput: Input { fn matches_dsl (&self, token: &str) -> bool; } /// A pre-configured mapping of input events to commands. -pub trait KeyMap<'source, S, C: FromDsl<'source, S> + Command, I: DslInput> { +pub trait KeyMap, C: Command, I: DslInput> { /// Try to find a command that matches the current input event. fn keybind_resolve (&self, state: &S, input: &I) -> Perhaps; } /// A [SourceIter] can be a [KeyMap]. -impl<'source, S, C: FromDsl<'source, S> + Command, I: DslInput> -KeyMap<'source, S, C, I> -for SourceIter<'source> { +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() { @@ -24,7 +22,7 @@ for SourceIter<'source> { match exp_iter.next() { Some(Token { value: Value::Sym(binding), .. }) => { if input.matches_dsl(binding) { - if let Some(command) = FromDsl::take_from(state, &mut exp_iter)? { + if let Some(command) = Dsl::::take(state, &mut exp_iter)? { return Ok(Some(command)) } } @@ -40,9 +38,7 @@ for SourceIter<'source> { } /// A [TokenIter] can be a [KeyMap]. -impl<'source, S, C: FromDsl<'source, S> + Command, I: DslInput> -KeyMap<'source, S, C, I> -for TokenIter<'source> { +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() { @@ -52,7 +48,7 @@ for TokenIter<'source> { match e.next() { Some(Token { value: Value::Sym(binding), .. }) => { if input.matches_dsl(binding) { - if let Some(command) = FromDsl::take_from(state, &mut e)? { + if let Some(command) = Dsl::::take(state, &mut e)? { return Ok(Some(command)) } } @@ -67,37 +63,25 @@ for TokenIter<'source> { } } -pub type InputLayerCond<'source, S> = BoxUsually + 'source>; +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<'source, - S, - C: Command + FromDsl<'source, S>, - I: DslInput, - M: KeyMap<'source, S, C, I> -> { - __: PhantomData<&'source (S, C, I)>, - pub layers: Vec<(InputLayerCond<'source, 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<'source, - S, - C: Command + FromDsl<'source, S>, - I: DslInput, - M: KeyMap<'source, S, C, I> -> Default for InputMap<'source, S, C, I, M>{ +impl Default for InputMap +where S: Dsl, C: Command, I: DslInput, M: KeyMap + Send + Sync { fn default () -> Self { Self { __: PhantomData, layers: vec![] } } } -impl<'source, - S, - C: Command + FromDsl<'source, S>, - I: DslInput, - M: KeyMap<'source, S, C, I> -> InputMap<'source, S, C, I, M> { +impl InputMap +where S: Dsl + 'static, C: Command, I: DslInput, M: KeyMap + Send + Sync { pub fn new (keymap: M) -> Self { Self::default().layer(keymap) } @@ -109,34 +93,26 @@ impl<'source, self.add_layer_if(Box::new(|_|Ok(true)), keymap); self } - pub fn layer_if (mut self, condition: InputLayerCond<'source, 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<'source, 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<'source, - S, - C: Command + FromDsl<'source, S>, - I: DslInput, - M: KeyMap<'source, S, C, I> -> std::fmt::Debug for InputMap<'source, S, C, I, M> { +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<'source, - S, - C: Command + FromDsl<'source, S>, - I: DslInput, - M: KeyMap<'source, S, C, I> -> KeyMap<'source, S, C, I> for InputMap<'source, S, C, I, M> { +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)? { diff --git a/input/src/input_handle.rs b/input/src/input_handle.rs index 2be276c..99bce29 100644 --- a/input/src/input_handle.rs +++ b/input/src/input_handle.rs @@ -2,7 +2,7 @@ use crate::*; use std::sync::{Mutex, Arc, RwLock}; /// Event source -pub trait Input: Sized { +pub trait Input: Send + Sync + Sized { /// Type of input event type Event; /// Result of handling input @@ -36,7 +36,7 @@ pub trait Input: Sized { } /// Handle input -pub trait Handle { +pub trait Handle: Send + Sync { fn handle (&mut self, _input: &E) -> Perhaps { Ok(None) } diff --git a/output/src/ops/align.rs b/output/src/ops/align.rs index 5702a40..a35659b 100644 --- a/output/src/ops/align.rs +++ b/output/src/ops/align.rs @@ -36,33 +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")] -from_dsl!(@a: 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 = state.take_or_fail(&mut iter.clone(), "expected content")?; - 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) +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) + } } -} else { - Ok(None) -}); +} impl Align { #[inline] pub const fn c (a: A) -> Self { Self(Alignment::Center, a) } @@ -79,7 +88,7 @@ impl Align { } impl> Content for Align { - fn content (&self) -> impl Render + '_ { + fn content (&self) -> impl Render { &self.1 } fn layout (&self, on: E::Area) -> E::Area { diff --git a/output/src/ops/bsp.rs b/output/src/ops/bsp.rs index 6062485..2819bbf 100644 --- a/output/src/ops/bsp.rs +++ b/output/src/ops/bsp.rs @@ -21,35 +21,40 @@ impl, B: Content> Content for Bsp { } } #[cfg(feature = "dsl")] -from_dsl!(@ab: Bsp: |state, iter|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.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - Some(Token { value: Value::Key("bsp/s"), .. }) => - Self::s(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - Some(Token { value: Value::Key("bsp/e"), .. }) => - Self::e(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - Some(Token { value: Value::Key("bsp/w"), .. }) => - Self::w(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - Some(Token { value: Value::Key("bsp/a"), .. }) => - Self::a(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - Some(Token { value: Value::Key("bsp/b"), .. }) => - Self::b(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - _ => unreachable!(), - })) -} else { - 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 e0dde77..cfdf2fd 100644 --- a/output/src/ops/cond.rs +++ b/output/src/ops/cond.rs @@ -17,26 +17,44 @@ impl Either { Self(c, a, b) } } + #[cfg(feature = "dsl")] -impl<'source, A, T: Dsl + Dsl> FromDsl<'source, T> for When { - fn take_from <'state> ( - state: &'state T, - token: &mut TokenIter<'source> - ) -> Perhaps { +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"), .. - }) = token.peek() { - let base = token.clone(); + }) = iter.peek() { + let base = iter.clone(); return Ok(Some(Self( - state.take_or_fail(token, "cond: no condition")?, - state.take_or_fail(token, "cond: no content")?, + state.take(iter)?.unwrap_or_else(||panic!("cond: no condition: {base:?}")), + state.get_content_or_fail(iter)? ))) } else { None }) } } + +#[cfg(feature = "dsl")] +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 { let Self(cond, item) = self; @@ -55,24 +73,7 @@ impl> Content for When { if *cond { item.render(to) } } } -#[cfg(feature = "dsl")] -impl<'source, A, B, T: Dsl + Dsl + Dsl> FromDsl<'source, T> for Either { - fn take_from <'state> ( - state: &'state T, - token: &mut TokenIter<'source> - ) -> Perhaps { - if let Some(Token { value: Value::Key("either"), .. }) = token.peek() { - let base = token.clone(); - let _ = token.next().unwrap(); - return Ok(Some(Self( - state.take_or_fail(token, "either: no condition")?, - state.take_or_fail(token, "either: no content 1")?, - state.take_or_fail(token, "either: no content 2")?, - ))) - } - Ok(None) - } -} + impl, B: Render> Content for Either { fn layout (&self, to: E::Area) -> E::Area { let Self(cond, a, b) = self; diff --git a/output/src/ops/stack.rs b/output/src/ops/stack.rs index 6498ab0..816713c 100644 --- a/output/src/ops/stack.rs +++ b/output/src/ops/stack.rs @@ -1,5 +1,4 @@ use crate::*; -use Direction::*; pub struct Stack { __: PhantomData, @@ -11,76 +10,68 @@ impl Stack { Self { direction, callback, __: Default::default(), } } pub fn north (callback: F) -> Self { - Self::new(North, callback) + Self::new(Direction::North, callback) } pub fn south (callback: F) -> Self { - Self::new(South, callback) + Self::new(Direction::South, callback) } pub fn east (callback: F) -> Self { - Self::new(East, callback) + Self::new(Direction::East, callback) } pub fn west (callback: F) -> Self { - Self::new(West, callback) + Self::new(Direction::West, callback) } } impl)->())->()> Content for Stack { fn layout (&self, mut to: E::Area) -> E::Area { let mut x = to.x(); let mut y = 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()); + let mut w = to.w(); + let mut h = to.h(); (self.callback)(&mut move |component: &dyn Render|{ - let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh(); + let layout = component.layout([x, y, w, h].into()); match self.direction { - South => { - y = y.plus(h); - h_used = h_used.plus(h); - h_remaining = h_remaining.minus(h); - w_used = w_used.max(w); + Direction::North => { + todo!() }, - East => { - x = x.plus(w); - w_used = w_used.plus(w); - w_remaining = w_remaining.minus(w); - h_used = h_used.max(h); + Direction::South => { + y = y + layout.h(); + h = h.minus(layout.h()); }, - North | West => { + Direction::East => { + x = x + layout.w(); + w = w.minus(layout.w()); + }, + Direction::West => { todo!() }, _ => unreachable!(), } }); - match self.direction { - North | West => { - todo!() - }, - South | East => { - [to.x(), to.y(), w_used.into(), h_used.into()].into() - }, - _ => unreachable!(), - } + to } fn render (&self, to: &mut E) { let mut x = to.x(); let mut y = 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()); + let mut w = to.w(); + let mut h = to.h(); (self.callback)(&mut move |component: &dyn Render|{ - let layout = component.layout([x, y, w_remaining, h_remaining].into()); + let layout = component.layout([x, y, w, h].into()); match self.direction { - South => { - y = y.plus(layout.h()); - h_remaining = h_remaining.minus(layout.h()); - h_used = h_used.plus(layout.h()); + Direction::North => { + todo!() + }, + Direction::South => { + y = y + layout.h(); + h = h.minus(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()); + Direction::East => { + x = x + layout.w(); + w = w.minus(layout.w()); to.place(layout, component); }, - North | West => { + Direction::West => { todo!() }, _ => unreachable!() diff --git a/output/src/ops/thunk.rs b/output/src/ops/thunk.rs index d0e2877..9d2c0bf 100644 --- a/output/src/ops/thunk.rs +++ b/output/src/ops/thunk.rs @@ -2,67 +2,72 @@ use crate::*; use std::marker::PhantomData; /// Lazily-evaluated [Render]able. -pub struct Thunk, F: Fn()->T>( +pub struct Thunk, F: Fn()->T + Send + Sync>( PhantomData, F ); -impl, F: Fn()->T> Thunk { +impl, F: Fn()->T + Send + Sync> Thunk { pub const fn new (thunk: F) -> Self { Self(PhantomData, thunk) } } -impl, F: Fn()->T> Content for Thunk { +impl, F: Fn()->T + Send + Sync> Content for Thunk { fn content (&self) -> impl Render { (self.1)() } } -pub struct ThunkBox( +pub struct ThunkBox<'a, E: Output>( PhantomData, - BoxBox>>, + BoxRenderBox<'a, E> + Send + Sync + 'a> ); -impl ThunkBox { - pub const fn new (thunk: BoxBox>>) -> Self { +impl<'a, E: Output> ThunkBox<'a, E> { + pub const fn new (thunk: BoxRenderBox<'a, E> + Send + Sync + 'a>) -> Self { Self(PhantomData, thunk) } } -impl Content for ThunkBox { - fn content (&self) -> impl Render { (&self.1)() } +impl<'a, E: Output> Content for ThunkBox<'a, E> { + fn content (&self) -> impl Render { (self.1)() } } -impl FromBox>>> for ThunkBox { - fn from (f: BoxBox>>) -> Self { - Self(PhantomData, f) +impl<'a, E, F, T> From for ThunkBox<'a, E> +where + E: Output, + F: Fn()->T + Send + Sync + 'a, + T: Render + Send + Sync + 'a +{ + fn from (f: F) -> Self { + Self(PhantomData, Box::new(move||f().boxed())) } } -//impl<'a, E: Output, F: Fn()->Box + 'a> + 'a> From for ThunkBox<'a, E> { +//impl<'a, E: Output, F: Fn()->Box + 'a> + Send + Sync + 'a> From for ThunkBox<'a, E> { //fn from (f: F) -> Self { //Self(Default::default(), Box::new(f)) //} //} -pub struct ThunkRender(PhantomData, F); -impl ThunkRender { +pub struct ThunkRender(PhantomData, F); +impl ThunkRender { pub fn new (render: F) -> Self { Self(PhantomData, render) } } -impl Content for ThunkRender { +impl Content for ThunkRender { fn render (&self, to: &mut E) { (self.1)(to) } } pub struct ThunkLayout< E: Output, - F1: Fn(E::Area)->E::Area, - F2: Fn(&mut E) + F1: Fn(E::Area)->E::Area + Send + Sync, + F2: Fn(&mut E) + Send + Sync >( PhantomData, F1, F2 ); -implE::Area, F2: Fn(&mut E)> ThunkLayout { +implE::Area + Send + Sync, F2: Fn(&mut E) + Send + Sync> ThunkLayout { pub fn new (layout: F1, render: F2) -> Self { Self(PhantomData, layout, render) } } impl Content for ThunkLayout where E: Output, - F1: Fn(E::Area)->E::Area, - F2: Fn(&mut E) + F1: Fn(E::Area)->E::Area + Send + Sync, + F2: Fn(&mut E) + Send + Sync { fn layout (&self, to: E::Area) -> E::Area { (self.1)(to) } fn render (&self, to: &mut E) { (self.2)(to) } diff --git a/output/src/ops/transform.rs b/output/src/ops/transform.rs index 808f56b..80d50ce 100644 --- a/output/src/ops/transform.rs +++ b/output/src/ops/transform.rs @@ -26,24 +26,31 @@ use crate::*; /// along either the X axis, the Y axis, or both. macro_rules! transform_xy { ($x:literal $y:literal $xy:literal |$self:ident : $Enum:ident, $to:ident|$area:expr) => { - pub enum $Enum { X(A), Y(A), XY(A) } - impl $Enum { - #[inline] pub const fn x (item: A) -> Self { Self::X(item) } - #[inline] pub const fn y (item: A) -> Self { Self::Y(item) } - #[inline] pub const fn xy (item: A) -> Self { Self::XY(item) } + pub enum $Enum { X(T), Y(T), XY(T) } + impl $Enum { + #[inline] pub const fn x (item: T) -> Self { + Self::X(item) + } + #[inline] pub const fn y (item: T) -> Self { + Self::Y(item) + } + #[inline] pub const fn xy (item: T) -> Self { + Self::XY(item) + } } #[cfg(feature = "dsl")] - impl<'source, A, T: Dsl> FromDsl<'source, T> for $Enum { - fn take_from <'state> (state: &'state T, iter: &mut TokenIter<'source>) -> Perhaps { + 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() { let mut base = iter.clone(); return Ok(Some(match iter.next() { Some(Token{value:Value::Key($x),..}) => - Self::x(state.take_or_fail(iter, "x: no content")?), + Self::x(state.get_content_or_fail(iter)?), Some(Token{value:Value::Key($y),..}) => - Self::y(state.take_or_fail(iter, "y: no content")?), + Self::y(state.get_content_or_fail(iter)?), Some(Token{value:Value::Key($xy),..}) => - Self::xy(state.take_or_fail(iter, "xy: no content")?), + Self::xy(state.get_content_or_fail(iter)?), _ => unreachable!() })) } @@ -51,7 +58,7 @@ macro_rules! transform_xy { } } impl> Content for $Enum { - fn content (&self) -> impl Render + '_ { + fn content (&self) -> impl Render { match self { Self::X(item) => item, Self::Y(item) => item, @@ -70,30 +77,31 @@ macro_rules! transform_xy { /// along either the X axis, the Y axis, or both. macro_rules! transform_xy_unit { ($x:literal $y:literal $xy:literal |$self:ident : $Enum:ident, $to:ident|$layout:expr) => { - pub enum $Enum { X(U, A), Y(U, A), XY(U, U, A), } - impl $Enum { - #[inline] pub const fn x (x: U, item: A) -> Self { Self::X(x, item) } - #[inline] pub const fn y (y: U, item: A) -> Self { Self::Y(y, item) } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { Self::XY(x, y, item) } + pub enum $Enum { X(U, T), Y(U, T), XY(U, U, T), } + impl $Enum { + #[inline] pub const fn x (x: U, item: T) -> Self { Self::X(x, item) } + #[inline] pub const fn y (y: U, item: T) -> Self { Self::Y(y, item) } + #[inline] pub const fn xy (x: U, y: U, item: T) -> Self { Self::XY(x, y, item) } } #[cfg(feature = "dsl")] - impl<'source, A, U: Coordinate, T: Dsl + Dsl> FromDsl<'source, T> for $Enum { - fn take_from <'state> (state: &'state T, iter: &mut TokenIter<'source>) -> Perhaps { + 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, "x: no unit")?, - state.take_or_fail(iter, "x: no content")?, + 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, "y: no unit")?, - state.take_or_fail(iter, "y: no content")?, + 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, "xy: no unit x")?, - state.take_or_fail(iter, "xy: no unit y")?, - state.take_or_fail(iter, "xy: no content")? + state.take_or_fail(iter, "no unit specified")?, + state.take_or_fail(iter, "no unit specified")?, + state.get_content_or_fail(iter)? ), _ => unreachable!(), }) @@ -103,7 +111,7 @@ macro_rules! transform_xy_unit { } } impl> Content for $Enum { - fn content (&self) -> impl Render + '_ { + fn content (&self) -> impl Render { Some(match self { Self::X(_, content) => content, Self::Y(_, content) => content, diff --git a/output/src/output.rs b/output/src/output.rs index 07c8ac2..b5cc1fc 100644 --- a/output/src/output.rs +++ b/output/src/output.rs @@ -29,8 +29,8 @@ pub trait Render { /// Write data to display. fn render (&self, output: &mut E); /// Perform type erasure, turning `self` into an opaque [RenderBox]. - fn boxed <'a> (self) -> Box + 'a> where Self: Sized + 'a { - Box::new(self) as Box + 'a> + fn boxed <'a> (self) -> RenderBox<'a, E> where Self: Send + Sync + Sized + 'a { + Box::new(self) as RenderBox<'a, E> } } @@ -47,20 +47,20 @@ impl> Render for C { /// Opaque pointer to a renderable living on the heap. /// /// Return this from [Content::content] to use dynamic dispatch. -pub type RenderBox = Box>; +pub type RenderBox<'a, E> = Box>; /// You can render from a box. -impl Content for RenderBox { - fn content (&self) -> impl Render + '_ { self.deref() } +impl<'a, E: Output> Content for RenderBox<'a, E> { + fn content (&self) -> impl Render { self.deref() } //fn boxed <'b> (self) -> RenderBox<'b, E> where Self: Sized + 'b { self } } /// Opaque pointer to a renderable. -pub type RenderDyn = dyn Render; +pub type RenderDyn<'a, E> = dyn Render + Send + Sync + 'a; /// You can render from an opaque pointer. -impl Content for &RenderDyn where Self: Sized { - fn content (&self) -> impl Render + '_ { +impl<'a, E: Output> Content for &RenderDyn<'a, E> where Self: Sized { + fn content (&self) -> impl Render { #[allow(suspicious_double_ref_op)] self.deref() } @@ -77,7 +77,7 @@ impl Content for &RenderDyn where Self: Sized { /// Composable renderable with static dispatch. pub trait Content { /// Return a [Render]able of a specific type. - fn content (&self) -> impl Render + '_ { () } + fn content (&self) -> impl Render { () } /// Perform layout. By default, delegates to [Self::content]. fn layout (&self, area: E::Area) -> E::Area { self.content().layout(area) } /// Draw to output. By default, delegates to [Self::content]. @@ -86,7 +86,7 @@ pub trait Content { /// Every pointer to [Content] is a [Content]. impl> Content for &C { - fn content (&self) -> impl Render + '_ { (*self).content() } + fn content (&self) -> impl Render { (*self).content() } fn layout (&self, area: E::Area) -> E::Area { (*self).layout(area) } fn render (&self, output: &mut E) { (*self).render(output) } } @@ -98,7 +98,7 @@ impl Content for () { } impl> Content for Option { - fn content (&self) -> impl Render + '_ { + fn content (&self) -> impl Render { self.as_ref() } fn layout (&self, area: E::Area) -> E::Area { @@ -117,7 +117,7 @@ impl> Content for Option { // Implement for all [Output]s. (|$self:ident:$Struct:ty| $content:expr) => { impl Content for $Struct { - fn content (&$self) -> impl Render + '_ { Some($content) } + fn content (&$self) -> impl Render { Some($content) } } }; // Implement for specific [Output]. @@ -127,7 +127,7 @@ impl> Content for Option { |$content:expr) => { impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Output> for $Struct $(<$($($L)? $($T)?),+>)? { - fn content (&$self) -> impl Render<$Output> + '_ { $content } + fn content (&$self) -> impl Render<$Output> { $content } } }; } diff --git a/output/src/view.rs b/output/src/view.rs index 2ae266e..8f40cc2 100644 --- a/output/src/view.rs +++ b/output/src/view.rs @@ -1,54 +1,63 @@ use crate::*; -//#[cfg(feature = "dsl")] -//#[macro_export] macro_rules! try_delegate { - //($s:ident, $dsl:expr, $T:ty) => { - //let value: Option<$T> = Dsl::take_from($s, $dsl)?; - //if let Some(value) = value { - //return Ok(Some(value.boxed())) - //} - //} -//} +#[cfg(feature = "dsl")] +#[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())) + } + } +} -//// Provides components to the view. -//#[cfg(feature = "dsl")] -//pub trait ViewContext<'state, E: Output + 'state>: - //FromDsl + FromDsl + FromDsl + Send + Sync -//{ - //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) - //} -//} - -//#[cfg(feature = "dsl")] -//impl<'context, O: Output + 'context, T: ViewContext<'context, O>> FromDsl for RenderBox<'context, O> { - //fn take_from <'state, 'source: 'state> (state: &'state T, token: &mut TokenIter<'source>) - //-> Perhaps> - //{ - //Ok(if let Some(content) = state.get_content_sym(token)? { - //Some(content) - //} else if let Some(content) = state.get_content_exp(token)? { - //Some(content) - //} else { - //None - //}) - //} -//} +// Provides components to the view. +#[cfg(feature = "dsl")] +pub trait ViewContext<'state, E: Output + 'state>: Send + Sync + + Dsl + + Dsl + + Dsl +{ + fn get_content_or_fail <'source: 'state> (&'state self, iter: &mut TokenIter<'source>) + -> Usually> + { + let base = iter.clone(); + if let Some(content) = self.get_content(iter)? { + Ok(content) + } else { + Err(format!("not found: {iter:?}").into()) + } + } + 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 a8ca8c9..27f68ff 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -84,7 +84,7 @@ impl ToTokens for CommandDef { let mut out = TokenStream2::new(); for (arg, ty) in arm.args() { write_quote_to(&mut out, quote! { - #arg: FromDsl::take_from_or_fail(self, words)?, + #arg: Dsl::take_or_fail(self, words)?, }); } out @@ -137,23 +137,19 @@ impl ToTokens for CommandDef { #[derive(Clone, Debug)] pub enum #command_enum { #(#variants)* } /// Not generated by [tengri_proc]. #block - /// Generated by [tengri_proc::command]. - /// - /// Means [#command_enum] is now a [Command] over [#state]. - /// Instances of [#command_enum] can be consumed by a - /// mutable pointer to [#state], invoking predefined operations - /// and optionally returning undo history data. + /// Generated by [tengri_proc]. impl ::tengri::input::Command<#state> for #command_enum { fn execute (self, state: &mut #state) -> Perhaps { match self { #(#implementations)* } } } - /// Generated by [tengri_proc::command]. - impl<'source> ::tengri::dsl::FromDsl<'source, #state> for #command_enum { - fn take_from <'state> ( - state: &'state #state, - words: &mut ::tengri::dsl::TokenIter<'source> - ) -> Perhaps { + /// Generated by [tengri_proc]. + impl ::tengri::dsl::Dsl<#command_enum> for #state { + fn take <'source, 'state> ( + &'state self, words: &mut TokenIter<'source> + ) + -> ::tengri::Perhaps<#command_enum> + { let mut words = words.clone(); let token = words.next(); todo!()//Ok(match token { #(#matchers)* _ => None }) diff --git a/proc/src/proc_expose.rs b/proc/src/proc_expose.rs index f7b934e..d6f23a8 100644 --- a/proc/src/proc_expose.rs +++ b/proc/src/proc_expose.rs @@ -60,7 +60,7 @@ impl ToTokens for ExposeDef { impl ToTokens for ExposeImpl { fn to_tokens (&self, out: &mut TokenStream2) { let Self(block, exposed) = self; - let state = &block.self_ty; + let target = &block.self_ty; write_quote_to(out, quote! { #block }); for (t, variants) in exposed.iter() { let formatted_type = format!("{}", quote! { #t }); @@ -84,15 +84,14 @@ impl ToTokens for ExposeImpl { }; let values = variants.iter().map(ExposeArm::from); write_quote_to(out, quote! { - /// Generated by [tengri_proc::expose]. - impl<'source> ::tengri::dsl::FromDsl<'source, #state> for #t { - fn take_from <'state> ( - state: &'state #state, - words: &mut ::tengri::dsl::TokenIter<'source> - ) -> Perhaps { - Ok(Some(match words.next().map(|x|x.value) { + /// Generated by [tengri_proc]. + 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)* + #(#values,)* _ => return Ok(None) })) } @@ -116,7 +115,7 @@ impl ToTokens for ExposeArm { let Self(key, value) = self; let key = LitStr::new(&key, Span::call_site()); write_quote_to(out, quote! { - Some(::tengri::dsl::Value::Sym(#key)) => state.#value(), + Some(::tengri::dsl::Value::Sym(#key)) => self.#value() }) } } diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 70511eb..48af79f 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -14,6 +14,8 @@ pub(crate) struct ViewImpl { exposed: BTreeMap, } +struct ViewArm(String, Ident); + impl Parse for ViewMeta { fn parse (input: ParseStream) -> Result { Ok(Self { @@ -44,70 +46,95 @@ impl ToTokens for ViewDef { let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self; let view = &block.self_ty; let mut available = vec![]; - let exposed: Vec<_> = exposed.iter().map(|(key, value)|{ - available.push(key.clone()); - write_quote(quote! { #key => Some(state.#value().boxed()), }) + let exposed: Vec<_> = exposed.iter().map(|(k,v)|{ + available.push(k.clone()); + ViewArm(k.clone(), v.clone()) }).collect(); let available: String = available.join(", "); let error_msg = LitStr::new( &format!("expected Sym(content), got: {{token:?}}, available: {available}"), Span::call_site() ); - write_quote_to(out, quote! { + for token in quote! { #block /// Generated by [tengri_proc]. - /// - /// Delegates the rendering of [#view] to the [#view::view} method, - /// which you will need to implement, e.g. passing a [TokenIter] - /// containing a layout and keybindings config from user dirs. impl ::tengri::output::Content<#output> for #view { fn content (&self) -> impl Render<#output> { self.view() } } /// Generated by [tengri_proc]. - /// - /// Gives [#view] the ability to construct the [Render]able - /// which might corresponds to a given [TokenStream], - /// while taking [#view]'s state into consideration. - impl<'source> ::tengri::dsl::FromDsl<'source, #view> for Box + 'source> { - fn take_from <'state> ( + impl<'state> ::tengri::dsl::FromDsl<'state, #view> for ::tengri::output::RenderBox<'state, #output> { + fn take_from <'source: 'state> ( state: &'state #view, - words: &mut ::tengri::dsl::TokenIter<'source>, + token: &mut ::tengri::dsl::TokenIter<'source> ) -> Perhaps { - Ok(words.peek().and_then(|::tengri::dsl::Token{ value, .. }|match value { - ::tengri::dsl::Value::Exp(_, exp) => { - todo!("builtin layout ops"); - //#builtins - None - }, - ::tengri::dsl::Value::Sym(sym) => match sym { - #(#exposed)* - _ => None - }, - _ => None - })) + Ok(match token.peek() { #(#exposed)* _ => None }) } } - }) + } { + out.append(token) + } } } -fn builtins () -> [TokenStream2;14] { - [ - quote! { When:: }, - quote! { Either:: }, - quote! { Align:: }, - quote! { Bsp:: }, - quote! { Fill:: }, - quote! { Fixed::<_, A> }, - quote! { Min::<_, A> }, - quote! { Max::<_, A> }, - quote! { Shrink::<_, A> }, - quote! { Expand::<_, A> }, - quote! { Push::<_, A> }, - quote! { Pull::<_, A> }, - quote! { Margin::<_, A> }, - quote! { Padding::<_, A> }, - ] +impl ToTokens for ViewArm { + fn to_tokens (&self, out: &mut TokenStream2) { + let Self(key, value) = self; + out.append(Ident::new("Some", Span::call_site())); + out.append(Group::new(Delimiter::Parenthesis, { + let mut out = TokenStream2::new(); + 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)); + out.append(Punct::new('>', Alone)); + out.append(Ident::new("Some", Span::call_site())); + out.append(Group::new(Delimiter::Parenthesis, { + let mut out = TokenStream2::new(); + 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())); + out.append(Punct::new('.', Alone)); + out.append(Ident::new("boxed", Span::call_site())); + out.append(Group::new(Delimiter::Parenthesis, TokenStream2::new())); + out + })); + out.append(Punct::new(',', Alone)); + } } diff --git a/tui/src/tui_engine.rs b/tui/src/tui_engine.rs index b8d60f4..0969cc3 100644 --- a/tui/src/tui_engine.rs +++ b/tui/src/tui_engine.rs @@ -72,8 +72,7 @@ pub trait TuiRun + Handle + 'static> { fn run (&self, state: &Arc>) -> Usually<()>; } -impl + Handle + Send + Sync + 'static> -TuiRun for Arc> { +impl + Handle + 'static> TuiRun for Arc> { fn run (&self, state: &Arc>) -> Usually<()> { let _input_thread = TuiIn::run_input(self, state, Duration::from_millis(100)); self.write().unwrap().setup()?; diff --git a/tui/src/tui_engine/tui_input.rs b/tui/src/tui_engine/tui_input.rs index c9cd788..75c5be2 100644 --- a/tui/src/tui_engine/tui_input.rs +++ b/tui/src/tui_engine/tui_input.rs @@ -21,7 +21,7 @@ impl Input for TuiIn { impl TuiIn { /// Spawn the input thread. - pub fn run_input + Send + Sync + 'static> ( + pub fn run_input + 'static> ( engine: &Arc>, state: &Arc>, timer: Duration