From 35ad37120554611197c1e30bbed657f310d332c3 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 28 Apr 2025 04:49:00 +0300 Subject: [PATCH] input: add InputMap; dsl/output/tui: Atom->Dsl --- dsl/src/dsl_context.rs | 63 ++++--- dsl/src/lib.rs | 2 +- input/src/{input.rs => _input.rs} | 0 .../src/{event_map.rs => _input_event_map.rs} | 0 input/src/command.rs | 54 ------ input/src/input_command.rs | 21 +++ input/src/input_dsl.rs | 162 ++++++++++++++++ input/src/{handle.rs => input_handle.rs} | 0 input/src/input_macros.rs | 131 +++++++++++++ input/src/keymap.rs | 177 ------------------ input/src/lib.rs | 26 +-- output/src/ops/transform.rs | 4 +- output/src/output.rs | 1 + output/src/view.rs | 6 +- tui/src/tui_engine/tui_input.rs | 4 +- 15 files changed, 374 insertions(+), 277 deletions(-) rename input/src/{input.rs => _input.rs} (100%) rename input/src/{event_map.rs => _input_event_map.rs} (100%) delete mode 100644 input/src/command.rs create mode 100644 input/src/input_command.rs create mode 100644 input/src/input_dsl.rs rename input/src/{handle.rs => input_handle.rs} (100%) create mode 100644 input/src/input_macros.rs delete mode 100644 input/src/keymap.rs diff --git a/dsl/src/dsl_context.rs b/dsl/src/dsl_context.rs index d7adc78..34f9dcd 100644 --- a/dsl/src/dsl_context.rs +++ b/dsl/src/dsl_context.rs @@ -1,48 +1,56 @@ use crate::*; -pub trait TryFromAtom<'a, T>: Sized { - fn try_from_expr (_state: &'a T, _iter: TokenIter<'a>) -> Option { None } + +pub trait TryFromDsl<'a, T>: Sized { + fn try_from_expr (_state: &'a T, _iter: TokenIter<'a>) -> Option { + None + } fn try_from_atom (state: &'a T, value: Value<'a>) -> Option { if let Exp(0, iter) = value { return Self::try_from_expr(state, iter) } None } } -pub trait TryIntoAtom: Sized { + +pub trait TryIntoDsl: Sized { fn try_into_atom (&self) -> Option; } + /// Map EDN tokens to parameters of a given type for a given context pub trait Context: Sized { fn get (&self, _atom: &Value) -> Option { None } - fn get_or_fail (&self, atom: &Value) -> U { - self.get(atom).expect("no value") + fn get_or_fail (&self, dsl: &Value) -> U { + self.get(dsl).expect("no value") } } + impl, U> Context for &T { - fn get (&self, atom: &Value) -> Option { - (*self).get(atom) + fn get (&self, dsl: &Value) -> Option { + (*self).get(dsl) } - fn get_or_fail (&self, atom: &Value) -> U { - (*self).get_or_fail(atom) + fn get_or_fail (&self, dsl: &Value) -> U { + (*self).get_or_fail(dsl) } } + impl, U> Context for Option { - fn get (&self, atom: &Value) -> Option { - self.as_ref().map(|s|s.get(atom)).flatten() + fn get (&self, dsl: &Value) -> Option { + self.as_ref().map(|s|s.get(dsl)).flatten() } - fn get_or_fail (&self, atom: &Value) -> U { - self.as_ref().map(|s|s.get_or_fail(atom)).expect("no provider") + fn get_or_fail (&self, dsl: &Value) -> U { + self.as_ref().map(|s|s.get_or_fail(dsl)).expect("no provider") } } + /// Implement `Context` for a context and type. #[macro_export] macro_rules! provide { // Provide a value to the EDN template ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { impl Context<$type> for $State { #[allow(unreachable_code)] - fn get (&$self, atom: &Value) -> Option<$type> { + fn get (&$self, dsl: &Value) -> Option<$type> { use Value::*; - Some(match atom { $(Sym($pat) => $expr,)* _ => return None }) + Some(match dsl { $(Sym($pat) => $expr,)* _ => return None }) } } }; @@ -50,13 +58,14 @@ impl, U> Context for Option { ($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { impl<$lt> Context<$lt, $type> for $State { #[allow(unreachable_code)] - fn get (&$lt $self, atom: &Value) -> Option<$type> { + fn get (&$lt $self, dsl: &Value) -> Option<$type> { use Value::*; - Some(match atom { $(Sym($pat) => $expr,)* _ => return None }) + Some(match dsl { $(Sym($pat) => $expr,)* _ => return None }) } } }; } + /// Implement `Context` for a context and numeric type. /// /// This enables support for numeric literals. @@ -64,22 +73,23 @@ impl, U> Context for Option { // Provide a value that may also be a numeric literal in the EDN, to a generic implementation. ($type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => { impl<$T: $Trait> Context<$type> for $T { - fn get (&$self, atom: &Value) -> Option<$type> { + fn get (&$self, dsl: &Value) -> Option<$type> { use Value::*; - Some(match atom { $(Sym($pat) => $expr,)* Num(n) => *n as $type, _ => return None }) + Some(match dsl { $(Sym($pat) => $expr,)* Num(n) => *n as $type, _ => return None }) } } }; // Provide a value that may also be a numeric literal in the EDN, to a concrete implementation. ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { impl Context<$type> for $State { - fn get (&$self, atom: &Value) -> Option<$type> { + fn get (&$self, dsl: &Value) -> Option<$type> { use Value::*; - Some(match atom { $(Sym($pat) => $expr,)* Num(n) => *n as $type, _ => return None }) + Some(match dsl { $(Sym($pat) => $expr,)* Num(n) => *n as $type, _ => return None }) } } }; } + /// Implement `Context` for a context and the boolean type. /// /// This enables support for boolean literals. @@ -87,14 +97,14 @@ impl, U> Context for Option { // Provide a value that may also be a numeric literal in the EDN, to a generic implementation. ($type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => { impl<$T: $Trait> Context<$type> for $T { - fn get (&$self, atom: &Value) -> Option<$type> { + fn get (&$self, dsl: &Value) -> Option<$type> { use Value::*; - Some(match atom { + Some(match dsl { Num(n) => match *n { 0 => false, _ => true }, Sym(":false") | Sym(":f") => false, Sym(":true") | Sym(":t") => true, $(Sym($pat) => $expr,)* - _ => return Context::get(self, atom) + _ => return Context::get(self, dsl) }) } } @@ -102,9 +112,9 @@ impl, U> Context for Option { // Provide a value that may also be a numeric literal in the EDN, to a concrete implementation. ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { impl Context<$type> for $State { - fn get (&$self, atom: &Value) -> Option<$type> { + fn get (&$self, dsl: &Value) -> Option<$type> { use Value::*; - Some(match atom { + Some(match dsl { Num(n) => match *n { 0 => false, _ => true }, Sym(":false") | Sym(":f") => false, Sym(":true") | Sym(":t") => true, @@ -115,6 +125,7 @@ impl, U> Context for Option { } }; } + #[cfg(test)] #[test] fn test_edn_context () { struct Test; provide_bool!(bool: |self: Test|{ diff --git a/dsl/src/lib.rs b/dsl/src/lib.rs index 176cfa6..81e6aa2 100644 --- a/dsl/src/lib.rs +++ b/dsl/src/lib.rs @@ -32,7 +32,7 @@ mod dsl_macros; ////include_str!("../../tui/examples/edn01.edn"), ////include_str!("../../tui/examples/edn02.edn"), ////] { - //////let items = Atom::read_all(example)?; + //////let items = Dsl::read_all(example)?; //////panic!("{layout:?}"); //////let content = >::from(&layout); ////} diff --git a/input/src/input.rs b/input/src/_input.rs similarity index 100% rename from input/src/input.rs rename to input/src/_input.rs diff --git a/input/src/event_map.rs b/input/src/_input_event_map.rs similarity index 100% rename from input/src/event_map.rs rename to input/src/_input_event_map.rs diff --git a/input/src/command.rs b/input/src/command.rs deleted file mode 100644 index 9204c20..0000000 --- a/input/src/command.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::*; - -#[macro_export] macro_rules! defcom { - ([$self:ident, $app:ident:$App:ty] $(($Command:ident $(( - $Variant:ident [$($($param:ident: $Param:ty),+)?] $expr:expr - ))*))*) => { - $(#[derive(Clone, Debug)] pub enum $Command { - $($Variant $(($($Param),+))?),* - })* - $(command!(|$self: $Command, $app: $App|match $self { - $($Command::$Variant $(($($param),+))? => $expr),* - });)* - }; - (|$self:ident, $app:ident:$App:ty| $($Command:ident { $( - $Variant:ident $(($($param:ident: $Param:ty),+))? => $expr:expr - )* $(,)? })*) => { - $(#[derive(Clone, Debug)] pub enum $Command { - $($Variant $(($($Param),+))?),* - })* - $(command!(|$self: $Command, $app: $App|match $self { - $($Command::$Variant $(($($param),+))? => $expr),* - });)* - }; -} - -#[macro_export] macro_rules! command { - ($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => { - impl$(<$($l),+>)? Command<$State> for $Command { - fn execute ($self, $state: &mut $State) -> Perhaps { - Ok($handler) - } - } - }; -} - -pub trait Command: Send + Sync + Sized { - fn execute (self, state: &mut S) -> Perhaps; - fn delegate (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps - where Self: Sized - { - Ok(self.execute(state)?.map(wrap)) - } -} - -impl> Command for Option { - fn execute (self, _: &mut S) -> Perhaps { - Ok(None) - } - fn delegate (self, _: &mut S, _: impl Fn(Self)->U) -> Perhaps - where Self: Sized - { - Ok(None) - } -} diff --git a/input/src/input_command.rs b/input/src/input_command.rs new file mode 100644 index 0000000..6051277 --- /dev/null +++ b/input/src/input_command.rs @@ -0,0 +1,21 @@ +use crate::*; + +pub trait Command: Send + Sync + Sized { + fn execute (self, state: &mut S) -> Perhaps; + fn delegate (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps + where Self: Sized + { + Ok(self.execute(state)?.map(wrap)) + } +} + +impl> Command for Option { + fn execute (self, _: &mut S) -> Perhaps { + Ok(None) + } + fn delegate (self, _: &mut S, _: impl Fn(Self)->U) -> Perhaps + where Self: Sized + { + Ok(None) + } +} diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs new file mode 100644 index 0000000..1e67b37 --- /dev/null +++ b/input/src/input_dsl.rs @@ -0,0 +1,162 @@ +use crate::*; + +/// A [Command] that can be constructed from a [Token]. +pub trait DslCommand<'a, C>: TryFromDsl<'a, C> + Command {} + +impl<'a, C, T: TryFromDsl<'a, C> + Command> DslCommand<'a, C> for T {} + +/// [Input] state that can be matched against a [Value]. +pub trait DslInput: Input { + fn matches_dsl (&self, token: &str) -> bool; +} + +/// A pre-configured mapping of input events to commands. +pub trait KeyMap<'a, S, C: DslCommand<'a, S>, I: DslInput> { + /// Try to find a command that matches the current input event. + fn command (&'a self, state: &'a S, input: &'a I) -> Option; +} + +/// A [SourceIter] can be a [KeyMap]. +impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for SourceIter<'a> { + fn command (&'a self, state: &'a S, input: &'a I) -> Option { + let mut iter = self.clone(); + while let Some((token, rest)) = iter.next() { + iter = rest; + match token { + Token { value: Value::Exp(0, exp_iter), .. } => { + let mut exp_iter = exp_iter.clone(); + match exp_iter.next() { + Some(Token { value: Value::Sym(binding), .. }) => { + if input.matches_dsl(binding) { + if let Some(command) = C::try_from_expr(state, exp_iter.clone()) { + return Some(command) + } + } + }, + _ => panic!("invalid config (expected symbol)") + } + }, + _ => panic!("invalid config (expected expression)") + } + } + None + } +} + +/// A [TokenIter] can be a [KeyMap]. +impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for TokenIter<'a> { + fn command (&'a self, state: &'a S, input: &'a I) -> Option { + let mut iter = self.clone(); + while let Some(next) = iter.next() { + match next { + Token { value: Value::Exp(0, exp_iter), .. } => { + let mut exp_iter = exp_iter.clone(); + match exp_iter.next() { + Some(Token { value: Value::Sym(binding), .. }) => { + if input.matches_dsl(binding) { + if let Some(command) = C::try_from_expr(state, exp_iter.clone()) { + return Some(command) + } + } + }, + _ => panic!("invalid config (expected symbol)") + } + }, + _ => panic!("invalid config (expected expression)") + } + } + None + } +} + +/// A collection of pre-configured mappings of input events to commands, +/// which may be made available subject to given conditions. +pub struct InputMap<'a, S, C: DslCommand<'a, S>, I: DslInput> { + layers: Vec<( + Boxbool + Send + Sync + 'a>, + Box + Send + Sync + 'a> + )>, +} + +impl<'a, S, C: DslCommand<'a, S>, I: DslInput> Default for InputMap<'a, S, C, I> { + fn default () -> Self { + Self { + layers: vec![] + } + } +} + +impl<'a, S, C: DslCommand<'a, S>, I: DslInput> InputMap<'a, S, C, I> { + pub fn new (keymap: impl KeyMap<'a, S, C, I> + Send + Sync + 'a) -> Self { + Self::default().layer(keymap) + } + pub fn layer ( + mut self, keymap: impl KeyMap<'a, S, C, I> + Send + Sync + 'a + ) -> Self { + self.add_layer(keymap); + self + } + pub fn add_layer ( + &mut self, keymap: impl KeyMap<'a, S, C, I> + Send + Sync + 'a + ) -> &mut Self { + self.add_layer_if(|_|true, keymap); + self + } + pub fn layer_if ( + mut self, + condition: impl Fn(&S)->bool + Send + Sync + 'a, + keymap: impl KeyMap<'a, S, C, I> + Send + Sync + 'a + ) -> Self { + self.add_layer_if(condition, keymap); + self + } + pub fn add_layer_if ( + &mut self, + condition: impl Fn(&S)->bool + Send + Sync + 'a, + keymap: impl KeyMap<'a, S, C, I> + Send + Sync + 'a + ) -> &mut Self { + self.layers.push((Box::new(condition), Box::new(keymap))); + self + } +} + +/// An [InputMap] can be a [KeyMap]. +impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for InputMap<'a, S, C, I> { + fn command (&'a self, state: &'a S, input: &'a I) -> Option { + todo!() + } +} + + //fn handle (&self, state: &mut T, input: &U) -> Perhaps { + //for layer in self.layers.iter() { + //if !(layer.0)(state) { + //continue + //} + //let command: Option = SourceIter(layer.1).command::<_, _, _>(state, input); + //if let Some(command) = command { + //if let Some(undo) = command.execute(state)? { + ////app.history.push(undo); + //} + //return Ok(Some(true)) + //} + //} + //Ok(None) + //} +//} + //fn layer (mut self, keymap: &'static str) -> Self { + //self.add_layer(keymap); + //self + //} + //fn layer_if (mut self, condition: impl Fn(&T)->bool + 'static, keymap: &'static str) -> Self { + //self.add_layer_if(condition, keymap); + //self + //} + //fn add_layer (&mut self, keymap: &'static str) -> &mut Self { + //self.layers.push((Box::new(|_|true), keymap)); + //self + //} + //fn add_layer_if (&mut self, condition: impl Fn(&T)->bool + 'static, keymap: &'static str) -> &mut Self { + //self.layers.push((Box::new(condition), keymap)); + //self + //} +//} diff --git a/input/src/handle.rs b/input/src/input_handle.rs similarity index 100% rename from input/src/handle.rs rename to input/src/input_handle.rs diff --git a/input/src/input_macros.rs b/input/src/input_macros.rs new file mode 100644 index 0000000..c3e13e9 --- /dev/null +++ b/input/src/input_macros.rs @@ -0,0 +1,131 @@ +use crate::*; + +/** Implement `Command` for given `State` and collection + * of `Variant` to `handler` mappings. */ +#[macro_export] macro_rules! defcom { + ([$self:ident, $state:ident:$State:ty] $(($Command:ident $(( + $Variant:ident [$($($param:ident: $Param:ty),+)?] $expr:expr + ))*))*) => { + $(#[derive(Clone, Debug)] pub enum $Command { + $($Variant $(($($Param),+))?),* + })* + $(command!(|$self: $Command, $state: $State|match $self { + $($Command::$Variant $(($($param),+))? => $expr),* + });)* + }; + (|$self:ident, $state:ident:$State:ty| $($Command:ident { $( + $Variant:ident $(($($param:ident: $Param:ty),+))? => $expr:expr + )* $(,)? })*) => { + $(#[derive(Clone, Debug)] pub enum $Command { + $($Variant $(($($Param),+))?),* + })* + $(command!(|$self: $Command, $state: $State|match $self { + $($Command::$Variant $(($($param),+))? => $expr),* + });)* + }; +} + +/** Implement `Command` for given `State` and `handler` */ +#[macro_export] macro_rules! command { + ($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => { + impl$(<$($l),+>)? Command<$State> for $Command { + fn execute ($self, $state: &mut $State) -> Perhaps { + Ok($handler) + } + } + }; +} + +/** Implement `DslCommand` for given `State` and `Command` */ +#[cfg(feature = "dsl")] +#[macro_export] macro_rules! atom_command { + ($Command:ty : |$state:ident:<$State:ident: $Trait:path>| { $(( + // identifier + $key:literal [ + // named parameters + $( + // argument name + $arg:ident + // if type is not provided defaults to Dsl + $( + // type:name separator + : + // argument type + $type:ty + )? + ),* + // rest of parameters + $(, ..$rest:ident)? + ] + // bound command: + $command:expr + ))* }) => { + impl<'a, $State: $Trait> TryFromDsl<'a, $State> for $Command { + fn try_from_expr ($state: &$State, iter: TokenIter) -> Option { + let iter = iter.clone(); + match iter.next() { + $(Some(Token { value: Value::Key($key), .. }) => { + let iter = iter.clone(); + $( + let next = iter.next(); + if next.is_none() { panic!("no argument: {}", stringify!($arg)); } + let $arg = next.unwrap(); + $(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? + )* + $(let $rest = iter.clone();)? + return $command + },)* + _ => None + } + None + } + } + }; + ($Command:ty : |$state:ident:$State:ty| { $(( + // identifier + $key:literal [ + // named parameters + $( + // argument name + $arg:ident + // if type is not provided defaults to Dsl + $( + // type:name separator + : + // argument type + $type:ty + )? + ),* + // rest of parameters + $(, ..$rest:ident)? + ] + // bound command: + $command:expr + ))* }) => { + impl<'a> TryFromDsl<'a, $State> for $Command { + fn try_from_expr ($state: &$State, iter: TokenIter) -> Option { + let mut iter = iter.clone(); + match iter.next() { + $(Some(Token { value: Value::Key($key), .. }) => { + let mut iter = iter.clone(); + $( + let next = iter.next(); + if next.is_none() { panic!("no argument: {}", stringify!($arg)); } + let $arg = next.unwrap(); + $(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? + )* + $(let $rest = iter.clone();)? + return $command + }),* + _ => None + } + } + } + }; + (@bind $state:ident =>$arg:ident ? : $type:ty) => { + let $arg: Option<$type> = Context::<$type>::get($state, $arg); + }; + (@bind $state:ident => $arg:ident : $type:ty) => { + let $arg: $type = Context::<$type>::get_or_fail($state, $arg); + }; +} diff --git a/input/src/keymap.rs b/input/src/keymap.rs deleted file mode 100644 index 629f8ac..0000000 --- a/input/src/keymap.rs +++ /dev/null @@ -1,177 +0,0 @@ -use crate::*; - -/// [Input] state that can be matched against a [Value]. -pub trait AtomInput: Input { - fn matches_atom (&self, token: &str) -> bool; -} - -#[cfg(feature = "dsl")] -pub trait KeyMap<'a> { - /// Try to find a command that matches the current input event. - fn command , I: AtomInput> (&'a self, state: &'a S, input: &'a I) - -> Option; -} - -#[cfg(feature = "dsl")] -impl<'a> KeyMap<'a> for SourceIter<'a> { - fn command , I: AtomInput> (&'a self, state: &'a S, input: &'a I) - -> Option - { - let mut iter = self.clone(); - while let Some((token, rest)) = iter.next() { - iter = rest; - match token { - Token { value: Value::Exp(0, exp_iter), .. } => { - let mut exp_iter = exp_iter.clone(); - match exp_iter.next() { - Some(Token { value: Value::Sym(binding), .. }) => { - if input.matches_atom(binding) { - if let Some(command) = C::try_from_expr(state, exp_iter.clone()) { - return Some(command) - } - } - }, - _ => panic!("invalid config (expected symbol)") - } - }, - _ => panic!("invalid config (expected expression)") - } - } - None - } -} - -#[cfg(feature = "dsl")] -impl<'a> KeyMap<'a> for TokenIter<'a> { - fn command , I: AtomInput> (&'a self, state: &'a S, input: &'a I) - -> Option - { - let mut iter = self.clone(); - while let Some(next) = iter.next() { - match next { - Token { value: Value::Exp(0, exp_iter), .. } => { - let mut exp_iter = exp_iter.clone(); - match exp_iter.next() { - Some(Token { value: Value::Sym(binding), .. }) => { - if input.matches_atom(binding) { - if let Some(command) = C::try_from_expr(state, exp_iter.clone()) { - return Some(command) - } - } - }, - _ => panic!("invalid config (expected symbol)") - } - }, - _ => panic!("invalid config (expected expression)") - } - } - None - } -} - -/// A [Command] that can be constructed from a [Token]. -#[cfg(feature = "dsl")] -pub trait AtomCommand<'a, C>: TryFromAtom<'a, C> + Command {} - -#[cfg(feature = "dsl")] -impl<'a, C, T: TryFromAtom<'a, C> + Command> AtomCommand<'a, C> for T {} - -/** Implement `AtomCommand` for given `State` and `Command` */ -#[cfg(feature = "dsl")] -#[macro_export] macro_rules! atom_command { - ($Command:ty : |$state:ident:<$State:ident: $Trait:path>| { $(( - // identifier - $key:literal [ - // named parameters - $( - // argument name - $arg:ident - // if type is not provided defaults to Atom - $( - // type:name separator - : - // argument type - $type:ty - )? - ),* - // rest of parameters - $(, ..$rest:ident)? - ] - // bound command: - $command:expr - ))* }) => { - impl<'a, $State: $Trait> TryFromAtom<'a, $State> for $Command { - fn try_from_expr ($state: &$State, iter: TokenIter) -> Option { - let iter = iter.clone(); - match iter.next() { - $(Some(Token { value: Value::Key($key), .. }) => { - let iter = iter.clone(); - $( - let next = iter.next(); - if next.is_none() { panic!("no argument: {}", stringify!($arg)); } - let $arg = next.unwrap(); - $(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? - )* - $(let $rest = iter.clone();)? - return $command - },)* - _ => None - } - None - } - } - }; - ($Command:ty : |$state:ident:$State:ty| { $(( - // identifier - $key:literal [ - // named parameters - $( - // argument name - $arg:ident - // if type is not provided defaults to Atom - $( - // type:name separator - : - // argument type - $type:ty - )? - ),* - // rest of parameters - $(, ..$rest:ident)? - ] - // bound command: - $command:expr - ))* }) => { - impl<'a> TryFromAtom<'a, $State> for $Command { - fn try_from_expr ($state: &$State, iter: TokenIter) -> Option { - let mut iter = iter.clone(); - match iter.next() { - $(Some(Token { value: Value::Key($key), .. }) => { - let mut iter = iter.clone(); - $( - let next = iter.next(); - if next.is_none() { panic!("no argument: {}", stringify!($arg)); } - let $arg = next.unwrap(); - $(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? - )* - $(let $rest = iter.clone();)? - return $command - }),* - _ => None - } - } - } - }; - (@bind $state:ident =>$arg:ident ? : $type:ty) => { - let $arg: Option<$type> = Context::<$type>::get($state, $arg); - }; - (@bind $state:ident => $arg:ident : $type:ty) => { - let $arg: $type = Context::<$type>::get_or_fail($state, $arg); - }; -} - -#[cfg(all(test, feature = "dsl"))] -#[test] fn test_atom_keymap () -> Usually<()> { - let keymap = SourceIter::new(""); - Ok(()) -} diff --git a/input/src/lib.rs b/input/src/lib.rs index e27a8cb..c4c230d 100644 --- a/input/src/lib.rs +++ b/input/src/lib.rs @@ -1,19 +1,15 @@ #![feature(associated_type_defaults)] -mod command; pub use self::command::*; -mod handle; pub use self::handle::*; -mod keymap; pub use self::keymap::*; +pub(crate) use std::error::Error; +pub(crate) type Perhaps = Result, Box>; +#[cfg(test)] pub(crate) type Usually = Result>; #[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; +#[cfg(feature = "dsl")] mod input_dsl; +#[cfg(feature = "dsl")] pub use self::input_dsl::*; -/// Standard error trait. -pub(crate) use std::error::Error; - -/// Standard optional result type. -pub(crate) type Perhaps = Result, Box>; - -/// Standard result type. -#[cfg(test)] -pub(crate) type Usually = Result>; +mod input_macros; +mod input_command; pub use self::input_command::*; +mod input_handle; pub use self::input_handle::*; #[cfg(test)] #[test] fn test_stub_input () -> Usually<()> { @@ -36,3 +32,9 @@ pub(crate) type Usually = Result>; assert!(!TestInput(false).is_done()); Ok(()) } + +#[cfg(all(test, feature = "dsl"))] +#[test] fn test_dsl_keymap () -> Usually<()> { + let keymap = SourceIter::new(""); + Ok(()) +} diff --git a/output/src/ops/transform.rs b/output/src/ops/transform.rs index 76e709d..0ed9c80 100644 --- a/output/src/ops/transform.rs +++ b/output/src/ops/transform.rs @@ -31,7 +31,7 @@ macro_rules! transform_xy { #[inline] pub const fn xy (item: T) -> Self { Self::XY(item) } } #[cfg(feature = "dsl")] - impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> + impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromDsl<'a, T> for $Enum> { fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option { let mut iter = iter.clone(); @@ -78,7 +78,7 @@ 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<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> + impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromDsl<'a, T> for $Enum> { fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option { let mut iter = iter.clone(); diff --git a/output/src/output.rs b/output/src/output.rs index af31438..98e3659 100644 --- a/output/src/output.rs +++ b/output/src/output.rs @@ -1,5 +1,6 @@ use crate::*; use std::ops::Deref; + /// Render target. pub trait Output: Send + Sync + Sized { /// Unit of length diff --git a/output/src/view.rs b/output/src/view.rs index 8845f26..28e15bb 100644 --- a/output/src/view.rs +++ b/output/src/view.rs @@ -79,8 +79,8 @@ pub trait ViewContext<'a, E: Output + 'a>: Send + Sync #[cfg(feature = "dsl")] #[macro_export] macro_rules! try_delegate { - ($s:ident, $atom:expr, $T:ty) => { - if let Some(value) = <$T>::try_from_atom($s, $atom) { + ($s:ident, $dsl:expr, $T:ty) => { + if let Some(value) = <$T>::try_from_atom($s, $dsl) { return Some(value.boxed()) } } @@ -89,7 +89,7 @@ pub trait ViewContext<'a, E: Output + 'a>: Send + Sync #[cfg(feature = "dsl")] #[macro_export] macro_rules! try_from_expr { (<$l:lifetime, $E:ident>: $Struct:ty: |$state:ident, $iter:ident|$body:expr) => { - impl<$l, $E: Output + $l, T: ViewContext<$l, $E>> TryFromAtom<$l, T> for $Struct { + impl<$l, $E: Output + $l, T: ViewContext<$l, $E>> TryFromDsl<$l, T> for $Struct { fn try_from_expr ($state: &$l T, $iter: TokenIter<'a>) -> Option { let mut $iter = $iter.clone(); $body; diff --git a/tui/src/tui_engine/tui_input.rs b/tui/src/tui_engine/tui_input.rs index 3cd6373..2dd06f4 100644 --- a/tui/src/tui_engine/tui_input.rs +++ b/tui/src/tui_engine/tui_input.rs @@ -49,8 +49,8 @@ impl TuiIn { } } -impl AtomInput for TuiIn { - fn matches_atom (&self, token: &str) -> bool { +impl DslInput for TuiIn { + fn matches_dsl (&self, token: &str) -> bool { if let Some(event) = KeyMatcher::new(token).build() { &event == self.event() } else {