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/Cargo.toml b/dsl/Cargo.toml index 256b599..c61a057 100644 --- a/dsl/Cargo.toml +++ b/dsl/Cargo.toml @@ -4,6 +4,9 @@ description = "UI metaframework, tiny S-expression-based DSL." version = { workspace = true } edition = { workspace = true } +[lib] +path = "src/dsl.rs" + [dependencies] tengri_core = { path = "../core" } konst = { workspace = true } diff --git a/dsl/src/lib.rs b/dsl/src/dsl.rs similarity index 58% rename from dsl/src/lib.rs rename to dsl/src/dsl.rs index 36fc266..37b8d9a 100644 --- a/dsl/src/lib.rs +++ b/dsl/src/dsl.rs @@ -14,7 +14,72 @@ pub(crate) use konst::iter::{ConstIntoIter, IsIteratorKind}; pub(crate) use konst::string::{split_at, str_range, char_indices}; pub(crate) use thiserror::Error; pub(crate) use self::DslError::*; -#[cfg(test)] mod test; +#[cfg(test)] mod dsl_test; + +#[macro_export] macro_rules! dsl_read_advance (($exp:ident, $pat:pat => $val:expr)=>{{ + let (head, tail) = $exp.advance(); $exp = tail; match head { + 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 { + /// The string representation for a dizzle. + type Str: DslStr; + /// The string representation for a dizzle. + 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 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; + fn val (&self) -> Val { self.clone() } +} + +/// The expression representation for a [Dsl] implementation. +/// [Cst] uses [CstIter]. [Ast] uses [VecDeque]. +pub trait DslExp: PartialEq + Clone + Debug + Dsl {} +impl DslExp for T {} +/// The string representation for a [Dsl] implementation. +/// [Cst] uses `&'s str`. [Ast] uses `Arc`. +pub trait DslStr: PartialEq + Clone + Default + Debug + AsRef + Deref { + fn as_str (&self) -> &str { self.as_ref() } + fn as_arc (&self) -> Arc { self.as_ref().into() } +} +impl + Deref> DslStr for Str {} /// Enumeration of values that may figure in an expression. /// Generic over string and expression storage. #[derive(Clone, Debug, PartialEq, Default)] @@ -40,34 +105,20 @@ pub enum Val { Error(DslError), } impl Copy for Val {} -impl Val { - pub fn convert (&self) -> Val where - T::Str: for<'a> From<&'a Str>, - T::Exp: for<'a> From<&'a Exp> - { +impl Val { + pub fn convert_to (&self, to_str: impl Fn(&S1)->S2, to_exp: impl Fn(&E1)->E2) -> Val { match self { Val::Nil => Val::Nil, Val::Num(u) => Val::Num(*u), - Val::Sym(s) => Val::Sym(s.into()), - Val::Key(s) => Val::Key(s.into()), - Val::Str(s) => Val::Str(s.into()), - Val::Exp(d, x) => Val::Exp(*d, x.into()), + Val::Sym(s) => Val::Sym(to_str(s)), + Val::Key(s) => Val::Key(to_str(s)), + Val::Str(s) => Val::Str(to_str(s)), + Val::Exp(d, x) => Val::Exp(*d, to_exp(x)), Val::Error(e) => Val::Error(*e) } } } -/// The expression representation for a [Dsl] implementation. -/// [Cst] uses [CstIter]. [Ast] uses [VecDeque]. -pub trait DslExp: PartialEq + Clone + Default + Debug + Dsl {} -impl DslExp for T {} -/// The string representation for a [Dsl] implementation. -/// [Cst] uses `&'s str`. [Ast] uses `Arc`. -pub trait DslStr: PartialEq + Clone + Default + Debug + AsRef + Deref { - fn as_str (&self) -> &str { self.as_ref() } - fn as_arc (&self) -> Arc { self.as_ref().into() } -} -impl + Deref> DslStr for Str {} -impl> Val { +impl> Val { pub const fn err (&self) -> Option {match self{Val::Error(e)=>Some(*e), _=>None}} pub const fn nil (&self) -> bool {match self{Val::Nil=>true, _=>false}} pub const fn num (&self) -> Option {match self{Val::Num(n)=>Some(*n), _=>None}} @@ -75,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)] @@ -89,7 +140,9 @@ pub struct Token { /// Length of span. pub length: usize, } +/// Tokens are copiable where possible. impl Copy for Token {} +/// Token methods. impl Token { pub const fn end (&self) -> usize { self.start.saturating_add(self.length) } @@ -103,83 +156,85 @@ impl Token { Self { value: self.value, ..*self } } } -/// To the [Dsl], a token is equivalent to its `value` field. -impl Dsl for Token { - type Str = Str; type Exp = Exp; - fn dsl (&self) -> Val { self.value.clone() } -} -/// Coerce to [Val] for predefined [Self::Str] and [Self::Exp]. -pub trait Dsl: Clone + Debug { - /// The string representation for a dizzle. - type Str: DslStr; - /// The expression representation for a dizzle. - type Exp: DslExp; - /// Request the top-level DSL [Val]ue. - /// May perform cloning or parsing. - fn dsl (&self) -> Val; - fn err (&self) -> Option {self.dsl().err()} - fn nil (&self) -> bool {self.dsl().nil()} - fn num (&self) -> Option {self.dsl().num()} - fn sym (&self) -> Option {self.dsl().sym()} - fn key (&self) -> Option {self.dsl().key()} - fn str (&self) -> Option {self.dsl().str()} - fn exp (&self) -> Option {self.dsl().exp()} - fn exp_depth (&self) -> Option {self.dsl().exp_depth()} - fn exp_head (&self) -> Val {self.dsl().exp_head()} - fn exp_tail (&self) -> Self::Exp {self.dsl().exp_tail()} - fn exp_each (&self, f: impl Fn(&Self) -> Usually<()>) -> Usually<()> { todo!() } -} -/// The most basic implementor of the [Dsl] trait. -impl Dsl for Val { - type Str = Str; type Exp = Exp; - fn dsl (&self) -> Val { self.clone() } -} -/// The abstract syntax tree (AST) can be produced from the CST -/// by cloning source slices into owned ([Arc]) string slices. -#[derive(Debug, Clone, Default, PartialEq)] -pub struct Ast(Arc, Ast>>>>); -pub type AstVal = Val, Ast>; -pub type AstToken = Token, Ast>; -impl Dsl for Ast { - type Str = Arc; type Exp = Ast; - fn dsl (&self) -> Val, Ast> { Val::Exp(0, Ast(self.0.clone())) } -} -impl<'s> From<&'s str> for Ast { - fn from (source: &'s str) -> Self { - let source: Arc = source.into(); - Self(CstIter(CstConstIter(source.as_ref())) - .map(|token|Arc::new(Token { - source: source.clone(), - start: token.start, - length: token.length, - value: match token.value { - Val::Nil => Val::Nil, - Val::Num(u) => Val::Num(u), - Val::Sym(s) => Val::Sym(s.into()), - Val::Key(s) => Val::Key(s.into()), - Val::Str(s) => Val::Str(s.into()), - Val::Exp(d, x) => Val::Exp(d, x.into()), - Val::Error(e) => Val::Error(e.into()) - }, - })) - .collect::>() - .into()) - } -} -impl<'s> From> for Ast { - fn from (cst: Cst<'s>) -> Self { - let mut tokens: VecDeque<_> = Default::default(); - Self(tokens.into()) - } -} /// The concrete syntax tree (CST) implements zero-copy /// parsing of the DSL from a string reference. CST items /// preserve info about their location in the source. /// CST stores strings as source references and expressions as [CstIter] instances. -#[derive(Debug, Copy, Clone, Default, PartialEq)] -pub struct Cst<'s>(pub CstIter<'s>); -pub type CstVal<'s> = Val<&'s str, Cst<'s>>; -pub type CstToken<'s> = Token<&'s str, Cst<'s>>; +#[derive(Debug, Copy, Clone, PartialEq, Default)] +pub enum Cst<'s> { + #[default] __, + Source(&'s str), + Token(CstToken<'s>), + Val(CstVal<'s>), + Iter(CstIter<'s>), + ConstIter(CstConstIter<'s>), +} +impl<'s> From<&'s str> for Cst<'s> { + fn from (src: &'s str) -> Self { + Cst::Source(src) + } +} +impl<'s> From> for Cst<'s> { + fn from (token: CstToken<'s>) -> Self { + Cst::Token(token) + } +} +impl<'s> From> for Cst<'s> { + fn from (val: CstVal<'s>) -> Self { + Cst::Val(val) + } +} +impl<'s> From> for Cst<'s> { + fn from (iter: CstIter<'s>) -> Self { + Cst::Iter(iter) + } +} +impl<'s> From> for Cst<'s> { + fn from (iter: CstConstIter<'s>) -> Self { + Cst::ConstIter(iter) + } +} +impl<'s> From> for Ast { + fn from (cst: Cst<'s>) -> Self { + match cst { + Cst::Source(source) | Cst::Iter(CstIter(CstConstIter(source))) | + Cst::ConstIter(CstConstIter(source)) => + Ast::Source(source.into()), + Cst::Val(value) => + Ast::Val(value.convert_to(|s|(*s).into(), |e|Box::new((*e).into()))), + Cst::Token(Token { source, start, length, value }) => { + let source = AsRef::::as_ref(source).into(); + let value = value.convert_to(|s|(*s).into(), |e|Box::new((*e).into())); + Ast::Token(Token { source, start, length, value }) + }, + _ => unreachable!() + } + } +} +/// The abstract syntax tree (AST) can be produced from the CST +/// by cloning source slices into owned ([Arc]) string slices. +#[derive(Debug, Clone, PartialEq, Default)] +pub enum Ast { + #[default] __, + Source(Arc), + Token(Token, Box>), + Val(Val, Box>), + Exp(Arc, Ast>>>>), +} +impl<'s> From<&'s str> for Ast { + fn from (src: &'s str) -> Self { + Self::Source(src.into()) + } +} +impl<'s, Str: DslStr, Exp: DslExp + Into + 's> From> for Ast { + fn from (Token { source, start, length, value }: Token) -> Self { + let source: Arc = source.as_ref().into(); + let value = value.convert_to(|s|s.as_ref().into(), |e|Box::new(e.clone().into())); + Self::Token(Token { source, start, length, value }) + } +} +pub type CstVal<'s> = Val<&'s str, &'s str>; +pub type CstToken<'s> = Token<&'s str, &'s str>; impl<'s> CstToken<'s> { pub const fn slice (&self) -> &str { str_range(self.source, self.start, self.end()) } @@ -192,19 +247,32 @@ impl<'s> CstToken<'s> { self } pub const fn grow_exp (&'s mut self, depth: isize, source: &'s str) -> &mut Self { - self.value = Val::Exp(depth, Cst(CstIter(CstConstIter(source)))); + self.value = Val::Exp(depth, source); self } } -impl<'s> Dsl for Cst<'s> { - type Str = &'s str; type Exp = Cst<'s>; - fn dsl (&self) -> Val { Val::Exp(0, Cst(self.0)) } -} -impl<'s> From<&'s str> for Cst<'s> { - fn from (source: &'s str) -> Self { - Self(CstIter(CstConstIter(source))) - } -} +//impl<'s> From<&'s str> for Ast { + //fn from (source: &'s str) -> Ast { + //let source: Arc = source.into(); + //Ast(CstIter(CstConstIter(source.as_ref())) + //.map(|token|Arc::new(Token { + //source: source.clone(), + //start: token.start, + //length: token.length, + //value: match token.value { + //Val::Nil => Val::Nil, + //Val::Num(u) => Val::Num(u), + //Val::Sym(s) => Val::Sym(s.into()), + //Val::Key(s) => Val::Key(s.into()), + //Val::Str(s) => Val::Str(s.into()), + //Val::Exp(d, x) => Val::Exp(d, x.into()), + //Val::Error(e) => Val::Error(e.into()) + //}, + //})) + //.collect::>() + //.into()) + //} +//} /// DSL-specific error codes. #[derive(Error, Debug, Copy, Clone, PartialEq, PanicFmt)] pub enum DslError { #[error("parse failed: not implemented")] @@ -287,20 +355,12 @@ pub const fn peek <'s> (mut value: CstVal<'s>, source: &'s str) -> CstToken<'s> } start = i; length = 1; - if is_exp_start(c) { - value = Exp(1, Cst(CstIter(CstConstIter(str_range(source, i, i+1))))); - } else if is_str_start(c) { - value = Str(str_range(source, i, i+1)); - } else if is_sym_start(c) { - value = Sym(str_range(source, i, i+1)); - } else if is_key_start(c) { - value = Key(str_range(source, i, i+1)); - } else if is_digit(c) { - value = match to_digit(c) { Ok(c) => Num(c), Err(e) => Error(e) }; - } else { - value = Error(Unexpected(c)); - break - } + 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(_)) { if is_str_end(c) { break @@ -327,13 +387,13 @@ pub const fn peek <'s> (mut value: CstVal<'s>, source: &'s str) -> CstToken<'s> } } else if let Exp(depth, exp) = value { if depth == 0 { - value = Exp(0, Cst(CstIter(CstConstIter(str_range(source, start, start + length))))); + value = Exp(0, str_range(source, start, start + length)); break } length += 1; value = Exp( if c == ')' { depth-1 } else if c == '(' { depth+1 } else { depth }, - Cst(CstIter(CstConstIter(str_range(source, start, start + length)))) + str_range(source, start, start + length) ); } else if let Num(m) = value { if is_num_end(c) { @@ -382,46 +442,53 @@ pub const fn to_digit (c: char) -> Result { _ => return Err(Unexpected(c)) }) } -/// `State` + [Dsl] -> `Self`. -pub trait FromDsl: Sized { - fn try_from_dsl (state: &State, dsl: &impl Dsl) -> Perhaps; - fn from_dsl (state: &State, dsl: &impl Dsl, err: impl Fn()->Box) -> Usually { - match Self::try_from_dsl(state, dsl)? { Some(dsl) => Ok(dsl), _ => Err(err()) } } -} -/// `self` + `Options` -> [Dsl] -pub trait IntoDsl { /*TODO*/ } -/// `self` + [Dsl] -> `Item` -pub trait DslInto { - fn try_dsl_into (&self, dsl: &impl Dsl) -> Perhaps; - fn dsl_into (&self, dsl: &impl Dsl, err: impl Fn()->Box) -> Usually { - match Self::try_dsl_into(self, dsl)? { Some(dsl) => Ok(dsl), _ => Err(err()) } } -} -/// `self` + `Item` -> [Dsl] -pub trait DslFrom { /*TODO*/ } -/// 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 } - })+ -)* }); +/// `T` + [Dsl] -> `Self`. +pub trait FromDsl: Sized { + fn from_dsl (state: &T, dsl: &impl Dsl) -> Perhaps; + fn from_dsl_or (state: &T, dsl: &impl Dsl, err: Box) -> Usually { + Self::from_dsl(state, dsl)?.ok_or(err) + } + fn from_dsl_or_else (state: &T, dsl: &impl Dsl, err: impl Fn()->Box) -> Usually { + Self::from_dsl(state, dsl)?.ok_or_else(err) + } +} -//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()); } -//} +impl, U> DslInto for U { + fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps { + T::from_dsl(self, dsl) + } +} + +/// `self` + [Dsl] -> `T` +pub trait DslInto { + fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps; + fn dsl_into_or (&self, dsl: &impl Dsl, err: Box) -> Usually { + self.dsl_into(dsl)?.ok_or(err) + } + fn dsl_into_or_else (&self, dsl: &impl Dsl, err: impl Fn()->Box) -> Usually { + self.dsl_into(dsl)?.ok_or_else(err) + } +} + +/// `self` + `T` + -> [Dsl] +pub trait IntoDsl { + fn into_dsl (&self, state: &T) -> Perhaps; + fn into_dsl_or (&self, state: &T, err: Box) -> Usually { + self.into_dsl(state)?.ok_or(err) + } + fn into_dsl_or_else (&self, state: &T, err: impl Fn()->Box) -> Usually { + self.into_dsl(state)?.ok_or_else(err) + } +} + +/// `self` + `T` -> [Dsl] +pub trait DslFrom { + fn dsl_from (&self, dsl: &impl Dsl) -> Perhaps; + fn dsl_from_or (&self, dsl: &impl Dsl, err: Box) -> Usually { + self.dsl_from(dsl)?.ok_or(err) + } + fn dsl_from_or_else (&self, dsl: &impl Dsl, err: impl Fn()->Box) -> Usually { + self.dsl_from(dsl)?.ok_or_else(err) + } +} diff --git a/dsl/src/dsl_test.rs b/dsl/src/dsl_test.rs new file mode 100644 index 0000000..e69de29 diff --git a/dsl/src/test.rs b/dsl/src/test.rs deleted file mode 100644 index dfb37f4..0000000 --- a/dsl/src/test.rs +++ /dev/null @@ -1,104 +0,0 @@ -use crate::*; -use proptest::prelude::*; - -#[test] fn test_iters () { - let mut iter = crate::CstIter::new(&":foo :bar"); - let _ = iter.next(); -} - -#[test] const fn test_const_iters () { - let iter = crate::CstConstIter::new(&":foo :bar"); - let _ = iter.next(); -} - -#[test] fn test_num () { - let _digit = Token::to_digit('0'); - let _digit = Token::to_digit('x'); - let _number = Token::to_number(&"123"); - let _number = Token::to_number(&"12asdf3"); -} - //proptest! { - //#[test] fn proptest_source_iter ( - //source in "\\PC*" - //) { - //let mut iter = crate::CstIter::new(&source); - ////let _ = iter.next() - //} - //#[test] fn proptest_token_iter ( - //source in "\\PC*" - //) { - //let mut iter = crate::TokenIter::new(&source); - ////let _ = iter.next(); - //} - //} - -//#[cfg(test)] mod test_token_prop { - //use crate::{Cst, CstMeta, Value::*}; - //use proptest::prelude::*; - //proptest! { - //#[test] fn test_token_prop ( - //source in "\\PC*", - //start in usize::MIN..usize::MAX, - //length in usize::MIN..usize::MAX, - //) { - //let token = Cst(Nil, CstMeta { source: &source, start, length }); - //let _ = token.slice(); - //} - //} -//} - -#[test] fn test_token () -> Result<(), Box> { - use crate::Val::*; - let source = ":f00"; - let mut token = CstToken::new(source, 0, 1, Sym(":")); - token = token.grow_sym(); - assert_eq!(token, CstToken::new(source, 0, 2, Sym(":f"))); - token = token.grow_sym(); - assert_eq!(token, CstToken::new(source, 0, 3, Sym(":f0"))); - token = token.grow_sym(); - assert_eq!(token, CstToken::new(source, 0, 4, Sym(":f00"))); - - assert_eq!(None, CstIter::new("").next()); - assert_eq!(None, CstIter::new(" \n \r \t ").next()); - - assert_eq!(Err(Unexpected('a')), CstIter::new(" 9a ").next().unwrap().value()); - - assert_eq!(Num(7), CstIter::new("7").next().unwrap().value()); - assert_eq!(Num(100), CstIter::new(" 100 ").next().unwrap().value()); - - assert_eq!(Sym(":123foo"), CstIter::new(" :123foo ").next().unwrap().value()); - assert_eq!(Sym("@bar456"), CstIter::new(" \r\r\n\n@bar456\t\t\t").next().unwrap().value()); - - assert_eq!(Key("foo123"), CstIter::new("foo123").next().unwrap().value()); - assert_eq!(Key("foo/bar"), CstIter::new("foo/bar").next().unwrap().value()); - - //assert_eq!(Str("foo/bar"), CstIter::new("\"foo/bar\"").next().unwrap().value()); - //assert_eq!(Str("foo/bar"), CstIter::new(" \"foo/bar\" ").next().unwrap().value()); - - Ok(()) -} - -//#[cfg(test)] #[test] fn test_examples () -> Result<(), DslErr> { - //// Let's pretend to render some view. - //let source = include_str!("../../tek/src/view_arranger.edn"); - //// The token iterator allows you to get the tokens represented by the source text. - //let mut view = TokenIter(source); - //// The token iterator wraps a const token+source iterator. - //assert_eq!(view.0.0, source); - //let mut expr = view.peek(); - //assert_eq!(view.0.0, source); - //assert_eq!(expr, Some(Token { - //source, start: 0, length: source.len() - 1, value: Exp(0, CstIter::new(&source[1..])) - //})); - ////panic!("{view:?}"); - ////panic!("{:#?}", expr); - ////for example in [ - ////include_str!("../../tui/examples/edn01.edn"), - ////include_str!("../../tui/examples/edn02.edn"), - ////] { - //////let items = Dsl::read_all(example)?; - //////panic!("{layout:?}"); - //////let content = >::from(&layout); - ////} - //Ok(()) -//} diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs index 5032319..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 @@ -7,210 +11,101 @@ 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 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>>; +/// that .event()binding's value is returned. +#[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: Ast) -> 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/output/src/ops.rs b/output/src/ops.rs index 6b6f401..846c86c 100644 --- a/output/src/ops.rs +++ b/output/src/ops.rs @@ -397,7 +397,7 @@ transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{ $op:literal $(/)? [$head: ident, $tail: ident] $expr:expr ) => { impl FromDsl for $Struct$(<$($A),+>)? { - fn try_from_dsl ( + fn from_dsl ( _state: &S, _dsl: &impl Dsl ) -> Perhaps { todo!() @@ -412,7 +412,7 @@ transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{ $op:literal $(/)? [$head: ident, $tail: ident] $expr:expr ) => { impl FromDsl for $Struct$(<$($A),+>)? { - fn try_from_dsl ( + fn from_dsl ( _state: &S, _dsl: &impl Dsl ) -> Perhaps { todo!() diff --git a/proc/src/proc_command.rs b/proc/src/proc_command.rs index 36b25d5..d0db6e3 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -150,10 +150,9 @@ impl ToTokens for CommandDef { } /// Generated by [tengri_proc::command]. impl ::tengri::dsl::FromDsl<#state> for #command_enum { - fn try_from_dsl ( - state: &#state, - value: &impl ::tengri::dsl::Dsl - ) -> Perhaps { + fn from_dsl (state: &#state, value: &impl ::tengri::dsl::Dsl) + -> Perhaps + { use ::tengri::dsl::Val::*; todo!()//Ok(match token { #(#matchers)* _ => None }) } diff --git a/proc/src/proc_expose.rs b/proc/src/proc_expose.rs index 3514696..cdfb2af 100644 --- a/proc/src/proc_expose.rs +++ b/proc/src/proc_expose.rs @@ -80,8 +80,8 @@ impl ExposeImpl { write_quote_to(out, quote! { /// Generated by [tengri_proc::expose]. impl ::tengri::dsl::FromDsl<#state> for #t { - fn try_from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { - Ok(Some(match dsl.dsl() { + fn from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { + Ok(Some(match dsl.val() { #arms _ => { return Ok(None) } })) diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 9edf714..312bdd9 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -63,7 +63,7 @@ impl ViewDef { impl<'state> ::tengri::dsl::DslInto< Box + 'state> > for #self_ty { - fn try_dsl_into (&self, dsl: &impl ::tengri::dsl::Dsl) + fn dsl_into (&self, dsl: &impl ::tengri::dsl::Dsl) -> Perhaps + 'state>> { Ok(match dsl.val() { #builtins #exposed _ => return Ok(None) }) 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 { diff --git a/tui/src/tui_engine/tui_input.rs b/tui/src/tui_engine/tui_input.rs index b58c594..1d89f97 100644 --- a/tui/src/tui_engine/tui_input.rs +++ b/tui/src/tui_engine/tui_input.rs @@ -1,7 +1,7 @@ use crate::*; use std::time::Duration; use std::thread::{spawn, JoinHandle}; -use crossterm::event::{poll, read}; +use crossterm::event::{Event, poll, read}; #[derive(Debug, Clone)] pub struct TuiIn { @@ -18,14 +18,14 @@ impl Input for TuiIn { fn done (&self) { self.exited.store(true, Relaxed); } } #[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] -pub struct TuiEvent(crossterm::event::Event); +pub struct TuiEvent(Event); impl Ord for TuiEvent { fn cmp (&self, other: &Self) -> std::cmp::Ordering { self.partial_cmp(other) .unwrap_or_else(||format!("{:?}", self).cmp(&format!("{other:?}"))) // FIXME perf } } -impl From for TuiEvent { - fn from (event: crossterm::event::Event) -> Self { +impl From for TuiEvent { + fn from (event: Event) -> Self { Self(event) } } @@ -50,7 +50,7 @@ impl TuiIn { if poll(timer).is_ok() { let event = read().unwrap(); match event { - crossterm::event::Event::Key(KeyEvent { + Event::Key(KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, kind: KeyEventKind::Press,