diff --git a/dsl/README.md b/dsl/README.md index 009a2f9..a5bcf22 100644 --- a/dsl/README.md +++ b/dsl/README.md @@ -100,15 +100,16 @@ or configuration statements, and look like this: this is the trait which differentiates "a thing" from "a thing that is many things". -|source |key|exp |head |tail | -|-----------------------|---|-------|---------|---------------| -|`a` |`a`|E0 |`a` |none | -|`(a)` |E1 |`a` |`a` |none | -|`a b c` |E2 |E0 |`a` |`b c` | -|`(a b c)` |E0 |`a b c`|`(a b c)`|none | -|`(a b c) d e f` |E0 |E2 |`(a b c)`|`d e f` | -|`a (b c d) e f` |E2 |E0 |`a` |`(b c d) e f` | +|source |key|exp |head |tail | +|---------------|---|-------|---------|---------------| +|`a` |`a`|e0 |`a` |None | +|`(a)` |e1 |`a` |`(a)` |None | +|`a b c` |e2 |e0 |`a` |`b c` | +|`(a b c)` |e1 |`a b c`|`(a b c)`| | +|`(a b c) d e` |e1 |e3 |`(a b c)`|`d e` | +|`a (b c d) e f`|e1 |e0 |`a` |`(b c d) e f` | -* **E0**: Expected `(` -* **E1**: Unexpected `(` -* **E2**: Trailing characters +* e0: Unexpected 'a' +* e1: Unexpected '(' +* e2: Unexpected 'b' +* e3: Unexpected 'd' diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index 51a5250..c43e2f1 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -6,58 +6,40 @@ extern crate const_panic; use const_panic::PanicFmt; use std::fmt::Debug; -pub(crate) use std::error::Error; -pub(crate) use std::sync::Arc; -pub(crate) use konst::iter::for_each; -pub(crate) use konst::string::{str_from, str_range, char_indices}; +pub(crate) use std::{error::Error, sync::Arc}; +pub(crate) use konst::{iter::for_each, string::{str_from, 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_parse; -pub(crate) use self::dsl_parse::*; -pub mod parse { pub use crate::dsl_parse::*; } #[cfg(test)] mod dsl_test; flex_trait!(Dsl: Debug + Send + Sync + Sized { fn src (&self) -> DslPerhaps<&str> { unreachable!("Dsl::src default impl") } }); -impl Dsl for Arc { - fn src (&self) -> DslPerhaps<&str> { Ok(Some(self.as_ref())) } -} -impl<'s> Dsl for &'s str { - fn src (&self) -> DslPerhaps<&str> { Ok(Some(self.as_ref())) } -} +impl Dsl for Arc { fn src (&self) -> DslPerhaps<&str> { Ok(Some(self.as_ref())) } } +impl<'s> Dsl for &'s str { fn src (&self) -> DslPerhaps<&str> { Ok(Some(self.as_ref())) } } impl Dsl for Option { - fn src (&self) -> DslPerhaps<&str> { - Ok(if let Some(dsl) = self { dsl.src()? } else { None }) - } + fn src (&self) -> DslPerhaps<&str> {Ok(if let Some(dsl) = self { dsl.src()? } else { None })} } impl Dsl for Result { - fn src (&self) -> DslPerhaps<&str> { - match self { Ok(dsl) => Ok(dsl.src()?), Err(e) => Err(*e) } - } + fn src (&self) -> DslPerhaps<&str> {match self {Ok(dsl) => Ok(dsl.src()?), Err(e) => Err(*e)}} } -impl DslExp for D {} -pub trait DslExp: Dsl { - fn exp (&self) -> DslPerhaps<&str> { ok_flat(self.src()?.map(exp_peek_inner)) } - fn head (&self) -> DslPerhaps<&str> { ok_flat(self.src()?.map(peek)) } - fn tail (&self) -> DslPerhaps<&str> { ok_flat(self.src()?.map(peek_tail(self.head()))) } +impl DslExp for D {} pub trait DslExp: Dsl { + fn exp (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(exp_peek_inner_only))} + fn head (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(peek))} + fn tail (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(peek_tail(self.head())))} } -impl DslSym for D {} -pub trait DslSym: Dsl { - fn sym (&self) -> DslPerhaps<&str> { ok_flat(self.src()?.map(sym_peek_only)) } +impl DslText for D {} pub trait DslText: Dsl { + fn text (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(text_peek_only))} } -impl DslKey for D {} -pub trait DslKey: Dsl { - fn key (&self) -> DslPerhaps<&str> { ok_flat(self.src()?.map(key_peek_only)) } +impl DslSym for D {} pub trait DslSym: Dsl { + fn sym (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(sym_peek_only))} } -impl DslText for D {} -pub trait DslText: Dsl { - fn text (&self) -> DslPerhaps<&str> { ok_flat(self.src()?.map(text_peek_only)) } +impl DslKey for D {} pub trait DslKey: Dsl { + fn key (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(key_peek_only))} } -impl DslNum for D {} -pub trait DslNum: Dsl { - fn num (&self) -> DslPerhaps<&str> { ok_flat(self.src()?.map(num_peek_only)) } +impl DslNum for D {} pub trait DslNum: Dsl { + fn num (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(num_peek_only))} } /// DSL-specific result type. pub type DslResult = Result; @@ -78,7 +60,7 @@ pub enum DslError { let mut $next: Option> = dsl.next()?.map(Into::into); let mut rest: Option> = dsl.rest()?.map(Into::into); loop { - if let Some($next) = $next { $body; } else { break } + if let Some($next) = $next { $body } else { break }; if let Some(next) = rest { $next = next.next()?.map(Into::into); rest = next.rest()?.map(Into::into); @@ -98,3 +80,220 @@ fn peek_tail <'a> (head: DslPerhaps<&'a str>) -> impl Fn(&'a str)->DslPerhaps<&' e => e } } +macro_rules! def_peek_seek(($peek:ident, $peek_only:ident, $seek:ident, $seek_start:ident, $seek_length:ident)=>{ + /// Find a slice corrensponding to a syntax token. + pub const fn $peek (source: &str) -> DslPerhaps<&str> { + match $seek(source) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some((start, length))) => Ok(Some(str_range(source, start, start + length))), + } + } + /// Find a slice corrensponding to a syntax token + /// but return an error if it isn't the only thing + /// in the source. + pub const fn $peek_only (source: &str) -> DslPerhaps<&str> { + match $seek(source) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some((start, length))) => { + if let Err(e) = no_trailing_non_whitespace(source, start + length) { return Err(e) } + Ok(Some(str_range(source, start, start + length))) + } + } + } + /// Find a start and length corresponding to a syntax token. + pub const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> { + match $seek_start(source) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some(start)) => match $seek_length(str_from(source, start)) { + Ok(Some(length)) => Ok(Some((start, length))), + Ok(None) => Ok(None), + Err(e) => Err(e), + }, + } + } +}); +def_peek_seek!(exp_peek, exp_peek_only, exp_seek, exp_seek_start, exp_seek_length); +pub const fn exp_peek_inner (source: &str) -> DslPerhaps<&str> { + match exp_peek(source) { + Ok(Some(peeked)) => { + let len = peeked.len(); + let start = if len > 0 { 1 } else { 0 }; + Ok(Some(str_range(source, start, start + len.saturating_sub(2)))) + }, + e => e + } +} +pub const fn exp_peek_inner_only (source: &str) -> DslPerhaps<&str> { + match exp_seek(source) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some((start, length))) => { + if let Err(e) = no_trailing_non_whitespace(source, start + length) { return Err(e) } + let peeked = str_range(source, start, start + length); + let len = peeked.len(); + let start = if len > 0 { 1 } else { 0 }; + Ok(Some(str_range(peeked, start, start + len.saturating_sub(2)))) + }, + } +} +pub const fn is_exp_start (c: char) -> bool { c == '(' } +pub const fn is_exp_end (c: char) -> bool { c == ')' } +pub const fn exp_seek_start (source: &str) -> DslPerhaps { + for_each!((i, c) in char_indices(source) => 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 (source: &str) -> DslPerhaps { + let mut depth = 0; + for_each!((i, c) in char_indices(source) => 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 + 1)) + } else { + depth -= 1; + } + }); + Err(Incomplete) +} +def_peek_seek!(sym_peek, sym_peek_only, 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 (source: &str) -> DslPerhaps { + for_each!((i, c) in char_indices(source) => 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 (source: &str) -> DslPerhaps { + for_each!((i, c) in char_indices(source) => if is_sym_end(c) { + return Ok(Some(i)) + } else if !is_sym_char(c) { + return Err(Unexpected(c)) + }); + Ok(Some(source.len())) +} +def_peek_seek!(key_peek, key_peek_only, 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 (source: &str) -> DslPerhaps { + for_each!((i, c) in char_indices(source) => 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 (source: &str) -> DslPerhaps { + for_each!((i, c) in char_indices(source) => if is_key_end(c) { + return Ok(Some(i)) + } else if !is_key_char(c) { + return Err(Unexpected(c)) + }); + Ok(Some(source.len())) +} +def_peek_seek!(text_peek, text_peek_only, 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 (source: &str) -> DslPerhaps { + for_each!((i, c) in char_indices(source) => 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 (source: &str) -> DslPerhaps { + for_each!((i, c) in char_indices(source) => if is_text_end(c) { return Ok(Some(i)) }); + Ok(None) +} +def_peek_seek!(num_peek, num_peek_only, num_seek, num_seek_start, num_seek_length); +pub const fn num_seek_start (source: &str) -> DslPerhaps { + for_each!((i, c) in char_indices(source) => 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 (source: &str) -> DslPerhaps { + for_each!((i, c) in char_indices(source) => 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 (digits: &str) -> Result { + let mut iter = char_indices(digits); + let mut value = 0; + while let Some(((_, c), next)) = iter.next() { + match to_digit(c) { + Ok(digit) => value = 10 * value + digit, + Err(e) => return Err(e), + } + iter = next; + } + Ok(value) +} +pub const fn to_digit (c: char) -> Result { + Ok(match c { + '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, + '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, + _ => return Err(Unexpected(c)) + }) +} +pub const fn peek (src: &str) -> DslPerhaps<&str> { + Ok(Some(match () { + _ if let Ok(Some(exp)) = exp_peek(src) => exp, + _ if let Ok(Some(sym)) = sym_peek(src) => sym, + _ if let Ok(Some(key)) = key_peek(src) => key, + _ if let Ok(Some(num)) = num_peek(src) => num, + _ if let Ok(Some(text)) = text_peek(src) => text, + _ => { + for_each!((_, c) in char_indices(src) => if !is_whitespace(c) { + return Err(Unexpected(c)) + }); + return Ok(None) + } + })) +} +pub const fn seek (src: &str) -> DslPerhaps<(usize, usize)> { + Ok(Some(match () { + _ if let Ok(Some(exp)) = exp_seek(src) => exp, + _ if let Ok(Some(sym)) = sym_seek(src) => sym, + _ if let Ok(Some(key)) = key_seek(src) => key, + _ if let Ok(Some(num)) = num_seek(src) => num, + _ if let Ok(Some(text)) = text_seek(src) => text, + _ => { + for_each!((_, c) in char_indices(src) => if !is_whitespace(c) { + return Err(Unexpected(c)) + }); + return Ok(None) + } + })) +} +pub const fn is_whitespace (c: char) -> bool { + matches!(c, ' '|'\n'|'\r'|'\t') +} +pub const fn no_trailing_non_whitespace (source: &str, offset: usize) -> DslResult<()> { + let tail = str_range(source, offset, source.len()); + for_each!((_, c) in char_indices(tail) => if !is_whitespace(c) { + return Err(Unexpected(c)) + }); + Ok(()) +} diff --git a/dsl/src/dsl_parse.rs b/dsl/src/dsl_parse.rs deleted file mode 100644 index bd8208d..0000000 --- a/dsl/src/dsl_parse.rs +++ /dev/null @@ -1,232 +0,0 @@ -use crate::*; - -macro_rules! def_peek_seek(($peek:ident, $peek_only:ident, $seek:ident, $seek_start:ident, $seek_length:ident)=>{ - /// Find a slice corrensponding to a syntax token. - 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 slice corrensponding to a syntax token - /// but return an error if it isn't the only thing - /// in the source. - pub const fn $peek_only (source: &str) -> DslPerhaps<&str> { - match $seek(source) { - Ok(Some((start, length))) => { - let remaining = source.len().saturating_sub(start + length); - let tail = str_range(source, start + length, remaining); - for_each!((_, c) in char_indices(tail) => if !is_whitespace(c) { - return Err(Unexpected(c)) - }); - 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. - pub const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> { - match $seek_start(source) { - Ok(Some(start)) => match $seek_length(str_from(source, start)) { - Ok(Some(length)) => Ok(Some((start, length))), - Ok(None) => Ok(None), - Err(e) => Err(e), - }, - Ok(None) => Ok(None), - Err(e) => Err(e) - } - } -}); - -pub const fn no_trailing_non_whitespace (source: &str, offset: usize) -> DslResult<()> { - let tail = str_range(source, offset, source.len().saturating_sub(offset)); - for_each!((_, c) in char_indices(tail) => if !is_whitespace(c) { - return Err(Unexpected(c)) - }); - Ok(()) -} - -def_peek_seek!(exp_peek, exp_peek_only, exp_seek, exp_seek_start, exp_seek_length); -pub const fn exp_peek_inner (source: &str) -> DslPerhaps<&str> { - match exp_peek(source) { - Ok(Some(peeked)) => { - let len = peeked.len(); - let start = if len > 0 { 1 } else { 0 }; - Ok(Some(str_range(source, start, start + len.saturating_sub(2)))) - }, - e => e - } -} -pub const fn exp_peek_inner_only (source: &str) -> DslPerhaps<&str> { - match exp_seek(source) { - Err(e) => Err(e), - Ok(None) => Ok(None), - Ok(Some((start, length))) => { - if let Err(e) = no_trailing_non_whitespace(source, start) { - return Err(e) - } - let start = if length > 0 { 1 } else { 0 }; - Ok(Some(str_range(source, start, start + length.saturating_sub(2)))) - }, - } -} -pub const fn is_exp_start (c: char) -> bool { c == '(' } -pub const fn is_exp_end (c: char) -> bool { c == ')' } -pub const fn exp_seek_start (source: &str) -> DslPerhaps { - for_each!((i, c) in char_indices(source) => 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 (source: &str) -> DslPerhaps { - let mut depth = 0; - for_each!((i, c) in char_indices(source) => 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 + 1)) - } else { - depth -= 1; - } - }); - Err(Incomplete) -} - -def_peek_seek!(sym_peek, sym_peek_only, 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 (source: &str) -> DslPerhaps { - for_each!((i, c) in char_indices(source) => 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 (source: &str) -> DslPerhaps { - for_each!((i, c) in char_indices(source) => if is_sym_end(c) { - return Ok(Some(i)) - } else if !is_sym_char(c) { - return Err(Unexpected(c)) - }); - Ok(Some(source.len())) -} - -def_peek_seek!(key_peek, key_peek_only, 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 (source: &str) -> DslPerhaps { - for_each!((i, c) in char_indices(source) => 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 (source: &str) -> DslPerhaps { - for_each!((i, c) in char_indices(source) => if is_key_end(c) { - return Ok(Some(i)) - } else if !is_key_char(c) { - return Err(Unexpected(c)) - }); - Ok(Some(source.len())) -} - -def_peek_seek!(text_peek, text_peek_only, 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 (source: &str) -> DslPerhaps { - for_each!((i, c) in char_indices(source) => 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 (source: &str) -> DslPerhaps { - for_each!((i, c) in char_indices(source) => if is_text_end(c) { return Ok(Some(i)) }); - Ok(None) -} - -def_peek_seek!(num_peek, num_peek_only, num_seek, num_seek_start, num_seek_length); -pub const fn num_seek_start (source: &str) -> DslPerhaps { - for_each!((i, c) in char_indices(source) => 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 (source: &str) -> DslPerhaps { - for_each!((i, c) in char_indices(source) => 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 (digits: &str) -> Result { - let mut iter = char_indices(digits); - let mut value = 0; - while let Some(((_, c), next)) = iter.next() { - match to_digit(c) { - Ok(digit) => value = 10 * value + digit, - Err(e) => return Err(e), - } - iter = next; - } - Ok(value) -} -pub const fn to_digit (c: char) -> Result { - Ok(match c { - '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, - '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, - _ => return Err(Unexpected(c)) - }) -} - -pub const fn peek (mut src: &str) -> DslPerhaps<&str> { - Ok(Some(match () { - _ if let Ok(Some(exp)) = exp_peek(src) => exp, - _ if let Ok(Some(sym)) = sym_peek(src) => sym, - _ if let Ok(Some(key)) = key_peek(src) => key, - _ if let Ok(Some(num)) = num_peek(src) => num, - _ if let Ok(Some(text)) = text_peek(src) => text, - _ => { - for_each!((i, c) in char_indices(src) => if !is_whitespace(c) { - return Err(Unexpected(c)) - }); - return Ok(None) - } - })) -} - -pub const fn seek (mut src: &str) -> DslPerhaps<(usize, usize)> { - Ok(Some(match () { - _ if let Ok(Some(exp)) = exp_seek(src) => exp, - _ if let Ok(Some(sym)) = sym_seek(src) => sym, - _ if let Ok(Some(key)) = key_seek(src) => key, - _ if let Ok(Some(num)) = num_seek(src) => num, - _ if let Ok(Some(text)) = text_seek(src) => text, - _ => { - for_each!((i, c) in char_indices(src) => if !is_whitespace(c) { - return Err(Unexpected(c)) - }); - return Ok(None) - } - })) -} - -pub const fn is_whitespace (c: char) -> bool { - matches!(c, ' '|'\n'|'\r'|'\t') -} diff --git a/dsl/src/dsl_test.rs b/dsl/src/dsl_test.rs index be491fe..05f0308 100644 --- a/dsl/src/dsl_test.rs +++ b/dsl/src/dsl_test.rs @@ -1,6 +1,6 @@ use crate::*; -macro_rules!is_ok_some(($exp:expr, $val:expr)=>{assert_eq!($exp, Ok(Some($val)))};); -macro_rules!is_ok_none(($exp:expr)=>{assert_eq!($exp, Ok(None))};); +macro_rules!is_some(($exp:expr, $val:expr)=>{assert_eq!($exp, Ok(Some($val)))};); +macro_rules!is_none(($exp:expr)=>{assert_eq!($exp, Ok(None))};); macro_rules!is_err(($exp:expr)=>{assert!($exp.is_err())}; ($exp:expr, $err:expr)=>{assert_eq!($exp, Err($err))};); #[test] fn test_exp () -> Result<(), DslError> { @@ -25,49 +25,49 @@ macro_rules!is_err(($exp:expr)=>{assert!($exp.is_err())}; assert!(!is_key_start(' ')); assert!(is_key_start('f')); - is_ok_some!(key_seek_start("foo"), 0); - is_ok_some!(key_seek_start("foo "), 0); - is_ok_some!(key_seek_start(" foo "), 1); - is_ok_some!(key_seek_length(&" foo "[1..]), 3); - is_ok_some!(key_seek("foo"), (0, 3)); - is_ok_some!(key_peek("foo"), "foo"); - is_ok_some!(key_seek("foo "), (0, 3)); - is_ok_some!(key_peek("foo "), "foo"); - is_ok_some!(key_seek(" foo "), (1, 3)); - is_ok_some!(key_peek(" foo "), "foo"); + is_some!(key_seek_start("foo"), 0); + is_some!(key_seek_start("foo "), 0); + is_some!(key_seek_start(" foo "), 1); + is_some!(key_seek_length(&" foo "[1..]), 3); + is_some!(key_seek("foo"), (0, 3)); + is_some!(key_peek("foo"), "foo"); + is_some!(key_seek("foo "), (0, 3)); + is_some!(key_peek("foo "), "foo"); + is_some!(key_seek(" foo "), (1, 3)); + is_some!(key_peek(" foo "), "foo"); is_err!("(foo)".key()); is_err!("foo".exp()); - is_ok_some!("(foo)".exp(), "foo"); - is_ok_some!("(foo)".head(), "(foo)"); - is_ok_none!("(foo)".tail()); + is_some!("(foo)".exp(), "foo"); + is_some!("(foo)".head(), "(foo)"); + is_none!("(foo)".tail()); - is_ok_some!("(foo bar baz)".exp(), "foo bar baz"); - is_ok_some!("(foo bar baz)".head(), "(foo bar baz)"); - is_ok_none!("(foo bar baz)".tail()); + is_some!("(foo bar baz)".exp(), "foo bar baz"); + is_some!("(foo bar baz)".head(), "(foo bar baz)"); + is_none!("(foo bar baz)".tail()); - is_ok_some!("(foo bar baz)".exp().head(), "foo"); - is_ok_some!("(foo bar baz)".exp().tail(), "bar baz"); - is_ok_some!("(foo bar baz)".exp().tail().head(), "bar"); - is_ok_some!("(foo bar baz)".exp().tail().tail(), "baz"); + is_some!("(foo bar baz)".exp().head(), "foo"); + is_some!("(foo bar baz)".exp().tail(), "bar baz"); + is_some!("(foo bar baz)".exp().tail().head(), "bar"); + is_some!("(foo bar baz)".exp().tail().tail(), "baz"); - is_ok_none!("foo".exp()); - is_ok_some!("foo".key(), "foo"); - is_ok_some!(" foo".key(), "foo"); - is_ok_some!(" foo ".key(), "foo"); + is_err!("foo".exp()); + is_some!("foo".key(), "foo"); + is_some!(" foo".key(), "foo"); + is_some!(" foo ".key(), "foo"); - assert_eq!(" foo ".head(), Ok(Some("foo"))); + is_some!(" foo ".head(), "foo"); //assert_eq!(" foo ".head().head(), Ok(None)); - assert_eq!(" foo ".head().tail(), Ok(None)); - assert_eq!(" foo ".tail(), Ok(None)); - assert_eq!(" foo ".tail().head(), Ok(None)); - assert_eq!(" foo ".tail().tail(), Ok(None)); + is_none!(" foo ".head().tail()); + is_none!(" foo ".tail()); + is_none!(" foo ".tail().head()); + is_none!(" foo ".tail().tail()); assert_eq!(" foo bar ".head(), Ok(Some("foo"))); //assert_eq!(" foo bar ".head().head(), Ok(None)); assert_eq!(" foo bar ".head().tail(), Ok(None)); - assert_eq!(" foo bar ".tail(), Ok(Some("bar"))); + assert_eq!(" foo bar ".tail(), Ok(Some(" bar "))); assert_eq!(" foo bar ".tail().head(), Ok(Some("bar"))); assert_eq!(" foo bar ".tail().tail(), Ok(None)); @@ -79,12 +79,19 @@ macro_rules!is_err(($exp:expr)=>{assert!($exp.is_err())}; assert_eq!(" (foo) (bar) ".head(), Ok(Some("(foo)"))); //assert_eq!(" (foo) (bar) ".head().head(), Ok(Some("foo"))); //assert_eq!(" (foo) (bar) ".head().head().head(), Ok(None)); - assert_eq!(" (foo) (bar) ".tail(), Ok(Some("(bar)"))); + is_some!(" (foo) (bar) ".tail(), " (bar) "); + is_some!(" (foo) (bar) ".tail().head(), "(bar)"); + is_some!(" (foo) (bar) ".tail().head().head(), "(bar)"); + is_some!(" (foo) (bar) ".tail().head().exp(), "bar"); + is_some!(" (foo) (bar) ".tail().head().exp().head(), "bar"); - assert_eq!(" (foo bar baz) ".head(), Ok(Some("(foo bar baz)"))); - assert_eq!(" (foo bar baz) ".head().head(), Ok(Some("foo"))); - assert_eq!(" (foo bar baz) ".head().tail(), Ok(Some("bar baz"))); - assert_eq!(" (foo bar baz) ".head().tail().head(), Ok(Some("bar"))); - assert_eq!(" (foo bar baz) ".head().tail().tail(), Ok(Some("baz"))); + is_some!(" (foo bar baz) ".head(), "(foo bar baz)"); + is_some!(" (foo bar baz) ".head().head(), "(foo bar baz)"); + is_some!(" (foo bar baz) ".exp(), "foo bar baz"); + is_some!(" (foo bar baz) ".exp().head(), "foo"); + is_some!(" (foo bar baz) ".exp().tail(), "bar baz"); + is_some!(" (foo bar baz) ".exp().tail().head(), "bar"); + is_some!(" (foo bar baz) ".exp().tail().tail(), "baz"); + is_none!(" (foo bar baz) ".tail()); Ok(()) } diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs index bb3c353..1f6ac42 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -1,10 +1,9 @@ use crate::*; - +use std::{sync::Arc, collections::BTreeMap, path::{Path, PathBuf}, fs::{exists, read_to_string}}; /// Map of each event (e.g. key combination) to /// all command expressions bound to it by /// all loaded input layers. type EventMapImpl = BTreeMap>>; - /// A collection of input bindings. /// /// Each contained layer defines a mapping from input event to command invocation @@ -16,7 +15,6 @@ type EventMapImpl = BTreeMap>>; /// that .event()binding's value is returned. #[derive(Debug)] pub struct EventMap(EventMapImpl); - /// An input binding. #[derive(Debug, Clone)] pub struct Binding { @@ -25,18 +23,14 @@ pub struct Binding { pub description: Option>, pub source: Option>, } - /// Input bindings are only returned if this evaluates to true #[derive(Clone)] pub struct Condition(Arcbool + Send + Sync>>); - impl_debug!(Condition |self, w| { write!(w, "*") }); - /// Default is always empty map regardless if `E` and `C` implement [Default]. impl Default for EventMap { fn default () -> Self { Self(Default::default()) } } - impl EventMap { /// Create a new event map pub fn new () -> Self { @@ -68,7 +62,7 @@ impl EventMap { /// Create event map from path to text file. pub fn from_path > (path: P) -> Usually where E: From> { if exists(path.as_ref())? { - Self::from_source(read_and_leak(path)?) + Self::from_source(read_to_string(path)?) } else { return Err(format!("(e5) not found: {:?}", path.as_ref()).into()) } @@ -117,7 +111,6 @@ impl EventMap { Ok(map) } } - impl Binding { fn from_dsl (dsl: impl Dsl) -> Usually { let mut command: Option = None; @@ -131,18 +124,9 @@ impl Binding { } } } - fn unquote (x: &str) -> &str { let mut chars = x.chars(); chars.next(); //chars.next_back(); chars.as_str() } - -fn read_and_leak (path: impl AsRef) -> Usually<&'static str> { - Ok(leak(String::from_utf8(std::fs::read(path.as_ref())?)?)) -} - -fn leak (x: impl AsRef) -> &'static str { - Box::leak(x.as_ref().into()) -} diff --git a/input/src/input_test.rs b/input/src/input_test.rs index 8373435..24576db 100644 --- a/input/src/input_test.rs +++ b/input/src/input_test.rs @@ -21,8 +21,8 @@ use crate::*; Ok(()) } -#[cfg(all(test, feature = "dsl"))] #[test] fn test_dsl_keymap () -> Usually<()> { - let _keymap = CstIter::new(""); - Ok(()) -} +//#[cfg(all(test, feature = "dsl"))] #[test] fn test_dsl_keymap () -> Usually<()> { + //let _keymap = CstIter::new(""); + //Ok(()) +//} diff --git a/input/src/lib.rs b/input/src/lib.rs index f219c64..800ee2f 100644 --- a/input/src/lib.rs +++ b/input/src/lib.rs @@ -1,13 +1,6 @@ #![feature(associated_type_defaults)] #![feature(if_let_guard)] - -pub(crate) use std::fmt::Debug; -pub(crate) use std::sync::Arc; -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; pub use self::input::*; #[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; diff --git a/tengri/src/test.rs b/tengri/src/test.rs index c782996..2df3c8a 100644 --- a/tengri/src/test.rs +++ b/tengri/src/test.rs @@ -1,86 +1,87 @@ -use crate::*; -use crate::{dsl::*, input::*, tui::TuiIn}; -use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}; -use std::cmp::Ordering; +// FIXME +//use crate::*; +//use crate::{dsl::*, input::*, tui::TuiIn}; +//use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}; +//use std::cmp::Ordering; -#[test] fn test_subcommand () -> Usually<()> { - #[derive(Debug)] struct Event(crossterm::event::Event); - impl Eq for Event {} - impl PartialEq for Event { fn eq (&self, other: &Self) -> bool { todo!() } } - impl Ord for Event { fn cmp (&self, other: &Self) -> Ordering { todo!() } } - impl PartialOrd for Event { fn partial_cmp (&self, other: &Self) -> Option { None } } - struct Test { keys: InputMap } +//#[test] fn test_subcommand () -> Usually<()> { + //#[derive(Debug)] struct Event(crossterm::event::Event); + //impl Eq for Event {} + //impl PartialEq for Event { fn eq (&self, other: &Self) -> bool { todo!() } } + //impl Ord for Event { fn cmp (&self, other: &Self) -> Ordering { todo!() } } + //impl PartialOrd for Event { fn partial_cmp (&self, other: &Self) -> Option { None } } + //struct Test { keys: InputMap } - handle!(TuiIn: |self: Test, input|Ok(None));/*if let Some(command) = self.keys.command(self, input) { - Ok(Some(true)) - } else { - Ok(None) - });*/ + //handle!(TuiIn: |self: Test, input|Ok(None));[>if let Some(command) = self.keys.command(self, input) { + //Ok(Some(true)) + //} else { + //Ok(None) + //});*/ - #[tengri_proc::command(Test)] - impl TestCommand { - fn do_thing (_state: &mut Test) -> Perhaps { - Ok(None) - } - fn do_thing_arg (_state: &mut Test, _arg: usize) -> Perhaps { - Ok(None) - } - fn do_sub (state: &mut Test, command: TestSubcommand) -> Perhaps { - Ok(command.execute(state)?.map(|command|Self::DoSub { command })) - } - } + //#[tengri_proc::command(Test)] + //impl TestCommand { + //fn do_thing (_state: &mut Test) -> Perhaps { + //Ok(None) + //} + //fn do_thing_arg (_state: &mut Test, _arg: usize) -> Perhaps { + //Ok(None) + //} + //fn do_sub (state: &mut Test, command: TestSubcommand) -> Perhaps { + //Ok(command.execute(state)?.map(|command|Self::DoSub { command })) + //} + //} - #[tengri_proc::command(Test)] - impl TestSubcommand { - fn do_other_thing (_state: &mut Test) -> Perhaps { - Ok(None) - } - fn do_other_thing_arg (_state: &mut Test, _arg: usize) -> Perhaps { - Ok(None) - } - } + //#[tengri_proc::command(Test)] + //impl TestSubcommand { + //fn do_other_thing (_state: &mut Test) -> Perhaps { + //Ok(None) + //} + //fn do_other_thing_arg (_state: &mut Test, _arg: usize) -> Perhaps { + //Ok(None) + //} + //} - let mut test = Test { - keys: InputMap::from_source(" - (@a do-thing) - (@b do-thing-arg 0) - (@c do-sub do-other-thing) - (@d do-sub do-other-thing-arg 0) - ")? - }; + //let mut test = Test { + //keys: InputMap::from_source(" + //(@a do-thing) + //(@b do-thing-arg 0) + //(@c do-sub do-other-thing) + //(@d do-sub do-other-thing-arg 0) + //")? + //}; - //assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { - //kind: KeyEventKind::Press, - //code: KeyCode::Char('a'), - //modifiers: KeyModifiers::NONE, - //state: KeyEventState::NONE, - //})))?); - //assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { - //kind: KeyEventKind::Press, - //code: KeyCode::Char('b'), - //modifiers: KeyModifiers::NONE, - //state: KeyEventState::NONE, - //})))?); - //assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { - //kind: KeyEventKind::Press, - //code: KeyCode::Char('c'), - //modifiers: KeyModifiers::NONE, - //state: KeyEventState::NONE, - //})))?); - //assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { - //kind: KeyEventKind::Press, - //code: KeyCode::Char('d'), - //modifiers: KeyModifiers::NONE, - //state: KeyEventState::NONE, - //})))?); - //assert_eq!(None, test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { - //kind: KeyEventKind::Press, - //code: KeyCode::Char('z'), - //modifiers: KeyModifiers::NONE, - //state: KeyEventState::NONE, - //})))?); - Ok(()) -} + ////assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { + ////kind: KeyEventKind::Press, + ////code: KeyCode::Char('a'), + ////modifiers: KeyModifiers::NONE, + ////state: KeyEventState::NONE, + ////})))?); + ////assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { + ////kind: KeyEventKind::Press, + ////code: KeyCode::Char('b'), + ////modifiers: KeyModifiers::NONE, + ////state: KeyEventState::NONE, + ////})))?); + ////assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { + ////kind: KeyEventKind::Press, + ////code: KeyCode::Char('c'), + ////modifiers: KeyModifiers::NONE, + ////state: KeyEventState::NONE, + ////})))?); + ////assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { + ////kind: KeyEventKind::Press, + ////code: KeyCode::Char('d'), + ////modifiers: KeyModifiers::NONE, + ////state: KeyEventState::NONE, + ////})))?); + ////assert_eq!(None, test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { + ////kind: KeyEventKind::Press, + ////code: KeyCode::Char('z'), + ////modifiers: KeyModifiers::NONE, + ////state: KeyEventState::NONE, + ////})))?); + //Ok(()) +//} //FIXME: //#[cfg(test)] #[test] fn test_dsl_context () { diff --git a/tui/src/lib.rs b/tui/src/lib.rs index e3792f1..50d7eeb 100644 --- a/tui/src/lib.rs +++ b/tui/src/lib.rs @@ -1,28 +1,17 @@ #![feature(type_changing_struct_update)] - mod tui_engine; pub use self::tui_engine::*; mod tui_content; pub use self::tui_content::*; - pub(crate) use ::tengri_core::*; - -pub use ::tengri_input as input; -pub(crate) use ::tengri_input::*; - -pub use ::tengri_output as output; -pub(crate) use ::tengri_output::*; - +pub use ::tengri_input as input; pub(crate) use ::tengri_input::*; +pub use ::tengri_output as output; pub(crate) use ::tengri_output::*; pub(crate) use atomic_float::AtomicF64; - pub use ::better_panic; pub(crate) use ::better_panic::{Settings, Verbosity}; - pub use ::palette; pub(crate) use ::palette::{*, convert::*, okhsl::*}; - pub use ::crossterm; pub(crate) use ::crossterm::{ ExecutableCommand, terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}, event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, }; - pub use ::ratatui; pub(crate) use ratatui::{ prelude::{Color, Style, Buffer}, style::Modifier, @@ -30,10 +19,8 @@ pub use ::ratatui; pub(crate) use ratatui::{ layout::{Size, Rect}, buffer::Cell }; - pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}; pub(crate) use std::io::{stdout, Stdout}; - #[cfg(test)] #[test] fn test_tui_engine () -> Usually<()> { use crate::*; //use std::sync::{Arc, RwLock}; @@ -55,7 +42,6 @@ pub(crate) use std::io::{stdout, Stdout}; //engine.run(&state)?; Ok(()) } - #[cfg(test)] #[test] fn test_parse_key () { //use KeyModifiers as Mods; let _test = |x: &str, y|assert_eq!(KeyMatcher::new(x).build(), Some(Event::Key(y)));