diff --git a/edn/src/atom.rs b/edn/src/atom.rs deleted file mode 100644 index 427a8e52..00000000 --- a/edn/src/atom.rs +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index afed7c33..00000000 --- a/edn/src/atom_arc.rs +++ /dev/null @@ -1,91 +0,0 @@ -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 deleted file mode 100644 index 37e9925f..00000000 --- a/edn/src/atom_ref.rs +++ /dev/null @@ -1,78 +0,0 @@ -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 index 07ef3a6d..b0a0b0be 100644 --- a/edn/src/context.rs +++ b/edn/src/context.rs @@ -1,26 +1,38 @@ 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 { +pub trait TryFromAtom: Sized { + fn try_from_atom (state: &T, value: Value) -> Option { + if let Value::Exp(0, iter) = value { return Self::try_from_expr(state, iter) } None } - fn get_or_fail (&self, atom: &impl Atom) -> U { + fn try_from_expr (_state: &T, _iter: TokenIter) -> Option { + None + } +} +pub trait TryIntoAtom: Sized { + fn try_into_atom (&self) -> Option; +} +/// Map EDN tokens to parameters of a given type for a given context +pub trait Context: Sized { + fn get (&self, _atom: &Value) -> Option { + None + } + fn get_or_fail (&self, atom: &Value) -> U { self.get(atom).expect("no value") } } impl, U> Context for &T { - fn get (&self, atom: &impl Atom) -> Option { + fn get (&self, atom: &Value) -> Option { (*self).get(atom) } - fn get_or_fail (&self, atom: &impl Atom) -> U { + fn get_or_fail (&self, atom: &Value) -> U { (*self).get_or_fail(atom) } } impl, U> Context for Option { - fn get (&self, atom: &impl Atom) -> Option { + fn get (&self, atom: &Value) -> Option { self.as_ref().map(|s|s.get(atom)).flatten() } - fn get_or_fail (&self, atom: &impl Atom) -> U { + fn get_or_fail (&self, atom: &Value) -> U { self.as_ref().map(|s|s.get_or_fail(atom)).expect("no provider") } } @@ -29,9 +41,10 @@ impl, U> Context for Option { // 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,)* + #[allow(unreachable_code)] + fn get (&$self, atom: &Value) -> Option<$type> { + Some(match atom { + $(Value::Sym($pat) => $expr,)* _ => return None }) } @@ -40,9 +53,10 @@ impl, U> Context for Option { // 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,)* + #[allow(unreachable_code)] + fn get (&$lt $self, atom: &Value) -> Option<$type> { + Some(match atom { + $(Value::Sym($pat) => $expr,)* _ => return None }) } @@ -56,10 +70,10 @@ impl, U> Context for Option { // 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, + fn get (&$self, atom: &Value) -> Option<$type> { + Some(match atom { + $(Value::Sym($pat) => $expr,)* + Value::Num(n) => *n as $type, _ => return None }) } @@ -68,10 +82,10 @@ impl, U> Context for Option { // 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, + fn get (&$self, atom: &Value) -> Option<$type> { + Some(match atom { + $(Value::Sym($pat) => $expr,)* + Value::Num(n) => *n as $type, _ => return None }) } @@ -84,17 +98,21 @@ impl, U> Context for Option { #[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 }) + fn get (&'a $self, atom: &'a Value) -> Option + 'a>> { + Some(match (atom.kind(), atom.text()) { + $(Value::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 }) + fn get (&'a $self, atom: &'a Value) -> Option + 'a>> { + Some(match (atom.kind(), atom.text()) { + $(Value::Sym($pat) => $expr,)* + _ => return None + }) } } } diff --git a/edn/src/iter.rs b/edn/src/iter.rs new file mode 100644 index 00000000..4e9b74d3 --- /dev/null +++ b/edn/src/iter.rs @@ -0,0 +1,87 @@ +use crate::*; +#[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 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(token) => Some((token, self.chomp(token.end()))), + None => None + } + } +} +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.value() { + Err(_) => return Some(token), + Nil => match c { + ' '|'\n'|'\r'|'\t' => token.grow(), + '(' => Token { + source, start, length: 1, + value: Value::Exp(1, TokenIter(str_range(source, start, start + 1))), + }, + '0'..='9' => Token { + source, start, length: 1, + value: match to_digit(c) { + Ok(c) => Value::Num(c), + Result::Err(e) => Value::Err(e) + } + }, + ':'|'@' => Token { + source, start, length: 1, + value: Value::Sym(str_range(source, start, start + 1)), + }, + '/'|'a'..='z' => Token { + source, start, length: 1, + value: Value::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.value() { + Nil => None, + _ => Some(token), + } +} +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, + Result::Err(e) => return Result::Err(e) + }); + 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 Result::Err(Unexpected(c)) + }) +} diff --git a/edn/src/lib.rs b/edn/src/lib.rs index 564fab84..90fab9fe 100644 --- a/edn/src/lib.rs +++ b/edn/src/lib.rs @@ -1,12 +1,14 @@ #![feature(adt_const_params)] #![feature(type_alias_impl_trait)] #![feature(impl_trait_in_fn_trait_return)] -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::*; +mod error; pub use self::error::*; +mod token; pub use self::token::*; +mod iter; pub use self::iter::*; +mod context; pub use self::context::*; +//mod atom; pub use self::atom::*; +//mod atom_ref; pub use self::atom_ref::*; +//mod atom_arc; pub use self::atom_arc::*; +pub(crate) use self::Value::*; pub(crate) use self::ParseError::*; //pub(crate) use self::TokenKind::*; pub(crate) use std::sync::Arc; @@ -40,6 +42,17 @@ pub(crate) use std::fmt::{Debug, Display, Formatter, Result as FormatResult, Err } } } +#[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(()) +} #[cfg(test)] #[test] fn test_lang () -> Result<(), ParseError> { use Atom::*; assert_eq!(Atom::read_all("")?, diff --git a/edn/src/token.rs b/edn/src/token.rs index 43def806..25c1750d 100644 --- a/edn/src/token.rs +++ b/edn/src/token.rs @@ -1,12 +1,12 @@ use crate::*; -use self::TokenKind::*; +use self::Value::*; #[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'a> { - source: &'a str, - start: usize, - length: usize, - kind: TokenKind<'a>, + pub source: &'a str, + pub start: usize, + pub length: usize, + pub value: Value<'a>, } -#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum TokenKind<'a> { +#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum Value<'a> { #[default] Nil, Err(ParseError), Num(usize), @@ -14,24 +14,9 @@ use self::TokenKind::*; Key(&'a str), Exp(usize, TokenIter<'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 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(token) => Some((token, self.chomp(token.end()))), - None => None - } - } -} impl<'a> Token<'a> { - pub const fn new (source: &'a str, kind: TokenKind<'a>, start: usize, length: usize) -> Self { - Self { source, kind, start, length } + pub const fn new (source: &'a str, value: Value<'a>, start: usize, length: usize) -> Self { + Self { source, value, start, length } } pub const fn end (&self) -> usize { self.start + self.length @@ -43,131 +28,34 @@ 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 { - self.kind + pub const fn value (&self) -> Value { + self.value } pub const fn error (self, error: ParseError) -> Self { - Self { kind: TokenKind::Err(error), ..self } + Self { value: Value::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 }; - match self.kind { - TokenKind::Exp(depth, source) => Self { - kind: TokenKind::Exp(depth + 1, TokenIter(self.grow().slice_source(source.0))), + match self.value { + Value::Exp(depth, source) => Self { + value: Value::Exp(depth + 1, TokenIter(self.grow().slice_source(source.0))), ..self.grow() }, _ => self.grow() } } pub const fn grow_out (self) -> Self { - match self.kind { - TokenKind::Exp(depth, source) => match depth { + match self.value { + Value::Exp(depth, source) => match depth { 0 => self.error(Unexpected(')')), d => Self { - kind: TokenKind::Exp(d - 1, TokenIter(self.grow().slice_source(source.0))), + value: Value::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), - } -} -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, - Result::Err(e) => return Result::Err(e) - }); - 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 Result::Err(Unexpected(c)) - }) -} -#[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/input/src/keymap.rs b/input/src/keymap.rs index c769cd3a..2c95d1c5 100644 --- a/input/src/keymap.rs +++ b/input/src/keymap.rs @@ -1,91 +1,38 @@ use crate::*; -pub trait KeyMap { - /// Try to find a command that matches the currently pressed key - fn command > (&self, state: &S, input: &impl AtomInput) -> Option; +/// [Input] state that can be matched against an [Atom]. +pub trait AtomInput: Input { + fn matches_atom (&self, token: &str) -> bool; } -//pub struct SourceKeyMap<'a>(&'a str); -//impl<'a> KeyMap for SourceKeyMap<'a> { - //fn command > (&self, state: &S, input: &impl AtomInput) -> Option { - //todo!(); - //None - //} -//} -//pub struct ParsedKeyMap<'a>(TokensIterator<'a>); -//impl<'a> KeyMap for ParsedKeyMap<'a> { - //fn command > (&self, state: &S, input: &impl AtomInput) -> Option { - //todo!(); - //None - //} -//} -//pub struct RefKeyMap<'a>(TokensIterator<'a>); -//impl<'a> KeyMap for RefKeyMap<'a> { - //fn command > (&self, state: &S, input: &impl AtomInput) -> Option { - //todo!(); - ////for token in self.0 { - ////match token?.kind() { - ////TokenKind::Exp => match atoms.as_slice() { - ////[key, command, args @ ..] => match (key.kind(), key.text()) { - ////(TokenKind::Sym, key) => { - ////if input.matches_atom(key) { - ////let command = C::from_atom(state, command, args); - ////if command.is_some() { - ////return command - ////} - ////} - ////}, - ////_ => panic!("invalid config: {item}") - ////}, - ////_ => panic!("invalid config: {item}") - ////} - ////_ => panic!("invalid config: {item}") - ////} - ////} - //None - //} -//} -pub struct ArcKeyMap(Vec); -impl KeyMap for ArcKeyMap { - fn command > (&self, state: &S, input: &impl AtomInput) -> Option { - for atom in self.0.iter() { - match atom { - ArcAtom::Exp(atoms) => match atoms.as_slice() { - [key, command, args @ ..] => match (key.kind(), key.text()) { - (TokenKind::Sym, key) => { - if input.matches_atom(key) { - let command = C::from_atom(state, command, args); - if command.is_some() { - return command - } - } - }, - _ => panic!("invalid config: {atom}") - }, - _ => panic!("invalid config: {atom}") +pub trait KeyMap { + /// Try to find a command that matches the current input event. + fn command , I: AtomInput> (&self, state: &S, input: &I) -> Option; +} +impl KeyMap for TokenIter<'_> { + fn command , I: AtomInput> (&self, state: &S, input: &I) -> Option { + for token in *self { + if let Value::Exp(0, iter) = token.value() { + if let Some((Token { value: Value::Sym(binding), .. }, _)) = iter.next() { + if input.matches_atom(binding) { + if let Some(command) = C::try_from_expr(state, iter.clone()) { + return Some(command) + } + } + } else { + panic!("invalid config: {token:?}") } - _ => panic!("invalid config: {atom}") + } else { + panic!("invalid config: {token:?}") } } None } } -/// [Input] state that can be matched against an [Atom]. -pub trait AtomInput: Input { - fn matches_atom (&self, token: &str) -> bool; - fn get_event <'a> (_: &impl Atom) -> Option { - None - } -} -/// Turns an EDN item sequence into a command enum variant. -pub trait AtomCommand: Command { - fn from_atom <'a> ( - state: &C, - head: &impl Atom, - tail: &'a [impl Atom] - ) -> Option; -} +/// A [Command] that can be constructed from a [Token]. +pub trait AtomCommand: TryFromAtom + Command {} +impl + Command> AtomCommand for T {} /** Implement `AtomCommand` for given `State` and `Command` */ #[macro_export] macro_rules! atom_command { - ($Command:ty : |$state:ident:<$T:ident: $Trait:path>| { $(( + ($Command:ty : |$state:ident:<$State:ident: $Trait:path>| { $(( // identifier $key:literal [ // named parameters @@ -106,22 +53,22 @@ pub trait AtomCommand: Command { // bound command: $command:expr ))* }) => { - impl<$T: $Trait> AtomCommand<$T> for $Command { - fn from_atom <'a> ( - $state: &$T, head: &impl Atom, tail: &'a [impl Atom] - ) -> Option { - $(if let (TokenKind::Key, $key, [ // if the identifier matches - // bind argument ids - $($arg),* - // bind rest parameters - $(, $rest @ ..)? - ]) = (head.kind(), head.text(), tail) { - $( - $(let $arg: Option<$type> = Context::<$type>::get($state, $arg);)? - )* - //$(atom_command!(@bind $state => $arg $(?)? : $type);)* - return Some($command) - })* + impl<$State: $Trait> TryFromAtom<$State> for $Command { + fn try_from_expr ($state: &$State, iter: TokenIter) -> Option { + match $iter.next() { + $(Some((Token { value: Value::Key($key), .. }, _)) => { + let iter = iter.clone(); + $( + let next = iter.next(); + if next.is_none() { panic!("no argument: {}", stringify!($arg)); } + let ($arg, _) = next.unwrap(); + $(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? + )* + $(let $rest = iter.clone();)? + return Some($command) + },)* + _ => None + } None } } @@ -147,25 +94,22 @@ pub trait AtomCommand: Command { // bound command: $command:expr ))* }) => { - impl AtomCommand<$State> for $Command { - fn from_atom <'a> ( - $state: &$State, - head: &impl Atom, - tail: &'a [impl Atom], - ) -> Option { - $(if let (TokenKind::Key, $key, [ // if the identifier matches - // bind argument ids - $($arg),* - // bind rest parameters - $(, $rest @ ..)? - ]) = (head.kind(), head.text(), tail) { - $( - $(let $arg: Option<$type> = Context::<$type>::get($state, $arg);)? - )* - //$(atom_command!(@bind $state => $arg $(?)? : $type);)* - return Some($command) - })* - None + impl TryFromAtom<$State> for $Command { + fn try_from_expr ($state: &$State, iter: TokenIter) -> Option { + match iter.next() { + $(Some((Token { value: Value::Key($key), .. }, _)) => { + let iter = iter.clone(); + $( + let next = iter.next(); + if next.is_none() { panic!("no argument: {}", stringify!($arg)); } + let ($arg, _) = next.unwrap(); + $(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? + )* + $(let $rest = iter.clone();)? + return Some($command) + }),* + _ => None + } } } }; @@ -176,6 +120,71 @@ pub trait AtomCommand: Command { let $arg: $type = Context::<$type>::get_or_fail($state, $arg); }; } +//pub struct SourceKeyMap<'a>(&'a str); +//impl<'a> KeyMap for SourceKeyMap<'a> { + //fn command > (&self, state: &S, input: &AtomInput) -> Option { + //todo!(); + //None + //} +//} +//pub struct ParsedKeyMap<'a>(TokensIterator<'a>); +//impl<'a> KeyMap for ParsedKeyMap<'a> { + //fn command > (&self, state: &S, input: &AtomInput) -> Option { + //todo!(); + //None + //} +//} +//pub struct RefKeyMap<'a>(TokensIterator<'a>); +//impl<'a> KeyMap for RefKeyMap<'a> { + //fn command > (&self, state: &S, input: &AtomInput) -> Option { + //todo!(); + ////for token in self.0 { + ////match token?.kind() { + ////TokenKind::Exp => match atoms.as_slice() { + ////[key, command, args @ ..] => match (key.kind(), key.text()) { + ////(TokenKind::Sym, key) => { + ////if input.matches_atom(key) { + ////let command = C::from_atom(state, command, args); + ////if command.is_some() { + ////return command + ////} + ////} + ////}, + ////_ => panic!("invalid config: {item}") + ////}, + ////_ => panic!("invalid config: {item}") + ////} + ////_ => panic!("invalid config: {item}") + ////} + ////} + //None + //} +//} +//pub struct ArcKeyMap(Vec); +//impl KeyMap for ArcKeyMap { + //fn command > (&self, state: &S, input: &AtomInput) -> Option { + //for atom in self.0.iter() { + //match atom { + //ArcAtom::Exp(atoms) => match atoms.as_slice() { + //[key, command, args @ ..] => match (key.kind(), key.text()) { + //(TokenKind::Sym, key) => { + //if input.matches_atom(key) { + //let command = C::from_atom(state, command, args); + //if command.is_some() { + //return command + //} + //} + //}, + //_ => panic!("invalid config: {atom}") + //}, + //_ => panic!("invalid config: {atom}") + //} + //_ => panic!("invalid config: {atom}") + //} + //} + //None + //} +//} #[cfg(test)] #[test] fn test_atom_keymap () -> Usually<()> { let keymap = KeyMap::new("")?; Ok(()) diff --git a/midi/src/midi_edit.rs b/midi/src/midi_edit.rs index e7ad2ed6..cfb20ace 100644 --- a/midi/src/midi_edit.rs +++ b/midi/src/midi_edit.rs @@ -1,4 +1,5 @@ use crate::*; +use Value::*; pub trait HasEditor { fn editor (&self) -> &Option; fn editor_mut (&mut self) -> &Option; @@ -31,6 +32,7 @@ pub trait HasEditor { pub struct MidiEditor { pub mode: PianoHorizontal, pub size: Measure, + keys: TokenIter<'static> } has_size!(|self: MidiEditor|&self.size); content!(TuiOut: |self: MidiEditor| { @@ -90,6 +92,7 @@ impl Default for MidiEditor { Self { mode: PianoHorizontal::new(None), size: Measure::new(), + keys: TokenIter(KEYS_EDIT), } } } @@ -207,27 +210,8 @@ atom_command!(MidiEditCommand: |state: MidiEditor| { SetTimeLock(bool), Show(Option>>), } -impl MidiEditCommand { - fn from_tui_event (state: &MidiEditor, input: &impl AtomInput) -> Usually> { - use AtomItem::*; - let atoms = AtomItem::read_all(KEYS_EDIT)?; - for atom in atoms.iter() { - if let Exp(e) = atom { - match e.as_slice() { - [Sym(key), command, args @ ..] if input.matches_atom(key) => { - return Ok(MidiEditCommand::from_atom(state, command, args)) - } - _ => {} - } - } else { - panic!("invalid config") - } - } - Ok(None) - } -} handle!(TuiIn: |self: MidiEditor, input|{ - Ok(if let Some(command) = MidiEditCommand::from_tui_event(self, input)? { + Ok(if let Some(command) = self.keys.command::<_, MidiEditCommand, _>(self, input) { let _undo = command.execute(self)?; Some(true) } else { diff --git a/midi/src/midi_pool.rs b/midi/src/midi_pool.rs index b6085d81..c9b6a33f 100644 --- a/midi/src/midi_pool.rs +++ b/midi/src/midi_pool.rs @@ -30,6 +30,11 @@ pub struct MidiPool { pub clip: AtomicUsize, /// Mode switch pub mode: Option, + + keys: TokenIter<'static>, + keys_rename: TokenIter<'static>, + keys_length: TokenIter<'static>, + keys_file: TokenIter<'static>, } /// Modes for clip pool #[derive(Debug, Clone)] @@ -50,6 +55,10 @@ impl Default for MidiPool { clips: Arc::from(RwLock::from(vec![])), clip: 0.into(), mode: None, + keys: TokenIter(KEYS_POOL), + keys_rename: TokenIter(KEYS_RENAME), + keys_length: TokenIter(KEYS_LENGTH), + keys_file: TokenIter(KEYS_FILE), } } } @@ -189,31 +198,13 @@ content!(TuiOut: |self: ClipLength| { row!(" ", bars(), ".", beats(), "[", ticks()), } }); -impl PoolCommand { - pub fn from_tui_event (state: &MidiPool, input: &impl AtomInput) -> Usually> { - use AtomItem::*; - let atoms: Vec> = AtomItem::read_all(match state.mode() { - Some(PoolMode::Rename(..)) => KEYS_RENAME, - Some(PoolMode::Length(..)) => KEYS_LENGTH, - Some(PoolMode::Import(..)) | Some(PoolMode::Export(..)) => KEYS_FILE, - _ => KEYS_POOL - })?; - for item in atoms { - match item { - Exp(e) => match e.as_slice() { - [Sym(key), command, args @ ..] if input.matches_atom(key) => { - return Ok(PoolCommand::from_atom(state, command, args)) - } - _ => {} - }, - _ => panic!("invalid config") - } - } - Ok(None) - } -} handle!(TuiIn: |self: MidiPool, input|{ - Ok(if let Some(command) = PoolCommand::from_tui_event(self, input)? { + Ok(if let Some(command) = match self.mode() { + Some(PoolMode::Rename(..)) => self.keys_rename, + Some(PoolMode::Length(..)) => self.keys_length, + Some(PoolMode::Import(..)) | Some(PoolMode::Export(..)) => self.keys_file, + _ => self.keys + }.command::(self, input) { let _undo = command.execute(self)?; Some(true) } else { @@ -276,11 +267,11 @@ provide!(ItemColor: |self: MidiPool| { atom_command!(PoolCommand: |state: MidiPool| { ("show" [a: bool] Self::Show(a.expect("no flag"))) ("select" [i: usize] Self::Select(i.expect("no index"))) - ("rename" [a, ..b] ClipRenameCommand::from_atom(state, &a.to_ref(), b).map(Self::Rename).expect("invalid command")) - ("length" [a, ..b] ClipLengthCommand::from_atom(state, &a.to_ref(), b).map(Self::Length).expect("invalid command")) - ("import" [a, ..b] FileBrowserCommand::from_atom(state, &a.to_ref(), b).map(Self::Import).expect("invalid command")) - ("export" [a, ..b] FileBrowserCommand::from_atom(state, &a.to_ref(), b).map(Self::Export).expect("invalid command")) - ("clip" [a, ..b] PoolClipCommand::from_atom(state, &a.to_ref(), b).map(Self::Clip).expect("invalid command")) + ("rename" [,..a] ClipRenameCommand::try_from_expr(state, a).map(Self::Rename).expect("invalid command")) + ("length" [,..a] ClipLengthCommand::try_from_expr(state, a).map(Self::Length).expect("invalid command")) + ("import" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Import).expect("invalid command")) + ("export" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Export).expect("invalid command")) + ("clip" [,..a] PoolClipCommand::try_from_expr(state, a).map(Self::Clip).expect("invalid command")) }); command!(|self: PoolCommand, state: MidiPool|{ use PoolCommand::*; diff --git a/output/src/op_align.rs b/output/src/op_align.rs index 7ec5264c..95918bc4 100644 --- a/output/src/op_align.rs +++ b/output/src/op_align.rs @@ -1,22 +1,22 @@ use crate::*; #[derive(Debug, Copy, Clone, Default)] pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } pub struct Align(Alignment, A); -try_from_atoms!(<'a, E>: Align>: |state, atoms| { - let head = atoms.next()?; - if head.kind() != TokenKind::Key { return None } - match head.text() { - "align/c" => return Some(Self::c(state.get_content(&atoms.next()?).expect("no content"))), - "align/x" => return Some(Self::x(state.get_content(&atoms.next()?).expect("no content"))), - "align/y" => return Some(Self::y(state.get_content(&atoms.next()?).expect("no content"))), - "align/n" => return Some(Self::n(state.get_content(&atoms.next()?).expect("no content"))), - "align/s" => return Some(Self::s(state.get_content(&atoms.next()?).expect("no content"))), - "align/e" => return Some(Self::e(state.get_content(&atoms.next()?).expect("no content"))), - "align/w" => return Some(Self::w(state.get_content(&atoms.next()?).expect("no content"))), - "align/nw" => return Some(Self::nw(state.get_content(&atoms.next()?).expect("no content"))), - "align/ne" => return Some(Self::ne(state.get_content(&atoms.next()?).expect("no content"))), - "align/sw" => return Some(Self::sw(state.get_content(&atoms.next()?).expect("no content"))), - "align/se" => return Some(Self::se(state.get_content(&atoms.next()?).expect("no content"))), - _ => {} +try_from_expr!(<'a, E>: Align>: |state, iter| { + if let Some((Token { value: Value::Key(key), .. }, _)) = iter.next() { + match key { + "align/c" => return Some(Self::c(state.get_content(&iter.next()?.0.value).expect("no content"))), + "align/x" => return Some(Self::x(state.get_content(&iter.next()?.0.value).expect("no content"))), + "align/y" => return Some(Self::y(state.get_content(&iter.next()?.0.value).expect("no content"))), + "align/n" => return Some(Self::n(state.get_content(&iter.next()?.0.value).expect("no content"))), + "align/s" => return Some(Self::s(state.get_content(&iter.next()?.0.value).expect("no content"))), + "align/e" => return Some(Self::e(state.get_content(&iter.next()?.0.value).expect("no content"))), + "align/w" => return Some(Self::w(state.get_content(&iter.next()?.0.value).expect("no content"))), + "align/nw" => return Some(Self::nw(state.get_content(&iter.next()?.0.value).expect("no content"))), + "align/ne" => return Some(Self::ne(state.get_content(&iter.next()?.0.value).expect("no content"))), + "align/sw" => return Some(Self::sw(state.get_content(&iter.next()?.0.value).expect("no content"))), + "align/se" => return Some(Self::se(state.get_content(&iter.next()?.0.value).expect("no content"))), + _ => {} + } } }); impl Align { diff --git a/output/src/op_bsp.rs b/output/src/op_bsp.rs index 45d40061..a0925ef7 100644 --- a/output/src/op_bsp.rs +++ b/output/src/op_bsp.rs @@ -31,35 +31,35 @@ impl, B: Content> Content for Bsp { } } } -try_from_atoms!(<'a, E>: Bsp, RenderBox<'a, E>>: |state, atoms| { - let head = atoms.next()?; - if head.kind() != TokenKind::Key { return None } - match head.text() { - "bsp/n" => return Some(Self::n( - state.get_content(&atoms.next()?).expect("no south"), - state.get_content(&atoms.next()?).expect("no north") - )), - "bsp/s" => return Some(Self::s( - state.get_content(&atoms.next()?).expect("no north"), - state.get_content(&atoms.next()?).expect("no south") - )), - "bsp/e" => return Some(Self::e( - state.get_content(&atoms.next()?).expect("no west"), - state.get_content(&atoms.next()?).expect("no east") - )), - "bsp/w" => return Some(Self::w( - state.get_content(&atoms.next()?).expect("no east"), - state.get_content(&atoms.next()?).expect("no west") - )), - "bsp/a" => return Some(Self::a( - state.get_content(&atoms.next()?).expect("no above"), - state.get_content(&atoms.next()?).expect("no below") - )), - "bsp/b" => return Some(Self::b( - state.get_content(&atoms.next()?).expect("no above"), - state.get_content(&atoms.next()?).expect("no below") - )), - _ => {} +try_from_expr!(<'a, E>: Bsp, RenderBox<'a, E>>: |state, iter| { + if let Some((Token { value: Value::Key(key), .. }, _)) = iter.next() { + match key { + "bsp/n" => return Some(Self::n( + state.get_content(&iter.next()?.0.value).expect("no south"), + state.get_content(&iter.next()?.0.value).expect("no north") + )), + "bsp/s" => return Some(Self::s( + state.get_content(&iter.next()?.0.value).expect("no north"), + state.get_content(&iter.next()?.0.value).expect("no south") + )), + "bsp/e" => return Some(Self::e( + state.get_content(&iter.next()?.0.value).expect("no west"), + state.get_content(&iter.next()?.0.value).expect("no east") + )), + "bsp/w" => return Some(Self::w( + state.get_content(&iter.next()?.0.value).expect("no east"), + state.get_content(&iter.next()?.0.value).expect("no west") + )), + "bsp/a" => return Some(Self::a( + state.get_content(&iter.next()?.0.value).expect("no above"), + state.get_content(&iter.next()?.0.value).expect("no below") + )), + "bsp/b" => return Some(Self::b( + state.get_content(&iter.next()?.0.value).expect("no above"), + state.get_content(&iter.next()?.0.value).expect("no below") + )), + _ => {} + } } }); impl Bsp { diff --git a/output/src/op_cond.rs b/output/src/op_cond.rs index 48757565..f8b8f321 100644 --- a/output/src/op_cond.rs +++ b/output/src/op_cond.rs @@ -5,29 +5,29 @@ impl When { pub fn new (c: bool, a: A) -> Self { Self(c, a) } } /// Show one item if a condition is true and another if the condition is false pub struct Either(pub bool, pub A, pub B); impl Either { pub fn new (c: bool, a: A, b: B) -> Self { Self(c, a, b) } } -try_from_atoms!(<'a, E>: When>: |state, atoms| { - let head = atoms.next()?; - if (head.kind(), head.text()) == (TokenKind::Key, "when") { - let condition = atoms.next(); - if let Some(ref condition) = condition { - let condition = state.get_bool(condition).expect("no condition"); - if let Some(ref content) = atoms.next() { - let content = state.get_content(content).expect("no atom"); +try_from_expr!(<'a, E>: When>: |state, iter| { + if let Some((Token { value: Value::Key("when"), .. }, _)) = iter.next() { + let iter = iter.clone(); + let condition = iter.next(); + if let Some((ref condition, _)) = condition { + let condition = state.get_bool(&condition.value).expect("no condition"); + if let Some((ref content, _)) = iter.next() { + let content = state.get_content(&content.value).expect("no atom"); return Some(Self(condition, content)) } } } }); -try_from_atoms!(<'a, E>: Either, RenderBox<'a, E>>: |state, atoms| { - let head = atoms.next()?; - if (head.kind(), head.text()) == (TokenKind::Key, "either") { - let condition = atoms.next(); - if let Some(ref condition) = condition { - let condition = state.get_bool(condition).expect("no condition"); - if let Some(ref content1) = atoms.next() { - let content1 = state.get_content(content1).expect("no content1"); - if let Some(ref content2) = atoms.next() { - let content2 = state.get_content(content2).expect("no content2"); +try_from_expr!(<'a, E>: Either, RenderBox<'a, E>>: |state, iter| { + if let Some((Token { value: Value::Key("either"), .. }, _)) = iter.next() { + let iter = iter.clone(); + let condition = iter.next(); + if let Some((ref condition, _)) = condition { + let condition = state.get_bool(&condition.value).expect("no condition"); + if let Some((ref content1, _)) = iter.next() { + let content1 = state.get_content(&content1.value).expect("no content1"); + if let Some((ref content2, _)) = iter.next() { + let content2 = state.get_content(&content2.value).expect("no content2"); return Some(Self(condition, content1, content2)) } } diff --git a/output/src/op_iter.rs b/output/src/op_iter.rs index 8096592c..378fdf69 100644 --- a/output/src/op_iter.rs +++ b/output/src/op_iter.rs @@ -1,5 +1,4 @@ use crate::*; -use RefAtom::*; pub fn map_south( item_offset: O::Unit, item_height: O::Unit, diff --git a/output/src/op_transform.rs b/output/src/op_transform.rs index 7b31ccec..23edc9be 100644 --- a/output/src/op_transform.rs +++ b/output/src/op_transform.rs @@ -1,5 +1,4 @@ use crate::*; -use RefAtom::*; /// Defines an enum that transforms its content /// along either the X axis, the Y axis, or both. /// @@ -13,21 +12,17 @@ macro_rules! transform_xy { pub fn y (item: T) -> Self { Self::Y(item) } pub fn xy (item: T) -> Self { Self::XY(item) } } - impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum> { - fn try_from_atoms (state: &'a T, mut atoms: impl Iterator> + 'a) -> Option { - let head = atoms.next()?; - if head.kind() != TokenKind::Key { return None } - Some(match head.text() { - $x => Self::x( - state.get_content(&atoms.next().expect("no content")).expect("no content") - ), - $y => Self::y( - state.get_content(&atoms.next().expect("no content")).expect("no content") - ), - $xy => Self::xy( - state.get_content(&atoms.next().expect("no content")).expect("no content") - ), - _ => return None + impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom + for $Enum> { + fn try_from_expr (state: &T, iter: TokenIter) -> Option { + Some(if let Some((Token { value: Value::Key($x), .. }, _)) = iter.next() { + Self::x(state.get_content(&iter.next().expect("no content").0.value).expect("no content")) + } else if let Some((Token { value: Value::Key($y), .. }, _)) = iter.next() { + Self::y(state.get_content(&iter.next().expect("no content").0.value).expect("no content")) + } else if let Some((Token { value: Value::Key($xy), .. }, _)) = iter.next() { + Self::xy(state.get_content(&iter.next().expect("no content").0.value).expect("no content")) + } else { + return None }) } } @@ -56,25 +51,24 @@ macro_rules! transform_xy_unit { pub fn y (y: U, item: T) -> Self { Self::Y(y, item) } pub fn xy (x: U, y: U, item: T) -> Self { Self::XY(x, y, item) } } - impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum> { - fn try_from_atoms (state: &'a T, mut atoms: impl Iterator> + 'a) -> Option { - let head = atoms.next()?; - if head.kind() != TokenKind::Key { return None } - Some(match head.text() { - $x => Self::x( - state.get_unit(&atoms.next().expect("no x")).expect("no x"), - state.get_content(&atoms.next().expect("no content")).expect("no content") - ), - $y => Self::y( - state.get_unit(&atoms.next().expect("no y")).expect("no y"), - state.get_content(&atoms.next().expect("no content")).expect("no content") - ), - $xy => Self::xy( - state.get_unit(&atoms.next().expect("no x")).expect("no x"), - state.get_unit(&atoms.next().expect("no y")).expect("no y"), - state.get_content(&atoms.next().expect("no content")).expect("no content"), - ), - _ => return None + impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom + for $Enum> { + fn try_from_expr (state: &T, iter: TokenIter) -> Option { + Some(if let Some((Token { value: Value::Key($x), .. }, _)) = iter.next() { + let x = state.get_unit(&iter.next().expect("no x").0.value).expect("no x"); + let c = state.get_content(&iter.next().expect("no content").0.value).expect("no content"); + Self::x(x, c) + } else if let Some((Token { value: Value::Key($y), .. }, _)) = iter.next() { + let y = state.get_unit(&iter.next().expect("no y").0.value).expect("no y"); + let c = state.get_content(&iter.next().expect("no content").0.value).expect("no content"); + Self::y(y, c) + } else if let Some((Token { value: Value::Key($xy), .. }, _)) = iter.next() { + let x = state.get_unit(&iter.next().expect("no x").0.value).expect("no x"); + let y = state.get_unit(&iter.next().expect("no y").0.value).expect("no y"); + let c = state.get_content(&iter.next().expect("no content").0.value).expect("no content"); + Self::xy(x, y, c) + } else { + return None }) } } diff --git a/output/src/view.rs b/output/src/view.rs index 6d40f0fe..1362f3d0 100644 --- a/output/src/view.rs +++ b/output/src/view.rs @@ -1,6 +1,6 @@ use crate::*; use std::{sync::Arc, fmt::Debug}; -use TokenKind::*; +use Value::*; /// Define an EDN-backed view. /// /// This consists of: @@ -14,8 +14,8 @@ use TokenKind::*; fn content(&$self) -> impl Render<$Output> { $content } } $( - impl<'a> Context<'a, $type> for $App { - fn get (&'a $self, atom: &'a impl Atom) -> Option<$type> { + impl Context<$type> for $App { + fn get (&$self, atom: &Value) -> Option<$type> { Some(match atom.to_ref() { $(Atom::Sym($sym) => $value,)* _ => return None }) } } @@ -26,7 +26,7 @@ use TokenKind::*; #[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>> { + fn get (&$self, atom: &Value) -> Option + 'a>> { Some(match atom.to_ref() { $(Atom::Sym($pat) => $expr),*, _ => return None @@ -36,7 +36,7 @@ use TokenKind::*; }; ($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>> { + fn get (&$self, atom: &Value) -> Option + 'a>> { Some(match atom.to_ref() { $(Atom::Sym($pat) => $expr),*, _ => return None @@ -79,7 +79,7 @@ where S: Debug, A: Debug { } } impl<'a, O, S, A> Content for AtomView<'a, O, S, A> -where O: Output, S: ViewContext<'a, O>, A: Atom { +where O: Output, S: ViewContext<'a, O>, A: Send + Sync { fn content (&self) -> impl Render { match self { Self::Inert => { panic!("inert rendered") }, @@ -103,49 +103,43 @@ pub trait ViewContext<'a, E: Output>: Sized + Send + Sync + Context + Context + 'a>> { - fn get_bool (&self, atom: &impl Atom) -> Option { - Some(match atom.kind() { - Num => match atom.num() { 0 => false, _ => true }, - Sym => match atom.text() { + fn get_bool (&self, atom: &Value) -> Option { + Some(match atom { + Num(n) => match *n { 0 => false, _ => true }, + Sym(x) => match *x { ":false" | ":f" => false, ":true" | ":t" => true, _ => return Context::get(self, atom) }, _ => return Context::get(self, atom) }) } - fn get_usize (&self, atom: &impl Atom) -> Option { - Some(match atom.kind() { - Num => atom.num(), - _ => return Context::get(self, atom) - }) + fn get_usize (&self, atom: &Value) -> Option { + Some(match atom {Num(n) => *n, _ => return Context::get(self, atom)}) } - fn get_unit (&self, atom: &impl Atom) -> Option { - Some(match atom.kind() { - Num => E::Unit::from(atom.num() as u16), - _ => return Context::get(self, atom) - }) + fn get_unit (&self, atom: &Value) -> Option { + Some(match atom {Num(n) => E::Unit::from(*n as u16), _ => return Context::get(self, atom)}) } - fn get_content (&self, atom: &impl Atom) -> Option + 'a>> where E: 'a { - try_delegate!(self, atom, When::>); - try_delegate!(self, atom, Either::, RenderBox<'a, E>>); - try_delegate!(self, atom, Align::>); - try_delegate!(self, atom, Bsp::, RenderBox<'a, E>>); - try_delegate!(self, atom, Fill::>); - try_delegate!(self, atom, Fixed::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Min::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Max::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Shrink::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Expand::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Push::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Pull::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Margin::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Padding::<_, RenderBox<'a, E>>); + fn get_content (&self, atom: &Value) -> Option + 'a>> where E: 'a { + try_delegate!(self, *atom, When::>); + try_delegate!(self, *atom, Either::, RenderBox<'a, E>>); + try_delegate!(self, *atom, Align::>); + try_delegate!(self, *atom, Bsp::, RenderBox<'a, E>>); + try_delegate!(self, *atom, Fill::>); + try_delegate!(self, *atom, Fixed::<_, RenderBox<'a, E>>); + try_delegate!(self, *atom, Min::<_, RenderBox<'a, E>>); + try_delegate!(self, *atom, Max::<_, RenderBox<'a, E>>); + try_delegate!(self, *atom, Shrink::<_, RenderBox<'a, E>>); + try_delegate!(self, *atom, Expand::<_, RenderBox<'a, E>>); + try_delegate!(self, *atom, Push::<_, RenderBox<'a, E>>); + try_delegate!(self, *atom, Pull::<_, RenderBox<'a, E>>); + try_delegate!(self, *atom, Margin::<_, RenderBox<'a, E>>); + try_delegate!(self, *atom, Padding::<_, RenderBox<'a, E>>); Some(Context::get_or_fail(self, atom)) } } #[macro_export] macro_rules! try_delegate { ($s:ident, $atom:expr, $T:ty) => { - if let Some(value) = <$T>::try_from_atoms($s, $atom) { + if let Some(value) = <$T>::try_from_atom($s, $atom) { return Some(value.boxed()) } } @@ -157,17 +151,13 @@ pub type AtomCallback<'a, O, State> = pub type AtomRenderCallback<'a, O, State> = Box>; -pub trait TryFromAtom<'a, T>: Sized { - fn try_from_atoms (state: &'a T, atoms: impl Iterator> + 'a) -> Option; -} -pub trait TryIntoAtom<'a, T>: Sized { - fn try_into_atoms (&self) -> Option>; -} -#[macro_export] macro_rules! try_from_atoms { - (<$l:lifetime, $E:ident>: $Struct:ty: |$state:ident,$atoms:ident|$body:expr) => { - impl<$l, $E: Output + $l, T: ViewContext<$l, $E>> TryFromAtom<$l, T> for $Struct { - fn try_from_atoms ($state: &$l T, mut $atoms: impl Iterator> + $l) -> Option { - $body; +#[macro_export] macro_rules! try_from_expr { + (<$l:lifetime, $E:ident>: $Struct:ty: |$state:ident, $atoms:ident|$body:expr) => { + impl<$l, $E: Output + $l, T: ViewContext<$l, $E>> TryFromAtom for $Struct { + fn try_from_atom ($state: &T, atom: Value) -> Option { + if let Value::Exp(0, $atoms) = atom { + $body; + } None } } diff --git a/plugin/src/plugin.rs b/plugin/src/plugin.rs index 9953b62c..29daa778 100644 --- a/plugin/src/plugin.rs +++ b/plugin/src/plugin.rs @@ -257,19 +257,19 @@ fn draw_header (state: &Plugin, to: &mut TuiOut, x: u16, y: u16, w: u16) { //} //}); -from_atom!("plugin/lv2" => |jack: &Arc>, args| -> Plugin { - let mut name = String::new(); - let mut path = String::new(); - atom!(atom in args { - Atom::Map(map) => { - if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) { - name = String::from(*n); - } - if let Some(Atom::Str(p)) = map.get(&Atom::Key(":path")) { - path = String::from(*p); - } - }, - _ => panic!("unexpected in lv2 '{name}'"), - }); - Plugin::new_lv2(jack, &name, &path) -}); +//from_atom!("plugin/lv2" => |jack: &Arc>, args| -> Plugin { + //let mut name = String::new(); + //let mut path = String::new(); + //atom!(atom in args { + //Atom::Map(map) => { + //if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) { + //name = String::from(*n); + //} + //if let Some(Atom::Str(p)) = map.get(&Atom::Key(":path")) { + //path = String::from(*p); + //} + //}, + //_ => panic!("unexpected in lv2 '{name}'"), + //}); + //Plugin::new_lv2(jack, &name, &path) +//}); diff --git a/sampler/src/sampler.rs b/sampler/src/sampler.rs index f47b617b..26578852 100644 --- a/sampler/src/sampler.rs +++ b/sampler/src/sampler.rs @@ -349,66 +349,66 @@ impl Sampler { } /////////////////////////////////////////////////////////////////////////////////////////////////// type MidiSample = (Option, Arc>); -from_atom!("sampler" => |jack: &Arc>, args| -> crate::Sampler { - let mut name = String::new(); - let mut dir = String::new(); - let mut samples = BTreeMap::new(); - atom!(atom in args { - Atom::Map(map) => { - if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) { - name = String::from(*n); - } - if let Some(Atom::Str(n)) = map.get(&Atom::Key(":dir")) { - dir = String::from(*n); - } - }, - Atom::List(args) => match args.first() { - Some(Atom::Symbol("sample")) => { - let (midi, sample) = MidiSample::from_atom((jack, &dir), &args[1..])?; - if let Some(midi) = midi { - samples.insert(midi, sample); - } else { - panic!("sample without midi binding: {}", sample.read().unwrap().name); - } - }, - _ => panic!("unexpected in sampler {name}: {args:?}") - }, - _ => panic!("unexpected in sampler {name}: {atom:?}") - }); - Self::new(jack, &name) -}); -from_atom!("sample" => |(_jack, dir): (&Arc>, &str), args| -> MidiSample { - let mut name = String::new(); - let mut file = String::new(); - let mut midi = None; - let mut start = 0usize; - atom!(atom in args { - Atom::Map(map) => { - if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) { - name = String::from(*n); - } - if let Some(Atom::Str(f)) = map.get(&Atom::Key(":file")) { - file = String::from(*f); - } - if let Some(Atom::Int(i)) = map.get(&Atom::Key(":start")) { - start = *i as usize; - } - if let Some(Atom::Int(m)) = map.get(&Atom::Key(":midi")) { - midi = Some(u7::from(*m as u8)); - } - }, - _ => panic!("unexpected in sample {name}"), - }); - let (end, data) = Sample::read_data(&format!("{dir}/{file}"))?; - Ok((midi, Arc::new(RwLock::new(crate::Sample { - name, - start, - end, - channels: data, - rate: None, - gain: 1.0 - })))) -}); +//from_atom!("sampler" => |jack: &Arc>, args| -> crate::Sampler { + //let mut name = String::new(); + //let mut dir = String::new(); + //let mut samples = BTreeMap::new(); + //atom!(atom in args { + //Atom::Map(map) => { + //if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) { + //name = String::from(*n); + //} + //if let Some(Atom::Str(n)) = map.get(&Atom::Key(":dir")) { + //dir = String::from(*n); + //} + //}, + //Atom::List(args) => match args.first() { + //Some(Atom::Symbol("sample")) => { + //let (midi, sample) = MidiSample::from_atom((jack, &dir), &args[1..])?; + //if let Some(midi) = midi { + //samples.insert(midi, sample); + //} else { + //panic!("sample without midi binding: {}", sample.read().unwrap().name); + //} + //}, + //_ => panic!("unexpected in sampler {name}: {args:?}") + //}, + //_ => panic!("unexpected in sampler {name}: {atom:?}") + //}); + //Self::new(jack, &name) +//}); +//from_atom!("sample" => |(_jack, dir): (&Arc>, &str), args| -> MidiSample { + //let mut name = String::new(); + //let mut file = String::new(); + //let mut midi = None; + //let mut start = 0usize; + //atom!(atom in args { + //Atom::Map(map) => { + //if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) { + //name = String::from(*n); + //} + //if let Some(Atom::Str(f)) = map.get(&Atom::Key(":file")) { + //file = String::from(*f); + //} + //if let Some(Atom::Int(i)) = map.get(&Atom::Key(":start")) { + //start = *i as usize; + //} + //if let Some(Atom::Int(m)) = map.get(&Atom::Key(":midi")) { + //midi = Some(u7::from(*m as u8)); + //} + //}, + //_ => panic!("unexpected in sample {name}"), + //}); + //let (end, data) = Sample::read_data(&format!("{dir}/{file}"))?; + //Ok((midi, Arc::new(RwLock::new(crate::Sample { + //name, + //start, + //end, + //channels: data, + //rate: None, + //gain: 1.0 + //})))) +//}); impl Iterator for Voice { type Item = [f32;2]; fn next (&mut self) -> Option { @@ -663,12 +663,12 @@ pub enum SamplerMode { } atom_command!(SamplerTuiCommand: |state: SamplerTui| { ("select" [i: usize] Self::Select(i.expect("no index"))) - ("import" [a, ..b] if let Some(command) = FileBrowserCommand::from_atom(state, a, b) { + ("import" [,..a] if let Some(command) = FileBrowserCommand::try_from_expr(state, a) { Self::Import(command) } else { return None }) - ("sample" [a, ..b] if let Some(command) = SamplerCommand::from_atom(&state.state, a, b) { + ("sample" [,..a] if let Some(command) = SamplerCommand::try_from_expr(&state.state, a) { Self::Sample(command) } else { return None @@ -717,7 +717,7 @@ provide!(u7: |self: Sampler| {}); provide!(Option>>: |self: Sampler| {}); provide!(usize: |self: Sampler| {}); provide!(f32: |self: Sampler| {}); -input_to_command!(FileBrowserCommand: |state:SamplerTui, input: Event|match input { _ => return None }); +//input_to_command!(FileBrowserCommand: |state:SamplerTui, input: Event|match input { _ => return None }); command!(|self: FileBrowserCommand,state:SamplerTui|match self { _ => todo!() }); //input_to_command!(SamplerTuiCommand: |state: SamplerTui, input: Event|match state.mode{ //Some(SamplerMode::Import(..)) => Self::Import( diff --git a/tek/src/lib.rs b/tek/src/lib.rs index deb78e6b..d33cac7e 100644 --- a/tek/src/lib.rs +++ b/tek/src/lib.rs @@ -142,6 +142,12 @@ impl TekCli { pub perf: PerfModel, pub editing: AtomicBool, pub history: Vec, + + keys: TokenIter<'static>, + keys_clip: TokenIter<'static>, + keys_track: TokenIter<'static>, + keys_scene: TokenIter<'static>, + keys_mix: TokenIter<'static>, } has_size!(|self: Tek|&self.size); has_clock!(|self: Tek|self.clock); @@ -643,19 +649,19 @@ atom_command!(TekCommand: |app: Tek| { (0, s) => Self::Select(Selection::Scene(s)), (t, s) => Self::Select(Selection::Clip(t, s)), }) - ("clip" [a, ..b] Self::Clip(ClipCommand::from_atom(app, &a.to_ref(), b) + ("clip" [,..a] Self::Clip(ClipCommand::try_from_expr(app, a) .expect("invalid command"))) - ("clock" [a, ..b] Self::Clock(ClockCommand::from_atom(app.clock(), &a.to_ref(), b) + ("clock" [,..a] Self::Clock(ClockCommand::try_from_expr(app.clock(), a) .expect("invalid command"))) - ("editor" [a, ..b] Self::Editor(MidiEditCommand::from_atom(app.editor.as_ref().expect("no editor"), &a.to_ref(), b) + ("editor" [,..a] Self::Editor(MidiEditCommand::try_from_expr(app.editor.as_ref().expect("no editor"), a) .expect("invalid command"))) - ("pool" [a, ..b] Self::Pool(PoolCommand::from_atom(app.pool.as_ref().expect("no pool"), &a.to_ref(), b) + ("pool" [,..a] Self::Pool(PoolCommand::try_from_expr(app.pool.as_ref().expect("no pool"), a) .expect("invalid command"))) - ("sampler" [a, ..b] Self::Sampler(SamplerCommand::from_atom(app.sampler.as_ref().expect("no sampler"), &a.to_ref(), b) + ("sampler" [,..a] Self::Sampler(SamplerCommand::try_from_expr(app.sampler.as_ref().expect("no sampler"), a) .expect("invalid command"))) - ("scene" [a, ..b] Self::Scene(SceneCommand::from_atom(app, &a.to_ref(), b) + ("scene" [,..a] Self::Scene(SceneCommand::try_from_expr(app, a) .expect("invalid command"))) - ("track" [a, ..b] Self::Track(TrackCommand::from_atom(app, &a.to_ref(), b) + ("track" [,..a] Self::Track(TrackCommand::try_from_expr(app, a) .expect("invalid command"))) }); command!(|self: TekCommand, app: Tek|match self { diff --git a/tui/src/tui_input.rs b/tui/src/tui_input.rs index 71b25a5e..f76a3a3c 100644 --- a/tui/src/tui_input.rs +++ b/tui/src/tui_input.rs @@ -54,9 +54,9 @@ impl AtomInput for TuiIn { false } } - fn get_event (item: &AtomItem>) -> Option { - match item { AtomItem::Sym(s) => KeyMatcher::new(s).build(), _ => None } - } + //fn get_event (item: &AtomItem>) -> Option { + //match item { AtomItem::Sym(s) => KeyMatcher::new(s).build(), _ => None } + //} } struct KeyMatcher { valid: bool,