diff --git a/core/src/lib.rs b/core/src/lib.rs index b512ae9..39d7e6c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,4 +1,4 @@ -use std::error::Error; +pub(crate) use std::error::Error; /// Standard result type. pub type Usually = Result>; @@ -16,29 +16,56 @@ pub type Perhaps = Result, Box>; } pub trait Has: Send + Sync { - fn get (&self) -> &T; + 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 (&$self) -> &$T { &$x } fn get_mut (&mut $self) -> &mut $T { &mut $x } } }; } pub trait MaybeHas: Send + Sync { - fn get (&self) -> Option<&T>; + fn get (&self) -> Option<&T>; fn get_mut (&mut self) -> Option<&mut T>; } #[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 (&$self) -> Option<&$T> $x 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]. + fn try_eval (&self, args: Args) -> Perhaps; + /// Invoke a custom operation, converting a `None` result to a custom `Box`. + fn eval >> (&self, args: Args, error: impl Fn()->E) + -> Usually + { + match self.try_eval(args)? { + Some(value) => Ok(value), + _ => Result::Err(format!("Eval: {}", error().into()).into()) + } + } +} + +//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_display.rs b/dsl/src/dsl_display.rs index f86b4be..ab190b7 100644 --- a/dsl/src/dsl_display.rs +++ b/dsl/src/dsl_display.rs @@ -1,30 +1,29 @@ use crate::*; -use std::fmt::{Display, Formatter, Error as FormatError}; + impl Display for Ast { fn fmt (&self, out: &mut Formatter) -> Result<(), FormatError> { - use Value::*; - write!(out, "{}", match &self.0 { - 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:?}"), - }) + match &self.0 { + Value::Nil => Ok(()), + Value::Err(e) => write!(out, "[error: {e}]"), + Value::Num(n) => write!(out, "{n}"), + Value::Sym(s) => write!(out, "{s}"), + Value::Key(s) => write!(out, "{s}"), + Value::Str(s) => write!(out, "{s}"), + Value::Exp(_, e) => write!(out, "{e:?}"), + } } } + impl<'source> Display for CstValue<'source> { fn fmt (&self, out: &mut Formatter) -> Result<(), FormatError> { - use Value::*; - 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:?}"), - }) + match self { + Value::Nil => Ok(()), + Value::Err(e) => write!(out, "[error: {e}]"), + Value::Num(n) => write!(out, "{n}"), + Value::Sym(s) => write!(out, "{s}"), + Value::Key(s) => write!(out, "{s}"), + Value::Str(s) => write!(out, "{s}"), + Value::Exp(_, e) => write!(out, "{e:?}"), + } } } diff --git a/dsl/src/dsl_domain.rs b/dsl/src/dsl_domain.rs deleted file mode 100644 index d8d502e..0000000 --- a/dsl/src/dsl_domain.rs +++ /dev/null @@ -1,84 +0,0 @@ -use crate::*; - -pub trait Eval { - fn try_eval (&self, input: Input) -> Perhaps; - fn eval >, F: Fn()->E> ( - &self, input: Input, error: F - ) -> Usually { - if let Some(value) = self.try_eval(input)? { - Ok(value) - } else { - Result::Err(format!("Eval: {}", error().into()).into()) - } - } -} - -//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) - //} -//} - -/// May construct [Self] from token stream. -pub trait Dsl: Sized { - fn try_provide (state: &State, source: Ast) -> Perhaps; - fn provide >, F: Fn()->E> ( - state: &State, source: Ast, error: F - ) -> Usually { - let next = format!("{source:?}"); - if let Some(value) = Self::try_provide(state, source)? { - Ok(value) - } else { - Result::Err(format!("Dsl: {}: {next:?}", error().into()).into()) - } - } -} - -//pub trait Give<'state, 'source, Type> { - ///// Implement this to be able to [Give] [Type] from the [Cst]. - ///// Advance the stream if returning `Ok>`. - //fn give (&'state self, words: Cst<'source>) -> Perhaps; - ///// Return custom error on [None]. - //fn give_or_fail >, F: Fn()->E> ( - //&'state self, mut words: Cst<'source>, error: F - //) -> Usually { - //let next = words.peek().map(|x|x.value).clone(); - //if let Some(value) = Give::::give(self, words)? { - //Ok(value) - //} else { - //Result::Err(format!("give: {}: {next:?}", error().into()).into()) - //} - //} -//} -//#[macro_export] macro_rules! give { - //($Type:ty|$state:ident:$State:ident,$words:ident|$expr:expr) => { - //impl Give<$Type> for $State { - //fn give (&self, mut $words: Cst) -> Perhaps<$Type> { - //let $state = self; - //$expr - //} - //} - //}; - //($Type:path$(,$Arg:ident)*|$state:ident,$words:ident|$expr:expr) => { - //impl)++ $(, $Arg)*> Give<$Type> for State { - //fn give (&self, mut $words: Cst) -> Perhaps<$Type> { - //let $state = self; - //$expr - //} - //} - //} -//} -/////// Implement the [Give] trait, which boils down to -/////// specifying two types and providing an expression. -//#[macro_export] macro_rules! from_dsl { - //($Type:ty: |$state:ident:$State:ty, $words:ident|$expr:expr) => { - //give! { $Type|$state:$State,$words|$expr } - //}; -//} - diff --git a/dsl/src/dsl_value.rs b/dsl/src/dsl_value.rs index f06cf84..25d7ec5 100644 --- a/dsl/src/dsl_value.rs +++ b/dsl/src/dsl_value.rs @@ -1,5 +1,20 @@ use crate::*; -use std::fmt::Display; + +/// Thing that may construct itself from state and [DslValue]. +pub trait Dsl: Sized { + fn try_provide (state: &State, value: impl DslValue) -> Perhaps; + fn provide ( + state: &State, + value: impl DslValue, + error: impl Fn()->Box + ) -> Usually { + match Self::try_provide(state, value)? { + Some(value) => Ok(value), + _ => Err(error()) + } + } +} + pub trait DslValue: PartialEq + Clone + Default + Debug { type Str: AsRef + PartialEq + Clone + Default + Debug; type Exp: PartialEq + Clone + Default + Debug; @@ -29,6 +44,7 @@ pub trait DslValue: PartialEq + Clone + Default + Debug { fn exp_head (&self) -> Option<&Self> { None } // TODO fn exp_tail (&self) -> Option<&[Self]> { None } // TODO } + impl< Str: AsRef + PartialEq + Clone + Default + Debug, Exp: PartialEq + Clone + Default + Debug, @@ -40,8 +56,20 @@ impl< } } -pub enum Value { Nil, Err(DslError), Num(usize), Sym(S), Key(S), Str(S), Exp(usize, X), } -impl Default for Value { fn default () -> Self { Self:: Nil } } +pub enum Value { + Nil, + Err(DslError), + Num(usize), + Sym(S), + Key(S), + Str(S), + Exp(usize, X), +} + +impl Default for Value { + fn default () -> Self { Self:: Nil } +} + impl PartialEq for Value { fn eq (&self, other: &Self) -> bool { use Value::*; @@ -57,6 +85,7 @@ impl PartialEq for Value { } } } + impl Clone for Value { fn clone (&self) -> Self { use Value::*; @@ -71,12 +100,15 @@ impl Clone for Value { } } } + impl Copy for Value {} + impl Debug for Value { fn fmt (&self, _f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { todo!() } } + impl Display for Value { fn fmt (&self, _f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { todo!() diff --git a/dsl/src/lib.rs b/dsl/src/lib.rs index 24a5ee0..485f09e 100644 --- a/dsl/src/lib.rs +++ b/dsl/src/lib.rs @@ -36,6 +36,7 @@ #![feature(impl_trait_in_fn_trait_return)] pub(crate) use ::tengri_core::*; pub(crate) use std::fmt::Debug; +pub(crate) use std::fmt::{Display, Formatter, Error as FormatError}; pub(crate) use konst::iter::{ConstIntoIter, IsIteratorKind}; pub(crate) use konst::string::{split_at, str_range, char_indices}; pub(crate) use thiserror::Error; @@ -43,7 +44,6 @@ pub(crate) use self::DslError::*; mod dsl_ast; pub use self::dsl_ast::*; mod dsl_cst; pub use self::dsl_cst::*; mod dsl_display; //pub use self::dsl_display::*; -mod dsl_domain; pub use self::dsl_domain::*; mod dsl_error; pub use self::dsl_error::*; mod dsl_iter; pub use self::dsl_iter::*; mod dsl_token; pub use self::dsl_token::*; diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs index d34a8f7..76f3c3b 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -4,7 +4,10 @@ use std::fmt::Debug; #[derive(Default, Debug)] pub struct InputLayers(Vec); -#[derive(Default, Debug)] pub struct InputLayer(Option, Ast); +#[derive(Default, Debug)] pub struct InputLayer { + condition: Option, + bindings: Ast, +} impl InputLayers { pub fn new (layer: Ast) -> Self { @@ -19,8 +22,8 @@ impl InputLayers { pub fn add_layer (&mut self, layer: Ast) -> &mut Self { self.add_layer_if(None, layer.into()); self } - pub fn add_layer_if (&mut self, condition: Option, binding: Ast) -> &mut Self { - self.0.push(InputLayer(condition, binding)); + pub fn add_layer_if (&mut self, condition: Option, bindings: Ast) -> &mut Self { + self.0.push(InputLayer { condition, bindings }); self } pub fn handle + Eval, I: Eval, O: Command> (&self, state: &mut S, input: I) -> Perhaps { @@ -33,13 +36,13 @@ pub struct InputHandle<'a, S>(&'a mut S, &'a [InputLayer]); impl<'a, S: Eval + Eval, I: Eval, O: Command> Eval for InputHandle<'a, S> { fn try_eval (&self, input: I) -> Perhaps { let Self(state, layers) = self; - for InputLayer(condition, binding) in layers.iter() { + for InputLayer { condition, bindings } in layers.iter() { let mut matches = true; if let Some(condition) = condition { matches = state.eval(condition.clone(), ||"input: no condition")?; } if matches - && let Some(exp) = binding.exp() + && let Some(exp) = bindings.exp() && let Some(ast) = exp.peek() && input.eval(ast.clone(), ||"InputLayers: input.eval(binding) failed")? && let Some(command) = state.try_eval(ast)? { diff --git a/output/src/ops_dsl.rs b/output/src/ops_dsl.rs index 5d131bb..94ded3f 100644 --- a/output/src/ops_dsl.rs +++ b/output/src/ops_dsl.rs @@ -2,8 +2,9 @@ use crate::*; use Value::*; impl Dsl for When where S: Eval + Eval { - fn try_provide (state: &S, source: Ast) -> Perhaps { - if let Exp(_, mut exp) = source.0 && let Some(Ast(Key(id))) = exp.peek() && *id == *"when" { + fn try_provide (state: &S, source: impl DslValue) -> Perhaps { + if let Exp(_, mut exp) = source.value() + && let Some(Ast(Key(id))) = exp.peek() && *id == *"when" { let _ = exp.next(); return Ok(Some(Self( state.eval(exp.next().unwrap(), ||"when: expected condition")?, @@ -15,8 +16,9 @@ impl Dsl for When where S: Eval + Eval { } impl Dsl for Either where S: Eval + Eval + Eval { - fn try_provide (state: &S, source: Ast) -> Perhaps { - if let Exp(_, mut exp) = source.0 && let Some(Ast(Key(id))) = exp.peek() && *id == *"either" { + fn try_provide (state: &S, source: impl DslValue) -> Perhaps { + if let Exp(_, mut exp) = source.value() + && let Some(Ast(Key(id))) = exp.peek() && *id == *"either" { let _ = exp.next(); return Ok(Some(Self( state.eval(exp.next().unwrap(), ||"either: expected condition")?, @@ -29,8 +31,8 @@ impl Dsl for Either where S: Eval + Eval + } impl Dsl for Align where S: Eval, A> { - fn try_provide (state: &S, source: Ast) -> Perhaps { - if let Exp(_, source) = source.0 { + fn try_provide (state: &S, source: impl DslValue) -> Perhaps { + if let Exp(_, source) = source.value() { let mut rest = source.clone(); return Ok(Some(match rest.next().as_ref().and_then(|x|x.key()) { Some("align/c") => Self::c(state.eval(rest.next(), ||"align/c: expected content")?), @@ -52,8 +54,8 @@ impl Dsl for Align where S: Eval, A> { } impl Dsl for Bsp where S: Eval, A> + Eval, B> { - fn try_provide (state: &S, source: Ast) -> Perhaps { - if let Exp(_, exp) = source.0 { + fn try_provide (state: &S, source: impl DslValue) -> Perhaps { + if let Exp(_, exp) = source.value() { let mut rest = exp.clone(); return Ok(Some(match rest.next().as_ref().and_then(|x|x.key()) { Some("bsp/n") => Self::n( diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index ef5aa49..75876d0 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -64,7 +64,9 @@ impl ToTokens for ViewDef { /// which might correspond to a given [TokenStream], /// while taking [#self_ty]'s state into consideration. impl<'state> ::tengri::dsl::Dsl + 'state>> for #self_ty { - fn try_provide (state: &'state #self_ty, mut words: ::tengri::dsl::Ast) -> Perhaps + 'state>> { + fn try_provide (state: &'state #self_ty, mut words: ::tengri::dsl::Ast) -> + Perhaps + 'state>> + { Ok(if let Some(::tengri::dsl::Token { value, .. }) = words.peek() { match value { #(#builtin)* #(#exposed)* _ => None } } else { diff --git a/tengri/src/test.rs b/tengri/src/test.rs index 817c11b..839baee 100644 --- a/tengri/src/test.rs +++ b/tengri/src/test.rs @@ -1,14 +1,12 @@ use crate::*; #[test] fn test_subcommand () -> Usually<()> { - use crate::input::{Command, Handle, handle}; - //use crate::dsl::TokenIter; use crate::tui::TuiIn; use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}; - //use crate::input::*; - //use crate::dsl::*; + use crate::input::*; + use crate::dsl::*; struct Test { - //keys: InputMap<'static, Test, TestCommand, TuiIn, TokenIter<'static>> + keys: InputLayers } handle!(TuiIn: |self: Test, input|if let Some(command) = self.keys.command(self, input) { Ok(Some(true)) @@ -37,12 +35,12 @@ use crate::*; } } let mut test = Test { - keys: InputMap::new(" + keys: InputLayers::new(SourceIter::new(" (@a do-thing) (@b do-thing-arg 0) (@c do-sub do-other-thing) (@d do-sub do-other-thing-arg 0) - ".into()) + ".into())) }; assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { kind: KeyEventKind::Press,