diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index 4372ff4..3859210 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -15,8 +15,8 @@ pub(crate) use ::{ tengri_core::* }; pub(crate) use self::DslError::*; -mod dsl_conv; pub use self::dsl_conv::*; #[cfg(test)] mod dsl_test; +mod dsl_macros; // Trait that designates any string-like as potentially parsable DSL. flex_trait!(Dsl: Debug + Send + Sync + Sized { fn src (&self) -> DslPerhaps<&str> { unreachable!("Dsl::src default impl") } @@ -148,54 +148,6 @@ pub const fn to_digit (c: char) -> Result { _ => return Err(Unexpected(c, None, Some("parse digit"))) }) } -#[macro_export] macro_rules! dsl_type (($T:ident { $($trait:tt)* } { - pub const fn $peek:ident $($_1:tt)?; - pub const fn $peek_only:ident $($_2:tt)?; - pub const fn $seek:ident $($_3:tt)?; - pub const fn $seek_start:ident ($source1:ident) $body1:block - pub const fn $seek_length:ident ($source2:ident) $body2:block -})=>{ - pub trait $T: Dsl { $($trait)* } - impl $T for D {} - pub const fn $seek_start ($source1: &str) -> DslPerhaps $body1 - pub const fn $seek_length ($source2: &str) -> DslPerhaps $body2 - /// Find a start and length corresponding to a syntax token. - pub const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> { - match $seek_start(source) { - Err(e) => Err(e), - Ok(None) => Ok(None), - Ok(Some(start)) => match $seek_length(str_from(source, start)) { - Ok(Some(length)) => Ok(Some((start, length))), - Ok(None) => Ok(None), - Err(e) => Err(e), - }, - } - } - /// Find a slice corrensponding to a syntax token. - pub const fn $peek (source: &str) -> DslPerhaps<&str> { - match $seek(source) { - Err(e) => Err(e), - Ok(None) => Ok(None), - Ok(Some((start, length))) => Ok(Some(str_range(source, start, start + length))), - } - } - /// Find a slice corrensponding to a syntax token - /// but return an error if it isn't the only thing - /// in the source. - pub const fn $peek_only (source: &str) -> DslPerhaps<&str> { - match $seek(source) { - Err(e) => Err(e), - Ok(None) => Ok(None), - Ok(Some((start, length))) => { - if let Err(e) = no_trailing_non_space(source, start + length, Some("peek_only")) { - Err(e) - } else { - Ok(Some(str_range(source, start, start + length))) - } - } - } - } -}); dsl_type!(DslWord { fn word (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(word_peek_only))} @@ -278,85 +230,25 @@ fn ok_flat (x: Option>) -> DslPerhaps { Ok(x.transpose()?.flatten()) } -/// Define a DSL namespace that provides values to words and expressions. -#[macro_export] macro_rules! dsl_ns ( - // Special form for numeric types - (num |$state:ident : $State: ty| $($($num:lifetime)? $Type:ty $(=> { $( - $pat:tt => $body:expr - ),* $(,)? })?;)+) => { - $(dsl_ns!(num |$state: $State| -> $($num)? $Type { $( $($pat => $body),* )? });)+ - }; - // Special form for numeric types - (num |$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { - impl<'t> DslNs<'t, $Type> for $State { - const WORDS: DslNsMap<'t, fn (&$State)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); - const EXPRS: DslNsMap<'t, fn (&$State, &str)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); - fn from_literal (&self, dsl: &D) -> Perhaps<$Type> { - Ok(if let Some(src) = dsl.src()? { - Some(to_number(src)? as $Type) - } else { - None - }) - } - } - }; - // A namespace may resolve one or more types. - (|$state:ident : $State: ty| $($Type:ty $(=> { $( $pat:tt => $body:expr ),* $(,)? })? ;)+) => { - $(dsl_ns!(|$state: $State| -> $Type { $( $($pat => $body),* )? });)+ - }; - // Regular form for single type - (|$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { - impl<'t> DslNs<'t, $Type> for $State { - const WORDS: DslNsMap<'t, fn (&$State)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); - const EXPRS: DslNsMap<'t, fn (&$State, &str)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); - } - }; - // Symbols only. - (@word ($state:ident: $State:ty) -> $Type:ty { - $word:literal => $body:expr - }) => { - ($word, |$state|Ok(Some($body))) - }; - // Expression handlers only. - (@exp ($state:ident: $State:ty) -> $Type:ty { - ($head:literal $(,$arg:ident:$ty:ty)* $(,)*) => $body:expr - }) => { ($head, |$state, tail: &str|{ - $( - let head = tail.head()?.unwrap_or_default(); - let tail = tail.tail()?.unwrap_or_default(); - let $arg: $ty = if let Some(arg) = $state.from(&head)? { - arg - } else { - return Err(format!("{}: missing argument: {} ({}); got: {tail}", - $head, - stringify!($arg), - stringify!($Type), - ).into()) - }; - )* - Ok(Some($body)) - }) }; - // Nothing else in symbols. - (@word ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { - ("", |_|Ok(None)) // FIXME don't emit at all - }; - // Nothing else in expression handlers. - (@exp ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { - ("", |_, _|Ok(None)) // FIXME don't emit at all - }; -); +/// Namespace mapping. +#[derive(Debug)] pub struct DslNsMap<'t, T: Debug + 't>(pub &'t [(&'t str, T)]); +/// Namespace mapping for [DslWord]s. +pub type DslWords<'t, T, U> = DslNsMap<'t, fn (&'t T)->Perhaps>; +/// Namespace mapping for [DslExpr]s. +pub type DslExprs<'t, T, U> = DslNsMap<'t, fn (&'t T, &str)->Perhaps>; +impl<'t, T: Debug + 't> DslNsMap<'t, T> { + /// Populate a namespace with pre-existing values. + pub const fn new (data: &'t [(&'t str, T)]) -> Self { Self(data) /* TODO a search trie */ } +} + pub trait DslNs<'t, T: 't>: 't { /// Known symbols. - const WORDS: DslNsMap<'t, fn (&Self)->Perhaps> = DslNsMap::new(&[]); + const WORDS: DslWords<'t, Self, T> = DslNsMap::new(&[]); /// Known expressions. - const EXPRS: DslNsMap<'t, fn (&Self, &str)->Perhaps> = DslNsMap::new(&[]); + const EXPRS: DslExprs<'t, Self, T> = DslNsMap::new(&[]); /// Resolve an expression or symbol. - fn from (&self, dsl: &D) -> Perhaps { + fn from (&'t self, dsl: &D) -> Perhaps { if let Ok(Some(literal)) = self.from_literal(dsl) { Ok(Some(literal)) } else if let Ok(Some(meaning)) = self.from_word(dsl) { @@ -370,14 +262,19 @@ pub trait DslNs<'t, T: 't>: 't { Ok(None) } /// Resolve a symbol if known. - fn from_word (&self, dsl: &D) -> Perhaps { + fn from_word (&'t self, dsl: &D) -> Perhaps { if let Some(dsl) = dsl.word()? { - for (key, get) in Self::WORDS.0 { if dsl == *key { return get(self) } } + for (key, get) in Self::WORDS.0.iter() { + if dsl == *key { + let value = get(self); + return value + } + } } return Ok(None) } /// Resolve an expression if known. - fn from_expr (&self, dsl: &D) -> Perhaps { + fn from_expr (&'t self, dsl: &D) -> Perhaps { if let Some(expr) = dsl.expr()? { for (key, get) in Self::EXPRS.0.iter() { if Some(*key) == expr.head()? { @@ -389,41 +286,26 @@ pub trait DslNs<'t, T: 't>: 't { } } -/// Namespace mapping. -#[derive(Debug)] -pub struct DslNsMap<'t, T: Debug + 't>(pub &'t [(&'t str, T)]); -impl<'t, T: Debug + 't> DslNsMap<'t, T> { - /// Populate a namespace with pre-existing values. - pub const fn new (data: &'t [(&'t str, T)]) -> Self { Self(data) /* TODO a search trie */ } +#[deprecated] +/// `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) + } } -#[macro_export] macro_rules!dsl_words((|$state:ident|->$Type:ty$({ - $($word:literal => $body:expr),* $(,)? -})?)=>{ - const WORDS: DslNsMap<'t, fn (&Self)->Perhaps<$Type>> = DslNsMap::new(&[$( - $(($word, |$state: &Self|{Ok(Some($body))})),* - )? ]); -}); - -#[macro_export] macro_rules!dsl_exprs((|$state:ident|->$Type:ty$({ - $($name:literal ($($arg:ident:$ty:ty),* $(,)?) => $body:expr),* $(,)? -})?)=>{ - const EXPRS: DslNsMap<'t, fn (&Self, &str)->Perhaps<$Type>> = DslNsMap::new(&[$( - $(($name, |$state: &Self, tail: &str|{ - $( - let head = tail.head()?.unwrap_or_default(); - let tail = tail.tail()?.unwrap_or_default(); - let $arg: $ty = if let Some(arg) = $state.from(&head)? { - arg - } else { - return Err(format!("{}: arg \"{}\" ({}) got: {head} {tail}", - $name, - stringify!($arg), - stringify!($ty), - ).into()) - }; - )* - Ok(Some($body)) - })),* - )? ]); -}); +#[deprecated] +/// `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) + } +} diff --git a/dsl/src/dsl_conv.rs b/dsl/src/dsl_conv.rs deleted file mode 100644 index 7bbb91c..0000000 --- a/dsl/src/dsl_conv.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::*; - -// TODO DEPRECATE: - -/// `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) - } -} - -/// `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 DslFrom { - fn dsl_from (&self, val: &T) -> Perhaps; - fn dsl_from_or (&self, val: &T, err: Box) -> Usually { - self.dsl_from(val)?.ok_or(err) - } - fn dsl_from_or_else (&self, val: &T, err: impl Fn()->Box) -> Usually { - self.dsl_from(val)?.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) - } -} - -//impl<'s, T: FromDsl, U> DslInto<'s, T> for U { - //fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps { - //T::from_dsl(self, dsl) - //} -//} - -//impl, U> IntoDsl for U { - //fn into_dsl (&self, state: &T) -> Perhaps { - //T::dsl_from(state, self) - //} -//} diff --git a/dsl/src/dsl_macros.rs b/dsl/src/dsl_macros.rs new file mode 100644 index 0000000..36d2c36 --- /dev/null +++ b/dsl/src/dsl_macros.rs @@ -0,0 +1,153 @@ +#[macro_export] macro_rules! dsl_type (($T:ident { $($trait:tt)* } { + pub const fn $peek:ident $($_1:tt)?; + pub const fn $peek_only:ident $($_2:tt)?; + pub const fn $seek:ident $($_3:tt)?; + pub const fn $seek_start:ident ($source1:ident) $body1:block + pub const fn $seek_length:ident ($source2:ident) $body2:block +})=>{ + pub trait $T: Dsl { $($trait)* } + impl $T for D {} + pub const fn $seek_start ($source1: &str) -> DslPerhaps $body1 + pub const fn $seek_length ($source2: &str) -> DslPerhaps $body2 + /// Find a start and length corresponding to a syntax token. + pub const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> { + match $seek_start(source) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some(start)) => match $seek_length(str_from(source, start)) { + Ok(Some(length)) => Ok(Some((start, length))), + Ok(None) => Ok(None), + Err(e) => Err(e), + }, + } + } + /// Find a slice corrensponding to a syntax token. + pub const fn $peek (source: &str) -> DslPerhaps<&str> { + match $seek(source) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some((start, length))) => Ok(Some(str_range(source, start, start + length))), + } + } + /// Find a slice corrensponding to a syntax token + /// but return an error if it isn't the only thing + /// in the source. + pub const fn $peek_only (source: &str) -> DslPerhaps<&str> { + match $seek(source) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some((start, length))) => { + if let Err(e) = no_trailing_non_space(source, start + length, Some("peek_only")) { + Err(e) + } else { + Ok(Some(str_range(source, start, start + length))) + } + } + } + } +}); + +#[macro_export] macro_rules!dsl_words( + (|$state:ident|->$Type:ty$({ + $($word:literal => $body:expr),* $(,)? + })?)=>{ + const WORDS: DslWords<'t, Self, $Type> = DslNsMap::new(&[$( + $(($word, move|$state: &Self|{Ok(Some($body))})),* + )? ]); + }); + +#[macro_export] macro_rules!dsl_exprs( + (|$state:ident|->$Type:ty$({ + $($name:literal ($($arg:ident:$ty:ty),* $(,)?) => $body:expr),* $(,)? + })?)=>{ + const EXPRS: DslExprs<'t, Self, $Type> = DslNsMap::new(&[$( + $(($name, |$state: &Self, tail: &str|{ + $( + let head = tail.head()?.unwrap_or_default(); + let tail = tail.tail()?.unwrap_or_default(); + let $arg: $ty = if let Some(arg) = $state.from(&head)? { + arg + } else { + return Err(format!("{}: arg \"{}\" ({}) got: {head} {tail}", + $name, + stringify!($arg), + stringify!($ty), + ).into()) + }; + )* + Ok(Some($body)) + })),* + )? ]); + }); + +/// Define a DSL namespace that provides values to words and expressions. +#[macro_export] macro_rules! dsl_ns ( + // Special form for numeric types + (num |$state:ident : $State: ty| $($($num:lifetime)? $Type:ty $(=> { $( + $pat:tt => $body:expr + ),* $(,)? })?;)+) => { + $(dsl_ns!(num |$state: $State| -> $($num)? $Type { $( $($pat => $body),* )? });)+ + }; + // Special form for numeric types + (num |$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { + impl<'t> DslNs<'t, $Type> for $State { + const WORDS: DslWords<'t, $State, $Type> = DslNsMap::new(&[ + $(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); + const EXPRS: DslExprs<'t, $State, $Type> = DslNsMap::new(&[ + $(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); + fn from_literal (&self, dsl: &D) -> Perhaps<$Type> { + Ok(if let Some(src) = dsl.src()? { + Some(to_number(src)? as $Type) + } else { + None + }) + } + } + }; + // A namespace may resolve one or more types. + (|$state:ident : $State: ty| $($Type:ty $(=> { $( $pat:tt => $body:expr ),* $(,)? })? ;)+) => { + $(dsl_ns!(|$state: $State| -> $Type { $( $($pat => $body),* )? });)+ + }; + // Regular form for single type + (|$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { + impl<'t> DslNs<'t, $Type> for $State { + const WORDS: DslNsMap<'t, fn (&$State)->Perhaps<$Type>> = + DslNsMap::new(&[$(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); + const EXPRS: DslNsMap<'t, fn (&$State, &str)->Perhaps<$Type>> = + DslNsMap::new(&[$(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); + } + }; + // Symbols only. + (@word ($state:ident: $State:ty) -> $Type:ty { + $word:literal => $body:expr + }) => { + ($word, |$state|Ok(Some($body))) + }; + // Expression handlers only. + (@exp ($state:ident: $State:ty) -> $Type:ty { + ($head:literal $(,$arg:ident:$ty:ty)* $(,)*) => $body:expr + }) => { ($head, |$state, tail: &str|{ + $( + let head = tail.head()?.unwrap_or_default(); + let tail = tail.tail()?.unwrap_or_default(); + let $arg: $ty = if let Some(arg) = $state.from(&head)? { + arg + } else { + return Err(format!("{}: missing argument: {} ({}); got: {tail}", + $head, + stringify!($arg), + stringify!($Type), + ).into()) + }; + )* + Ok(Some($body)) + }) }; + // Nothing else in symbols. + (@word ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { + ("", |_|Ok(None)) // FIXME don't emit at all + }; + // Nothing else in expression handlers. + (@exp ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { + ("", |_, _|Ok(None)) // FIXME don't emit at all + }; +);