mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-06 11:46:42 +01:00
178 lines
5.2 KiB
Rust
178 lines
5.2 KiB
Rust
use crate::*;
|
|
use std::error::Error;
|
|
|
|
/// Standard result type for DSL-specific operations.
|
|
pub type DslResult<T> = Result<T, DslErr>;
|
|
|
|
/// 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<Str, Exp> {
|
|
#[default]
|
|
Nil,
|
|
Err(DslErr),
|
|
Num(usize),
|
|
Sym(Str),
|
|
Key(Str),
|
|
Str(Str),
|
|
Exp(usize, Exp),
|
|
}
|
|
|
|
pub fn dsl_val <A: Into<X>, B: Into<Y>, X, Y> (val: DslVal<A, B>) -> DslVal<X, Y> {
|
|
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<str>;
|
|
type Exp: PartialEq + Clone + Default + Debug + Dsl;
|
|
fn nth (&self, index: usize) -> Option<DslVal<Self::Str, Self::Exp>>;
|
|
fn val (&self) -> DslVal<Self::Str, Self::Exp> {
|
|
self.nth(0).unwrap_or(DslVal::Nil)
|
|
}
|
|
// exp-only nth here?
|
|
}
|
|
|
|
impl<
|
|
Str: PartialEq + Clone + Default + Debug + AsRef<str>,
|
|
Exp: PartialEq + Clone + Default + Debug + Dsl,
|
|
> Dsl for DslVal<Str, Exp> {
|
|
type Str = Str;
|
|
type Exp = Exp;
|
|
fn val (&self) -> DslVal<Str, Exp> {
|
|
self.clone()
|
|
}
|
|
fn nth (&self, _index: usize) -> Option<DslVal<Str, Exp>> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
/// May construct self from state and DSL.
|
|
pub trait DslFrom<State>: Sized {
|
|
fn try_dsl_from (state: &State, value: &impl Dsl) -> Perhaps<Self>;
|
|
fn dsl_from (
|
|
state: &State, value: &impl Dsl, error: impl Fn()->Box<dyn Error>
|
|
) -> Usually<Self> {
|
|
match Self::try_dsl_from(state, value)? {
|
|
Some(value) => Ok(value),
|
|
_ => Err(error())
|
|
}
|
|
}
|
|
}
|
|
|
|
/// May construct another from self and DSL.
|
|
pub trait DslInto<Item> {
|
|
fn try_dsl_into (&self, dsl: &impl Dsl) -> Perhaps<Item>;
|
|
fn dsl_into (
|
|
&self, value: &impl Dsl, error: impl Fn()->Box<dyn Error>
|
|
) -> Usually<Item> {
|
|
match Self::try_dsl_into(self, value)? {
|
|
Some(value) => Ok(value),
|
|
_ => Err(error())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<Str, Exp> DslVal<Str, Exp> {
|
|
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<usize> {
|
|
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<usize> {
|
|
todo!()
|
|
}
|
|
pub fn exp_head (&self) -> Option<&Self> {
|
|
todo!()
|
|
} // TODO
|
|
pub fn exp_tail (&self) -> Option<&Exp> {
|
|
todo!()
|
|
} // TODO
|
|
pub fn peek (&self) -> Option<Self> {
|
|
todo!()
|
|
}
|
|
pub fn next (&mut self) -> Option<Self> {
|
|
todo!()
|
|
}
|
|
pub fn rest (self) -> Vec<Self> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
impl<Str: Copy, Exp: Copy> Copy for DslVal<Str, Exp> {}
|
|
|
|
impl<Str: AsRef<str>, Exp> DslVal<Str, Exp> {
|
|
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 <T, F> (&self, namespace: &str, cb: F) -> Perhaps<T>
|
|
where F: Fn(&str, &Exp)-> Perhaps<T> {
|
|
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));
|