use crate::*; pub type GetDslExpr<'a, S, T> = for<'b> fn(&'a S, &'b str)->Perhaps; pub type DslExprs<'a, S, T> = &'a [(&'a str, GetDslExpr<'a, S, T>)]; dsl_type!(DslExpr { fn expr (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(expr_peek_inner_only))} fn head (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(peek))} fn tail (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(peek_tail))} /// my other car is a cdr :< fn each (&self, mut cb: impl FnMut(&str)->Usually<()>) -> Usually<()> { Ok(if let Some(head) = self.head()? { cb(head)?; if let Some(tail) = self.tail()? { tail.each(cb)?; } }) } } { pub const fn expr_peek [generated]; pub const fn expr_peek_only [generated]; pub const fn expr_seek [generated]; pub const fn expr_seek_start (src) { for_each!((i, c) in char_indices(src) => if is_expr_start(c) { return Ok(Some(i)) } else if !is_space(c) { return Err(Unexpected(c, Some(i), Some("expected expression start"))) }); Ok(None) } pub const fn expr_seek_length (src) { let mut depth = 0; for_each!((i, c) in char_indices(src) => if is_expr_start(c) { depth += 1; } else if is_expr_end(c) { if depth == 0 { return Err(Unexpected(c, Some(i), Some("expected expression end"))) } else if depth == 1 { return Ok(Some(i + 1)) } else { depth -= 1; } }); Err(Incomplete) } }); #[macro_export] macro_rules!dsl_exprs(($l:lifetime |$state:ident|->$Type:ty$({ $($name:literal ($($arg:ident:$ty:ty),* $(,)?) => $body:expr),* $(,)? })?)=>{ const EXPRS: DslExprs<$l, Self, $Type> = &[$( $({ let get: GetDslExpr<$l, Self, $Type> = |$state: &$l Self, tail_base|{ let tail = tail_base; $( 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 as $Type)) }; ($name, get) }),* )? ]; }); pub trait DslNsExprs<'a, T: 'a>: 'a { /// Known expressions. const EXPRS: DslExprs<'a, Self, T> = &[]; /// Resolve an expression if known. fn from_expr (&'a self, dsl: impl DslExpr + 'a) -> Perhaps { let head = dsl.head()?; for (key, get) in Self::EXPRS.iter() { if Some(*key) == head { return get(self, dsl.tail()?.unwrap_or("")) } } return Ok(None) } }