mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-06 11:46:42 +01:00
core, input, output, dsl: factor away 'flexi' traits
This commit is contained in:
parent
8cbd7dd8e8
commit
9f7d0efda5
11 changed files with 355 additions and 339 deletions
|
|
@ -1,5 +1,3 @@
|
|||
use crate::*;
|
||||
|
||||
/// Define and reexport submodules.
|
||||
#[macro_export] macro_rules! modules(
|
||||
($($($feat:literal?)? $name:ident),* $(,)?) => { $(
|
||||
|
|
@ -12,7 +10,37 @@ use crate::*;
|
|||
)* };
|
||||
);
|
||||
|
||||
/// Define a trait for various wrapper types. */
|
||||
/// Define a trait an implement it for read-only wrapper types. */
|
||||
#[macro_export] macro_rules! flex_trait (
|
||||
($Trait:ident $(<$($A:ident:$T:ident),+>)? $(:$dep:ident $(+$dep2:ident)*)? {
|
||||
$(fn $fn:ident (&$self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)*
|
||||
}) => {
|
||||
pub trait $Trait $(<$($A: $T),+>)? $(:$dep$(+$dep2)*)? {
|
||||
$(fn $fn (&$self $(,$arg:$ty)*) -> $ret $body)*
|
||||
}
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &_T_ {
|
||||
$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })*
|
||||
}
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ {
|
||||
$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (**$self).$fn($($arg),*) })*
|
||||
}
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<_T_> {
|
||||
$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })*
|
||||
}
|
||||
//impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> {
|
||||
//$(fn $fn (&$self $(,$arg:$ty)*) -> $ret {
|
||||
//if let Some(this) = $self { this.$fn($($arg),*) } else { Ok(None) }
|
||||
//})*
|
||||
//}
|
||||
//impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Mutex<_T_> {
|
||||
//$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).lock().unwrap().$fn($($arg),*) })*
|
||||
//}
|
||||
//impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::RwLock<_T_> {
|
||||
//$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { $self.read().unwrap().$fn($($arg),*) })*
|
||||
//}
|
||||
});
|
||||
|
||||
/// Define a trait an implement it for various mutation-enabled wrapper types. */
|
||||
#[macro_export] macro_rules! flex_trait_mut (
|
||||
($Trait:ident $(<$($A:ident:$T:ident),+>)? {
|
||||
$(fn $fn:ident (&mut $self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)*
|
||||
|
|
@ -20,24 +48,24 @@ use crate::*;
|
|||
pub trait $Trait $(<$($A: $T),+>)? {
|
||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret $body)*
|
||||
}
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ {
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ {
|
||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })*
|
||||
}
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> {
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> {
|
||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret {
|
||||
if let Some(this) = $self { this.$fn($($arg),*) } else { Ok(None) }
|
||||
})*
|
||||
}
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Mutex<_T_> {
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Mutex<_T_> {
|
||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.get_mut().unwrap().$fn($($arg),*) })*
|
||||
}
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::Mutex<_T_>> {
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::Mutex<_T_>> {
|
||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.lock().unwrap().$fn($($arg),*) })*
|
||||
}
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::RwLock<_T_> {
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::RwLock<_T_> {
|
||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.write().unwrap().$fn($($arg),*) })*
|
||||
}
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::RwLock<_T_>> {
|
||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::RwLock<_T_>> {
|
||||
$(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.write().unwrap().$fn($($arg),*) })*
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,48 +6,78 @@
|
|||
extern crate const_panic;
|
||||
use const_panic::PanicFmt;
|
||||
use std::fmt::Debug;
|
||||
pub(crate) use ::tengri_core::*;
|
||||
|
||||
pub(crate) use std::error::Error;
|
||||
pub(crate) use std::sync::Arc;
|
||||
|
||||
pub(crate) use konst::string::{str_range, char_indices};
|
||||
pub(crate) use thiserror::Error;
|
||||
pub(crate) use ::tengri_core::*;
|
||||
|
||||
pub(crate) use self::DslError::*;
|
||||
|
||||
mod dsl_conv; pub use self::dsl_conv::*;
|
||||
mod dsl_types; pub use self::dsl_types::*;
|
||||
#[cfg(test)] mod dsl_test;
|
||||
mod dsl_conv;
|
||||
pub use self::dsl_conv::*;
|
||||
|
||||
pub trait Dsl: Debug + Send + Sync + Sized {
|
||||
fn src (&self) -> &str;
|
||||
}
|
||||
mod dsl_parse;
|
||||
pub(crate) use self::dsl_parse::*;
|
||||
pub mod parse { pub use crate::dsl_parse::*; }
|
||||
|
||||
impl<'s> Dsl for &'s str {
|
||||
fn src (&self) -> &str { self }
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod dsl_test;
|
||||
|
||||
impl Dsl for String {
|
||||
fn src (&self) -> &str { self.as_str() }
|
||||
}
|
||||
|
||||
impl Dsl for std::sync::Arc<str> {
|
||||
flex_trait!(Dsl: Debug + Send + Sync + Sized {
|
||||
fn src (&self) -> &str {
|
||||
unreachable!("Dsl::src default impl")
|
||||
}
|
||||
});
|
||||
impl Dsl for Arc<str> {
|
||||
fn src (&self) -> &str { self.as_ref() }
|
||||
}
|
||||
|
||||
impl<D: Dsl> Dsl for &D {
|
||||
fn src (&self) -> &str { (*self).src() }
|
||||
impl<'s> Dsl for &'s str {
|
||||
fn src (&self) -> &str { self.as_ref() }
|
||||
}
|
||||
|
||||
impl<D: Dsl> Dsl for &mut D {
|
||||
fn src (&self) -> &str { (**self).src() }
|
||||
}
|
||||
|
||||
impl<D: Dsl> Dsl for std::sync::Arc<D> {
|
||||
fn src (&self) -> &str { (*self).src() }
|
||||
}
|
||||
|
||||
impl<D: Dsl> Dsl for Option<D> {
|
||||
fn src (&self) -> &str { if let Some(dsl) = self { dsl.src() } else { "" } }
|
||||
}
|
||||
|
||||
impl<D: Dsl> DslExp for D {}
|
||||
pub trait DslExp: Dsl {
|
||||
fn exp (&self) -> DslPerhaps<&str> {
|
||||
Ok(exp_peek(self.src())?)
|
||||
}
|
||||
fn head (&self) -> DslPerhaps<&str> {
|
||||
Ok(peek(&self.src()[1..])?)
|
||||
}
|
||||
fn tail (&self) -> DslPerhaps<&str> {
|
||||
Ok(if let Some((head_start, head_len)) = seek(&self.src()[1..])? {
|
||||
peek(&self.src()[(1+head_start+head_len)..])?
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dsl> DslSym for D {}
|
||||
pub trait DslSym: Dsl {
|
||||
fn sym (&self) -> DslPerhaps<&str> { crate::parse::sym_peek(self.src()) }
|
||||
}
|
||||
|
||||
impl<D: Dsl> DslKey for D {}
|
||||
pub trait DslKey: Dsl {
|
||||
fn key (&self) -> DslPerhaps<&str> { crate::parse::key_peek(self.src()) }
|
||||
}
|
||||
|
||||
impl<D: Dsl> DslText for D {}
|
||||
pub trait DslText: Dsl {
|
||||
fn text (&self) -> DslPerhaps<&str> { crate::parse::text_peek(self.src()) }
|
||||
}
|
||||
|
||||
impl<D: Dsl> DslNum for D {}
|
||||
pub trait DslNum: Dsl {
|
||||
fn num (&self) -> DslPerhaps<&str> { crate::parse::num_peek(self.src()) }
|
||||
}
|
||||
|
||||
/// DSL-specific result type.
|
||||
pub type DslResult<T> = Result<T, DslError>;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
use crate::*;
|
||||
|
||||
macro_rules! iter_chars(($source:expr => |$i:ident, $c:ident|$val:expr)=>{
|
||||
while let Some((($i, $c), next)) = char_indices($source).next() {
|
||||
$source = next.as_str(); $val } });
|
||||
|
||||
macro_rules! def_peek_seek(($peek:ident, $seek:ident, $seek_start:ident, $seek_length:ident)=>{
|
||||
/// Find a slice corrensponding to a syntax token.
|
||||
const fn $peek (source: &str) -> DslPerhaps<&str> {
|
||||
pub const fn $peek (source: &str) -> DslPerhaps<&str> {
|
||||
match $seek(source) {
|
||||
Ok(Some((start, length))) => Ok(Some(str_range(source, start, start + length))),
|
||||
Ok(None) => Ok(None),
|
||||
Err(e) => Err(e) } }
|
||||
/// Find a start and length corresponding to a syntax token.
|
||||
const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> {
|
||||
pub const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> {
|
||||
match $seek_start(source) {
|
||||
Ok(Some(start)) => match $seek_length(str_range(source, start, source.len() - start)) {
|
||||
Ok(Some(length)) => Ok(Some((start, length))),
|
||||
|
|
@ -20,6 +22,130 @@ macro_rules! def_peek_seek(($peek:ident, $seek:ident, $seek_start:ident, $seek_l
|
|||
Ok(None) => Ok(None),
|
||||
Err(e) => Err(e) } } });
|
||||
|
||||
def_peek_seek!(exp_peek, exp_seek, exp_seek_start, exp_seek_length);
|
||||
pub const fn is_exp_start (c: char) -> bool { matches!(c, '(') }
|
||||
pub const fn is_exp_end (c: char) -> bool { matches!(c, ')') }
|
||||
pub const fn exp_seek_start (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_exp_start(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_whitespace(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
pub const fn exp_seek_length (mut source: &str) -> DslPerhaps<usize> {
|
||||
let mut depth = 0;
|
||||
iter_chars!(source => |i, c| if is_exp_start(c) {
|
||||
depth += 1;
|
||||
} else if is_exp_end(c) {
|
||||
if depth == 0 {
|
||||
return Err(Unexpected(c))
|
||||
} else if depth == 1 {
|
||||
return Ok(Some(i))
|
||||
} else {
|
||||
depth -= 1;
|
||||
}
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
def_peek_seek!(sym_peek, sym_seek, sym_seek_start, sym_seek_length);
|
||||
pub const fn is_sym_start (c: char) -> bool { matches!(c, ':'|'@') }
|
||||
pub const fn is_sym_char (c: char) -> bool { matches!(c, 'a'..='z'|'A'..='Z'|'0'..='9'|'-') }
|
||||
pub const fn is_sym_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') }
|
||||
pub const fn sym_seek_start (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_sym_start(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_whitespace(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
pub const fn sym_seek_length (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_sym_end(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_sym_char(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
def_peek_seek!(key_peek, key_seek, key_seek_start, key_seek_length);
|
||||
pub const fn is_key_start (c: char) -> bool { matches!(c, '/'|'a'..='z') }
|
||||
pub const fn is_key_char (c: char) -> bool { matches!(c, 'a'..='z'|'0'..='9'|'-'|'/') }
|
||||
pub const fn is_key_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') }
|
||||
pub const fn key_seek_start (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_key_start(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_whitespace(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
pub const fn key_seek_length (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_key_end(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_key_char(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
def_peek_seek!(text_peek, text_seek, text_seek_start, text_seek_length);
|
||||
pub const fn is_text_start (c: char) -> bool { matches!(c, '"') }
|
||||
pub const fn is_text_end (c: char) -> bool { matches!(c, '"') }
|
||||
pub const fn text_seek_start (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_text_start(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_whitespace(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
pub const fn text_seek_length (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_text_end(c) { return Ok(Some(i)) });
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
def_peek_seek!(num_peek, num_seek, num_seek_start, num_seek_length);
|
||||
pub const fn num_seek_start (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_digit(c) {
|
||||
return Ok(Some(i));
|
||||
} else if !is_whitespace(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
pub const fn num_seek_length (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_num_end(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_digit(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
pub const fn is_digit (c: char) -> bool { matches!(c, '0'..='9') }
|
||||
pub const fn is_num_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') }
|
||||
pub const fn to_number <D: Dsl> (digits: &str) -> Result<usize, DslError> {
|
||||
let mut iter = char_indices(digits);
|
||||
let mut value = 0;
|
||||
while let Some(((_, c), next)) = iter.next() {
|
||||
match to_digit(c) {
|
||||
Ok(digit) => value = 10 * value + digit,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
iter = next;
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
pub const fn to_digit (c: char) -> Result<usize, DslError> {
|
||||
Ok(match c {
|
||||
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4,
|
||||
'5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
|
||||
_ => return Err(Unexpected(c))
|
||||
})
|
||||
}
|
||||
|
||||
pub const fn peek (mut src: &str) -> DslPerhaps<&str> {
|
||||
Ok(Some(match () {
|
||||
_ if let Ok(Some(exp)) = exp_peek(src) => exp,
|
||||
|
|
@ -52,152 +178,6 @@ pub const fn seek (mut src: &str) -> DslPerhaps<(usize, usize)> {
|
|||
}))
|
||||
}
|
||||
|
||||
const fn is_whitespace (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t') }
|
||||
pub trait DslExp<'s>: Dsl + From<&'s str> {
|
||||
fn exp (&'s self) -> DslPerhaps<Self> {
|
||||
Ok(exp_peek(self.src())?.map(Into::into))
|
||||
}
|
||||
fn head (&'s self) -> DslPerhaps<Self> {
|
||||
Ok(peek(&self.src()[1..])?.map(Into::into))
|
||||
}
|
||||
fn tail (&'s self) -> DslPerhaps<Self> {
|
||||
Ok(if let Some((head_start, head_len)) = seek(&self.src()[1..])? {
|
||||
peek(&self.src()[(1+head_start+head_len)..])?.map(Into::into)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
//impl<'s, D: DslExp<'s>> DslExp<'s> for Option<D> {}
|
||||
impl<'s, D: Dsl + From<&'s str>> DslExp<'s> for D {}
|
||||
def_peek_seek!(exp_peek, exp_seek, exp_seek_start, exp_seek_length);
|
||||
const fn is_exp_start (c: char) -> bool { matches!(c, '(') }
|
||||
const fn is_exp_end (c: char) -> bool { matches!(c, ')') }
|
||||
const fn exp_seek_start (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_exp_start(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_whitespace(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
const fn exp_seek_length (mut source: &str) -> DslPerhaps<usize> {
|
||||
let mut depth = 0;
|
||||
iter_chars!(source => |i, c| if is_exp_start(c) {
|
||||
depth += 1;
|
||||
} else if is_exp_end(c) {
|
||||
if depth == 0 {
|
||||
return Err(Unexpected(c))
|
||||
} else if depth == 1 {
|
||||
return Ok(Some(i))
|
||||
} else {
|
||||
depth -= 1;
|
||||
}
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub trait DslSym: Dsl { fn sym (&self) -> DslPerhaps<&str> { sym_peek(self.src()) } }
|
||||
impl<D: Dsl> DslSym for D {}
|
||||
def_peek_seek!(sym_peek, sym_seek, sym_seek_start, sym_seek_length);
|
||||
const fn is_sym_start (c: char) -> bool { matches!(c, ':'|'@') }
|
||||
const fn is_sym_char (c: char) -> bool { matches!(c, 'a'..='z'|'A'..='Z'|'0'..='9'|'-') }
|
||||
const fn is_sym_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') }
|
||||
const fn sym_seek_start (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_sym_start(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_whitespace(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
const fn sym_seek_length (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_sym_end(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_sym_char(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub trait DslKey: Dsl { fn key (&self) -> DslPerhaps<&str> { key_peek(self.src()) } }
|
||||
impl<D: Dsl> DslKey for D {}
|
||||
def_peek_seek!(key_peek, key_seek, key_seek_start, key_seek_length);
|
||||
const fn is_key_start (c: char) -> bool { matches!(c, '/'|'a'..='z') }
|
||||
const fn is_key_char (c: char) -> bool { matches!(c, 'a'..='z'|'0'..='9'|'-'|'/') }
|
||||
const fn is_key_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') }
|
||||
const fn key_seek_start (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_key_start(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_whitespace(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
const fn key_seek_length (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_key_end(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_key_char(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub trait DslText: Dsl { fn text (&self) -> DslPerhaps<&str> { text_peek(self.src()) } }
|
||||
impl<D: Dsl> DslText for D {}
|
||||
def_peek_seek!(text_peek, text_seek, text_seek_start, text_seek_length);
|
||||
const fn is_text_start (c: char) -> bool { matches!(c, '"') }
|
||||
const fn is_text_end (c: char) -> bool { matches!(c, '"') }
|
||||
const fn text_seek_start (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_text_start(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_whitespace(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
const fn text_seek_length (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_text_end(c) { return Ok(Some(i)) });
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub trait DslNum: Dsl { fn num (&self) -> DslPerhaps<&str> { num_peek(self.src()) } }
|
||||
impl<D: Dsl> DslNum for D {}
|
||||
def_peek_seek!(num_peek, num_seek, num_seek_start, num_seek_length);
|
||||
const fn num_seek_start (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_digit(c) {
|
||||
return Ok(Some(i));
|
||||
} else if !is_whitespace(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
const fn num_seek_length (mut source: &str) -> DslPerhaps<usize> {
|
||||
iter_chars!(source => |i, c| if is_num_end(c) {
|
||||
return Ok(Some(i))
|
||||
} else if !is_digit(c) {
|
||||
return Err(Unexpected(c))
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
const fn is_digit (c: char) -> bool { matches!(c, '0'..='9') }
|
||||
const fn is_num_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') }
|
||||
pub const fn to_number <D: Dsl> (digits: &str) -> Result<usize, DslError> {
|
||||
let mut iter = char_indices(digits);
|
||||
let mut value = 0;
|
||||
while let Some(((_, c), next)) = iter.next() {
|
||||
match to_digit(c) {
|
||||
Ok(digit) => value = 10 * value + digit,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
iter = next;
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
pub const fn to_digit (c: char) -> Result<usize, DslError> {
|
||||
Ok(match c {
|
||||
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4,
|
||||
'5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
|
||||
_ => return Err(Unexpected(c))
|
||||
})
|
||||
pub const fn is_whitespace (c: char) -> bool {
|
||||
matches!(c, ' '|'\n'|'\r'|'\t')
|
||||
}
|
||||
|
|
@ -1,5 +1,25 @@
|
|||
use crate::*;
|
||||
|
||||
/// Event source
|
||||
pub trait Input: Sized {
|
||||
/// Type of input event
|
||||
type Event;
|
||||
/// Result of handling input
|
||||
type Handled; // TODO: make this an Option<Box dyn Command<Self>> containing the undo
|
||||
/// Currently handled event
|
||||
fn event (&self) -> &Self::Event;
|
||||
/// Whether component should exit
|
||||
fn is_done (&self) -> bool;
|
||||
/// Mark component as done
|
||||
fn done (&self);
|
||||
}
|
||||
|
||||
flex_trait_mut!(Handle <E: Input> {
|
||||
fn handle (&mut self, _input: &E) -> Perhaps<E::Handled> {
|
||||
Ok(None)
|
||||
}
|
||||
});
|
||||
|
||||
pub trait Command<S>: Send + Sync + Sized {
|
||||
fn execute (self, state: &mut S) -> Perhaps<Self>;
|
||||
fn delegate <T> (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps<T>
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
use crate::*;
|
||||
|
||||
/// Map of each event (e.g. key combination) to
|
||||
/// all command expressions bound to it by
|
||||
/// all loaded input layers.
|
||||
type EventMapImpl<E, C> = BTreeMap<E, Vec<Binding<C>>>;
|
||||
|
||||
/// A collection of input bindings.
|
||||
///
|
||||
/// Each contained layer defines a mapping from input event to command invocation
|
||||
|
|
@ -14,8 +16,26 @@ type EventMapImpl<E, C> = BTreeMap<E, Vec<Binding<C>>>;
|
|||
/// that .event()binding's value is returned.
|
||||
#[derive(Debug)]
|
||||
pub struct EventMap<E, C>(EventMapImpl<E, C>);
|
||||
|
||||
/// An input binding.
|
||||
#[derive(Debug)]
|
||||
pub struct Binding<C> {
|
||||
pub command: C,
|
||||
pub condition: Option<Condition>,
|
||||
pub description: Option<Arc<str>>,
|
||||
pub source: Option<Arc<PathBuf>>,
|
||||
}
|
||||
|
||||
/// Input bindings are only returned if this evaluates to true
|
||||
pub struct Condition(Box<dyn Fn()->bool + Send + Sync>);
|
||||
|
||||
impl_debug!(Condition |self, w| { write!(w, "*") });
|
||||
|
||||
/// Default is always empty map regardless if `E` and `C` implement [Default].
|
||||
impl<E, C> Default for EventMap<E, C> { fn default () -> Self { Self(Default::default()) } }
|
||||
impl<E, C> Default for EventMap<E, C> {
|
||||
fn default () -> Self { Self(Default::default()) }
|
||||
}
|
||||
|
||||
impl<E: Clone + Ord, C> EventMap<E, C> {
|
||||
/// Create a new event map
|
||||
pub fn new () -> Self {
|
||||
|
|
@ -28,7 +48,7 @@ impl<E: Clone + Ord, C> EventMap<E, C> {
|
|||
}
|
||||
/// Add a binding to an event map.
|
||||
pub fn add (&mut self, event: E, binding: Binding<C>) -> &mut Self {
|
||||
if (!self.0.contains_key(&event)) {
|
||||
if !self.0.contains_key(&event) {
|
||||
self.0.insert(event.clone(), Default::default());
|
||||
}
|
||||
self.0.get_mut(&event).unwrap().push(binding);
|
||||
|
|
@ -57,47 +77,46 @@ impl<E: Clone + Ord, C> EventMap<E, C> {
|
|||
Self::from_dsl(&mut source.as_ref())
|
||||
}
|
||||
/// Create event map from DSL tokenizer.
|
||||
pub fn from_dsl <'s, D: DslExp<'s> + 's> (dsl: &'s mut D) -> Usually<Self> where E: From<Arc<str>> {
|
||||
pub fn from_dsl <'s, > (dsl: &'s mut impl Dsl) -> Usually<Self> where E: From<Arc<str>> {
|
||||
let mut map: Self = Default::default();
|
||||
let mut head: Option<D> = dsl.head()?;
|
||||
let mut tail: Option<D> = dsl.tail()?;
|
||||
loop {
|
||||
if let Some(ref token) = head {
|
||||
if let Some(ref text) = token.text()? {
|
||||
map.0.extend(Self::from_path(PathBuf::from(text))?.0);
|
||||
continue
|
||||
if let Some(dsl) = dsl.exp()? {
|
||||
let mut head = dsl.head()?;
|
||||
let mut tail = dsl.tail()?;
|
||||
loop {
|
||||
if let Some(ref token) = head {
|
||||
if let Some(ref text) = token.text()? {
|
||||
map.0.extend(Self::from_path(PathBuf::from(text))?.0);
|
||||
continue
|
||||
}
|
||||
//if let Some(ref exp) = token.exp()? {
|
||||
////_ if let Some(sym) = token.head()?.sym()?.as_ref() => {
|
||||
////todo!()
|
||||
////},
|
||||
////_ if Some(&"if") == token.head()?.key()?.as_ref() => {
|
||||
////todo!()
|
||||
////},
|
||||
//return Err(format!("unexpected: {:?}", token.exp()).into())
|
||||
//}
|
||||
return Err(format!("unexpected: {token:?}").into())
|
||||
} else {
|
||||
break
|
||||
}
|
||||
if let Some(next) = tail {
|
||||
head = next.head()?;
|
||||
tail = next.tail()?;
|
||||
} else {
|
||||
break
|
||||
}
|
||||
//if let Some(ref exp) = token.exp()? {
|
||||
////_ if let Some(sym) = token.head()?.sym()?.as_ref() => {
|
||||
////todo!()
|
||||
////},
|
||||
////_ if Some(&"if") == token.head()?.key()?.as_ref() => {
|
||||
////todo!()
|
||||
////},
|
||||
//return Err(format!("unexpected: {:?}", token.exp()).into())
|
||||
//}
|
||||
return Err(format!("unexpected: {token:?}").into())
|
||||
} else {
|
||||
break
|
||||
}
|
||||
if let Some(next) = tail {
|
||||
head = next.head()?;
|
||||
tail = next.tail()?;
|
||||
} else {
|
||||
break
|
||||
}
|
||||
} else if let Some(text) = dsl.text()? {
|
||||
todo!("load from file path")
|
||||
} else {
|
||||
todo!("return error for invalid input")
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
}
|
||||
/// An input binding.
|
||||
#[derive(Debug)]
|
||||
pub struct Binding<C> {
|
||||
pub command: C,
|
||||
pub condition: Option<Condition>,
|
||||
pub description: Option<Arc<str>>,
|
||||
pub source: Option<Arc<PathBuf>>,
|
||||
}
|
||||
|
||||
impl<C> Binding<C> {
|
||||
fn from_dsl (dsl: impl Dsl) -> Usually<Self> {
|
||||
let mut command: Option<C> = None;
|
||||
|
|
@ -111,8 +130,6 @@ impl<C> Binding<C> {
|
|||
}
|
||||
}
|
||||
}
|
||||
pub struct Condition(Box<dyn Fn()->bool + Send + Sync>);
|
||||
impl_debug!(Condition |self, w| { write!(w, "*") });
|
||||
|
||||
fn unquote (x: &str) -> &str {
|
||||
let mut chars = x.chars();
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
use crate::*;
|
||||
use std::sync::{Mutex, Arc, RwLock};
|
||||
|
||||
/// Event source
|
||||
pub trait Input: Sized {
|
||||
/// Type of input event
|
||||
type Event;
|
||||
/// Result of handling input
|
||||
type Handled; // TODO: make this an Option<Box dyn Command<Self>> containing the undo
|
||||
/// Currently handled event
|
||||
fn event (&self) -> &Self::Event;
|
||||
/// Whether component should exit
|
||||
fn is_done (&self) -> bool;
|
||||
/// Mark component as done
|
||||
fn done (&self);
|
||||
}
|
||||
|
||||
/// Implement the [Handle] trait.
|
||||
#[macro_export] macro_rules! handle {
|
||||
(|$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
|
||||
impl<E: Engine> ::tengri::input::Handle<E> for $Struct {
|
||||
fn handle (&mut $self, $input: &E) -> Perhaps<E::Handled> {
|
||||
$handler
|
||||
}
|
||||
}
|
||||
};
|
||||
($E:ty: |$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
|
||||
impl ::tengri::input::Handle<$E> for $Struct {
|
||||
fn handle (&mut $self, $input: &$E) ->
|
||||
Perhaps<<$E as ::tengri::input::Input>::Handled>
|
||||
{
|
||||
$handler
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flex_trait_mut!(Handle <E: Input> {
|
||||
fn handle (&mut self, _input: &E) -> Perhaps<E::Handled> {
|
||||
Ok(None)
|
||||
}
|
||||
});
|
||||
|
||||
//pub trait Handle<E: Input> {
|
||||
//fn handle (&mut self, _input: &E) -> Perhaps<E::Handled> {
|
||||
//Ok(None)
|
||||
//}
|
||||
//}
|
||||
//impl<E: Input, H: Handle<E>> Handle<E> for &mut H {
|
||||
//fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||
//(*self).handle(context)
|
||||
//}
|
||||
//}
|
||||
//impl<E: Input, H: Handle<E>> Handle<E> for Option<H> {
|
||||
//fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||
//if let Some(handle) = self {
|
||||
//handle.handle(context)
|
||||
//} else {
|
||||
//Ok(None)
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
//impl<H, E: Input> Handle<E> for Mutex<H> where H: Handle<E> {
|
||||
//fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||
//self.get_mut().unwrap().handle(context)
|
||||
//}
|
||||
//}
|
||||
//impl<H, E: Input> Handle<E> for Arc<Mutex<H>> where H: Handle<E> {
|
||||
//fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||
//self.lock().unwrap().handle(context)
|
||||
//}
|
||||
//}
|
||||
//impl<H, E: Input> Handle<E> for RwLock<H> where H: Handle<E> {
|
||||
//fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||
//self.write().unwrap().handle(context)
|
||||
//}
|
||||
//}
|
||||
//impl<H, E: Input> Handle<E> for Arc<RwLock<H>> where H: Handle<E> {
|
||||
//fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
|
||||
//self.write().unwrap().handle(context)
|
||||
//}
|
||||
//}
|
||||
|
|
@ -1,10 +1,30 @@
|
|||
/** Implement `Command` for given `State` and `handler` */
|
||||
/// Implement [Command] for given `State` and `handler`
|
||||
#[macro_export] macro_rules! command {
|
||||
($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => {
|
||||
impl$(<$($l),+>)? Command<$State> for $Command {
|
||||
impl$(<$($l),+>)? ::tengri::input::Command<$State> for $Command {
|
||||
fn execute ($self, $state: &mut $State) -> Perhaps<Self> {
|
||||
Ok($handler)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement [Handle] for given `State` and `handler`.
|
||||
#[macro_export] macro_rules! handle {
|
||||
(|$self:ident:$State:ty,$input:ident|$handler:expr) => {
|
||||
impl<E: Engine> ::tengri::input::Handle<E> for $State {
|
||||
fn handle (&mut $self, $input: &E) -> Perhaps<E::Handled> {
|
||||
$handler
|
||||
}
|
||||
}
|
||||
};
|
||||
($E:ty: |$self:ident:$State:ty,$input:ident|$handler:expr) => {
|
||||
impl ::tengri::input::Handle<$E> for $State {
|
||||
fn handle (&mut $self, $input: &$E) ->
|
||||
Perhaps<<$E as ::tengri::input::Input>::Handled>
|
||||
{
|
||||
$handler
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
28
input/src/input_test.rs
Normal file
28
input/src/input_test.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
use crate::*;
|
||||
|
||||
#[test] fn test_stub_input () -> Usually<()> {
|
||||
use crate::*;
|
||||
struct TestInput(bool);
|
||||
enum TestEvent { Test1 }
|
||||
impl Input for TestInput {
|
||||
type Event = TestEvent;
|
||||
type Handled = ();
|
||||
fn event (&self) -> &Self::Event {
|
||||
&TestEvent::Test1
|
||||
}
|
||||
fn is_done (&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
fn done (&self) {}
|
||||
}
|
||||
let _ = TestInput(true).event();
|
||||
assert!(TestInput(true).is_done());
|
||||
assert!(!TestInput(false).is_done());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "dsl"))] #[test] fn test_dsl_keymap () -> Usually<()> {
|
||||
let _keymap = CstIter::new("");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -3,41 +3,14 @@
|
|||
|
||||
pub(crate) use std::fmt::Debug;
|
||||
pub(crate) use std::sync::Arc;
|
||||
pub(crate) use std::collections::{BTreeMap, HashMap};
|
||||
pub(crate) use std::collections::BTreeMap;
|
||||
pub(crate) use std::path::{Path, PathBuf};
|
||||
pub(crate) use std::fs::exists;
|
||||
pub(crate) use tengri_core::*;
|
||||
|
||||
mod input_macros;
|
||||
mod input_command; pub use self::input_command::*;
|
||||
mod input_handle; pub use self::input_handle::*;
|
||||
|
||||
mod input; pub use self::input::*;
|
||||
#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*;
|
||||
#[cfg(feature = "dsl")] mod input_dsl;
|
||||
#[cfg(feature = "dsl")] pub use self::input_dsl::*;
|
||||
|
||||
#[cfg(test)] #[test] fn test_stub_input () -> Usually<()> {
|
||||
use crate::*;
|
||||
struct TestInput(bool);
|
||||
enum TestEvent { Test1 }
|
||||
impl Input for TestInput {
|
||||
type Event = TestEvent;
|
||||
type Handled = ();
|
||||
fn event (&self) -> &Self::Event {
|
||||
&TestEvent::Test1
|
||||
}
|
||||
fn is_done (&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
fn done (&self) {}
|
||||
}
|
||||
let _ = TestInput(true).event();
|
||||
assert!(TestInput(true).is_done());
|
||||
assert!(!TestInput(false).is_done());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "dsl"))] #[test] fn test_dsl_keymap () -> Usually<()> {
|
||||
let _keymap = CstIter::new("");
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(test)] mod input_test;
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{
|
|||
/// the layout elements that are provided by this crate.
|
||||
#[cfg(feature = "dsl")] mod ops_dsl {
|
||||
use crate::*;
|
||||
use ::tengri_dsl::*;
|
||||
//use ::tengri_dsl::*;
|
||||
|
||||
//macro_rules! dsl {
|
||||
//($(
|
||||
|
|
|
|||
2
proc/src/proc_flex.rs
Normal file
2
proc/src/proc_flex.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// TODO: #[derive(Flex, FlexMut, FlexOption, FlexResult ... ?)]
|
||||
// better derive + annotations
|
||||
Loading…
Add table
Add a link
Reference in a new issue