From dc7b713108cef9852f2a33487ac6c3bc7949c11b Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 18 Jan 2025 13:38:21 +0100 Subject: [PATCH] wip: overcomplicating it on the way to simplifying it ultimately --- edn/src/atom.rs | 10 + edn/src/atom_arc.rs | 91 ++++++++ edn/src/atom_ref.rs | 78 +++++++ edn/src/context.rs | 101 +++++++++ edn/src/error.rs | 21 ++ edn/src/lib.rs | 52 +++-- edn/src/token.rs | 500 ++++++++++++-------------------------------- output/src/view.rs | 8 +- 8 files changed, 475 insertions(+), 386 deletions(-) create mode 100644 edn/src/atom.rs create mode 100644 edn/src/atom_arc.rs create mode 100644 edn/src/atom_ref.rs create mode 100644 edn/src/context.rs create mode 100644 edn/src/error.rs diff --git a/edn/src/atom.rs b/edn/src/atom.rs new file mode 100644 index 00000000..427a8e52 --- /dev/null +++ b/edn/src/atom.rs @@ -0,0 +1,10 @@ +use crate::*; +#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum AtomType { + #[default] Nil, Num, Sym, Key, Exp +} +pub trait Atom: Sized + Send + Sync { + fn kind (&self) -> TokenKind; + fn text (&self) -> &str; + fn num (&self) -> usize; + fn exp (&self) -> impl Iterator; +} diff --git a/edn/src/atom_arc.rs b/edn/src/atom_arc.rs new file mode 100644 index 00000000..afed7c33 --- /dev/null +++ b/edn/src/atom_arc.rs @@ -0,0 +1,91 @@ +use crate::*; +pub type ArcAtomResult = ParseResult; +//const_iter!(|self: ArcAtomIter| => ArcAtom => + //Some(if let Some(token) = Iterator::next(&mut self.0) { + //token.to_arc_atom(self.0.0).unwrap() + //} else { + //return None + //})); +#[derive(Clone, PartialEq)] pub enum ArcAtom { + Num(usize), + Sym(Arc), + Key(Arc), + Exp(Vec), +} +impl ArcAtom { + pub fn read_all <'a> (source: &'a str) -> impl Iterator> + 'a { + TokenResultIterator::new(source).map(move |result: TokenResult<'a>|match result{ + Err(e) => Err(e), + Ok(token) => match token.kind() { + Nil => Err(ParseError::Empty), + Num => match to_number(token.slice_source(source).into()) { + Ok(n) => Ok(ArcAtom::Num(n)), + Err(e) => return Err(e) + }, + Sym => Ok(ArcAtom::Sym(token.slice_source(source).into())), + Key => Ok(ArcAtom::Key(token.slice_source(source).into())), + Exp => Ok(ArcAtom::Exp({ + let mut iter = Self::read_all(token.slice_source(source)); + let mut atoms = vec![]; + while let Some(atom) = iter.next() { + atoms.push(atom?); + } + atoms + })) + }, + }) + } +} + +impl Atom for ArcAtom { + fn kind (&self) -> TokenKind { + match self { + Self::Num(_) => TokenKind::Num, + Self::Sym(_) => TokenKind::Sym, + Self::Key(_) => TokenKind::Key, + Self::Exp(_) => TokenKind::Exp, + } + } + fn text (&self) -> &str { + match self { + Self::Num(_)|Self::Exp(_) => "", + Self::Sym(s) => s.as_ref(), + Self::Key(k) => k.as_ref(), + } + } + fn num (&self) -> usize { + match self { + Self::Num(n) => *n, + _ => 0 + } + } + fn exp (&self) -> impl Iterator { + let cheap_clone = |t: &ArcAtom|t.clone(); + if let Self::Exp(e) = self { + return e.iter().map(cheap_clone) + } else { + let empty: &[ArcAtom] = &[]; + empty.iter().map(cheap_clone) + } + } +} +impl<'a> Debug for ArcAtom { + fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> { + match self { + Self::Num(n) => write!(f, "(num {n})"), + Self::Sym(t) => write!(f, "(sym {t:?})"), + Self::Key(t) => write!(f, "(key {t:?})"), + Self::Exp(u) => write!(f, "(exp {u:?})"), + } + } +} +impl<'a> Display for ArcAtom { + fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> { + match self { + Self::Num(n) => write!(f, "{n}"), + Self::Sym(t) => write!(f, "{t}"), + Self::Key(t) => write!(f, "{t}"), + Self::Exp(u) => write!(f, "({})", join(u.iter().map(|i|format!("{}", &i)), " ")) + } + } +} diff --git a/edn/src/atom_ref.rs b/edn/src/atom_ref.rs new file mode 100644 index 00000000..37e9925f --- /dev/null +++ b/edn/src/atom_ref.rs @@ -0,0 +1,78 @@ +use crate::*; +pub type RefAtomResult<'a> = ParseResult>; +#[derive(Copy, Clone, PartialEq)] pub struct RefAtomIter<'a>(pub TokenIter<'a>); +const_iter!(<'a>|self: RefAtomIter<'a>| => RefAtom<'a> => + Some(if let Some(token) = Iterator::next(&mut self.0) { + token.to_ref_atom(self.0.0).unwrap() + } else { + return None + })); +impl<'a> From<&'a str> for RefAtomIter<'a> { + fn from (source: &'a str) -> Self { Self::from_source(source) } +} +impl<'a> From> for RefAtomIter<'a> { + fn from (iter: TokenIter<'a>) -> Self { Self::from_tokens_iter(iter) } +} +impl<'a> RefAtomIter<'a> { + pub const fn from_source (source: &'a str) -> Self { Self(TokenIter(source)) } + pub const fn from_tokens_iter (iter: TokenIter<'a>) -> Self { Self(iter) } +} +#[derive(Copy, Clone, PartialEq)] pub struct RefAtomResultIterator<'a>(pub TokenResultIterator<'a>); +const_iter!(<'a>|self: RefAtomResultIterator<'a>| => RefAtomResult<'a> => + Some(if let Some(result) = Iterator::next(&mut self.0) { + match result { Ok(token) => token.to_ref_atom(self.0.0), Err(e) => Err(e), } + } else { + return None + })); +impl<'a> From<&'a str> for RefAtomResultIterator<'a> { + fn from (source: &'a str) -> Self { Self::from_source(source) } +} +impl<'a> From> for RefAtomResultIterator<'a> { + fn from (iter: TokenResultIterator<'a>) -> Self { Self::from_tokens_iter(iter) } +} +impl<'a> RefAtomResultIterator<'a> { + pub const fn from_source (source: &'a str) -> Self { Self(TokenResultIterator(source)) } + pub const fn from_tokens_iter (iter: TokenResultIterator<'a>) -> Self { Self(iter) } +} +#[derive(Copy, Clone, PartialEq)] pub enum RefAtom<'a> { + Num(usize), Sym(&'a str), Key(&'a str), Exp(RefAtomIter<'a>), +} +impl<'a> RefAtom<'a> { + pub fn to_arc_atom (&self) -> ArcAtom { todo!() } + pub fn read_all <'b: 'a> (source: &'b str) -> impl Iterator>> + 'b { + TokenResultIterator::new(source).map(move |result|match result { + Ok(token) => token.to_ref_atom(source), Err(e) => Err(e), + }) + } +} +impl<'a> Atom for RefAtom<'a> { + fn kind (&self) -> TokenKind { + match self { + Self::Num(_) => TokenKind::Num, + Self::Sym(_) => TokenKind::Sym, + Self::Key(_) => TokenKind::Key, + Self::Exp(_) => TokenKind::Exp, + } + } + fn text (&self) -> &str { + match self { + Self::Num(_)|Self::Exp(_) => "", + Self::Sym(s) => s, + Self::Key(k) => k, + } + } + fn num (&self) -> usize { match self { Self::Num(n) => *n, _ => 0 } } + fn exp (&self) -> impl Iterator> { + match self { Self::Exp(e) => *e, _ => RefAtomIter::from("") } + } +} +impl<'a> Debug for RefAtom<'a> { + fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> { + match self { + RefAtom::Num(n) => write!(f, "(num {n})"), + RefAtom::Sym(t) => write!(f, "(sym {t:?})"), + RefAtom::Key(t) => write!(f, "(key {t:?})"), + RefAtom::Exp(u) => write!(f, "(exp {})", join(u.clone().map(|t|format!("{t:?}")), ",")) + } + } +} diff --git a/edn/src/context.rs b/edn/src/context.rs new file mode 100644 index 00000000..07ef3a6d --- /dev/null +++ b/edn/src/context.rs @@ -0,0 +1,101 @@ +use crate::*; +/// Map EDN tokens to parameters of a given type for a given context +pub trait Context: Sized { + fn get (&self, _atom: &impl Atom) -> Option { + None + } + fn get_or_fail (&self, atom: &impl Atom) -> U { + self.get(atom).expect("no value") + } +} +impl, U> Context for &T { + fn get (&self, atom: &impl Atom) -> Option { + (*self).get(atom) + } + fn get_or_fail (&self, atom: &impl Atom) -> U { + (*self).get_or_fail(atom) + } +} +impl, U> Context for Option { + fn get (&self, atom: &impl Atom) -> Option { + self.as_ref().map(|s|s.get(atom)).flatten() + } + fn get_or_fail (&self, atom: &impl Atom) -> U { + self.as_ref().map(|s|s.get_or_fail(atom)).expect("no provider") + } +} +/// Implement `Context` for a context and type. +#[macro_export] macro_rules! provide { + // Provide a value to the EDN template + ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl Context<$type> for $State { + fn get (&$self, atom: &impl Atom) -> Option<$type> { + Some(match (atom.kind(), atom.text()) { + $((TokenKind::Sym, $pat) => $expr,)* + _ => return None + }) + } + } + }; + // Provide a value more generically + ($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl<$lt> Context<$lt, $type> for $State { + fn get (&$lt $self, atom: &impl Atom) -> Option<$type> { + Some(match (atom.kind(), atom.text()) { + $((TokenKind::Sym, $pat) => $expr,)* + _ => return None + }) + } + } + }; +} +/// Implement `Context` for a context and numeric type. +/// +/// This enables support for numeric literals. +#[macro_export] macro_rules! provide_num { + // Provide a value that may also be a numeric literal in the EDN, to a generic implementation. + ($type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl<$T: $Trait> Context<$type> for $T { + fn get (&$self, atom: &impl Atom) -> Option<$type> { + Some(match (atom.kind(), atom.text()) { + $((TokenKind::Sym, $pat) => $expr,)* + (TokenKind::Num, _) => atom.num() as $type, + _ => return None + }) + } + } + }; + // Provide a value that may also be a numeric literal in the EDN, to a concrete implementation. + ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl Context<$type> for $State { + fn get (&$self, atom: &impl Atom) -> Option<$type> { + Some(match (atom.kind(), atom.text()) { + $((TokenKind::Sym, $pat) => $expr,)* + (TokenKind::Num, _) => atom.num() as $type, + _ => return None + }) + } + } + }; +} +/// Implement `Context` for a context and content type. +/// +/// This enables support for layout expressions. +#[macro_export] macro_rules! provide_content { + (|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl<'a, E: Output> Context<'a, Box + 'a>> for $State { + fn get (&'a $self, atom: &'a impl Atom) -> Option + 'a>> { + use RefAtom::*; + Some(match atom.to_ref() { $(RefAtom::Sym($pat) => $expr),*, _ => return None }) + } + } + }; + ($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl<'a> Context<'a, Box + 'a>> for $State { + fn get (&'a $self, atom: &'a impl Atom) -> Option + 'a>> { + use RefAtom::*; + Some(match atom.to_ref() { $(Sym($pat) => $expr),*, _ => return None }) + } + } + } +} diff --git a/edn/src/error.rs b/edn/src/error.rs new file mode 100644 index 00000000..24e40caf --- /dev/null +++ b/edn/src/error.rs @@ -0,0 +1,21 @@ +use crate::*; +pub type ParseResult = Result; +#[derive(Debug, Copy, Clone, PartialEq)] pub enum ParseError { + Unimplemented, + Empty, + Incomplete, + Unexpected(char), + Code(u8), +} +impl Error for ParseError {} +impl Display for ParseError { + fn fmt (&self, f: &mut Formatter) -> FormatResult { + match self { + Unimplemented => write!(f, "unimplemented"), + Empty => write!(f, "empty"), + Incomplete => write!(f, "incomplete"), + Unexpected(c) => write!(f, "unexpected '{c}'"), + Code(i) => write!(f, "error #{i}"), + } + } +} diff --git a/edn/src/lib.rs b/edn/src/lib.rs index 2087dc1e..564fab84 100644 --- a/edn/src/lib.rs +++ b/edn/src/lib.rs @@ -1,8 +1,45 @@ +#![feature(adt_const_params)] #![feature(type_alias_impl_trait)] #![feature(impl_trait_in_fn_trait_return)] -mod token; pub use self::token::*; -pub(crate) use std::fmt::{Debug, Display, Formatter, Error as FormatError}; +mod error; pub use self::error::*; +mod token; pub use self::token::*; +mod atom; pub use self::atom::*; +mod atom_ref; pub use self::atom_ref::*; +mod atom_arc; pub use self::atom_arc::*; +mod context; pub use self::context::*; +pub(crate) use self::ParseError::*; +//pub(crate) use self::TokenKind::*; pub(crate) use std::sync::Arc; +//pub(crate) use std::marker::ConstParamTy; +pub(crate) use itertools::join; +pub(crate) use konst::iter::{ConstIntoIter, IsIteratorKind}; +pub(crate) use konst::string::{split_at, str_range, char_indices}; +pub(crate) use std::error::Error; +pub(crate) use std::fmt::{Debug, Display, Formatter, Result as FormatResult, Error as FormatError}; +/// Static iteration helper. +#[macro_export] macro_rules! iterate { + ($expr:expr => $arg: pat => $body:expr) => { + let mut iter = $expr; + while let Some(($arg, next)) = iter.next() { + $body; + iter = next; + } + } +} +/// Implement the const iterator pattern. +#[macro_export] macro_rules! const_iter { + ($(<$l:lifetime>)?|$self:ident: $Struct:ty| => $Item:ty => $expr:expr) => { + impl$(<$l>)? Iterator for $Struct { + type Item = $Item; + fn next (&mut $self) -> Option<$Item> { $expr } + } + impl$(<$l>)? ConstIntoIter for $Struct { + type Kind = IsIteratorKind; + type Item = $Item; + type IntoIter = Self; + } + } +} #[cfg(test)] #[test] fn test_lang () -> Result<(), ParseError> { use Atom::*; assert_eq!(Atom::read_all("")?, @@ -34,14 +71,3 @@ pub(crate) use std::sync::Arc; } Ok(()) } -#[cfg(test)] #[test] fn test_token () -> Result<(), Box> { - use Token::*; - assert_eq!(Nil, Token::chomp_one("")?); - assert_eq!(Nil, Token::chomp_one(" \n \r \t ")?); - assert_eq!(Num("8", 0, 1), Token::chomp_one("8")?); - assert_eq!(Num(" 8 ", 3, 1), Token::chomp_one(" 8 ")?); - assert_eq!(Sym(":foo", 0, 4), Token::chomp_one(":foo")?); - assert_eq!(Sym("@bar", 0, 4), Token::chomp_one("@bar")?); - assert_eq!(Key("foo/bar", 0, 7), Token::chomp_one("foo/bar")?); - Ok(()) -} diff --git a/edn/src/token.rs b/edn/src/token.rs index 27a342eb..43def806 100644 --- a/edn/src/token.rs +++ b/edn/src/token.rs @@ -1,119 +1,41 @@ use crate::*; -//use std::iter::IntoIterator; -use konst::iter::{ConstIntoIter, IsIteratorKind}; -use konst::string::{split_at, str_range, char_indices}; -use itertools::join; -use self::ParseError::*; use self::TokenKind::*; -#[derive(Debug)] pub enum ParseError { - Unimplemented, Empty, Incomplete, Unexpected(char), Code(u8), +#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'a> { + source: &'a str, + start: usize, + length: usize, + kind: TokenKind<'a>, } -impl std::fmt::Display for ParseError { - fn fmt (&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Unimplemented => write!(f, "unimplemented"), - Empty => write!(f, "empty"), - Incomplete => write!(f, "incomplete"), - Unexpected(c) => write!(f, "unexpected '{c}'"), - Code(i) => write!(f, "error #{i}"), - } - } +#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum TokenKind<'a> { + #[default] Nil, + Err(ParseError), + Num(usize), + Sym(&'a str), + Key(&'a str), + Exp(usize, TokenIter<'a>), } -impl std::error::Error for ParseError {} -macro_rules! iterate { - ($expr:expr => $arg: pat => $body:expr) => { - let mut iter = $expr; - while let Some(($arg, next)) = iter.next() { - $body; - iter = next; - } - } -} -#[derive(Copy, Clone, PartialEq)] pub struct TokensIterator<'a>(&'a str); -type TokenResult<'a> = Result, ParseError>; -impl<'a> Iterator for TokensIterator<'a> { - type Item = TokenResult<'a>; - fn next (&mut self) -> Option> { self.next_mut().map(|(result, _)|result) } -} -impl<'a> ConstIntoIter for TokensIterator<'a> { - type Kind = IsIteratorKind; - type Item = Token<'a>; - type IntoIter = Self; -} -impl<'a> TokensIterator<'a> { +#[derive(Copy, Clone, Debug, PartialEq)] pub struct TokenIter<'a>(pub &'a str); +const_iter!(<'a>|self: TokenIter<'a>| => Token<'a> => self.next_mut().map(|(result, _)|result)); +impl<'a> From<&'a str> for TokenIter<'a> {fn from (source: &'a str) -> Self{Self::new(source)}} +impl<'a> TokenIter<'a> { pub const fn new (source: &'a str) -> Self { Self(source) } - pub const fn split (&self, index: usize) -> Self { Self(split_at(self.0, index).1) } - pub const fn next (mut self) -> Option<(TokenResult<'a>, Self)> { - Self::next_mut(&mut self) - } - pub const fn next_mut (&mut self) -> Option<(TokenResult<'a>, Self)> { + pub const fn chomp (&self, index: usize) -> Self { Self(split_at(self.0, index).1) } + pub const fn next (mut self) -> Option<(Token<'a>, Self)> { Self::next_mut(&mut self) } + pub const fn peek (&self) -> Option> { peek_src(self.0) } + pub const fn next_mut (&mut self) -> Option<(Token<'a>, Self)> { match self.peek() { - Some(Ok(token)) => Some((Ok(token), self.split(token.end()))), - Some(Err((l, e))) => Some((Err(e), self.split(l))), + Some(token) => Some((token, self.chomp(token.end()))), None => None } } - pub const fn peek (&self) -> Option, (usize, ParseError)>> { - let mut token: Token<'a> = Token::new(self.0, Nil, 0, 0, 0); - iterate!(char_indices(self.0) => (index, c) => token = match token.kind() { - Nil => match c { - '(' => Token::new(self.0, Exp, index, 1, 1), - ':'|'@' => Token::new(self.0, Sym, index, 1, 0), - '0'..='9' => Token::new(self.0, Num, index, 1, 0), - '/'|'a'..='z' => Token::new(self.0, Key, index, 1, 0), - ' '|'\n'|'\r'|'\t' => token.grow(), - _ => return Some(Err((token.end(), Unexpected(c)))) - }, - Num => match c { - '0'..='9' => token.grow(), - ' '|'\n'|'\r'|'\t' => return Some(Ok(token)), - _ => return Some(Err((token.end(), Unexpected(c)))) - }, - Sym => match c { - 'a'..='z'|'0'..='9'|'-' => token.grow(), - ' '|'\n'|'\r'|'\t' => return Some(Ok(token)), - _ => return Some(Err((token.end(), Unexpected(c)))) - }, - Key => match c { - 'a'..='z'|'0'..='9'|'-'|'/' => token.grow(), - ' '|'\n'|'\r'|'\t' => return Some(Ok(token)), - _ => return Some(Err((token.end(), Unexpected(c)))) - }, - Exp => match token.depth { - 0 => return Some(Ok(token)), - _ => match c { - ')' => match token.grow_out() { - Ok(token) => token, - Err(e) => return Some(Err((token.end(), e))) - }, - '(' => token.grow_in(), - _ => token.grow(), - } - }, - }); - match token.kind() { - Nil => None, - _ => Some(Err((token.end(), ParseError::Incomplete))) - } - } -} -#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum TokenKind { - #[default] Nil, Num, Sym, Key, Exp -} -#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'a> { - source: &'a str, - kind: TokenKind, - start: usize, - length: usize, - depth: usize, } impl<'a> Token<'a> { - pub const fn new ( - source: &'a str, kind: TokenKind, start: usize, length: usize, depth: usize - ) -> Self { - Self { source, kind, start, length, depth } + pub const fn new (source: &'a str, kind: TokenKind<'a>, start: usize, length: usize) -> Self { + Self { source, kind, start, length } + } + pub const fn end (&self) -> usize { + self.start + self.length } - pub const fn end (&self) -> usize { self.start + self.length } pub const fn slice (&'a self) -> &'a str { self.slice_source(self.source) //str_range(self.source, self.start, self.end()) @@ -121,287 +43,131 @@ impl<'a> Token<'a> { pub const fn slice_source <'b> (&'a self, source: &'b str) -> &'b str { str_range(source, self.start, self.end()) } - pub const fn kind (&self) -> TokenKind { Nil } + pub const fn kind (&self) -> TokenKind { + self.kind + } + pub const fn error (self, error: ParseError) -> Self { + Self { kind: TokenKind::Err(error), ..self } + } pub const fn grow (self) -> Self { Self { length: self.length + 1, ..self } } pub const fn grow_in (self) -> Self { - Self { length: self.length + 1, depth: self.depth + 1, ..self } - } - pub const fn grow_out (self) -> Result { - match self.depth { - 0 => Err(Unexpected(')')), - d => Ok(Self { length: self.length + 1, depth: d - 1, ..self }) - } - } - pub const fn to_ref_atom (&self, source: &'a str) -> Result, ParseError> { + //Self { length: self.length + 1, depth: self.depth + 1, ..self }; match self.kind { - Nil => Err(ParseError::Empty), - Num => match to_number(self.slice_source(source)) { - Err(e) => Err(e), - Ok(n) => Ok(RefAtom::Num(n)), + TokenKind::Exp(depth, source) => Self { + kind: TokenKind::Exp(depth + 1, TokenIter(self.grow().slice_source(source.0))), + ..self.grow() }, - Sym => Ok(RefAtom::Sym(self.slice_source(source))), - Key => Ok(RefAtom::Key(self.slice_source(source))), - Exp => Ok( - RefAtom::Exp(RefAtomsIterator(TokensIterator::new(self.slice_source(source)))) - ) + _ => self.grow() } } + pub const fn grow_out (self) -> Self { + match self.kind { + TokenKind::Exp(depth, source) => match depth { + 0 => self.error(Unexpected(')')), + d => Self { + kind: TokenKind::Exp(d - 1, TokenIter(self.grow().slice_source(source.0))), + ..self.grow() + } + }, + _ => self.grow() + } + } + //pub const fn to_ref_atom (&self, source: &'a str) -> RefAtom<'a> { + //match self.kind { + //TokenKind::Nil => Err(ParseError::Empty), + //TokenKind::Num => match to_number(self.slice_source(source)) { + //Err(e) => Err(e), + //Ok(n) => Ok(RefAtom::Num(n)), + //}, + //TokenKind::Sym => Ok(RefAtom::Sym(self.slice_source(source))), + //TokenKind::Key => Ok(RefAtom::Key(self.slice_source(source))), + //TokenKind::Exp => Ok( + //RefAtom::Exp(RefAtomIterator(TokenIter::new(self.slice_source(source)))) + //) + //} + //} +} +pub const fn peek_src <'a> (source: &'a str) -> Option> { + let mut token: Token<'a> = Token::new(source, Nil, 0, 0); + iterate!(char_indices(source) => (start, c) => token = match token.kind() { + Err(_) => return Some(token), + Nil => match c { + ' '|'\n'|'\r'|'\t' => token.grow(), + '(' => Token { + source, start, length: 1, + kind: TokenKind::Exp(1, TokenIter(str_range(source, start, start + 1))), + }, + '0'..='9' => Token { + source, start, length: 1, + kind: match to_digit(c) { + Ok(c) => TokenKind::Num(c), + Result::Err(e) => TokenKind::Err(e) + } + }, + ':'|'@' => Token { + source, start, length: 1, + kind: TokenKind::Sym(str_range(source, start, start + 1)), + }, + '/'|'a'..='z' => Token { + source, start, length: 1, + kind: TokenKind::Key(str_range(source, start, start + 1)), + }, + _ => token.error(Unexpected(c)) + }, + Num(_) => match c { + '0'..='9' => token.grow(), + ' '|'\n'|'\r'|'\t' => return Some(token), + _ => token.error(Unexpected(c)) + }, + Sym(_) => match c { + 'a'..='z'|'0'..='9'|'-' => token.grow(), + ' '|'\n'|'\r'|'\t' => return Some(token), + _ => token.error(Unexpected(c)) + }, + Key(_) => match c { + 'a'..='z'|'0'..='9'|'-'|'/' => token.grow(), + ' '|'\n'|'\r'|'\t' => return Some(token), + _ => token.error(Unexpected(c)) + }, + Exp(depth, _) => match depth { + 0 => return Some(token), + _ => match c { + ')' => token.grow_out(), + '(' => token.grow_in(), + _ => token.grow(), + } + }, + }); + match token.kind() { + Nil => None, + _ => Some(token), + } } -const fn to_number (digits: &str) -> Result { +pub const fn to_number (digits: &str) -> Result { let mut value = 0; iterate!(char_indices(digits) => (_, c) => match to_digit(c) { Ok(digit) => value = 10 * value + digit, - Err(e) => return Err(e) + Result::Err(e) => return Result::Err(e) }); Ok(value) } -const fn to_digit (c: char) -> Result { +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)) + _ => return Result::Err(Unexpected(c)) }) } -pub trait Atom: Sized + Send + Sync { - fn kind (&self) -> TokenKind; - fn text (&self) -> &str; - fn num (&self) -> usize; -} -#[derive(Copy, Clone, PartialEq)] pub enum RefAtom<'a> { - Num(usize), - Sym(&'a str), - Key(&'a str), - Exp(RefAtomsIterator<'a>), -} -type RefAtomResult<'a> = Result, ParseError>; -#[derive(Copy, Clone, PartialEq)] pub struct RefAtomsIterator<'a>(TokensIterator<'a>); -impl<'a> Iterator for RefAtomsIterator<'a> { - type Item = RefAtomResult<'a>; - fn next (&mut self) -> Option> { - Some(if let Some(result) = Iterator::next(&mut self.0) { - match result { Ok(token) => token.to_ref_atom(self.0.0), Err(e) => Err(e), } - } else { - return None - }) - } -} -impl<'a> RefAtom<'a> { - pub fn to_arc_atom (&self) -> ArcAtom { - todo!() - } - pub fn read_all <'b: 'a> (source: &'b str) - -> impl Iterator, ParseError>> + 'b - { - TokensIterator::new(source).map(move |result|match result { - Ok(token) => token.to_ref_atom(source), Err(e) => Err(e), - }) - } -} -impl<'a> Atom for RefAtom<'a> { - fn kind (&self) -> TokenKind { - match self { - Self::Num(_) => TokenKind::Num, - Self::Sym(_) => TokenKind::Sym, - Self::Key(_) => TokenKind::Key, - Self::Exp(_) => TokenKind::Exp, - } - } - fn text (&self) -> &str { - match self { - Self::Num(_)|Self::Exp(_) => "", - Self::Sym(s) => s, - Self::Key(k) => k, - } - } - fn num (&self) -> usize { - match self { - Self::Num(n) => *n, - _ => 0 - } - } -} -impl<'a> Debug for RefAtom<'a> { - fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> { - match self { - RefAtom::Num(n) => write!(f, "(num {n})"), - RefAtom::Sym(t) => write!(f, "(sym {t:?})"), - RefAtom::Key(t) => write!(f, "(key {t:?})"), - RefAtom::Exp(u) => write!(f, "(exp {})", join(u.clone().map(|t|format!("{t:?}")), ",")) - } - } -} -#[derive(Clone, PartialEq)] pub enum ArcAtom { - Num(usize), - Sym(Arc), - Key(Arc), - Exp(Vec), -} -impl ArcAtom { - pub fn read_all <'a> (source: &'a str) -> impl Iterator> + 'a { - TokensIterator::new(source).map(move |result: TokenResult<'a>|match result{ - Err(e) => Err(e), - Ok(token) => match token.kind { - Nil => Err(ParseError::Empty), - Num => match to_number(token.slice_source(source).into()) { - Ok(n) => Ok(ArcAtom::Num(n)), - Err(e) => return Err(e) - }, - Sym => Ok(ArcAtom::Sym(token.slice_source(source).into())), - Key => Ok(ArcAtom::Key(token.slice_source(source).into())), - Exp => Ok(ArcAtom::Exp({ - let mut iter = Self::read_all(token.slice_source(source)); - let mut atoms = vec![]; - while let Some(atom) = iter.next() { - atoms.push(atom?); - } - atoms - })) - }, - }) - } -} -impl<'a> Atom for ArcAtom { - fn kind (&self) -> TokenKind { - match self { - Self::Num(_) => TokenKind::Num, - Self::Sym(_) => TokenKind::Sym, - Self::Key(_) => TokenKind::Key, - Self::Exp(_) => TokenKind::Exp, - } - } - fn text (&self) -> &str { - match self { - Self::Num(_)|Self::Exp(_) => "", - Self::Sym(s) => s.as_ref(), - Self::Key(k) => k.as_ref(), - } - } - fn num (&self) -> usize { - match self { - Self::Num(n) => *n, - _ => 0 - } - } -} -impl<'a> Debug for ArcAtom { - fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> { - match self { - Self::Num(n) => write!(f, "(num {n})"), - Self::Sym(t) => write!(f, "(sym {t:?})"), - Self::Key(t) => write!(f, "(key {t:?})"), - Self::Exp(u) => write!(f, "(exp {u:?})"), - } - } -} -impl<'a> Display for ArcAtom { - fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> { - match self { - Self::Num(n) => write!(f, "{n}"), - Self::Sym(t) => write!(f, "{t}"), - Self::Key(t) => write!(f, "{t}"), - Self::Exp(u) => write!(f, "({})", join(u.iter().map(|i|format!("{}", &i)), " ")) - } - } -} -/// Map EDN tokens to parameters of a given type for a given context -pub trait Context: Sized { - fn get (&self, _atom: &impl Atom) -> Option { - None - } - fn get_or_fail (&self, atom: &impl Atom) -> U { - self.get(atom).expect("no value") - } -} -impl, U> Context for &T { - fn get (&self, atom: &impl Atom) -> Option { - (*self).get(atom) - } - fn get_or_fail (&self, atom: &impl Atom) -> U { - (*self).get_or_fail(atom) - } -} -impl, U> Context for Option { - fn get (&self, atom: &impl Atom) -> Option { - self.as_ref().map(|s|s.get(atom)).flatten() - } - fn get_or_fail (&self, atom: &impl Atom) -> U { - self.as_ref().map(|s|s.get_or_fail(atom)).expect("no provider") - } -} -/// Implement `Context` for a context and type. -#[macro_export] macro_rules! provide { - // Provide a value to the EDN template - ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { - impl Context<$type> for $State { - fn get (&$self, atom: &impl Atom) -> Option<$type> { - Some(match (atom.kind(), atom.text()) { - $((TokenKind::Sym, $pat) => $expr,)* - _ => return None - }) - } - } - }; - // Provide a value more generically - ($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { - impl<$lt> Context<$lt, $type> for $State { - fn get (&$lt $self, atom: &impl Atom) -> Option<$type> { - Some(match (atom.kind(), atom.text()) { - $((TokenKind::Sym, $pat) => $expr,)* - _ => return None - }) - } - } - }; -} -/// Implement `Context` for a context and numeric type. -/// -/// This enables support for numeric literals. -#[macro_export] macro_rules! provide_num { - // Provide a value that may also be a numeric literal in the EDN, to a generic implementation. - ($type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => { - impl<$T: $Trait> Context<$type> for $T { - fn get (&$self, atom: &impl Atom) -> Option<$type> { - Some(match (atom.kind(), atom.text()) { - $((TokenKind::Sym, $pat) => $expr,)* - (TokenKind::Num, _) => atom.num() as $type, - _ => return None - }) - } - } - }; - // Provide a value that may also be a numeric literal in the EDN, to a concrete implementation. - ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { - impl Context<$type> for $State { - fn get (&$self, atom: &impl Atom) -> Option<$type> { - Some(match (atom.kind(), atom.text()) { - $((TokenKind::Sym, $pat) => $expr,)* - (TokenKind::Num, _) => atom.num() as $type, - _ => return None - }) - } - } - }; -} -/// Implement `Context` for a context and content type. -/// -/// This enables support for layout expressions. -#[macro_export] macro_rules! provide_content { - (|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { - impl<'a, E: Output> Context<'a, Box + 'a>> for $State { - fn get (&'a $self, atom: &'a impl Atom) -> Option + 'a>> { - use RefAtom::*; - Some(match atom.to_ref() { $(RefAtom::Sym($pat) => $expr),*, _ => return None }) - } - } - }; - ($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { - impl<'a> Context<'a, Box + 'a>> for $State { - fn get (&'a $self, atom: &'a impl Atom) -> Option + 'a>> { - use RefAtom::*; - Some(match atom.to_ref() { $(Sym($pat) => $expr),*, _ => return None }) - } - } - } +#[cfg(test)] #[test] fn test_token () -> Result<(), Box> { + use Token::*; + assert_eq!(Nil, Token::chomp_one("")?); + assert_eq!(Nil, Token::chomp_one(" \n \r \t ")?); + assert_eq!(Num("8", 0, 1), Token::chomp_one("8")?); + assert_eq!(Num(" 8 ", 3, 1), Token::chomp_one(" 8 ")?); + assert_eq!(Sym(":foo", 0, 4), Token::chomp_one(":foo")?); + assert_eq!(Sym("@bar", 0, 4), Token::chomp_one("@bar")?); + assert_eq!(Key("foo/bar", 0, 7), Token::chomp_one("foo/bar")?); + Ok(()) } diff --git a/output/src/view.rs b/output/src/view.rs index 7a27dc55..6d40f0fe 100644 --- a/output/src/view.rs +++ b/output/src/view.rs @@ -145,12 +145,8 @@ pub trait ViewContext<'a, E: Output>: Sized + Send + Sync } #[macro_export] macro_rules! try_delegate { ($s:ident, $atom:expr, $T:ty) => { - if $atom.kind() == TokenKind::Exp { - if let [head, tail @ ..] = $atom.as_slice() { - if let Some(value) = <$T>::try_from_atoms($s, head, tail) { - return Some(value.boxed()) - } - } + if let Some(value) = <$T>::try_from_atoms($s, $atom) { + return Some(value.boxed()) } } }