fixed up some parsing and removed some edn mentions

This commit is contained in:
🪞👃🪞 2025-01-18 01:54:06 +01:00
parent 5e7b867aba
commit 452bdf9598
15 changed files with 290 additions and 292 deletions

View file

@ -30,17 +30,17 @@ macro_rules! iterate {
} }
} }
} }
#[derive(Clone, PartialEq)] pub struct TokenIterator<'a>(&'a str); #[derive(Clone, PartialEq)] pub struct TokensIterator<'a>(&'a str);
impl<'a> Iterator for TokenIterator<'a> { impl<'a> Iterator for TokensIterator<'a> {
type Item = TokenResult<'a>; type Item = TokenResult<'a>;
fn next (&mut self) -> Option<TokenResult<'a>> { self.next_mut().map(|(result, _)|result) } fn next (&mut self) -> Option<TokenResult<'a>> { self.next_mut().map(|(result, _)|result) }
} }
impl<'a> ConstIntoIter for TokenIterator<'a> { impl<'a> ConstIntoIter for TokensIterator<'a> {
type Kind = IsIteratorKind; type Kind = IsIteratorKind;
type Item = Token<'a>; type Item = Token<'a>;
type IntoIter = Self; type IntoIter = Self;
} }
impl<'a> TokenIterator<'a> { impl<'a> TokensIterator<'a> {
pub const fn new (source: &'a str) -> Self { Self(source) } 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 split (&self, index: usize) -> Self { Self(split_at(self.0, index).1) }
pub const fn next (mut self) -> Option<(TokenResult<'a>, Self)> { pub const fn next (mut self) -> Option<(TokenResult<'a>, Self)> {
@ -124,17 +124,19 @@ impl<'a> Token<'a> {
d => Ok(Self { length: self.length + 1, depth: d - 1, ..self }) d => Ok(Self { length: self.length + 1, depth: d - 1, ..self })
} }
} }
pub const fn to_ref_atom (&'a self) -> Result<RefAtom, ParseError> { pub const fn to_ref_atom (&self, source: &'a str) -> Result<RefAtom<'a>, ParseError> {
Ok(match self.kind { match self.kind {
Nil => return Err(ParseError::Empty), Nil => Err(ParseError::Empty),
Num => match to_number(self.slice()) { Num => match to_number(self.slice_source(source)) {
Ok(n) => RefAtom::Num(n), Err(e) => Err(e),
Err(e) => return Err(e) Ok(n) => Ok(RefAtom::Num(n)),
}, },
Sym => RefAtom::Sym(self.slice()), Sym => Ok(RefAtom::Sym(self.slice_source(source))),
Key => RefAtom::Key(self.slice()), Key => Ok(RefAtom::Key(self.slice_source(source))),
Exp => todo!() Exp => Ok(
}) RefAtom::Exp(RefAtomsIterator(TokensIterator::new(self.slice_source(source))))
)
}
} }
} }
const fn to_number (digits: &str) -> Result<usize, ParseError> { const fn to_number (digits: &str) -> Result<usize, ParseError> {
@ -155,32 +157,21 @@ const fn to_digit (c: char) -> Result<usize, ParseError> {
pub trait Atom: Sized { pub trait Atom: Sized {
fn kind (&self) -> TokenKind; fn kind (&self) -> TokenKind;
fn text (&self) -> &str; fn text (&self) -> &str;
fn num (&self) -> usize;
} }
#[derive(Clone, PartialEq)] pub enum RefAtom<'a> { #[derive(Clone, PartialEq)] pub enum RefAtom<'a> {
Num(usize), Num(usize),
Sym(&'a str), Sym(&'a str),
Key(&'a str), Key(&'a str),
Exp(RefAtomIterator<'a>), Exp(RefAtomsIterator<'a>),
} }
type RefAtomResult<'a> = Result<RefAtom<'a>, ParseError>; type RefAtomResult<'a> = Result<RefAtom<'a>, ParseError>;
#[derive(Clone, PartialEq)] pub struct RefAtomIterator<'a>(TokenIterator<'a>); #[derive(Clone, PartialEq)] pub struct RefAtomsIterator<'a>(TokensIterator<'a>);
impl<'a> Iterator for RefAtomIterator<'a> { impl<'a> Iterator for RefAtomsIterator<'a> {
type Item = RefAtomResult<'a>; type Item = RefAtomResult<'a>;
fn next (&mut self) -> Option<RefAtomResult<'a>> { fn next (&mut self) -> Option<RefAtomResult<'a>> {
Some(if let Some(result) = Iterator::next(&mut self.0) { Some(if let Some(result) = Iterator::next(&mut self.0) {
match result { match result { Ok(token) => token.to_ref_atom(self.0.0), Err(e) => Err(e), }
Err(e) => Err(e),
Ok(token) => match token.kind {
Nil => Err(ParseError::Empty),
Num => match to_number(token.slice_source(self.0.0)) {
Ok(n) => Ok(RefAtom::Num(n)),
Err(e) => Err(e)
},
Sym => Ok(RefAtom::Sym(token.slice_source(self.0.0))),
Key => Ok(RefAtom::Key(token.slice_source(self.0.0))),
Exp => todo!()
}
}
} else { } else {
return None return None
}) })
@ -193,20 +184,8 @@ impl<'a> RefAtom<'a> {
pub fn read_all <'b: 'a> (source: &'b str) pub fn read_all <'b: 'a> (source: &'b str)
-> impl Iterator<Item = Result<RefAtom<'b>, ParseError>> + 'b -> impl Iterator<Item = Result<RefAtom<'b>, ParseError>> + 'b
{ {
TokenIterator::new(source).map(move |result|match result{ TokensIterator::new(source).map(move |result|match result {
Err(e) => Err(e), Ok(token) => token.to_ref_atom(source), Err(e) => Err(e),
Ok(token) => match token.kind {
Nil => Err(ParseError::Empty),
Num => match to_number(token.slice_source(source)) {
Ok(n) => Ok(RefAtom::Num(n)),
Err(e) => return Err(e)
},
Sym => Ok(RefAtom::Sym(token.slice_source(source))),
Key => Ok(RefAtom::Key(token.slice_source(source))),
Exp => Ok(
RefAtom::Exp(RefAtomIterator(TokenIterator::new(token.slice_source(source))))
)
},
}) })
} }
} }
@ -226,6 +205,12 @@ impl<'a> Atom for RefAtom<'a> {
Self::Key(k) => k, Self::Key(k) => k,
} }
} }
fn num (&self) -> usize {
match self {
Self::Num(n) => *n,
_ => 0
}
}
} }
impl<'a> Debug for RefAtom<'a> { impl<'a> Debug for RefAtom<'a> {
fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> { fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> {
@ -245,7 +230,7 @@ impl<'a> Debug for RefAtom<'a> {
} }
impl ArcAtom { impl ArcAtom {
pub fn read_all <'a> (source: &'a str) -> impl Iterator<Item = Result<ArcAtom, ParseError>> + 'a { pub fn read_all <'a> (source: &'a str) -> impl Iterator<Item = Result<ArcAtom, ParseError>> + 'a {
TokenIterator::new(source).map(move |result: TokenResult<'a>|match result{ TokensIterator::new(source).map(move |result: TokenResult<'a>|match result{
Err(e) => Err(e), Err(e) => Err(e),
Ok(token) => match token.kind { Ok(token) => match token.kind {
Nil => Err(ParseError::Empty), Nil => Err(ParseError::Empty),
@ -283,6 +268,12 @@ impl<'a> Atom for ArcAtom {
Self::Key(k) => k.as_ref(), Self::Key(k) => k.as_ref(),
} }
} }
fn num (&self) -> usize {
match self {
Self::Num(n) => *n,
_ => 0
}
}
} }
impl<'a> Debug for ArcAtom { impl<'a> Debug for ArcAtom {
fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> { fn fmt (&self, f: &mut Formatter<'_>) -> Result<(), FormatError> {
@ -306,27 +297,27 @@ impl<'a> Display for ArcAtom {
} }
/// Map EDN tokens to parameters of a given type for a given context /// Map EDN tokens to parameters of a given type for a given context
pub trait Context<'a, U>: Sized { pub trait Context<'a, U>: Sized {
fn get (&'a self, _edn: &'a impl Atom) -> Option<U> { fn get (&'a self, _atom: &'a impl Atom) -> Option<U> {
None None
} }
fn get_or_fail (&'a self, edn: &'a impl Atom) -> U { fn get_or_fail (&'a self, atom: &'a impl Atom) -> U {
self.get(edn).expect("no value") self.get(atom).expect("no value")
} }
} }
impl<'a, T: Context<'a, U>, U> Context<'a, U> for &T { impl<'a, T: Context<'a, U>, U> Context<'a, U> for &T {
fn get (&'a self, edn: &'a impl Atom) -> Option<U> { fn get (&'a self, atom: &'a impl Atom) -> Option<U> {
(*self).get(edn) (*self).get(atom)
} }
fn get_or_fail (&'a self, edn: &'a impl Atom) -> U { fn get_or_fail (&'a self, atom: &'a impl Atom) -> U {
(*self).get_or_fail(edn) (*self).get_or_fail(atom)
} }
} }
impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option<T> { impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option<T> {
fn get (&'a self, edn: &'a impl Atom) -> Option<U> { fn get (&'a self, atom: &'a impl Atom) -> Option<U> {
self.as_ref().map(|s|s.get(edn)).flatten() self.as_ref().map(|s|s.get(atom)).flatten()
} }
fn get_or_fail (&'a self, edn: &'a impl Atom) -> U { fn get_or_fail (&'a self, atom: &'a impl Atom) -> U {
self.as_ref().map(|s|s.get_or_fail(edn)).expect("no provider") self.as_ref().map(|s|s.get_or_fail(atom)).expect("no provider")
} }
} }
/// Implement `Context` for a context and type. /// Implement `Context` for a context and type.
@ -334,18 +325,22 @@ impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option<T> {
// Provide a value to the EDN template // Provide a value to the EDN template
($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a> Context<'a, $type> for $State { impl<'a> Context<'a, $type> for $State {
fn get (&'a $self, edn: &'a impl Atom) -> Option<$type> { fn get (&'a $self, atom: &'a impl Atom) -> Option<$type> {
use RefAtom::*; Some(match (atom.kind(), atom.text()) {
Some(match edn.to_ref() { $(Sym($pat) => $expr,)* _ => return None }) $((TokenKind::Sym, $pat) => $expr,)*
_ => return None
})
} }
} }
}; };
// Provide a value more generically // Provide a value more generically
($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { ($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<$lt> Context<$lt, $type> for $State { impl<$lt> Context<$lt, $type> for $State {
fn get (&$lt $self, edn: &$lt impl Atom) -> Option<$type> { fn get (&$lt $self, atom: &$lt impl Atom) -> Option<$type> {
use RefAtom::*; Some(match (atom.kind(), atom.text()) {
Some(match edn.to_ref() { $(Sym($pat) => $expr,)* _ => return None }) $((TokenKind::Sym, $pat) => $expr,)*
_ => return None
})
} }
} }
}; };
@ -357,18 +352,24 @@ impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option<T> {
// Provide a value that may also be a numeric literal in the EDN, to a generic implementation. // 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),* $(,)? }) => { ($type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a, $T: $Trait> Context<'a, $type> for $T { impl<'a, $T: $Trait> Context<'a, $type> for $T {
fn get (&'a $self, edn: &'a impl Atom) -> Option<$type> { fn get (&'a $self, atom: &'a impl Atom) -> Option<$type> {
use RefAtom::*; Some(match (atom.kind(), atom.text()) {
Some(match edn.to_ref() { $(Sym($pat) => $expr,)* Num(n) => n as $type, _ => return None }) $((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. // 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),* $(,)? }) => { ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a> Context<'a, $type> for $State { impl<'a> Context<'a, $type> for $State {
fn get (&'a $self, edn: &'a impl Atom) -> Option<$type> { fn get (&'a $self, atom: &'a impl Atom) -> Option<$type> {
use RefAtom::*; Some(match (atom.kind(), atom.text()) {
Some(match edn.to_ref() { $(Sym($pat) => $expr,)* Num(n) => n as $type, _ => return None }) $((TokenKind::Sym, $pat) => $expr,)*
(TokenKind::Num, _) => atom.num() as $type,
_ => return None
})
} }
} }
}; };
@ -379,27 +380,35 @@ impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option<T> {
#[macro_export] macro_rules! provide_content { #[macro_export] macro_rules! provide_content {
(|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { (|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a, E: Output> Context<'a, Box<dyn Render<E> + 'a>> for $State { impl<'a, E: Output> Context<'a, Box<dyn Render<E> + 'a>> for $State {
fn get (&'a $self, edn: &'a impl Atom) -> Option<Box<dyn Render<E> + 'a>> { fn get (&'a $self, atom: &'a impl Atom) -> Option<Box<dyn Render<E> + 'a>> {
use RefAtom::*; use RefAtom::*;
Some(match edn.to_ref() { $(RefAtom::Sym($pat) => $expr),*, _ => return None }) Some(match atom.to_ref() { $(RefAtom::Sym($pat) => $expr),*, _ => return None })
} }
} }
}; };
($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { ($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a> Context<'a, Box<dyn Render<$Output> + 'a>> for $State { impl<'a> Context<'a, Box<dyn Render<$Output> + 'a>> for $State {
fn get (&'a $self, edn: &'a impl Atom) -> Option<Box<dyn Render<$Output> + 'a>> { fn get (&'a $self, atom: &'a impl Atom) -> Option<Box<dyn Render<$Output> + 'a>> {
use RefAtom::*; use RefAtom::*;
Some(match edn.to_ref() { $(Sym($pat) => $expr),*, _ => return None }) Some(match atom.to_ref() { $(Sym($pat) => $expr),*, _ => return None })
} }
} }
} }
} }
pub trait TryFromEdn<'a, T>: Sized { #[macro_export] macro_rules! try_delegate {
fn try_from_edn (state: &'a T, head: &impl Atom, tail: &'a [impl Atom]) -> ($s:ident, $atom:expr, $T:ty) => {
if let [head, tail @ ..] = $atom.as_slice() {
if let Some(value) = <$T>::try_from_atom($s, head, tail) {
return Some(value.boxed())
}
}
}
}
pub trait TryFromAtom<'a, T>: Sized {
fn try_from_atom (state: &'a T, head: &impl Atom, tail: &'a [impl Atom]) ->
Option<Self>; Option<Self>;
} }
pub trait TryIntoAtom<'a, T>: Sized {
pub trait TryIntoEdn<'a, T>: Sized { fn try_from_atom (state: &'a T, head: &impl Atom, tail: &'a [impl Atom]) ->
fn try_from_edn (state: &'a T, head: &impl Atom, tail: &'a [impl Atom]) ->
Option<Self>; Option<Self>;
} }

View file

@ -1,58 +1,58 @@
use crate::*; use crate::*;
pub trait KeyMap { pub trait KeyMap {
/// Try to find a command that matches the currently pressed key /// Try to find a command that matches the currently pressed key
fn command <S, C: EdnCommand<S>> (&self, state: &S, input: &impl EdnInput) -> Option<C>; fn command <S, C: AtomCommand<S>> (&self, state: &S, input: &impl AtomInput) -> Option<C>;
}
pub struct SourceKeyMap<'a>(&'a str);
impl<'a> KeyMap for SourceKeyMap<'a> {
fn command <S, C: EdnCommand<S>> (&self, state: &S, input: &impl EdnInput) -> Option<C> {
todo!();
None
}
}
pub struct ParsedKeyMap<'a>(TokenIterator<'a>);
impl<'a> KeyMap for ParsedKeyMap<'a> {
fn command <S, C: EdnCommand<S>> (&self, state: &S, input: &impl EdnInput) -> Option<C> {
todo!();
None
}
}
pub struct RefKeyMap<'a>(TokenIterator<'a>);
impl<'a> KeyMap for RefKeyMap<'a> {
fn command <S, C: EdnCommand<S>> (&self, state: &S, input: &impl EdnInput) -> Option<C> {
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_edn(key) {
//let command = C::from_edn(state, command, args);
//if command.is_some() {
//return command
//}
//}
//},
//_ => panic!("invalid config: {item}")
//},
//_ => panic!("invalid config: {item}")
//}
//_ => panic!("invalid config: {item}")
//}
//}
None
}
} }
//pub struct SourceKeyMap<'a>(&'a str);
//impl<'a> KeyMap for SourceKeyMap<'a> {
//fn command <S, C: AtomCommand<S>> (&self, state: &S, input: &impl AtomInput) -> Option<C> {
//todo!();
//None
//}
//}
//pub struct ParsedKeyMap<'a>(TokensIterator<'a>);
//impl<'a> KeyMap for ParsedKeyMap<'a> {
//fn command <S, C: AtomCommand<S>> (&self, state: &S, input: &impl AtomInput) -> Option<C> {
//todo!();
//None
//}
//}
//pub struct RefKeyMap<'a>(TokensIterator<'a>);
//impl<'a> KeyMap for RefKeyMap<'a> {
//fn command <S, C: AtomCommand<S>> (&self, state: &S, input: &impl AtomInput) -> Option<C> {
//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<ArcAtom>); pub struct ArcKeyMap(Vec<ArcAtom>);
impl<'a> KeyMap for ArcKeyMap { impl<'a> KeyMap for ArcKeyMap {
fn command <S, C: EdnCommand<S>> (&self, state: &S, input: &impl EdnInput) -> Option<C> { fn command <S, C: AtomCommand<S>> (&self, state: &S, input: &impl AtomInput) -> Option<C> {
for atom in self.0.iter() { for atom in self.0.iter() {
match atom { match atom {
ArcAtom::Exp(atoms) => match atoms.as_slice() { ArcAtom::Exp(atoms) => match atoms.as_slice() {
[key, command, args @ ..] => match (key.kind(), key.text()) { [key, command, args @ ..] => match (key.kind(), key.text()) {
(TokenKind::Sym, key) => { (TokenKind::Sym, key) => {
if input.matches_edn(key) { if input.matches_atom(key) {
let command = C::from_edn(state, command, args); let command = C::from_atom(state, command, args);
if command.is_some() { if command.is_some() {
return command return command
} }
@ -69,22 +69,22 @@ impl<'a> KeyMap for ArcKeyMap {
} }
} }
/// [Input] state that can be matched against an [Atom]. /// [Input] state that can be matched against an [Atom].
pub trait EdnInput: Input { pub trait AtomInput: Input {
fn matches_edn (&self, token: &str) -> bool; fn matches_atom (&self, token: &str) -> bool;
fn get_event <'a> (_: &impl Atom) -> Option<Self::Event> { fn get_event <'a> (_: &impl Atom) -> Option<Self::Event> {
None None
} }
} }
/// Turns an EDN item sequence into a command enum variant. /// Turns an EDN item sequence into a command enum variant.
pub trait EdnCommand<C>: Command<C> { pub trait AtomCommand<C>: Command<C> {
fn from_edn <'a> ( fn from_atom <'a> (
state: &C, state: &C,
head: &impl Atom, head: &impl Atom,
tail: &'a [impl Atom] tail: &'a [impl Atom]
) -> Option<Self>; ) -> Option<Self>;
} }
/** Implement `EdnCommand` for given `State` and `Command` */ /** Implement `AtomCommand` for given `State` and `Command` */
#[macro_export] macro_rules! edn_command { #[macro_export] macro_rules! atom_command {
($Command:ty : |$state:ident:<$T:ident: $Trait:path>| { $(( ($Command:ty : |$state:ident:<$T:ident: $Trait:path>| { $((
// identifier // identifier
$key:literal [ $key:literal [
@ -106,22 +106,20 @@ pub trait EdnCommand<C>: Command<C> {
// bound command: // bound command:
$command:expr $command:expr
))* }) => { ))* }) => {
impl<$T: $Trait> EdnCommand<$T> for $Command { impl<$T: $Trait> AtomCommand<$T> for $Command {
fn from_edn <'a> ( fn from_atom <'a> (
$state: &$T, $state: &$T, head: &impl Atom, tail: &'a [impl Atom]
head: &impl Atom,
tail: &'a [impl Atom]
) -> Option<Self> { ) -> Option<Self> {
$(if let (Atom::Key($key), [ // if the identifier matches $(if let (TokenType::Key, $key, [ // if the identifier matches
// bind argument ids // bind argument ids
$($arg),* $($arg),*
// bind rest parameters // bind rest parameters
$(, $rest @ ..)? $(, $rest @ ..)?
]) = (head.to_ref(), tail) { ]) = (head.kind(), head.text(), tail) {
$( $(
$(let $arg: Option<$type> = Context::<$type>::get($state, $arg);)? $(let $arg: Option<$type> = Context::<$type>::get($state, $arg);)?
)* )*
//$(edn_command!(@bind $state => $arg $(?)? : $type);)* //$(atom_command!(@bind $state => $arg $(?)? : $type);)*
return Some($command) return Some($command)
})* })*
None None
@ -149,22 +147,22 @@ pub trait EdnCommand<C>: Command<C> {
// bound command: // bound command:
$command:expr $command:expr
))* }) => { ))* }) => {
impl EdnCommand<$State> for $Command { impl AtomCommand<$State> for $Command {
fn from_edn <'a> ( fn from_atom <'a> (
$state: &$State, $state: &$State,
head: &impl Atom, head: &impl Atom,
tail: &'a [impl Atom], tail: &'a [impl Atom],
) -> Option<Self> { ) -> Option<Self> {
$(if let (Atom::Key($key), [ // if the identifier matches $(if let (TokenType::Key, $key, [ // if the identifier matches
// bind argument ids // bind argument ids
$($arg),* $($arg),*
// bind rest parameters // bind rest parameters
$(, $rest @ ..)? $(, $rest @ ..)?
]) = (head.to_ref(), tail) { ]) = (head.kind(), head.text(), tail) {
$( $(
$(let $arg: Option<$type> = Context::<$type>::get($state, $arg);)? $(let $arg: Option<$type> = Context::<$type>::get($state, $arg);)?
)* )*
//$(edn_command!(@bind $state => $arg $(?)? : $type);)* //$(atom_command!(@bind $state => $arg $(?)? : $type);)*
return Some($command) return Some($command)
})* })*
None None
@ -178,7 +176,7 @@ pub trait EdnCommand<C>: Command<C> {
let $arg: $type = Context::<$type>::get_or_fail($state, $arg); let $arg: $type = Context::<$type>::get_or_fail($state, $arg);
}; };
} }
#[cfg(test)] #[test] fn test_edn_keymap () -> Usually<()> { #[cfg(test)] #[test] fn test_atom_keymap () -> Usually<()> {
let keymap = KeyMap::new("")?; let keymap = KeyMap::new("")?;
Ok(()) Ok(())
} }

View file

@ -184,7 +184,7 @@ impl MidiViewer for MidiEditor {
fn clip_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> { self.mode.clip_mut() } fn clip_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> { self.mode.clip_mut() }
fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) } fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) }
} }
edn_command!(MidiEditCommand: |state: MidiEditor| { atom_command!(MidiEditCommand: |state: MidiEditor| {
("note/put" [_a: bool] Self::PutNote) ("note/put" [_a: bool] Self::PutNote)
("note/del" [_a: bool] Self::PutNote) ("note/del" [_a: bool] Self::PutNote)
("note/pos" [a: usize] Self::SetNoteCursor(a.expect("no note cursor"))) ("note/pos" [a: usize] Self::SetNoteCursor(a.expect("no note cursor")))
@ -208,14 +208,14 @@ edn_command!(MidiEditCommand: |state: MidiEditor| {
Show(Option<Arc<RwLock<MidiClip>>>), Show(Option<Arc<RwLock<MidiClip>>>),
} }
impl MidiEditCommand { impl MidiEditCommand {
fn from_tui_event (state: &MidiEditor, input: &impl EdnInput) -> Usually<Option<Self>> { fn from_tui_event (state: &MidiEditor, input: &impl AtomInput) -> Usually<Option<Self>> {
use EdnItem::*; use AtomItem::*;
let edns = EdnItem::read_all(KEYS_EDIT)?; let atoms = AtomItem::read_all(KEYS_EDIT)?;
for item in edns.iter() { for atom in atoms.iter() {
if let Exp(e) = item { if let Exp(e) = atom {
match e.as_slice() { match e.as_slice() {
[Sym(key), command, args @ ..] if input.matches_edn(key) => { [Sym(key), command, args @ ..] if input.matches_atom(key) => {
return Ok(MidiEditCommand::from_edn(state, command, args)) return Ok(MidiEditCommand::from_atom(state, command, args))
} }
_ => {} _ => {}
} }

View file

@ -190,19 +190,19 @@ content!(TuiOut: |self: ClipLength| {
} }
}); });
impl PoolCommand { impl PoolCommand {
pub fn from_tui_event (state: &MidiPool, input: &impl EdnInput) -> Usually<Option<Self>> { pub fn from_tui_event (state: &MidiPool, input: &impl AtomInput) -> Usually<Option<Self>> {
use EdnItem::*; use AtomItem::*;
let edns: Vec<EdnItem<_>> = EdnItem::read_all(match state.mode() { let atoms: Vec<AtomItem<_>> = AtomItem::read_all(match state.mode() {
Some(PoolMode::Rename(..)) => KEYS_RENAME, Some(PoolMode::Rename(..)) => KEYS_RENAME,
Some(PoolMode::Length(..)) => KEYS_LENGTH, Some(PoolMode::Length(..)) => KEYS_LENGTH,
Some(PoolMode::Import(..)) | Some(PoolMode::Export(..)) => KEYS_FILE, Some(PoolMode::Import(..)) | Some(PoolMode::Export(..)) => KEYS_FILE,
_ => KEYS_POOL _ => KEYS_POOL
})?; })?;
for item in edns { for item in atoms {
match item { match item {
Exp(e) => match e.as_slice() { Exp(e) => match e.as_slice() {
[Sym(key), command, args @ ..] if input.matches_edn(key) => { [Sym(key), command, args @ ..] if input.matches_atom(key) => {
return Ok(PoolCommand::from_edn(state, command, args)) return Ok(PoolCommand::from_atom(state, command, args))
} }
_ => {} _ => {}
}, },
@ -273,14 +273,14 @@ provide!(ItemColor: |self: MidiPool| {
/// Update the contents of the clip pool /// Update the contents of the clip pool
Clip(PoolClipCommand), Clip(PoolClipCommand),
} }
edn_command!(PoolCommand: |state: MidiPool| { atom_command!(PoolCommand: |state: MidiPool| {
("show" [a: bool] Self::Show(a.expect("no flag"))) ("show" [a: bool] Self::Show(a.expect("no flag")))
("select" [i: usize] Self::Select(i.expect("no index"))) ("select" [i: usize] Self::Select(i.expect("no index")))
("rename" [a, ..b] ClipRenameCommand::from_edn(state, &a.to_ref(), b).map(Self::Rename).expect("invalid command")) ("rename" [a, ..b] ClipRenameCommand::from_atom(state, &a.to_ref(), b).map(Self::Rename).expect("invalid command"))
("length" [a, ..b] ClipLengthCommand::from_edn(state, &a.to_ref(), b).map(Self::Length).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_edn(state, &a.to_ref(), b).map(Self::Import).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_edn(state, &a.to_ref(), b).map(Self::Export).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_edn(state, &a.to_ref(), b).map(Self::Clip).expect("invalid command")) ("clip" [a, ..b] PoolClipCommand::from_atom(state, &a.to_ref(), b).map(Self::Clip).expect("invalid command"))
}); });
command!(|self: PoolCommand, state: MidiPool|{ command!(|self: PoolCommand, state: MidiPool|{
use PoolCommand::*; use PoolCommand::*;
@ -308,7 +308,7 @@ command!(|self: PoolCommand, state: MidiPool|{
SetLength(usize, usize), SetLength(usize, usize),
SetColor(usize, ItemColor), SetColor(usize, ItemColor),
} }
edn_command!(PoolClipCommand: |state: MidiPool| { atom_command!(PoolClipCommand: |state: MidiPool| {
("add" [i: usize, c: MidiClip] Self::Add(i.expect("no index"), c.expect("no clip"))) ("add" [i: usize, c: MidiClip] Self::Add(i.expect("no index"), c.expect("no clip")))
("delete" [i: usize] Self::Delete(i.expect("no index"))) ("delete" [i: usize] Self::Delete(i.expect("no index")))
("swap" [a: usize, b: usize] Self::Swap(a.expect("no index"), b.expect("no index"))) ("swap" [a: usize, b: usize] Self::Swap(a.expect("no index"), b.expect("no index")))
@ -389,7 +389,7 @@ impl<T: HasClips> Command<T> for PoolClipCommand {
Confirm, Confirm,
Set(Arc<str>), Set(Arc<str>),
} }
edn_command!(ClipRenameCommand: |state: MidiPool| { atom_command!(ClipRenameCommand: |state: MidiPool| {
("begin" [] Self::Begin) ("begin" [] Self::Begin)
("cancel" [] Self::Cancel) ("cancel" [] Self::Cancel)
("confirm" [] Self::Confirm) ("confirm" [] Self::Confirm)
@ -429,7 +429,7 @@ command!(|self: ClipRenameCommand, state: MidiPool|{
Inc, Inc,
Dec, Dec,
} }
edn_command!(ClipLengthCommand: |state: MidiPool| { atom_command!(ClipLengthCommand: |state: MidiPool| {
("begin" [] Self::Begin) ("begin" [] Self::Begin)
("cancel" [] Self::Cancel) ("cancel" [] Self::Cancel)
("next" [] Self::Next) ("next" [] Self::Next)
@ -475,7 +475,7 @@ command!(|self: ClipLengthCommand, state: MidiPool|{
} }
None None
}); });
edn_command!(FileBrowserCommand: |state: MidiPool| { atom_command!(FileBrowserCommand: |state: MidiPool| {
("begin" [] Self::Begin) ("begin" [] Self::Begin)
("cancel" [] Self::Cancel) ("cancel" [] Self::Cancel)
("confirm" [] Self::Confirm) ("confirm" [] Self::Confirm)

View file

@ -45,8 +45,8 @@ impl<E: Output, A: Content<E>> Content<E> for Align<E, A> {
to.place(Content::layout(self, to.area()), &self.content()) to.place(Content::layout(self, to.area()), &self.content())
} }
} }
impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for Align<E, RenderBox<'a, E>> { impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for Align<E, RenderBox<'a, E>> {
fn try_from_edn ( fn try_from_atom (
state: &'a T, state: &'a T,
head: &impl Atom, head: &impl Atom,
tail: &'a [impl Atom] tail: &'a [impl Atom]

View file

@ -93,8 +93,8 @@ impl<E: Output, A: Content<E>, B: Content<E>> BspAreas<E, A, B> for Bsp<E, A, B>
fn direction (&self) -> Direction { self.1 } fn direction (&self) -> Direction { self.1 }
fn contents (&self) -> (&A, &B) { (&self.2, &self.3) } fn contents (&self) -> (&A, &B) { (&self.2, &self.3) }
} }
impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for Bsp<E, RenderBox<'a, E>, RenderBox<'a, E>> { impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for Bsp<E, RenderBox<'a, E>, RenderBox<'a, E>> {
fn try_from_edn (s: &'a T, head: &impl Atom, tail: &'a [impl Atom]) -> Option<Self> { fn try_from_atom (s: &'a T, head: &impl Atom, tail: &'a [impl Atom]) -> Option<Self> {
Some(match (head.to_ref(), tail) { Some(match (head.to_ref(), tail) {
(Key("bsp/n"), [a, b]) => Self::n( (Key("bsp/n"), [a, b]) => Self::n(
s.get_content(a).expect("no south"), s.get_content(a).expect("no south"),

View file

@ -7,8 +7,8 @@ impl<E, A> When<E, A> {
Self(Default::default(), c, a) Self(Default::default(), c, a)
} }
} }
impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for When<E, RenderBox<'a, E>> { impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for When<E, RenderBox<'a, E>> {
fn try_from_edn ( fn try_from_atom (
state: &'a T, head: &impl Atom, tail: &'a [impl Atom] state: &'a T, head: &impl Atom, tail: &'a [impl Atom]
) -> Option<Self> { ) -> Option<Self> {
if let (Key("when"), [condition, content]) = (head.to_ref(), tail) { if let (Key("when"), [condition, content]) = (head.to_ref(), tail) {
@ -47,8 +47,8 @@ impl<E, A, B> Either<E, A, B> {
Self(Default::default(), c, a, b) Self(Default::default(), c, a, b)
} }
} }
impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for Either<E, RenderBox<'a, E>, RenderBox<'a, E>> { impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for Either<E, RenderBox<'a, E>, RenderBox<'a, E>> {
fn try_from_edn (state: &'a T, head: &impl Atom, tail: &'a [impl Atom]) -> Option<Self> { fn try_from_atom (state: &'a T, head: &impl Atom, tail: &'a [impl Atom]) -> Option<Self> {
if let (Key("either"), [condition, content, alternative]) = (head.to_ref(), tail) { if let (Key("either"), [condition, content, alternative]) = (head.to_ref(), tail) {
Some(Self::new( Some(Self::new(
state.get_bool(condition).expect("either: no condition"), state.get_bool(condition).expect("either: no condition"),

View file

@ -27,8 +27,8 @@ macro_rules! transform_xy {
$area $area
} }
} }
impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for $Enum<E, RenderBox<'a, E>> { impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum<E, RenderBox<'a, E>> {
fn try_from_edn ( fn try_from_atom (
state: &'a T, head: &impl Atom, tail: &'a [impl Atom] state: &'a T, head: &impl Atom, tail: &'a [impl Atom]
) -> Option<Self> { ) -> Option<Self> {
Some(match (head.to_ref(), tail) { Some(match (head.to_ref(), tail) {
@ -82,8 +82,8 @@ macro_rules! transform_xy_unit {
$layout.into() $layout.into()
} }
} }
impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromEdn<'a, T> for $Enum<E, E::Unit, RenderBox<'a, E>> { impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum<E, E::Unit, RenderBox<'a, E>> {
fn try_from_edn ( fn try_from_atom (
state: &'a T, head: &impl Atom, tail: &'a [impl Atom] state: &'a T, head: &impl Atom, tail: &'a [impl Atom]
) -> Option<Self> { ) -> Option<Self> {
Some(match (head.to_ref(), tail) { Some(match (head.to_ref(), tail) {

View file

@ -1,5 +1,6 @@
use crate::*; use crate::*;
use std::{sync::Arc, marker::PhantomData}; use std::{sync::Arc, marker::PhantomData};
use TokenKind::*;
/// Define an EDN-backed view. /// Define an EDN-backed view.
/// ///
/// This consists of: /// This consists of:
@ -14,8 +15,8 @@ use std::{sync::Arc, marker::PhantomData};
} }
$( $(
impl<'a> Context<'a, $type> for $App { impl<'a> Context<'a, $type> for $App {
fn get (&'a $self, edn: &'a impl Atom) -> Option<$type> { fn get (&'a $self, atom: &'a impl Atom) -> Option<$type> {
Some(match edn.to_ref() { $(Atom::Sym($sym) => $value,)* _ => return None }) Some(match atom.to_ref() { $(Atom::Sym($sym) => $value,)* _ => return None })
} }
} }
)* )*
@ -25,8 +26,8 @@ use std::{sync::Arc, marker::PhantomData};
#[macro_export] macro_rules! provide_content { #[macro_export] macro_rules! provide_content {
(|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { (|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a, E: Output> Context<'a, Box<dyn Render<E> + 'a>> for $State { impl<'a, E: Output> Context<'a, Box<dyn Render<E> + 'a>> for $State {
fn get (&'a $self, edn: &'a impl Atom) -> Option<Box<dyn Render<E> + 'a>> { fn get (&'a $self, atom: &'a impl Atom) -> Option<Box<dyn Render<E> + 'a>> {
Some(match edn.to_ref() { Some(match atom.to_ref() {
$(Atom::Sym($pat) => $expr),*, $(Atom::Sym($pat) => $expr),*,
_ => return None _ => return None
}) })
@ -35,8 +36,8 @@ use std::{sync::Arc, marker::PhantomData};
}; };
($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { ($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a> Context<'a, Box<dyn Render<$Output> + 'a>> for $State { impl<'a> Context<'a, Box<dyn Render<$Output> + 'a>> for $State {
fn get (&'a $self, edn: &'a impl Atom) -> Option<Box<dyn Render<$Output> + 'a>> { fn get (&'a $self, atom: &'a impl Atom) -> Option<Box<dyn Render<$Output> + 'a>> {
Some(match edn.to_ref() { Some(match atom.to_ref() {
$(Atom::Sym($pat) => $expr),*, $(Atom::Sym($pat) => $expr),*,
_ => return None _ => return None
}) })
@ -45,22 +46,22 @@ use std::{sync::Arc, marker::PhantomData};
} }
} }
/// Renders from EDN source and context. /// Renders from EDN source and context.
#[derive(Default)] pub enum EdnView<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> { #[derive(Default)] pub enum AtomView<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> {
#[default] Inert, #[default] Inert,
Ok(T, Atom<Arc<str>>), Ok(T, Atom<Arc<str>>),
//render: Box<dyn Fn(&'a T)->Box<dyn Render<E> + Send + Sync + 'a> + Send + Sync + 'a> //render: Box<dyn Fn(&'a T)->Box<dyn Render<E> + Send + Sync + 'a> + Send + Sync + 'a>
Err(String), Err(String),
_Unused(PhantomData<&'a E>), _Unused(PhantomData<&'a E>),
} }
impl<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> std::fmt::Debug for EdnView<'a, E, T> { impl<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> std::fmt::Debug for AtomView<'a, E, T> {
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self { match self {
Self::Inert | Self::_Unused(_) => Self::Inert | Self::_Unused(_) =>
write!(f, "EdnView::Inert"), write!(f, "AtomView::Inert"),
Self::Ok(state, view) => Self::Ok(state, view) =>
write!(f, "EdnView::Ok(state={state:?} view={view:?}"), write!(f, "AtomView::Ok(state={state:?} view={view:?}"),
Self::Err(error) => Self::Err(error) =>
write!(f, "EdnView::Err({error})"), write!(f, "AtomView::Err({error})"),
_ => unreachable!() _ => unreachable!()
} }
} }
@ -71,40 +72,29 @@ impl<'a, E: Output, T> ViewContext<'a, E> for T where T:
Context<'a, E::Unit> + Context<'a, E::Unit> +
Context<'a, Box<dyn Render<E> + 'a>> Context<'a, Box<dyn Render<E> + 'a>>
{} {}
impl<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> EdnView<'a, E, T> { impl<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> AtomView<'a, E, T> {
pub fn from_source (state: T, source: &'a str) -> Self { pub fn from_source (state: T, source: &'a str) -> Self {
match Atom::read_one(&source) { match Atom::read_one(&source) {
Ok((layout, _)) => Self::Ok(state, layout), Ok((layout, _)) => Self::Ok(state, layout),
Err(error) => Self::Err(format!("{error} in {source}")) Err(error) => Self::Err(format!("{error} in {source}"))
} }
} }
pub fn from_items (state: T, items: Vec<impl Atom>) -> Self { pub fn from_atoms (state: T, atoms: Vec<impl Atom>) -> Self {
Self::Ok(state, Atom::Exp(items).to_arc()) Self::Ok(state, Atom::Exp(atoms).to_arc())
} }
} }
impl<E: Output, T: for<'a>ViewContext<'a, E> + Send + Sync + std::fmt::Debug> Content<E> for EdnView<'_, E, T> { impl<E: Output, T: for<'a>ViewContext<'a, E> + Send + Sync + std::fmt::Debug> Content<E> for AtomView<'_, E, T> {
fn content (&self) -> impl Render<E> { fn content (&self) -> impl Render<E> {
match self { match self {
Self::Ok(state, layout) => state.get_content(layout), Self::Ok(state, layout) => state.get_content(layout),
Self::Err(_error) => { Self::Err(_error) => {
panic!("{self:?}"); panic!("{self:?}");
//TODO:&format!("EdnView error: {error:?}")) // FIXME: String is not Render //TODO:&format!("AtomView error: {error:?}")) // FIXME: String is not Render
}, },
_ => todo!() _ => todo!()
} }
} }
} }
macro_rules! edn_try_delegate {
($s:ident, $e:expr, $T:ty) => {
if let [head, tail @ ..] = $e.as_slice() {
if let Some(content) = <$T>::try_from_edn($s, head, tail) {
return Some(content.boxed())
}
}
}
}
/// Provides values to the template /// Provides values to the template
pub trait ViewContext<'a, E: Output>: pub trait ViewContext<'a, E: Output>:
Context<'a, bool> + Context<'a, bool> +
@ -112,52 +102,53 @@ pub trait ViewContext<'a, E: Output>:
Context<'a, E::Unit> + Context<'a, E::Unit> +
Context<'a, Box<dyn Render<E> + 'a>> Context<'a, Box<dyn Render<E> + 'a>>
{ {
fn get_bool (&'a self, item: &'a impl Atom) -> Option<bool> { fn get_bool (&'a self, atom: &'a impl Atom) -> Option<bool> {
Some(match &item { Some(match atom.kind() {
Sym(s) => match s.as_ref() { Sym => match atom.text().as_ref() {
":false" | ":f" => false, ":false" | ":f" => false,
":true" | ":t" => true, ":true" | ":t" => true,
_ => return Context::get(self, item) _ => return Context::get(self, atom)
}, },
Num(0) => false, Num => match atom.num() {
Num(_) => true, 0 => false,
_ => return Context::get(self, item) _ => true
},
_ => return Context::get(self, atom)
}) })
} }
fn get_usize (&'a self, item: &'a impl Atom) -> Option<usize> { fn get_usize (&'a self, atom: &'a impl Atom) -> Option<usize> {
Some(match &item { Num(n) => *n, _ => return Context::get(self, item) }) Some(match atom.kind() {
} Num => atom.num(),
fn get_unit (&'a self, item: &'a impl Atom) -> Option<E::Unit> { _ => return Context::get(self, atom)
Some(match &item { Num(n) => (*n as u16).into(), _ => return Context::get(self, item) })
}
fn get_content (&'a self, item: &'a impl Atom) -> Option<Box<dyn Render<E> + 'a>> where E: 'a {
Some(match item {
Nil => Box::new(()),
Exp(ref e) => {
edn_try_delegate!(self, e, When::<_, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Either::<_, RenderBox<'a, E>, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Align::<_, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Bsp::<_, RenderBox<'a, E>, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Fill::<_, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Fixed::<_, _, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Min::<_, _, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Max::<_, _, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Shrink::<_, _, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Expand::<_, _, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Push::<_, _, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Pull::<_, _, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Margin::<_, _, RenderBox<'a, E>>);
edn_try_delegate!(self, e, Padding::<_, _, RenderBox<'a, E>>);
Context::get_or_fail(self, &item)
},
_ => Context::get_or_fail(self, &item)
}) })
//panic!("no content") }
fn get_unit (&'a self, atom: &'a impl Atom) -> Option<E::Unit> {
Some(match atom.kind() {
Num => E::Unit::from(atom.num() as u16),
_ => return Context::get(self, atom)
})
}
fn get_content (&'a self, atom: &'a impl Atom) -> Option<Box<dyn Render<E> + 'a>> where E: 'a {
try_delegate!(self, atom, When::<_, RenderBox<'a, E>>);
try_delegate!(self, atom, Either::<_, RenderBox<'a, E>, RenderBox<'a, E>>);
try_delegate!(self, atom, Align::<_, RenderBox<'a, E>>);
try_delegate!(self, atom, Bsp::<_, RenderBox<'a, E>, RenderBox<'a, E>>);
try_delegate!(self, atom, Fill::<_, RenderBox<'a, E>>);
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>>);
Context::get_or_fail(self, atom)
} }
} }
// A function that returns a `RenderBox. // A function that returns a `RenderBox.
pub type EdnCallback<'a, O, State> = pub type AtomCallback<'a, O, State> =
dyn Fn(&'a State)-> RenderBox<'a, O> + Send + Sync + 'a; dyn Fn(&'a State)-> RenderBox<'a, O> + Send + Sync + 'a;
// A box containing a function that returns a `RenderBox. // A box containing a function that returns a `RenderBox.
pub type EdnRenderCallback<'a, O, State> = pub type AtomRenderCallback<'a, O, State> =
Box<EdnCallback<'a, O, State>>; Box<AtomCallback<'a, O, State>>;

View file

@ -257,15 +257,15 @@ fn draw_header (state: &Plugin, to: &mut TuiOut, x: u16, y: u16, w: u16) {
//} //}
//}); //});
from_edn!("plugin/lv2" => |jack: &Arc<RwLock<JackConnection>>, args| -> Plugin { from_atom!("plugin/lv2" => |jack: &Arc<RwLock<JackConnection>>, args| -> Plugin {
let mut name = String::new(); let mut name = String::new();
let mut path = String::new(); let mut path = String::new();
edn!(edn in args { atom!(atom in args {
Edn::Map(map) => { Atom::Map(map) => {
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) {
name = String::from(*n); name = String::from(*n);
} }
if let Some(Edn::Str(p)) = map.get(&Edn::Key(":path")) { if let Some(Atom::Str(p)) = map.get(&Atom::Key(":path")) {
path = String::from(*p); path = String::from(*p);
} }
}, },

View file

@ -349,22 +349,22 @@ impl Sampler {
} }
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
type MidiSample = (Option<u7>, Arc<RwLock<crate::Sample>>); type MidiSample = (Option<u7>, Arc<RwLock<crate::Sample>>);
from_edn!("sampler" => |jack: &Arc<RwLock<JackConnection>>, args| -> crate::Sampler { from_atom!("sampler" => |jack: &Arc<RwLock<JackConnection>>, args| -> crate::Sampler {
let mut name = String::new(); let mut name = String::new();
let mut dir = String::new(); let mut dir = String::new();
let mut samples = BTreeMap::new(); let mut samples = BTreeMap::new();
edn!(edn in args { atom!(atom in args {
Edn::Map(map) => { Atom::Map(map) => {
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) {
name = String::from(*n); name = String::from(*n);
} }
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":dir")) { if let Some(Atom::Str(n)) = map.get(&Atom::Key(":dir")) {
dir = String::from(*n); dir = String::from(*n);
} }
}, },
Edn::List(args) => match args.first() { Atom::List(args) => match args.first() {
Some(Edn::Symbol("sample")) => { Some(Atom::Symbol("sample")) => {
let (midi, sample) = MidiSample::from_edn((jack, &dir), &args[1..])?; let (midi, sample) = MidiSample::from_atom((jack, &dir), &args[1..])?;
if let Some(midi) = midi { if let Some(midi) = midi {
samples.insert(midi, sample); samples.insert(midi, sample);
} else { } else {
@ -373,27 +373,27 @@ from_edn!("sampler" => |jack: &Arc<RwLock<JackConnection>>, args| -> crate::Samp
}, },
_ => panic!("unexpected in sampler {name}: {args:?}") _ => panic!("unexpected in sampler {name}: {args:?}")
}, },
_ => panic!("unexpected in sampler {name}: {edn:?}") _ => panic!("unexpected in sampler {name}: {atom:?}")
}); });
Self::new(jack, &name) Self::new(jack, &name)
}); });
from_edn!("sample" => |(_jack, dir): (&Arc<RwLock<JackConnection>>, &str), args| -> MidiSample { from_atom!("sample" => |(_jack, dir): (&Arc<RwLock<JackConnection>>, &str), args| -> MidiSample {
let mut name = String::new(); let mut name = String::new();
let mut file = String::new(); let mut file = String::new();
let mut midi = None; let mut midi = None;
let mut start = 0usize; let mut start = 0usize;
edn!(edn in args { atom!(atom in args {
Edn::Map(map) => { Atom::Map(map) => {
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) {
name = String::from(*n); name = String::from(*n);
} }
if let Some(Edn::Str(f)) = map.get(&Edn::Key(":file")) { if let Some(Atom::Str(f)) = map.get(&Atom::Key(":file")) {
file = String::from(*f); file = String::from(*f);
} }
if let Some(Edn::Int(i)) = map.get(&Edn::Key(":start")) { if let Some(Atom::Int(i)) = map.get(&Atom::Key(":start")) {
start = *i as usize; start = *i as usize;
} }
if let Some(Edn::Int(m)) = map.get(&Edn::Key(":midi")) { if let Some(Atom::Int(m)) = map.get(&Atom::Key(":midi")) {
midi = Some(u7::from(*m as u8)); midi = Some(u7::from(*m as u8));
} }
}, },
@ -661,14 +661,14 @@ pub enum SamplerMode {
Select(usize), Select(usize),
Sample(SamplerCommand), Sample(SamplerCommand),
} }
edn_command!(SamplerTuiCommand: |state: SamplerTui| { atom_command!(SamplerTuiCommand: |state: SamplerTui| {
("select" [i: usize] Self::Select(i.expect("no index"))) ("select" [i: usize] Self::Select(i.expect("no index")))
("import" [a, ..b] if let Some(command) = FileBrowserCommand::from_edn(state, a, b) { ("import" [a, ..b] if let Some(command) = FileBrowserCommand::from_atom(state, a, b) {
Self::Import(command) Self::Import(command)
} else { } else {
return None return None
}) })
("sample" [a, ..b] if let Some(command) = SamplerCommand::from_edn(&state.state, a, b) { ("sample" [a, ..b] if let Some(command) = SamplerCommand::from_atom(&state.state, a, b) {
Self::Sample(command) Self::Sample(command)
} else { } else {
return None return None
@ -677,7 +677,7 @@ edn_command!(SamplerTuiCommand: |state: SamplerTui| {
provide!(usize: |self: SamplerTui| {}); provide!(usize: |self: SamplerTui| {});
provide!(PathBuf: |self: SamplerTui| {}); provide!(PathBuf: |self: SamplerTui| {});
provide!(Arc<str>: |self: SamplerTui| {}); provide!(Arc<str>: |self: SamplerTui| {});
edn_command!(FileBrowserCommand: |state: SamplerTui| { atom_command!(FileBrowserCommand: |state: SamplerTui| {
("begin" [] Self::Begin) ("begin" [] Self::Begin)
("cancel" [] Self::Cancel) ("cancel" [] Self::Cancel)
("confirm" [] Self::Confirm) ("confirm" [] Self::Confirm)
@ -695,7 +695,7 @@ edn_command!(FileBrowserCommand: |state: SamplerTui| {
NoteOn(u7, u7), NoteOn(u7, u7),
NoteOff(u7), NoteOff(u7),
} }
edn_command!(SamplerCommand: |state: Sampler| { atom_command!(SamplerCommand: |state: Sampler| {
("record/begin" [i: u7] ("record/begin" [i: u7]
Self::RecordBegin(i.expect("no index"))) Self::RecordBegin(i.expect("no index")))
("record/cancel" [] ("record/cancel" []

View file

@ -119,7 +119,7 @@ impl TekCli {
/// Must not be dropped for the duration of the process /// Must not be dropped for the duration of the process
pub jack: Arc<RwLock<JackConnection>>, pub jack: Arc<RwLock<JackConnection>>,
/// View definition /// View definition
pub edn: String, pub atom: String,
/// Source of time /// Source of time
pub clock: Clock, pub clock: Clock,
/// Theme /// Theme
@ -168,7 +168,7 @@ provide_num!(usize: |self: Tek| {
":track" => self.selected.track().unwrap_or(0), ":track" => self.selected.track().unwrap_or(0),
":track-next" => (self.selected.track().unwrap_or(0) + 1).min(self.tracks.len()), ":track-next" => (self.selected.track().unwrap_or(0) + 1).min(self.tracks.len()),
":track-prev" => self.selected.track().unwrap_or(0).saturating_sub(1) }); ":track-prev" => self.selected.track().unwrap_or(0).saturating_sub(1) });
view!(TuiOut: |self: Tek| self.size.of(EdnView::from_source(self, self.edn.as_ref())); { view!(TuiOut: |self: Tek| self.size.of(AtomView::from_source(self, self.atom.as_ref())); {
bool {}; bool {};
isize {}; isize {};
Color {}; Color {};
@ -206,7 +206,7 @@ impl Tek {
midi_froms: &[PortConnection], midi_tos: &[PortConnection], midi_froms: &[PortConnection], midi_tos: &[PortConnection],
) -> Usually<Self> { ) -> Usually<Self> {
let tek = Self { let tek = Self {
edn: include_str!("./view_transport.edn").to_string(), atom: include_str!("./view_transport.edn").to_string(),
jack: jack.clone(), jack: jack.clone(),
color: ItemPalette::random(), color: ItemPalette::random(),
clock: Clock::new(jack, bpm), clock: Clock::new(jack, bpm),
@ -226,7 +226,7 @@ impl Tek {
let clip = MidiClip::new("Clip", true, 384usize, None, Some(ItemColor::random().into())); let clip = MidiClip::new("Clip", true, 384usize, None, Some(ItemColor::random().into()));
let clip = Arc::new(RwLock::new(clip)); let clip = Arc::new(RwLock::new(clip));
Ok(Self { Ok(Self {
edn: include_str!("./view_sequencer.edn").to_string(), atom: include_str!("./view_sequencer.edn").to_string(),
pool: Some((&clip).into()), pool: Some((&clip).into()),
editor: Some((&clip).into()), editor: Some((&clip).into()),
editing: false.into(), editing: false.into(),
@ -242,7 +242,7 @@ impl Tek {
audio_froms: &[&[PortConnection];2], audio_tos: &[&[PortConnection];2], audio_froms: &[&[PortConnection];2], audio_tos: &[&[PortConnection];2],
) -> Usually<Self> { ) -> Usually<Self> {
let app = Self { let app = Self {
edn: include_str!("./view_groovebox.edn").to_string(), atom: include_str!("./view_groovebox.edn").to_string(),
sampler: Some(Sampler::new(jack, &"sampler", midi_froms, audio_froms, audio_tos)?), sampler: Some(Sampler::new(jack, &"sampler", midi_froms, audio_froms, audio_tos)?),
..Self::new_sequencer(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)? ..Self::new_sequencer(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
}; };
@ -259,7 +259,7 @@ impl Tek {
scenes: usize, tracks: usize, track_width: usize, scenes: usize, tracks: usize, track_width: usize,
) -> Usually<Self> { ) -> Usually<Self> {
let mut arranger = Self { let mut arranger = Self {
edn: include_str!("./view_arranger.edn").to_string(), atom: include_str!("./view_arranger.edn").to_string(),
..Self::new_groovebox( ..Self::new_groovebox(
jack, bpm, sync_lead, sync_follow, jack, bpm, sync_lead, sync_follow,
midi_froms, midi_tos, audio_froms, audio_tos, midi_froms, midi_tos, audio_froms, audio_tos,
@ -628,7 +628,7 @@ handle!(TuiIn: |self: Tek, input|Ok({
Track(TrackCommand), Track(TrackCommand),
Zoom(Option<usize>), Zoom(Option<usize>),
} }
edn_command!(TekCommand: |app: Tek| { atom_command!(TekCommand: |app: Tek| {
("stop-all" [] Self::StopAll) ("stop-all" [] Self::StopAll)
("undo" [d: usize] Self::History(-(d.unwrap_or(0)as isize))) ("undo" [d: usize] Self::History(-(d.unwrap_or(0)as isize)))
("redo" [d: usize] Self::History(d.unwrap_or(0) as isize)) ("redo" [d: usize] Self::History(d.unwrap_or(0) as isize))
@ -643,19 +643,19 @@ edn_command!(TekCommand: |app: Tek| {
(0, s) => Self::Select(Selection::Scene(s)), (0, s) => Self::Select(Selection::Scene(s)),
(t, s) => Self::Select(Selection::Clip(t, s)), (t, s) => Self::Select(Selection::Clip(t, s)),
}) })
("clip" [a, ..b] Self::Clip(ClipCommand::from_edn(app, &a.to_ref(), b) ("clip" [a, ..b] Self::Clip(ClipCommand::from_atom(app, &a.to_ref(), b)
.expect("invalid command"))) .expect("invalid command")))
("clock" [a, ..b] Self::Clock(ClockCommand::from_edn(app.clock(), &a.to_ref(), b) ("clock" [a, ..b] Self::Clock(ClockCommand::from_atom(app.clock(), &a.to_ref(), b)
.expect("invalid command"))) .expect("invalid command")))
("editor" [a, ..b] Self::Editor(MidiEditCommand::from_edn(app.editor.as_ref().expect("no editor"), &a.to_ref(), b) ("editor" [a, ..b] Self::Editor(MidiEditCommand::from_atom(app.editor.as_ref().expect("no editor"), &a.to_ref(), b)
.expect("invalid command"))) .expect("invalid command")))
("pool" [a, ..b] Self::Pool(PoolCommand::from_edn(app.pool.as_ref().expect("no pool"), &a.to_ref(), b) ("pool" [a, ..b] Self::Pool(PoolCommand::from_atom(app.pool.as_ref().expect("no pool"), &a.to_ref(), b)
.expect("invalid command"))) .expect("invalid command")))
("sampler" [a, ..b] Self::Sampler(SamplerCommand::from_edn(app.sampler.as_ref().expect("no sampler"), &a.to_ref(), b) ("sampler" [a, ..b] Self::Sampler(SamplerCommand::from_atom(app.sampler.as_ref().expect("no sampler"), &a.to_ref(), b)
.expect("invalid command"))) .expect("invalid command")))
("scene" [a, ..b] Self::Scene(SceneCommand::from_edn(app, &a.to_ref(), b) ("scene" [a, ..b] Self::Scene(SceneCommand::from_atom(app, &a.to_ref(), b)
.expect("invalid command"))) .expect("invalid command")))
("track" [a, ..b] Self::Track(TrackCommand::from_edn(app, &a.to_ref(), b) ("track" [a, ..b] Self::Track(TrackCommand::from_atom(app, &a.to_ref(), b)
.expect("invalid command"))) .expect("invalid command")))
}); });
command!(|self: TekCommand, app: Tek|match self { command!(|self: TekCommand, app: Tek|match self {
@ -852,7 +852,7 @@ impl Track {
SetZoom(usize), SetZoom(usize),
SetColor(usize, ItemPalette), SetColor(usize, ItemPalette),
} }
edn_command!(TrackCommand: |app: Tek| { atom_command!(TrackCommand: |app: Tek| {
("add" [] Self::Add) ("add" [] Self::Add)
("size" [a: usize] Self::SetSize(a.unwrap())) ("size" [a: usize] Self::SetSize(a.unwrap()))
("zoom" [a: usize] Self::SetZoom(a.unwrap())) ("zoom" [a: usize] Self::SetZoom(a.unwrap()))
@ -1025,7 +1025,7 @@ impl Scene {
SetColor(usize, ItemPalette), SetColor(usize, ItemPalette),
Enqueue(usize), Enqueue(usize),
} }
edn_command!(SceneCommand: |app: Tek| { atom_command!(SceneCommand: |app: Tek| {
("add" [] Self::Add) ("add" [] Self::Add)
("del" [a: usize] Self::Del(0)) ("del" [a: usize] Self::Del(0))
("zoom" [a: usize] Self::SetZoom(a.unwrap())) ("zoom" [a: usize] Self::SetZoom(a.unwrap()))
@ -1129,7 +1129,7 @@ trait HasScenes: HasSelection + HasEditor + Send + Sync {
SetLoop(usize, usize, bool), SetLoop(usize, usize, bool),
SetColor(usize, usize, ItemPalette), SetColor(usize, usize, ItemPalette),
} }
edn_command!(ClipCommand: |app: Tek| { atom_command!(ClipCommand: |app: Tek| {
("get" [a: usize ,b: usize] ("get" [a: usize ,b: usize]
Self::Get(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1))) Self::Get(a.unwrap().saturating_sub(1), b.unwrap().saturating_sub(1)))
("put" [a: usize, b: usize, c: Option<Arc<RwLock<MidiClip>>>] ("put" [a: usize, b: usize, c: Option<Arc<RwLock<MidiClip>>>]

View file

@ -24,7 +24,7 @@ pub enum ClockCommand {
} }
provide_num!(u32: |self: Clock| {}); provide_num!(u32: |self: Clock| {});
provide!(f64: |self: Clock| {}); provide!(f64: |self: Clock| {});
edn_command!(ClockCommand: |state: Clock| { atom_command!(ClockCommand: |state: Clock| {
("play" [] Self::Play(None)) ("play" [] Self::Play(None))
("play" [t: u32] Self::Play(t)) ("play" [t: u32] Self::Play(t))
("pause" [] Self::Pause(None)) ("pause" [] Self::Pause(None))

View file

@ -20,7 +20,7 @@ handle!(TuiIn: |self: Example, input|{
return Ok(None) return Ok(None)
}); });
enum ExampleCommand { Next, Previous } enum ExampleCommand { Next, Previous }
edn_command!(ExampleCommand: |app: Example| { atom_command!(ExampleCommand: |app: Example| {
(":prev" [] Self::Previous) (":prev" [] Self::Previous)
(":next" [] Self::Next) (":next" [] Self::Next)
}); });
@ -51,7 +51,7 @@ view!(TuiOut: |self: Example|{
let code = Tui::bg(Color::Rgb(10,60,10), let code = Tui::bg(Color::Rgb(10,60,10),
Push::y(2, Align::n(format!("{}", EXAMPLES[self.0])))); Push::y(2, Align::n(format!("{}", EXAMPLES[self.0]))));
let content = Tui::bg(Color::Rgb(10,10,60), let content = Tui::bg(Color::Rgb(10,10,60),
EdnView::from_source(self, EXAMPLES[self.0])); AtomView::from_source(self, EXAMPLES[self.0]));
self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content))) self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content)))
}; { }; {
bool {}; bool {};

View file

@ -46,16 +46,16 @@ impl TuiIn {
}) })
} }
} }
impl EdnInput for TuiIn { impl AtomInput for TuiIn {
fn matches_edn (&self, token: &str) -> bool { fn matches_atom (&self, token: &str) -> bool {
if let Some(event) = KeyMatcher::new(token).build() { if let Some(event) = KeyMatcher::new(token).build() {
&event == self.event() &event == self.event()
} else { } else {
false false
} }
} }
fn get_event (item: &EdnItem<impl AsRef<str>>) -> Option<Event> { fn get_event (item: &AtomItem<impl AsRef<str>>) -> Option<Event> {
match item { EdnItem::Sym(s) => KeyMatcher::new(s).build(), _ => None } match item { AtomItem::Sym(s) => KeyMatcher::new(s).build(), _ => None }
} }
} }
struct KeyMatcher { struct KeyMatcher {