diff --git a/core/src/lib.rs b/core/src/lib.rs index b06b55e..cc04597 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,38 +1,48 @@ pub(crate) use std::error::Error; + /// Standard result type. pub type Usually = Result>; + /// Standard optional result type. pub type Perhaps = Result, Box>; -/// 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( + +/// Implement the `From` trait. +#[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 }}}; - ($($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( + 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 { ($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]. @@ -47,3 +57,15 @@ 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 c61a057..256b599 100644 --- a/dsl/Cargo.toml +++ b/dsl/Cargo.toml @@ -4,9 +4,6 @@ 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/dsl_test.rs b/dsl/src/dsl_test.rs deleted file mode 100644 index e69de29..0000000 diff --git a/dsl/src/dsl.rs b/dsl/src/lib.rs similarity index 58% rename from dsl/src/dsl.rs rename to dsl/src/lib.rs index 37b8d9a..36fc266 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/lib.rs @@ -14,72 +14,7 @@ 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 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 {} +#[cfg(test)] mod test; /// Enumeration of values that may figure in an expression. /// Generic over string and expression storage. #[derive(Clone, Debug, PartialEq, Default)] @@ -105,20 +40,34 @@ pub enum Val { Error(DslError), } impl Copy for Val {} -impl Val { - pub fn convert_to (&self, to_str: impl Fn(&S1)->S2, to_exp: impl Fn(&E1)->E2) -> Val { +impl Val { + pub fn convert (&self) -> Val where + T::Str: for<'a> From<&'a Str>, + T::Exp: for<'a> From<&'a Exp> + { match self { Val::Nil => Val::Nil, Val::Num(u) => Val::Num(*u), - 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::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) } } } -impl> Val { +/// 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 { 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}} @@ -126,7 +75,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 depth (&self) -> Option {match self{Val::Exp(d, _)=>Some(*d), _=>None}} + pub const fn exp_depth (&self) -> Option {match self{Val::Exp(d, _)=>Some(*d), _=>None}} } /// Parsed substring with range and value. #[derive(Debug, Clone, Default, PartialEq)] @@ -140,9 +89,7 @@ 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) } @@ -156,85 +103,83 @@ impl Token { Self { value: self.value, ..*self } } } -/// 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, PartialEq, Default)] -pub enum Cst<'s> { - #[default] __, - Source(&'s str), - Token(CstToken<'s>), - Val(CstVal<'s>), - Iter(CstIter<'s>), - ConstIter(CstConstIter<'s>), +/// 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() } } -impl<'s> From<&'s str> for Cst<'s> { - fn from (src: &'s str) -> Self { - Cst::Source(src) - } +/// 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!() } } -impl<'s> From> for Cst<'s> { - fn from (token: CstToken<'s>) -> Self { - Cst::Token(token) - } +/// The most basic implementor of the [Dsl] trait. +impl Dsl for Val { + type Str = Str; type Exp = Exp; + fn dsl (&self) -> Val { self.clone() } } -impl<'s> From> for Cst<'s> { - fn from (val: CstVal<'s>) -> Self { - Cst::Val(val) - } +/// 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> 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<&'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 { - 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!() - } + let mut tokens: VecDeque<_> = Default::default(); + Self(tokens.into()) } } -/// 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>; +/// 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>>; impl<'s> CstToken<'s> { pub const fn slice (&self) -> &str { str_range(self.source, self.start, self.end()) } @@ -247,32 +192,19 @@ 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, source); + self.value = Val::Exp(depth, Cst(CstIter(CstConstIter(source)))); self } } -//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()) - //} -//} +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))) + } +} /// DSL-specific error codes. #[derive(Error, Debug, Copy, Clone, PartialEq, PanicFmt)] pub enum DslError { #[error("parse failed: not implemented")] @@ -355,12 +287,20 @@ 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)) } - else if is_digit(c) { match to_digit(c) { Ok(c) => Num(c), Err(e) => Error(e) } } - else { value = Error(Unexpected(c)); break } + 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 + } } else if matches!(value, Str(_)) { if is_str_end(c) { break @@ -387,13 +327,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, str_range(source, start, start + length)); + value = Exp(0, Cst(CstIter(CstConstIter(str_range(source, start, start + length))))); break } length += 1; value = Exp( if c == ')' { depth-1 } else if c == '(' { depth+1 } else { depth }, - str_range(source, start, start + length) + Cst(CstIter(CstConstIter(str_range(source, start, start + length)))) ); } else if let Num(m) = value { if is_num_end(c) { @@ -442,53 +382,46 @@ pub const fn to_digit (c: char) -> Result { _ => return Err(Unexpected(c)) }) } - -/// `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) - } +/// `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()) } } } - -impl, U> DslInto for U { - fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps { - T::from_dsl(self, dsl) - } +/// `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*/ } -/// `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) - } -} +/// 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 } + })+ +)* }); -/// `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) - } -} +//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()); } +//} diff --git a/dsl/src/test.rs b/dsl/src/test.rs new file mode 100644 index 0000000..dfb37f4 --- /dev/null +++ b/dsl/src/test.rs @@ -0,0 +1,104 @@ +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 2780474..5032319 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -1,8 +1,4 @@ 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 @@ -11,101 +7,210 @@ type EventMapImpl = BTreeMap>>; /// /// 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(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) - } -} +/// 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>>; /// An input binding. -#[derive(Debug)] -pub struct Binding { - command: C, - condition: Option, +#[derive(Debug, Default)] pub struct Binding { + condition: Option, + command: D, description: Option>, source: Option>, } -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 Default for EventMap { + fn default () -> Self { + Self(Default::default()) } } -pub struct Condition(Boxbool + Send + Sync>); -impl_debug!(Condition |self, w| { write!(w, "*") }); +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) + //} +//} fn unquote (x: &str) -> &str { let mut chars = x.chars(); diff --git a/output/src/ops.rs b/output/src/ops.rs index 846c86c..6b6f401 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 from_dsl ( + fn try_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 from_dsl ( + fn try_from_dsl ( _state: &S, _dsl: &impl Dsl ) -> Perhaps { todo!() diff --git a/proc/src/proc_command.rs b/proc/src/proc_command.rs index d0db6e3..36b25d5 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -150,9 +150,10 @@ impl ToTokens for CommandDef { } /// Generated by [tengri_proc::command]. impl ::tengri::dsl::FromDsl<#state> for #command_enum { - fn from_dsl (state: &#state, value: &impl ::tengri::dsl::Dsl) - -> Perhaps - { + fn try_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 cdfb2af..3514696 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 from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { - Ok(Some(match dsl.val() { + fn try_from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { + Ok(Some(match dsl.dsl() { #arms _ => { return Ok(None) } })) diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 312bdd9..9edf714 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 dsl_into (&self, dsl: &impl ::tengri::dsl::Dsl) + fn try_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 6e3acfd..7fc44b9 100644 --- a/tui/src/tui_engine/tui_buffer.rs +++ b/tui/src/tui_engine/tui_buffer.rs @@ -20,9 +20,11 @@ pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut C pub content: Vec } -impl_debug!(BigBuffer |self, f| { - write!(f, "[BB {}x{} ({})]", self.width, self.height, self.content.len()) -}); +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 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 1d89f97..b58c594 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::{Event, poll, read}; +use crossterm::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(Event); +pub struct TuiEvent(crossterm::event::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: Event) -> Self { +impl From for TuiEvent { + fn from (event: crossterm::event::Event) -> Self { Self(event) } } @@ -50,7 +50,7 @@ impl TuiIn { if poll(timer).is_ok() { let event = read().unwrap(); match event { - Event::Key(KeyEvent { + crossterm::event::Event::Key(KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, kind: KeyEventKind::Press,