diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index e8aad19..f57c8ce 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -1,494 +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) => Ok($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 depth (&self) -> Option {self.val().depth()} - fn head (&self) -> Val {self.val().head()} - fn tail (&self) -> Self::Exp {self.val().tail()} - fn advance (&self) -> (Val, Self::Exp) { (self.head(), self.tail()) } - fn each (&self, f: impl Fn(&Self) -> Usually<()>) -> Usually<()> { todo!() } - fn exp_next (&mut self) -> Option> { todo!() } +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) } -} -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 Ast { - type Str = Arc; - type Exp = Ast; - fn val (&self) -> Val { Val::Exp(0, self.clone()) } -} -impl Dsl for Token { - type Str = Str; - type Exp = Exp; - fn val (&self) -> Val { self.value.clone() } -} -impl Dsl for Val { - type Str = Str; - type Exp = Exp; - fn val (&self) -> Val { self.clone() } + fn src (&self) -> &str { self } } -/// 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 Dsl for std::sync::Arc { + fn src (&self) -> &str { self.as_ref() } } -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 Dsl for Option { + fn src (&self) -> &str { if let Some(dsl) = self { dsl.src() } else { "" } } } -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 Dsl for &D { + fn src (&self) -> &str { (*self).src() } } -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 depth (&self) -> Option {match self{Val::Exp(d, _)=>Some(*d), _=>None}} + +impl Dsl for &mut D { + fn src (&self) -> &str { (**self).src() } } -/// 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)) - }) -} - -/// `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) - } +#[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 9f6d8de..0bec9a8 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -54,30 +54,20 @@ impl EventMap { } /// Create event map from string. pub fn from_source (source: impl AsRef) -> Usually where E: From> { - Self::from_dsl(Cst::from(source.as_ref())) + Self::from_dsl(source.as_ref()) } /// Create event map from DSL tokenizer. pub fn from_dsl (mut dsl: impl Dsl) -> Usually where E: From> { - use Val::*; let mut map: Self = Default::default(); - loop { - match dsl.exp_next().unwrap() { - Str(path) => { - map.0.extend(Self::from_path(PathBuf::from(path.as_ref() as &str))?.0) - }, - Exp(_, e) => match e.head() { - Key(k) if k.as_ref() == "if" => { - todo!("conditional binding {:?}", dsl.tail()); - }, - Sym(s) => { - let key: Arc = s.as_ref().into(); - let key: E = key.into(); - map.add(key, Binding::from_dsl(e)?); - todo!("binding {s:?} {:?}", dsl.tail()); - }, - _ => panic!(), - }, - _ => panic!(), + 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) 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) + //} + //} + //}); } }