From 8e59003a31ae729a2c80896799f20d128a005302 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 19 Jul 2025 19:53:02 +0300 Subject: [PATCH] fix(input): sorting out event map --- core/src/lib.rs | 72 +++----- dsl/src/dsl.rs | 75 +++----- input/src/input_dsl.rs | 289 ++++++++++--------------------- tui/src/tui_engine/tui_buffer.rs | 8 +- 4 files changed, 140 insertions(+), 304 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index cc04597..b06b55e 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,48 +1,38 @@ pub(crate) use std::error::Error; - /// Standard result type. pub type Usually = Result>; - /// Standard optional result type. pub type Perhaps = Result, Box>; - -/// Implement the `From` trait. -#[macro_export] macro_rules! from { +/// Implement [`Debug`] in bulk. +#[macro_export] macro_rules! impl_debug(($($S:ty|$self:ident,$w:ident|$body:block)*)=>{ + $(impl std::fmt::Debug for $S { + fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body + })* }); +/// Implement [`From`] in bulk. +#[macro_export] macro_rules! from( ($(<$($lt:lifetime),+>)?|$state:ident:$Source:ty|$Target:ty=$cb:expr) => { impl $(<$($lt),+>)? From<$Source> for $Target { - fn from ($state:$Source) -> Self { $cb } - } - }; -} - -pub trait Has: Send + Sync { - fn get (&self) -> &T; - fn get_mut (&mut self) -> &mut T; -} - -#[macro_export] macro_rules! has { - ($T:ty: |$self:ident : $S:ty| $x:expr) => { - impl Has<$T> for $S { - fn get (&$self) -> &$T { &$x } - fn get_mut (&mut $self) -> &mut $T { &mut $x } - } - }; -} - -pub trait MaybeHas: Send + Sync { - fn get (&self) -> Option<&T>; - fn get_mut (&mut self) -> Option<&mut T>; -} - -#[macro_export] macro_rules! maybe_has { + fn from ($state:$Source) -> Self { $cb }}}; + ($($Struct:ty { $( + $(<$($l:lifetime),* $($T:ident$(:$U:ident)?),*>)? ($source:ident: $From:ty) $expr:expr + );+ $(;)? })*) => { $( + $(impl $(<$($l),* $($T$(:$U)?),*>)? From<$From> for $Struct { + fn from ($source: $From) -> Self { $expr } })+ )* }; ); +/// Type-dispatched `get` and `get_mut`. +pub trait Has: Send + Sync { fn get (&self) -> &T; fn get_mut (&mut self) -> &mut T; } +/// Implement [Has]. +#[macro_export] macro_rules! has(($T:ty: |$self:ident : $S:ty| $x:expr) => { + impl Has<$T> for $S { + fn get (&$self) -> &$T { &$x } + fn get_mut (&mut $self) -> &mut $T { &mut $x } } };); +/// Type-dispatched `get` and `get_mut` that return an [Option]-wrapped result. +pub trait MaybeHas: Send + Sync { fn get (&self) -> Option<&T>; fn get_mut (&mut self) -> Option<&mut T>; } +/// Implement [MaybeHas]. +#[macro_export] macro_rules! maybe_has( ($T:ty: |$self:ident : $S:ty| $x:block; $y:block $(;)?) => { impl MaybeHas<$T> for $S { fn get (&$self) -> Option<&$T> $x - fn get_mut (&mut $self) -> Option<&mut $T> $y - } - }; -} - + fn get_mut (&mut $self) -> Option<&mut $T> $y } };); /// May compute a `RetVal` from `Args`. pub trait Eval { /// A custom operation on [Args] that may return [Result::Err] or [Option::None]. @@ -57,15 +47,3 @@ pub trait Eval { } } } - -//impl, I, O> Eval for &S { - //fn try_eval (&self, input: I) -> Perhaps { - //(*self).try_eval(input) - //} -//} - -//impl, I: Ast, O: Dsl> Eval for S { - //fn try_eval (&self, input: I) -> Perhaps { - //Dsl::try_provide(self, input) - //} -//} diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index 2f1c150..37b8d9a 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -18,7 +18,7 @@ pub(crate) use self::DslError::*; #[macro_export] macro_rules! dsl_read_advance (($exp:ident, $pat:pat => $val:expr)=>{{ let (head, tail) = $exp.advance(); $exp = tail; match head { - Some($pat) => $val, _ => Err(format!("(e4) unexpected {head:?}").into()) }}}); + Some($pat) => Ok($val), _ => Err(format!("(e4) unexpected {head:?}").into()) }}}); /// Coerce to [Val] for predefined [Self::Str] and [Self::Exp]. pub trait Dsl: Clone + Debug { @@ -28,49 +28,41 @@ pub trait Dsl: Clone + Debug { type Exp: DslExp; /// Request the top-level DSL [Val]ue. /// May perform cloning or parsing. - fn val (&self) -> Val; - fn err (&self) -> Option {self.val().err()} - fn nil (&self) -> bool {self.val().nil()} - fn num (&self) -> Option {self.val().num()} - fn sym (&self) -> Option {self.val().sym()} - fn key (&self) -> Option {self.val().key()} - fn str (&self) -> Option {self.val().str()} - fn exp (&self) -> Option {self.val().exp()} - fn exp_depth (&self) -> Option {self.val().exp_depth()} - fn exp_head (&self) -> Val {self.val().exp_head()} - fn exp_tail (&self) -> Self::Exp {self.val().exp_tail()} - fn exp_each (&self, f: impl Fn(&Self) -> Usually<()>) -> Usually<()> { - todo!() - } - fn advance (&self) -> (Val, Self::Exp) { - (self.exp_head(), self.exp_tail()) - } + fn val (&self) -> Val; + fn err (&self) -> Option {self.val().err()} + fn nil (&self) -> bool {self.val().nil()} + fn num (&self) -> Option {self.val().num()} + fn sym (&self) -> Option {self.val().sym()} + fn key (&self) -> Option {self.val().key()} + fn str (&self) -> Option {self.val().str()} + fn exp (&self) -> Option {self.val().exp()} + fn depth (&self) -> Option {self.val().depth()} + fn head (&self) -> Val {self.val().head()} + fn tail (&self) -> Self::Exp {self.val().tail()} + fn advance (&self) -> (Val, Self::Exp) { (self.head(), self.tail()) } + fn each (&self, f: impl Fn(&Self) -> Usually<()>) -> Usually<()> { todo!() } + fn exp_next (&mut self) -> Option> { todo!() } } - impl<'s> Dsl for &'s str { type Str = &'s str; type Exp = &'s str; fn val (&self) -> Val { Val::Exp(0, self) } } - impl<'s> Dsl for Cst<'s> { type Str = &'s str; type Exp = Cst<'s>; fn val (&self) -> Val { Val::Exp(0, *self) } } - impl Dsl for Ast { type Str = Arc; type Exp = Ast; fn val (&self) -> Val { Val::Exp(0, self.clone()) } } - impl Dsl for Token { type Str = Str; type Exp = Exp; fn val (&self) -> Val { self.value.clone() } } - impl Dsl for Val { type Str = Str; type Exp = Exp; @@ -134,7 +126,7 @@ impl> Val { pub const fn key (&self) -> Option<&Str> {match self{Val::Key(k)=>Some(k), _=>None}} pub const fn str (&self) -> Option<&Str> {match self{Val::Str(s)=>Some(s), _=>None}} pub const fn exp (&self) -> Option<&Exp> {match self{Val::Exp(_, x)=>Some(x),_=>None}} - pub const fn exp_depth (&self) -> Option {match self{Val::Exp(d, _)=>Some(*d), _=>None}} + pub const fn depth (&self) -> Option {match self{Val::Exp(d, _)=>Some(*d), _=>None}} } /// Parsed substring with range and value. #[derive(Debug, Clone, Default, PartialEq)] @@ -363,10 +355,10 @@ pub const fn peek <'s> (mut value: CstVal<'s>, source: &'s str) -> CstToken<'s> } start = i; length = 1; - value = if is_exp_start(c) { Exp(1, str_range(source, i, i+1)) } - else if is_str_start(c) { Str(str_range(source, i, i+1)) } - else if is_sym_start(c) { Sym(str_range(source, i, i+1)) } - else if is_key_start(c) { Key(str_range(source, i, i+1)) } + value = if is_exp_start(c) { Exp(1, str_range(source, i, i+1)) } + else if is_str_start(c) { Str(str_range(source, i, i+1)) } + else if is_sym_start(c) { Sym(str_range(source, i, i+1)) } + else if is_key_start(c) { Key(str_range(source, i, i+1)) } else if is_digit(c) { match to_digit(c) { Ok(c) => Num(c), Err(e) => Error(e) } } else { value = Error(Unexpected(c)); break } } else if matches!(value, Str(_)) { @@ -451,33 +443,6 @@ pub const fn to_digit (c: char) -> Result { }) } -/// Implement type conversions. -macro_rules! from(($($Struct:ty { $( - $(<$($l:lifetime),* $($T:ident$(:$U:ident)?),*>)? ($source:ident: $From:ty) $expr:expr -);+ $(;)? })*) => { $( - $(impl $(<$($l),* $($T$(:$U)?),*>)? From<$From> for $Struct { - fn from ($source: $From) -> Self { $expr } - })+ -)* }); - -//from! { - ////Vec> { <'s> (val: CstIter<'s>) val.collect(); } - //CstConstIter<'s> { - //<'s> (src: &'s str) Self(src); - //<'s> (iter: CstIter<'s>) iter.0; } - //CstIter<'s> { - //<'s> (src: &'s str) Self(CstConstIter(src)); - //<'s> (iter: CstConstIter<'s>) Self(iter); } - //Cst<'s> { <'s> (src: &'s str) Self(CstIter(CstConstIter(src))); } - //Vec { <'s> (val: CstIter<'s>) val.map(Into::into).collect(); } - //Token> { <'s> (token: Token>) Self { value: token.value.into(), span: token.span.into() } } - //Ast { - //<'s> (src: &'s str) Ast::from(CstIter(CstConstIter(src))); - //<'s> (cst: Cst<'s>) Ast(VecDeque::from([dsl_val(cst.val())]).into()); - //<'s> (iter: CstIter<'s>) Ast(iter.map(|x|x.value.into()).collect::>().into()); - // (token: Token) Ast(VecDeque::from([dsl_val(token.val())]).into()); } -//} - /// `T` + [Dsl] -> `Self`. pub trait FromDsl: Sized { fn from_dsl (state: &T, dsl: &impl Dsl) -> Perhaps; diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs index 5403b66..2780474 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -1,4 +1,8 @@ use crate::*; +/// Map of each event (e.g. key combination) to +/// all command expressions bound to it by +/// all loaded input layers. +type EventMapImpl = BTreeMap>>; /// A collection of input bindings. /// /// Each contained layer defines a mapping from input event to command invocation @@ -8,209 +12,100 @@ use crate::*; /// When a key is pressed, the bindings for it are checked in sequence. /// When the first non-conditional or true conditional binding is executed, /// that .event()binding's value is returned. -#[derive(Debug)] pub struct EventMap( - /// Map of each event (e.g. key combination) to - /// all command expressions bound to it by - /// all loaded input layers. - pub EventBindings -); -type EventBindings = BTreeMap>>; +#[derive(Debug)] +pub struct EventMap(EventMapImpl); +/// Default is always empty map regardless if `E` and `C` implement [Default]. +impl Default for EventMap { fn default () -> Self { Self(Default::default()) } } +impl EventMap { + /// Create a new event map + pub fn new () -> Self { + Default::default() + } + /// Add a binding to an owned event map. + pub fn def (mut self, event: E, binding: Binding) -> Self { + self.add(event, binding); + self + } + /// Add a binding to an event map. + pub fn add (&mut self, event: E, binding: Binding) -> &mut Self { + if (!self.0.contains_key(&event)) { + self.0.insert(event.clone(), Default::default()); + } + self.0.get_mut(&event).unwrap().push(binding); + self + } + /// Return the binding(s) that correspond to an event. + pub fn query (&self, event: E) -> Option<&[Binding]> { + self.0.get(&event).map(|x|x.as_slice()) + } + /// Return the first binding that corresponds to an event, considering conditions. + pub fn dispatch (&self, event: E) -> Option<&Binding> { + self.query(event) + .map(|bb|bb.iter().filter(|b|b.condition.as_ref().map(|c|(c.0)()).unwrap_or(true)).next()) + .flatten() + } + /// Create event map from path to text file. + pub fn from_path > (path: P) -> Usually where E: From> { + if exists(path.as_ref())? { + Self::from_source(read_and_leak(path)?) + } else { + return Err(format!("(e5) not found: {:?}", path.as_ref()).into()) + } + } + /// Create event map from string. + pub fn from_source (source: impl AsRef) -> Usually where E: From> { + Self::from_dsl(Cst::from(source.as_ref())) + } + /// Create event map from DSL tokenizer. + pub fn from_dsl (mut dsl: impl Dsl) -> Usually where E: From> { + use Val::*; + let mut map: Self = Default::default(); + loop { + match dsl.exp_next().unwrap() { + Str(path) => { + map.0.extend(Self::from_path(PathBuf::from(path.as_ref() as &str))?.0) + }, + Exp(_, e) => match e.head() { + Key(k) if k.as_ref() == "if" => { + todo!("conditional binding {:?}", dsl.tail()); + }, + Sym(s) => { + let key: Arc = s.as_ref().into(); + let key: E = key.into(); + map.add(key, Binding::from_dsl(e)?); + todo!("binding {s:?} {:?}", dsl.tail()); + }, + _ => panic!(), + }, + _ => panic!(), + } + } + Ok(map) + } +} /// An input binding. -#[derive(Debug, Default)] pub struct Binding { - condition: Option, - command: D, +#[derive(Debug)] +pub struct Binding { + command: C, + condition: Option, description: Option>, source: Option>, } -impl Default for EventMap { - fn default () -> Self { - Self(Default::default()) +impl Binding { + fn from_dsl (dsl: impl Dsl) -> Usually { + let mut command: Option = None; + let mut condition: Option = None; + let mut description: Option> = None; + let mut source: Option> = None; + if let Some(command) = command { + Ok(Self { command, condition, description, source }) + } else { + Err(format!("no command in {dsl:?}").into()) + } } } -impl> + Clone> EventMap { - /// Create input layer collection from DSL. - pub fn new (dsl: Ast) -> Usually { - use Val::*; - let mut input_map: EventBindings = Default::default(); - match dsl.exp_head() { - Str(path) => { - let path = PathBuf::from(path.as_ref() as &str); - for (key, val) in Self::from_path(&path)?.0.into_iter() { - todo!("import {path:?} {key:?} {val:?}"); - let key: E = key.into(); - if !input_map.contains_key(&key) { - input_map.insert(key, vec![]); - } - } - }, - Sym(s) => { - let key: Arc = s.as_ref().into(); - let key: E = key.into(); - if !input_map.contains_key(&key) { - input_map.insert(key.clone(), vec![]); - } - todo!("binding {s:?} {:?}", dsl.exp_tail()); - }, - Key(k) if k.as_ref() == "if" => { - todo!("conditional binding {:?}", dsl.exp_tail()); - }, - _ => return Err(format!("invalid form in keymap: {dsl:?}").into()) - } - Ok(Self(input_map)) - } - /// Create input layer collection from string. - pub fn from_source (source: &str) -> Usually { - Self::new(Ast::from(source)) - } - /// Create input layer collection from path to text file. - pub fn from_path > (path: P) -> Usually { - if !exists(path.as_ref())? { - return Err(format!("(e5) not found: {path:?}").into()) - } - Self::from_source(read_and_leak(path)?) - } - /// Evaluate the active layers for a given state, - /// returning the command to be executed, if any. - pub fn handle (&self, state: &mut S, event: &E) -> Perhaps where - S: DslInto + DslInto, - O: Command - { - todo!(); - //let layers = self.0.as_slice(); - //for InputLayer { cond, bind } in layers.iter() { - //let mut matches = true; - //if let Some(cond) = cond { - //matches = state.dsl_into(cond, ||format!("input: no cond").into())?; - //} - //if matches - //&& let Some(exp) = bind.val().exp_head() - //&& input.dsl_into(exp, ||format!("EventMap: input.eval(binding) failed").into())? - //&& let Some(command) = state.try_dsl_into(exp)? { - //return Ok(Some(command)) - //} - //} - Ok(None) - } - /* - /// Create an input map with a single non-condal layer. - /// (Use [Default::default] to get an empty map.) - pub fn new (layer: Val) -> Self { - Self::default().layer(layer) - } - /// Add layer, return `Self`. - pub fn layer (mut self, layer: Val) -> Self { - self.add_layer(layer); self - } - /// Add condal layer, return `Self`. - pub fn layer_if (mut self, cond: Val, layer: Val) -> Self { - self.add_layer_if(Some(cond), layer); self - } - /// Add layer, return `&mut Self`. - pub fn add_layer (&mut self, layer: Val) -> &mut Self { - self.add_layer_if(None, layer.into()); self - } - /// Add condal layer, return `&mut Self`. - pub fn add_layer_if (&mut self, cond: Option>, bind: Val) -> &mut Self { - self.0.push(InputLayer { cond, bind }); - self - } - */ -} - - - - //let mut keys = iter.unwrap(); - //let mut map = EventMap::default(); - //while let Some(token) = keys.next() { - //if let Value::Exp(_, mut exp) = token.value { - //let next = exp.next(); - //if let Some(Token { value: Value::Key(sym), .. }) = next { - //match sym { - //"layer" => { - //if let Some(Token { value: Value::Str(path), .. }) = exp.peek() { - //let path = base.as_ref().parent().unwrap().join(unquote(path)); - //if !std::fs::exists(&path)? { - //return Err(format!("(e5) not found: {path:?}").into()) - //} - //map.add_layer(read_and_leak(path)?.into()); - //print!("layer:\n path: {:?}...", exp.0.0.trim()); - //println!("ok"); - //} else { - //return Err(format!("(e4) unexpected non-string {next:?}").into()) - //} - //}, - - //"layer-if" => { - //let mut cond = None; - - //if let Some(Token { value: Value::Sym(sym), .. }) = exp.next() { - //cond = Some(leak(sym)); - //} else { - //return Err(format!("(e4) unexpected non-symbol {next:?}").into()) - //}; - - //if let Some(Token { value: Value::Str(path), .. }) = exp.peek() { - //let path = base.as_ref().parent().unwrap().join(unquote(path)); - //if !std::fs::exists(&path)? { - //return Err(format!("(e5) not found: {path:?}").into()) - //} - //print!("layer-if:\n cond: {}\n path: {path:?}...", - //cond.unwrap_or_default()); - //let keys = read_and_leak(path)?.into(); - //let cond = cond.unwrap(); - //println!("ok"); - //map.add_layer_if( - //Box::new(move |state: &App|Take::take_or_fail( - //state, exp, ||"missing input layer conditional" - //)), keys - //); - //} else { - //return Err(format!("(e4) unexpected non-symbol {next:?}").into()) - //} - //}, - - //_ => return Err(format!("(e3) unexpected symbol {sym:?}").into()) - //} - //} else { - //return Err(format!("(e2) unexpected exp {:?}", next.map(|x|x.value)).into()) - //} - //} else { - //return Err(format!("(e1) unexpected token {token:?}").into()) - //} - //} - //Ok(map) - //} -//{ -//} - //fn from (source: &'s str) -> Self { - //// this should be for single layer: - //use Val::*; - //let mut layers = vec![]; - //let mut source = CstIter::from(source); - //while let Some(Exp(_, mut iter)) = source.next().map(|x|x.value) { - //let mut iter = iter.clone(); - //layers.push(match iter.next().map(|x|x.value) { - //Some(Sym(sym)) if sym.starts_with("@") => InputLayer { - //cond: None, - //bind: vec![[ - //dsl_val(source.nth(1).unwrap()), - //dsl_val(source.nth(2).unwrap()), - //]], - //}, - //Some(Str(layer)) => InputLayer { - //cond: None, - //bind: dsl_val(source.nth(1).unwrap()), - //}, - //Some(Key("if")) => InputLayer { - //cond: Some(dsl_val(source.nth(1).unwrap())), - //bind: dsl_val(source.nth(2).unwrap()), - //}, - //_ => panic!("invalid token in keymap"), - //}) - //} - //Self(layers) - //} -//} +pub struct Condition(Boxbool + Send + Sync>); +impl_debug!(Condition |self, w| { write!(w, "*") }); fn unquote (x: &str) -> &str { let mut chars = x.chars(); diff --git a/tui/src/tui_engine/tui_buffer.rs b/tui/src/tui_engine/tui_buffer.rs index 7fc44b9..6e3acfd 100644 --- a/tui/src/tui_engine/tui_buffer.rs +++ b/tui/src/tui_engine/tui_buffer.rs @@ -20,11 +20,9 @@ pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut C pub content: Vec } -impl std::fmt::Debug for BigBuffer { - fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - write!(f, "[BB {}x{} ({})]", self.width, self.height, self.content.len()) - } -} +impl_debug!(BigBuffer |self, f| { + write!(f, "[BB {}x{} ({})]", self.width, self.height, self.content.len()) +}); impl BigBuffer { pub fn new (width: usize, height: usize) -> Self {