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..f57c8ce 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -1,529 +1,58 @@ -#![feature(adt_const_params)] -#![feature(type_alias_impl_trait)] +//#![feature(adt_const_params)] +//#![feature(type_alias_impl_trait)] #![feature(impl_trait_in_fn_trait_return)] #![feature(const_precise_live_drops)] extern crate const_panic; -use const_panic::{concat_panic, PanicFmt}; +use const_panic::PanicFmt; +use std::fmt::Debug; pub(crate) use ::tengri_core::*; -use std::ops::Deref; pub(crate) use std::error::Error; -pub(crate) use std::fmt::Debug; pub(crate) use std::sync::Arc; -pub(crate) use std::collections::VecDeque; -pub(crate) use konst::iter::{ConstIntoIter, IsIteratorKind}; -pub(crate) use konst::string::{split_at, str_range, char_indices}; +pub(crate) use konst::string::{str_range, char_indices}; pub(crate) use thiserror::Error; pub(crate) use self::DslError::*; + +mod dsl_conv; pub use self::dsl_conv::*; +mod dsl_types; pub use self::dsl_types::*; #[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) => $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 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()) - } +pub trait Dsl: Debug + Send + Sync + Sized { + fn src (&self) -> &str; } impl<'s> Dsl for &'s str { - type Str = &'s str; - type Exp = &'s str; - fn val (&self) -> Val { Val::Exp(0, self) } + fn src (&self) -> &str { 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 std::sync::Arc { + fn src (&self) -> &str { self.as_ref() } } -impl Dsl for Ast { - type Str = Arc; - type Exp = Ast; - fn val (&self) -> Val { Val::Exp(0, self.clone()) } +impl Dsl for Option { + fn src (&self) -> &str { if let Some(dsl) = self { dsl.src() } else { "" } } } -impl Dsl for Token { - type Str = Str; - type Exp = Exp; - fn val (&self) -> Val { self.value.clone() } +impl Dsl for &D { + fn src (&self) -> &str { (*self).src() } } -impl Dsl for Val { - type Str = Str; - type Exp = Exp; - fn val (&self) -> Val { self.clone() } +impl Dsl for &mut D { + fn src (&self) -> &str { (**self).src() } } -/// 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)] -pub enum Val { - /// Empty expression - #[default] Nil, - /// Unsigned integer literal - Num(usize), - /// An identifier that starts with `.` - Sym(Str), - /// An identifier that doesn't start with `:` - Key(Str), - /// A quoted string literal - Str(Str), - /// A DSL expression. - Exp( - /// Number of unclosed parentheses. Must be 0 to be valid. - isize, - /// Expression content. - Exp - ), - /// An error. - 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 { - 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::Error(e) => Val::Error(*e) - } - } -} -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}} - pub const fn sym (&self) -> Option<&Str> {match self{Val::Sym(s)=>Some(s), _=>None}} - 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}} -} -/// Parsed substring with range and value. -#[derive(Debug, Clone, Default, PartialEq)] -pub struct Token { - /// Meaning of token. - pub value: Val, - /// Reference to source text. - pub source: Str, - /// Index of 1st character of span. - pub start: usize, - /// 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) } - pub const fn value (&self) - -> &Val { &self.value } - pub const fn err (&self) - -> Option { if let Val::Error(e) = self.value { Some(e) } else { None } } - pub const fn new (source: Str, start: usize, length: usize, value: Val) - -> Self { Self { value, start, length, source } } - pub const fn copy (&self) -> Self where Val: Copy, Str: Copy, Exp: Copy { - 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>), -} -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()) } - pub const fn slice_exp (&self) -> &str { - str_range(self.source, self.start.saturating_add(1), self.end()) } - pub const fn grow (&mut self) -> &mut Self { - let max_length = self.source.len().saturating_sub(self.start); - self.length = self.length + 1; - if self.length > max_length { self.length = max_length } - self - } - pub const fn grow_exp (&'s mut self, depth: isize, source: &'s str) -> &mut Self { - self.value = Val::Exp(depth, 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()) - //} -//} +/// DSL-specific result type. +pub type DslResult = Result; + +/// DSL-specific optional result type. +pub type DslPerhaps = Result, DslError>; + /// DSL-specific error codes. -#[derive(Error, Debug, Copy, Clone, PartialEq, PanicFmt)] pub enum DslError { - #[error("parse failed: not implemented")] - Unimplemented, - #[error("parse failed: empty")] - Empty, - #[error("parse failed: incomplete")] - Incomplete, - #[error("parse failed: unexpected character '{0}'")] - Unexpected(char), - #[error("parse failed: error #{0}")] - Code(u8), - #[error("end reached")] - End -} -/// Provides native [Iterator] API over [CstConstIter], emitting [Cst] items. -/// -/// [Cst::next] returns just the [Cst] and mutates `self`, -/// instead of returning an updated version of the struct as [CstConstIter::next] does. -#[derive(Copy, Clone, Debug, Default, PartialEq)] -pub struct CstIter<'s>(pub CstConstIter<'s>); -impl<'s> CstIter<'s> { - pub const fn new (source: &'s str) -> Self { Self(CstConstIter::new(source)) } -} -impl<'s> Iterator for CstIter<'s> { - type Item = CstToken<'s>; - fn next (&mut self) -> Option { - match self.0.advance() { - Some((item, rest)) => { self.0 = rest; Some(item.into()) }, - None => None, - } - } -} -/// Holds a reference to the source text. -/// [CstConstIter::next] emits subsequent pairs of: -/// * a [Cst] and -/// * the source text remaining -/// * [ ] TODO: maybe [CstConstIter::next] should wrap the remaining source in `Self` ? -#[derive(Copy, Clone, Debug, Default, PartialEq)] -pub struct CstConstIter<'s>(pub &'s str); -impl<'s> From <&'s str> for CstConstIter<'s> { - fn from (src: &'s str) -> Self { Self(src) } -} -impl<'s> Iterator for CstConstIter<'s> { - type Item = CstToken<'s>; - fn next (&mut self) -> Option> { self.advance().map(|x|x.0) } -} -impl<'s> ConstIntoIter for CstConstIter<'s> { - type Kind = IsIteratorKind; - type Item = Cst<'s>; - type IntoIter = Self; -} -impl<'s> CstConstIter<'s> { - pub const fn new (source: &'s str) -> Self { Self(source) } - pub const fn chomp (&self, index: usize) -> Self { Self(split_at(self.0, index).1) } - pub const fn advance (&mut self) -> Option<(CstToken<'s>, Self)> { - match peek(Val::Nil, self.0) { - Token { value: Val::Nil, .. } => None, - token => { - let end = self.chomp(token.end()); - Some((token.copy(), end)) - }, - } - } -} - -pub const fn peek <'s> (mut value: CstVal<'s>, source: &'s str) -> CstToken<'s> { - use Val::*; - let mut start = 0; - let mut length = 0; - let mut source = source; - loop { - if let Some(((i, c), next)) = char_indices(source).next() { - if matches!(value, Error(_)) { - break - } else if matches!(value, Nil) { - if is_whitespace(c) { - length += 1; - continue - } - 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 } - } else if matches!(value, Str(_)) { - if is_str_end(c) { - break - } else { - value = Str(str_range(source, start, start + length + 1)); - } - } else if matches!(value, Sym(_)) { - if is_sym_end(c) { - break - } else if is_sym_char(c) { - value = Sym(str_range(source, start, start + length + 1)); - } else { - value = Error(Unexpected(c)); - } - } else if matches!(value, Key(_)) { - if is_key_end(c) { - break - } - length += 1; - if is_key_char(c) { - value = Key(str_range(source, start, start + length + 1)); - } else { - value = Error(Unexpected(c)); - } - } else if let Exp(depth, exp) = value { - if depth == 0 { - 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 }, - str_range(source, start, start + length) - ); - } else if let Num(m) = value { - if is_num_end(c) { - break - } - length += 1; - match to_digit(c) { - Ok(n) => { value = Num(n+10*m); }, - Err(e) => { value = Error(e); } - } - } - } else { - break - } - } - return Token { value, source, start, length } -} -const fn is_whitespace (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t') } -const fn is_digit (c: char) -> bool { matches!(c, '0'..='9') } -const fn is_num_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } -const fn is_key_start (c: char) -> bool { matches!(c, '/'|'a'..='z') } -const fn is_key_char (c: char) -> bool { matches!(c, 'a'..='z'|'0'..='9'|'-'|'/') } -const fn is_key_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } -const fn is_sym_start (c: char) -> bool { matches!(c, ':'|'@') } -const fn is_sym_char (c: char) -> bool { matches!(c, 'a'..='z'|'A'..='Z'|'0'..='9'|'-') } -const fn is_sym_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } -const fn is_str_start (c: char) -> bool { matches!(c, '"') } -const fn is_str_end (c: char) -> bool { matches!(c, '"') } -const fn is_exp_start (c: char) -> bool { matches!(c, '(') } -pub const fn to_number (digits: &str) -> Result { - let mut iter = char_indices(digits); - let mut value = 0; - while let Some(((_, c), next)) = iter.next() { - match to_digit(c) { - Ok(digit) => value = 10 * value + digit, - Err(e) => return Err(e), - } - iter = next; - } - Ok(value) -} -pub const fn to_digit (c: char) -> Result { - Ok(match c { - '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, - '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, - _ => return Err(Unexpected(c)) - }) -} - -/// 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; - 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) - } -} - -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) - } +#[derive(Error, Debug, Copy, Clone, PartialEq, PanicFmt)] +pub enum DslError { + #[error("parse failed: not implemented")] Unimplemented, + #[error("parse failed: empty")] Empty, + #[error("parse failed: incomplete")] Incomplete, + #[error("parse failed: unexpected character '{0}'")] Unexpected(char), + #[error("parse failed: error #{0}")] Code(u8), + #[error("end reached")] End } diff --git a/dsl/src/dsl_conv.rs b/dsl/src/dsl_conv.rs new file mode 100644 index 0000000..65f05d6 --- /dev/null +++ b/dsl/src/dsl_conv.rs @@ -0,0 +1,51 @@ +use crate::*; + +/// `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) + } +} + +impl<'s, T: FromDsl, U> DslInto<'s, T> for U { + fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps { + T::from_dsl(self, dsl) + } +} + +/// `self` + [Dsl] -> `T` +pub trait DslInto<'s, T> { + fn dsl_into (&'s self, dsl: &impl Dsl) -> Perhaps; + fn dsl_into_or (&'s self, dsl: &impl Dsl, err: Box) -> Usually { + self.dsl_into(dsl)?.ok_or(err) + } + fn dsl_into_or_else (&'s 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_types.rs b/dsl/src/dsl_types.rs new file mode 100644 index 0000000..0a5402d --- /dev/null +++ b/dsl/src/dsl_types.rs @@ -0,0 +1,176 @@ +use crate::*; + +macro_rules! iter_chars(($source:expr => |$i:ident, $c:ident|$val:expr)=>{ + while let Some((($i, $c), next)) = char_indices($source).next() { + $source = next.as_str(); $val } }); + +macro_rules! def_peek_seek(($peek:ident, $seek:ident, $seek_start:ident, $seek_length:ident)=>{ + /// Find a slice corrensponding to a syntax token. + const fn $peek (source: &str) -> DslPerhaps<&str> { + match $seek(source) { + Ok(Some((start, length))) => Ok(Some(str_range(source, start, start + length))), + Ok(None) => Ok(None), + Err(e) => Err(e) } } + /// Find a start and length corresponding to a syntax token. + const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> { + match $seek_start(source) { + Ok(Some(start)) => match $seek_length(str_range(source, start, source.len() - start)) { + Ok(Some(length)) => Ok(Some((start, length))), + Ok(None) => Ok(None), + Err(e) => Err(e), + }, + Ok(None) => Ok(None), + Err(e) => Err(e) } } }); + +const fn is_whitespace (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t') } + +pub trait DslExp: Dsl { + fn exp (&self) -> DslPerhaps { + todo!(); + Ok(Some(self)) + } + fn exp_head (&self) -> DslPerhaps { + todo!(); + Ok(Some(self)) + } + fn exp_tail (&self) -> DslPerhaps { + todo!(); + Ok(Some(self)) + } + fn exp_next (&mut self) -> DslPerhaps { + todo!(); + Ok(Some(self)) + } +} +impl DslExp for D {} +def_peek_seek!(exp_peek, exp_seek, exp_seek_start, exp_seek_length); +const fn is_exp_start (c: char) -> bool { matches!(c, '(') } +const fn is_exp_end (c: char) -> bool { matches!(c, ')') } +const fn exp_seek_start (mut source: &str) -> DslPerhaps { + iter_chars!(source => |i, c| if is_exp_start(c) { + return Ok(Some(i)) + } else if !is_whitespace(c) { + return Err(Unexpected(c)) + }); + Ok(None) +} +const fn exp_seek_length (mut source: &str) -> DslPerhaps { + let mut depth = 0; + iter_chars!(source => |i, c| if is_exp_start(c) { + depth += 1; + } else if is_exp_end(c) { + if depth == 0 { + return Err(Unexpected(c)) + } else if depth == 1 { + return Ok(Some(i)) + } else { + depth -= 1; + } + }); + Ok(None) +} + +pub trait DslSym: Dsl { fn sym (&self) -> DslPerhaps<&str> { sym_peek(self.src()) } } +impl DslSym for D {} +def_peek_seek!(sym_peek, sym_seek, sym_seek_start, sym_seek_length); +const fn is_sym_start (c: char) -> bool { matches!(c, ':'|'@') } +const fn is_sym_char (c: char) -> bool { matches!(c, 'a'..='z'|'A'..='Z'|'0'..='9'|'-') } +const fn is_sym_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } +const fn sym_seek_start (mut source: &str) -> DslPerhaps { + iter_chars!(source => |i, c| if is_sym_start(c) { + return Ok(Some(i)) + } else if !is_whitespace(c) { + return Err(Unexpected(c)) + }); + Ok(None) +} +const fn sym_seek_length (mut source: &str) -> DslPerhaps { + iter_chars!(source => |i, c| if is_sym_end(c) { + return Ok(Some(i)) + } else if !is_sym_char(c) { + return Err(Unexpected(c)) + }); + Ok(None) +} + +pub trait DslKey: Dsl { fn key (&self) -> DslPerhaps<&str> { key_peek(self.src()) } } +impl DslKey for D {} +def_peek_seek!(key_peek, key_seek, key_seek_start, key_seek_length); +const fn is_key_start (c: char) -> bool { matches!(c, '/'|'a'..='z') } +const fn is_key_char (c: char) -> bool { matches!(c, 'a'..='z'|'0'..='9'|'-'|'/') } +const fn is_key_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } +const fn key_seek_start (mut source: &str) -> DslPerhaps { + iter_chars!(source => |i, c| if is_key_start(c) { + return Ok(Some(i)) + } else if !is_whitespace(c) { + return Err(Unexpected(c)) + }); + Ok(None) +} +const fn key_seek_length (mut source: &str) -> DslPerhaps { + iter_chars!(source => |i, c| if is_key_end(c) { + return Ok(Some(i)) + } else if !is_key_char(c) { + return Err(Unexpected(c)) + }); + Ok(None) +} + +pub trait DslText: Dsl { fn text (&self) -> DslPerhaps<&str> { text_peek(self.src()) } } +impl DslText for D {} +def_peek_seek!(text_peek, text_seek, text_seek_start, text_seek_length); +const fn is_text_start (c: char) -> bool { matches!(c, '"') } +const fn is_text_end (c: char) -> bool { matches!(c, '"') } +const fn text_seek_start (mut source: &str) -> DslPerhaps { + iter_chars!(source => |i, c| if is_text_start(c) { + return Ok(Some(i)) + } else if !is_whitespace(c) { + return Err(Unexpected(c)) + }); + Ok(None) +} +const fn text_seek_length (mut source: &str) -> DslPerhaps { + iter_chars!(source => |i, c| if is_text_end(c) { return Ok(Some(i)) }); + Ok(None) +} + +pub trait DslNum: Dsl { fn num (&self) -> DslPerhaps<&str> { num_peek(self.src()) } } +impl DslNum for D {} +def_peek_seek!(num_peek, num_seek, num_seek_start, num_seek_length); +const fn num_seek_start (mut source: &str) -> DslPerhaps { + iter_chars!(source => |i, c| if is_digit(c) { + return Ok(Some(i)); + } else if !is_whitespace(c) { + return Err(Unexpected(c)) + }); + Ok(None) +} +const fn num_seek_length (mut source: &str) -> DslPerhaps { + iter_chars!(source => |i, c| if is_num_end(c) { + return Ok(Some(i)) + } else if !is_digit(c) { + return Err(Unexpected(c)) + }); + Ok(None) +} +const fn is_digit (c: char) -> bool { matches!(c, '0'..='9') } +const fn is_num_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } +pub const fn to_number (digits: &str) -> Result { + let mut iter = char_indices(digits); + let mut value = 0; + while let Some(((_, c), next)) = iter.next() { + match to_digit(c) { + Ok(digit) => value = 10 * value + digit, + Err(e) => return Err(e), + } + iter = next; + } + Ok(value) +} +pub const fn to_digit (c: char) -> Result { + Ok(match c { + '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, + '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, + _ => return Err(Unexpected(c)) + }) +} diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs index 5403b66..0bec9a8 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,90 @@ 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>>; -/// An input binding. -#[derive(Debug, Default)] pub struct Binding { - condition: Option, - command: D, - description: Option>, - source: Option>, -} -impl Default for EventMap { - fn default () -> Self { - Self(Default::default()) +#[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() } -} -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 }); + /// 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(source.as_ref()) + } + /// Create event map from DSL tokenizer. + pub fn from_dsl (mut dsl: impl Dsl) -> Usually where E: From> { + let mut map: Self = Default::default(); + while let Some(dsl) = dsl.exp_next()? { + if let Some(path) = dsl.text()? { + map.0.extend(Self::from_path(PathBuf::from(path.as_ref() as &str))?.0) + } else if dsl.exp_head()?.key()? == Some("if") { + todo!() + //map.add(sym.into(), Binding::from_dsl(dsl.exp_tail())?); + } else if let Some(sym) = dsl.exp_head()?.sym()? { + todo!() + //map.add(sym.into(), Binding::from_dsl(dsl.exp_tail())?); + } + } + Ok(map) + } } - - - - //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) - //} -//} +/// An input binding. +#[derive(Debug)] +pub struct Binding { + pub command: C, + pub condition: Option, + pub description: Option>, + pub 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()) + } + } +} +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/proc/src/proc_command.rs b/proc/src/proc_command.rs index d0db6e3..48ae0fe 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -153,7 +153,6 @@ impl ToTokens for CommandDef { 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 cdfb2af..23bf062 100644 --- a/proc/src/proc_expose.rs +++ b/proc/src/proc_expose.rs @@ -67,63 +67,104 @@ impl ToTokens for ExposeImpl { } } +fn is_num (t: &impl AsRef) -> bool { + return matches!(t.as_ref(), + | "u8" | "u16" | "u32" | "u64" | "usize" + | "i8" | "i16" | "i32" | "i64" | "isize") } + impl ExposeImpl { fn expose_variants ( &self, out: &mut TokenStream2, t: &ExposeType, variants: &BTreeMap ) { let Self(ItemImpl { self_ty: state, .. }, ..) = self; - let arms = variants.iter().map(|(key, value)|{ + let type_is = format!("{}", quote! { #t }); + let variants = variants.iter().map(|(key, value)|{ let key = LitStr::new(&key, Span::call_site()); - quote! { #key => state.#value(), } + quote! { Some(#key) => Ok(Some(state.#value())), } }); - let arms = Self::with_predefined(t, quote! { #(#arms)* }); - 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() { - #arms - _ => { return Ok(None) } - })) + write_quote_to(out, if &type_is == "bool" { + quote! { + /// Generated by [tengri_proc::expose]. + impl ::tengri::dsl::FromDsl<#state> for #t { + fn from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { + match dsl.key()? { + Some("true") => Ok(Some(true)), + Some("false") => Ok(Some(false)), + _ => match dsl.sym()? { #(#variants)* _ => Ok(None) } + } + } } } - }); - } - fn with_predefined (t: &ExposeType, variants: impl ToTokens) -> impl ToTokens { - let formatted_type = format!("{}", quote! { #t }); - if &formatted_type == "bool" { - return quote! { - ::tengri::dsl::Val::Sym(s) => match s.as_ref() { - ":true" => true, - ":false" => false, - #variants - _ => { return Ok(None) } - }, + } else if is_num(&type_is) { + quote! { + /// Generated by [tengri_proc::expose]. + impl ::tengri::dsl::FromDsl<#state> for #t { + fn from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { + match dsl.num()? { + Some(n) => Ok(Some(n.parse::<#t>()?)), + _ => match dsl.sym()? { #(#variants)* _ => Ok(None) } + } + } + } } - } - if matches!(formatted_type.as_str(), - "u8" | "u16" | "u32" | "u64" | "usize" | - "i8" | "i16" | "i32" | "i64" | "isize") - { - let num_err = LitStr::new( - &format!("{{n}}: failed to convert to {formatted_type}"), - Span::call_site() - ); - return quote! { - ::tengri::dsl::Val::Num(n) => TryInto::<#t>::try_into(n) - .unwrap_or_else(|_|panic!(#num_err)), - ::tengri::dsl::Val::Sym(s) => match s.as_ref() { - #variants - _ => { return Ok(None) } - }, + } else { + quote! { + /// Generated by [tengri_proc::expose]. + impl ::tengri::dsl::FromDsl<#state> for #t { + fn from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { + match dsl.sym()? { #(#variants)* _ => Ok(None) } + } + } } - } - return quote! { - ::tengri::dsl::Val::Sym(s) => match s.as_ref() { - #variants - _ => { return Ok(None) } - }, - } + }) + //let arms = variants.iter().map(|(key, value)|{ + //let key = LitStr::new(&key, Span::call_site()); + //quote! { #key => state.#value(), } + //}); + //if &type_is == "bool" { + //return quote! { + //::tengri::dsl::Val::Sym(s) => match s.as_ref() { + //":true" => true, + //":false" => false, + //#variants + //_ => { return Ok(None) } + //}, + //} + //} + //if matches!(type_is.as_str(), + //"u8" | "u16" | "u32" | "u64" | "usize" | + //"i8" | "i16" | "i32" | "i64" | "isize") + //{ + //let num_err = LitStr::new( + //&format!("{{n}}: failed to convert to {type_is}"), + //Span::call_site() + //); + //return quote! { + //::tengri::dsl::Val::Num(n) => TryInto::<#t>::try_into(n) + //.unwrap_or_else(|_|panic!(#num_err)), + //::tengri::dsl::Val::Sym(s) => match s.as_ref() { + //#variants + //_ => { return Ok(None) } + //}, + //} + //} + //let arms = quote! { + //::tengri::dsl::Val::Sym(s) => match s.as_ref() { + //#variants + //_ => { return Ok(None) } + //}, + //}; + //let builtins = + //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 { + //$builtins + //$defined + //Ok(None) + //} + //} + //}); } } diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 312bdd9..61cbe6e 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -60,10 +60,10 @@ impl ViewDef { /// Makes [#self_ty] able to construct the [Render]able /// which might correspond to a given [TokenStream], /// while taking [#self_ty]'s state into consideration. - impl<'state> ::tengri::dsl::DslInto< + impl<'state> ::tengri::dsl::DslInto<'state, Box + 'state> > for #self_ty { - fn dsl_into (&self, dsl: &impl ::tengri::dsl::Dsl) + fn dsl_into (&'state self, dsl: &impl ::tengri::dsl::Dsl) -> Perhaps + 'state>> { Ok(match dsl.val() { #builtins #exposed _ => return Ok(None) }) @@ -77,8 +77,11 @@ impl ViewDef { let Self(ViewMeta { output }, ViewImpl { .. }) = self; let builtins = builtins_with_boxes_output(quote! { #output }).map(|builtin|quote! { ::tengri::dsl::Val::Exp(_, expr) => return Ok(Some( - #builtin::from_dsl(self, expr, ||Box::new("failed to load builtin".into()))? - .boxed() + #builtin::from_dsl_or_else( + self, + &expr, + || { format!("failed to load builtin").into() } + )?.boxed() )), }); quote! { #(#builtins)* } @@ -89,7 +92,7 @@ impl ViewDef { let exposed = exposed.iter().map(|(key, value)|write_quote(quote! { #key => return Ok(Some(self.#value().boxed())), })); - quote! { ::tengri::dsl::Val::Sym(key) => match key.as_ref() { #(#exposed)* } } + quote! { ::tengri::dsl::Val::Sym(key) => match key.as_ref() { #(#exposed)* _ => panic!() } } } } 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 {