diff --git a/dsl/README.md b/dsl/README.md index 97f1d2c..a5bcf22 100644 --- a/dsl/README.md +++ b/dsl/README.md @@ -92,3 +92,24 @@ or configuration statements, and look like this: * [ ] const parse * [ ] live reload * [ ] serialize modified code back to original indentation + +## implementation notes + +### `DslExp` trait behavior + +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)` |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: Unexpected 'a' +* e1: Unexpected '(' +* e2: Unexpected 'b' +* e3: Unexpected 'd' diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index d8de0f5..c43e2f1 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -6,87 +6,45 @@ 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::string::{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; - +#[cfg(test)] mod dsl_test; flex_trait!(Dsl: Debug + Send + Sync + Sized { - fn src (&self) -> &str { - unreachable!("Dsl::src default impl") - } + fn src (&self) -> DslPerhaps<&str> { unreachable!("Dsl::src default impl") } }); -impl Dsl for Arc { - fn src (&self) -> &str { self.as_ref() } -} -impl<'s> Dsl for &'s str { - fn src (&self) -> &str { 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) -> &str { if let Some(dsl) = self { dsl.src() } else { "" } } + fn src (&self) -> DslPerhaps<&str> {Ok(if let Some(dsl) = self { dsl.src()? } else { None })} } - -impl DslExp for D {} -pub trait DslExp: Dsl { - fn exp (&self) -> DslPerhaps<&str> { - Ok(exp_peek(self.src())?) - } - fn head (&self) -> DslPerhaps<&str> { - let start = 1; - let src = self.src(); - let src = &src[start.min(src.len().saturating_sub(1))..]; - peek(src) - } - fn tail (&self) -> DslPerhaps<&str> { - let start = 1; - let src = self.src(); - let src = &src[start.min(src.len().saturating_sub(1))..]; - Ok(if let Some((head_start, head_len)) = seek(src)? { - let start = 1 + head_start + head_len; - let src = self.src(); - let src = &src[start.min(src.len().saturating_sub(1))..]; - peek(src)? - } else { - None - }) - } +impl Dsl for Result { + fn src (&self) -> DslPerhaps<&str> {match self {Ok(dsl) => Ok(dsl.src()?), Err(e) => Err(*e)}} } - -impl DslSym for D {} -pub trait DslSym: Dsl { - fn sym (&self) -> DslPerhaps<&str> { crate::parse::sym_peek(self.src()) } +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 DslKey for D {} -pub trait DslKey: Dsl { - fn key (&self) -> DslPerhaps<&str> { crate::parse::key_peek(self.src()) } +impl DslText for D {} pub trait DslText: Dsl { + fn text (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(text_peek_only))} } - -impl DslText for D {} -pub trait DslText: Dsl { - fn text (&self) -> DslPerhaps<&str> { crate::parse::text_peek(self.src()) } +impl DslSym for D {} pub trait DslSym: Dsl { + fn sym (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(sym_peek_only))} } - -impl DslNum for D {} -pub trait DslNum: Dsl { - fn num (&self) -> DslPerhaps<&str> { crate::parse::num_peek(self.src()) } +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))} } - /// DSL-specific result type. pub type DslResult = Result; - /// DSL-specific optional result type. pub type DslPerhaps = Result, DslError>; - /// DSL-specific error codes. #[derive(Error, Debug, Copy, Clone, PartialEq, PanicFmt)] pub enum DslError { @@ -97,3 +55,245 @@ pub enum DslError { #[error("parse failed: error #{0}")] Code(u8), #[error("end reached")] End } +#[macro_export] macro_rules! dsl_for_each (($dsl:expr => |$next:ident|$body:expr)=>{ + let mut dsl: Arc = $dsl.src().into(); + 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) = rest { + $next = next.next()?.map(Into::into); + rest = next.rest()?.map(Into::into); + } else { + break + } + } +}); +fn ok_flat (x: Option>) -> DslPerhaps { Ok(x.transpose()?.flatten()) } +fn peek_tail <'a> (head: DslPerhaps<&'a str>) -> impl Fn(&'a str)->DslPerhaps<&'a str> { + move|src|match head { + Ok(Some(next)) => { + let src = &src[src.len().min(1 + next.len())..]; + for c in src.chars() { if !is_whitespace(c) { return Ok(Some(src)) } } + Ok(None) + }, + 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 25eef20..0000000 --- a/dsl/src/dsl_parse.rs +++ /dev/null @@ -1,183 +0,0 @@ -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. - 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. - 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))), - Ok(None) => Ok(None), - Err(e) => Err(e), - }, - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 (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, - _ => { - iter_chars!(src => |_i, c| 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, - _ => { - iter_chars!(src => |_i, c| 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 e69de29..05f0308 100644 --- a/dsl/src/dsl_test.rs +++ b/dsl/src/dsl_test.rs @@ -0,0 +1,97 @@ +use crate::*; +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> { + let e0 = DslError::Unexpected('a'); + let e1 = DslError::Unexpected('('); + let e2 = DslError::Unexpected('b'); + let e3 = DslError::Unexpected('d'); + let check = |src: &str, key, exp, head, tail|{ + assert_eq!(src.key(), key, "{src}"); + assert_eq!(src.exp(), exp, "{src}"); + assert_eq!(src.head(), head, "{src}"); + assert_eq!(src.tail(), tail, "{src}"); + }; + check("a", Ok(Some("a")), Err(e0), Ok(Some("a")), Ok(None)); + check("(a)", Err(e1), Ok(Some("a")), Ok(Some("(a)")), Ok(None)); + check("a b c", Err(e2), Err(e0), Ok(Some("a")), Ok(Some("b c"))); + check("(a b c)", Err(e1), Ok(Some("a b c")), Ok(Some("(a b c)")), Ok(None)); + check("(a b c) d e f", Err(e1), Err(e3), Ok(Some("(a b c)")), Ok(Some("d e f"))); + check("a (b c d) e f", Err(e1), Err(e0), Ok(Some("a")), Ok(Some("(b c d) e f"))); + + assert!(is_whitespace(' ')); + assert!(!is_key_start(' ')); + assert!(is_key_start('f')); + + 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_some!("(foo)".exp(), "foo"); + is_some!("(foo)".head(), "(foo)"); + is_none!("(foo)".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_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_err!("foo".exp()); + is_some!("foo".key(), "foo"); + is_some!(" foo".key(), "foo"); + is_some!(" foo ".key(), "foo"); + + is_some!(" foo ".head(), "foo"); + //assert_eq!(" foo ".head().head(), 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().head(), Ok(Some("bar"))); + assert_eq!(" foo bar ".tail().tail(), Ok(None)); + + assert_eq!(" (foo) ".head(), Ok(Some("(foo)"))); + //assert_eq!(" (foo) ".head().head(), Ok(Some("foo"))); + //assert_eq!(" (foo) ".head().head().head(), Ok(None)); + assert_eq!(" (foo) ".tail(), Ok(None)); + + 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)); + 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"); + + 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/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/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)));