diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index f57c8ce..37b8d9a 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -1,58 +1,494 @@ -//#![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::PanicFmt; -use std::fmt::Debug; +use const_panic::{concat_panic, PanicFmt}; 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 konst::string::{str_range, char_indices}; +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 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; -pub trait Dsl: Debug + Send + Sync + Sized { - fn src (&self) -> &str; -} +#[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!() } +} impl<'s> Dsl for &'s str { - fn src (&self) -> &str { self } + 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() } } -impl Dsl for std::sync::Arc { - fn src (&self) -> &str { self.as_ref() } +/// 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 Option { - fn src (&self) -> &str { if let Some(dsl) = self { dsl.src() } else { "" } } +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 &D { - fn src (&self) -> &str { (*self).src() } +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 &mut 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}} } - -/// DSL-specific result type. -pub type DslResult = Result; - -/// DSL-specific optional result type. -pub type DslPerhaps = Result, DslError>; - +/// 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 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 +#[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, U> DslInto for U { + fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps { + T::from_dsl(self, dsl) + } +} + +/// `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 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_conv.rs b/dsl/src/dsl_conv.rs deleted file mode 100644 index 65f05d6..0000000 --- a/dsl/src/dsl_conv.rs +++ /dev/null @@ -1,51 +0,0 @@ -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 deleted file mode 100644 index 0a5402d..0000000 --- a/dsl/src/dsl_types.rs +++ /dev/null @@ -1,176 +0,0 @@ -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 0bec9a8..2780474 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -35,11 +35,11 @@ impl EventMap { self } /// Return the binding(s) that correspond to an event. - pub fn query (&self, event: &E) -> Option<&[Binding]> { - self.0.get(event).map(|x|x.as_slice()) + pub fn query (&self, event: E) -> Option<&[Binding]> { + self.0.get(&event).map(|x|x.as_slice()) } /// Return the first binding that corresponds to an event, considering conditions. - pub fn dispatch (&self, event: &E) -> Option<&Binding> { + pub fn dispatch (&self, event: E) -> Option<&Binding> { self.query(event) .map(|bb|bb.iter().filter(|b|b.condition.as_ref().map(|c|(c.0)()).unwrap_or(true)).next()) .flatten() @@ -54,20 +54,30 @@ impl EventMap { } /// Create event map from string. pub fn from_source (source: impl AsRef) -> Usually where E: From> { - Self::from_dsl(source.as_ref()) + Self::from_dsl(Cst::from(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(); - 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())?); + 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!(), } } Ok(map) @@ -76,10 +86,10 @@ impl EventMap { /// An input binding. #[derive(Debug)] pub struct Binding { - pub command: C, - pub condition: Option, - pub description: Option>, - pub source: Option>, + command: C, + condition: Option, + description: Option>, + source: Option>, } impl Binding { fn from_dsl (dsl: impl Dsl) -> Usually { diff --git a/proc/src/proc_command.rs b/proc/src/proc_command.rs index 48ae0fe..d0db6e3 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -153,6 +153,7 @@ 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 23bf062..cdfb2af 100644 --- a/proc/src/proc_expose.rs +++ b/proc/src/proc_expose.rs @@ -67,104 +67,63 @@ 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 type_is = format!("{}", quote! { #t }); - let variants = variants.iter().map(|(key, value)|{ + let arms = variants.iter().map(|(key, value)|{ let key = LitStr::new(&key, Span::call_site()); - quote! { Some(#key) => Ok(Some(state.#value())), } + quote! { #key => state.#value(), } }); - 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) } - } - } + 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) } + })) } } - } 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) } - } - } - } + }); + } + 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 { - 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) } - } - } + } + 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) } + }, } - }) - //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) - //} - //} - //}); + } + return quote! { + ::tengri::dsl::Val::Sym(s) => match s.as_ref() { + #variants + _ => { return Ok(None) } + }, + } } } diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 61cbe6e..312bdd9 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -60,10 +60,10 @@ impl ViewDef { /// Makes [#self_ty] able to construct the [Render]able /// which might correspond to a given [TokenStream], /// while taking [#self_ty]'s state into consideration. - impl<'state> ::tengri::dsl::DslInto<'state, + impl<'state> ::tengri::dsl::DslInto< Box + 'state> > for #self_ty { - fn dsl_into (&'state self, dsl: &impl ::tengri::dsl::Dsl) + fn dsl_into (&self, dsl: &impl ::tengri::dsl::Dsl) -> Perhaps + 'state>> { Ok(match dsl.val() { #builtins #exposed _ => return Ok(None) }) @@ -77,11 +77,8 @@ impl ViewDef { let Self(ViewMeta { output }, ViewImpl { .. }) = self; let builtins = builtins_with_boxes_output(quote! { #output }).map(|builtin|quote! { ::tengri::dsl::Val::Exp(_, expr) => return Ok(Some( - #builtin::from_dsl_or_else( - self, - &expr, - || { format!("failed to load builtin").into() } - )?.boxed() + #builtin::from_dsl(self, expr, ||Box::new("failed to load builtin".into()))? + .boxed() )), }); quote! { #(#builtins)* } @@ -92,7 +89,7 @@ impl ViewDef { let exposed = exposed.iter().map(|(key, value)|write_quote(quote! { #key => return Ok(Some(self.#value().boxed())), })); - quote! { ::tengri::dsl::Val::Sym(key) => match key.as_ref() { #(#exposed)* _ => panic!() } } + quote! { ::tengri::dsl::Val::Sym(key) => match key.as_ref() { #(#exposed)* } } } }