use crate::*; use std::error::Error; /// Standard result type for DSL-specific operations. pub type DslResult = Result; /// DSL-specific error codes. #[derive(Error, Debug, Copy, Clone, PartialEq)] pub enum DslErr { #[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), } /// Enumeration of possible DSL tokens. /// Generic over string and expression storage. /// /// * [ ] FIXME: Value may be [Err] which may shadow [Result::Err] /// * [DslVal::Exp] wraps an expression depth and a [CstIter] /// with the remaining part of the expression. /// * expression depth other that 0 mean unclosed parenthesis. /// * closing and unopened parenthesis panics during reading. /// * [ ] TODO: signed depth might be interesting /// * [DslVal::Sym] and [DslVal::Key] are stringish literals /// with slightly different parsing rules. /// * [DslVal::Num] is an unsigned integer literal. #[derive(Clone, Debug, PartialEq, Default)] pub enum DslVal { #[default] Nil, Err(DslErr), Num(usize), Sym(Str), Key(Str), Str(Str), Exp(usize, Exp), } pub fn dsl_val , B: Into, X, Y> (val: DslVal) -> DslVal { use DslVal::*; match val { Nil => Nil, Err(e) => Err(e), Num(u) => Num(u), Sym(s) => Sym(s.into()), Key(s) => Key(s.into()), Str(s) => Str(s.into()), Exp(d, x) => Exp(d, x.into()), } } pub trait Dsl: Debug { type Str: PartialEq + Clone + Default + Debug + AsRef; type Exp: PartialEq + Clone + Default + Debug + Dsl; fn nth (&self, index: usize) -> Option>; fn val (&self) -> DslVal { self.nth(0).unwrap_or(DslVal::Nil) } // exp-only nth here? } impl< Str: PartialEq + Clone + Default + Debug + AsRef, Exp: PartialEq + Clone + Default + Debug + Dsl, > Dsl for DslVal { type Str = Str; type Exp = Exp; fn val (&self) -> DslVal { self.clone() } fn nth (&self, _index: usize) -> Option> { todo!() } } /// May construct self from state and DSL. pub trait DslFrom: Sized { fn try_dsl_from (state: &State, value: &impl Dsl) -> Perhaps; fn dsl_from ( state: &State, value: &impl Dsl, error: impl Fn()->Box ) -> Usually { match Self::try_dsl_from(state, value)? { Some(value) => Ok(value), _ => Err(error()) } } } /// May construct another from self and DSL. pub trait DslInto { fn try_dsl_into (&self, dsl: &impl Dsl) -> Perhaps; fn dsl_into ( &self, value: &impl Dsl, error: impl Fn()->Box ) -> Usually { match Self::try_dsl_into(self, value)? { Some(value) => Ok(value), _ => Err(error()) } } } impl DslVal { pub fn is_nil (&self) -> bool { matches!(self, Self::Nil) } pub fn as_err (&self) -> Option<&DslErr> { if let Self::Err(e) = self { Some(e) } else { None } } pub fn as_num (&self) -> Option { if let Self::Num(n) = self { Some(*n) } else { None } } pub fn as_exp (&self) -> Option<&Exp> { if let Self::Exp(_, x) = self { Some(x) } else { None } } pub fn exp_depth (&self) -> Option { todo!() } pub fn exp_head (&self) -> Option<&Self> { todo!() } // TODO pub fn exp_tail (&self) -> Option<&Exp> { todo!() } // TODO pub fn peek (&self) -> Option { todo!() } pub fn next (&mut self) -> Option { todo!() } pub fn rest (self) -> Vec { todo!() } } impl Copy for DslVal {} impl, Exp> DslVal { pub fn as_sym (&self) -> Option<&str> { if let Self::Sym(s) = self { Some(s.as_ref()) } else { None } } pub fn as_key (&self) -> Option<&str> { if let Self::Key(k) = self { Some(k.as_ref()) } else { None } } pub fn as_str (&self) -> Option<&str> { if let Self::Str(s) = self { Some(s.as_ref()) } else { None } } pub fn exp_match (&self, namespace: &str, cb: F) -> Perhaps where F: Fn(&str, &Exp)-> Perhaps { if let Some(Self::Key(key)) = self.exp_head() && key.as_ref().starts_with(namespace) && let Some(tail) = self.exp_tail() { cb(key.as_ref().split_at(namespace.len()).1, tail) } else { Ok(None) } } } macro_rules! from_str { ($Struct:ty |$source:ident| $expr:expr) => { impl<'s> From<&'s str> for $Struct { fn from ($source: &'s str) -> Self { $expr } } } } from_str!(Ast|source|Self::from(CstIter::from(source))); from_str!(Cst<'s>|source|Self(CstIter(CstConstIter(source)))); from_str!(CstIter<'s>|source|Self(CstConstIter(source))); from_str!(CstConstIter<'s>|source|Self::new(source));