diff --git a/Cargo.lock b/Cargo.lock index 08f6979..17d931b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,9 +86,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" [[package]] name = "bumpalo" @@ -119,9 +119,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "compact_str" @@ -151,9 +151,9 @@ dependencies = [ [[package]] name = "const_panic" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e" +checksum = "bb8a602185c3c95b52f86dc78e55a6df9a287a7a93ddbcf012509930880cf879" dependencies = [ "const_panic_proc_macros", "typewit", @@ -377,9 +377,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", @@ -486,9 +486,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "linux-raw-sys" @@ -504,9 +504,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litrs" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "lock_api" @@ -691,9 +691,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -709,7 +709,7 @@ dependencies = [ "bitflags", "lazy_static", "num-traits", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -778,9 +778,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -865,9 +865,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags", ] @@ -880,9 +880,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustix" @@ -912,9 +912,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" @@ -963,9 +963,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -1018,9 +1018,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -1029,15 +1029,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix 1.0.8", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1126,18 +1126,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", @@ -1146,9 +1146,9 @@ dependencies = [ [[package]] name = "typewit" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb77c29baba9e4d3a6182d51fa75e3215c7fd1dab8f4ea9d107c716878e55fc0" +checksum = "97e72ba082eeb9da9dc68ff5a2bf727ef6ce362556e8d29ec1aed3bd05e7d86a" dependencies = [ "typewit_proc_macros", ] @@ -1319,6 +1319,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-sys" version = "0.59.0" @@ -1334,7 +1340,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -1355,10 +1361,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", diff --git a/core/src/core_macros.rs b/core/src/core_macros.rs index d5ada8f..0da5094 100644 --- a/core/src/core_macros.rs +++ b/core/src/core_macros.rs @@ -12,10 +12,10 @@ /// 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)*)? { + ($Trait:ident $(<$($A:ident:$T:ident),+>)? $(:$dep:ident $(<$dtt:tt>)? $(+$dep2:ident $(<$dtt2:tt>)?)*)? { $(fn $fn:ident (&$self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)* }) => { - pub trait $Trait $(<$($A: $T),+>)? $(:$dep$(+$dep2)*)? { + pub trait $Trait $(<$($A: $T),+>)? $(:$dep $(<$dtt>)? $(+$dep2 $(<$dtt2>)?)*)? { $(fn $fn (&$self $(,$arg:$ty)*) -> $ret $body)* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &_T_ { diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index ce30384..de6a978 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -16,9 +16,23 @@ pub(crate) use ::{ }; pub(crate) use self::DslError::*; mod dsl_conv; pub use self::dsl_conv::*; -mod dsl_ns; pub use self::dsl_ns::*; -mod dsl_src; pub use self::dsl_src::*; #[cfg(test)] mod dsl_test; +// Trait that designates any string-like as potentially parsable DSL. +flex_trait!(Dsl: Debug + Send + Sync + Sized { + fn src (&self) -> DslPerhaps<&str> { unreachable!("Dsl::src default impl") } +}); +impl<'x> Dsl for &'x str { + fn src (&self) -> DslPerhaps<&str> { Ok(Some(self)) } +} +impl Dsl for Arc { + 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 })} +} +impl Dsl for Result { + fn src (&self) -> DslPerhaps<&str> {match self {Ok(dsl) => Ok(dsl.src()?), Err(e) => Err(*e)}} +} /// DSL-specific result type. pub type DslResult = Result; /// DSL-specific optional result type. @@ -39,27 +53,10 @@ pub enum DslError { #[error("end reached")] End } -fn ok_flat (x: Option>) -> DslPerhaps { - Ok(x.transpose()?.flatten()) -} -pub const fn is_space (c: char) -> bool { - matches!(c, ' '|'\n'|'\r'|'\t') -} -pub const fn no_trailing_non_space ( - src: &str, offset: usize, context: Option<&'static str> -) -> DslResult<()> { - Ok(for_each!((i, c) in char_indices(str_range(src, offset, src.len())) => if !is_space(c) { - return Err(Unexpected(c, Some(offset + i), if let Some(context) = context { - Some(context) - } else { - Some("trailing non-space") - })) - })) -} pub const fn peek (src: &str) -> DslPerhaps<&str> { Ok(Some(if let Ok(Some(expr)) = expr_peek(src) { expr } else if let Ok(Some(word)) = word_peek(src) { word } else - if let Ok(Some(num)) = num_peek(src) { num } else + //if let Ok(Some(num)) = num_peek(src) { num } else if let Ok(Some(text)) = text_peek(src) { text } else if let Err(e) = no_trailing_non_space(src, 0, Some("peek")) { return Err(e) } else { return Ok(None) })) @@ -67,7 +64,7 @@ pub const fn peek (src: &str) -> DslPerhaps<&str> { pub const fn seek (src: &str) -> DslPerhaps<(usize, usize)> { Ok(Some(if let Ok(Some(expr)) = expr_seek(src) { expr } else if let Ok(Some(word)) = word_seek(src) { word } else - if let Ok(Some(num)) = num_seek(src) { num } else + //if let Ok(Some(num)) = num_seek(src) { num } else if let Ok(Some(text)) = text_seek(src) { text } else if let Err(e) = no_trailing_non_space(src, 0, Some("seek")) { return Err(e) } else { return Ok(None) })) @@ -83,7 +80,74 @@ pub const fn peek_tail (src: &str) -> DslPerhaps<&str> { }, } } - +pub const fn expr_peek_inner (src: &str) -> DslPerhaps<&str> { + match expr_peek(src) { + Ok(Some(peeked)) => { + let len = peeked.len(); + let start = if len > 0 { 1 } else { 0 }; + Ok(Some(str_range(src, start, start + len.saturating_sub(2)))) + }, + e => e + } +} +pub const fn expr_peek_inner_only (src: &str) -> DslPerhaps<&str> { + match expr_seek(src) { + Err(e) => Err(e), + Ok(None) => Ok(None), + Ok(Some((start, length))) => { + if let Err(e) = no_trailing_non_space(src, start + length, Some("expr_peek_inner_only")) { + Err(e) + } else { + let peeked = str_range(src, 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_space (c: char) -> bool { + matches!(c, ' '|'\n'|'\r'|'\t') +} +pub const fn no_trailing_non_space ( + src: &str, offset: usize, context: Option<&'static str> +) -> DslResult<()> { + Ok(for_each!((i, c) in char_indices(str_range(src, offset, src.len())) => if !is_space(c) { + return Err(Unexpected(c, Some(offset + i), if let Some(context) = context { + Some(context) + } else { + Some("trailing non-space") + })) + })) +} +pub const fn is_word_char (c: char) -> bool { + matches!(c, 'a'..='z'|'A'..='Z'|'0'..='9'|'-'|'/'|'@'|':') +} +pub const fn is_word_end (c: char) -> bool { is_space(c) || is_expr_end(c) } +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 is_expr_start (c: char) -> bool { c == '(' } +pub const fn is_expr_end (c: char) -> bool { c == ')' } +pub const fn is_digit (c: char) -> bool { matches!(c, '0'..='9') } +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, None, Some("parse digit"))) + }) +} #[macro_export] macro_rules! dsl_type (($T:ident { $($trait:tt)* } { pub const fn $peek:ident $($_1:tt)?; pub const fn $peek_only:ident $($_2:tt)?; @@ -93,6 +157,20 @@ pub const fn peek_tail (src: &str) -> DslPerhaps<&str> { })=>{ pub trait $T: Dsl { $($trait)* } impl $T for D {} + pub const fn $seek_start ($source1: &str) -> DslPerhaps $body1 + pub const fn $seek_length ($source2: &str) -> DslPerhaps $body2 + /// 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), + }, + } + } /// Find a slice corrensponding to a syntax token. pub const fn $peek (source: &str) -> DslPerhaps<&str> { match $seek(source) { @@ -109,29 +187,53 @@ pub const fn peek_tail (src: &str) -> DslPerhaps<&str> { Err(e) => Err(e), Ok(None) => Ok(None), Ok(Some((start, length))) => { - if let Err(e) = no_trailing_non_space(source, start + length, Some("peek_only")) { return Err(e) } - Ok(Some(str_range(source, start, start + length))) + if let Err(e) = no_trailing_non_space(source, start + length, Some("peek_only")) { + Err(e) + } else { + 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), - }, - } - } - pub const fn $seek_start ($source1: &str) -> DslPerhaps $body1 - pub const fn $seek_length ($source2: &str) -> DslPerhaps $body2 }); -pub const fn is_expr_start (c: char) -> bool { c == '(' } -pub const fn is_expr_end (c: char) -> bool { c == ')' } +dsl_type!(DslWord { + fn word (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(word_peek_only))} +} { + pub const fn word_peek [generated]; + pub const fn word_peek_only [generated]; + pub const fn word_seek [generated]; + pub const fn word_seek_start (src) { + for_each!((i, c) in char_indices(src) => if + is_word_char(c) { return Ok(Some(i)) } else + if !is_space(c) { return Err(Unexpected(c, Some(i), Some("word_seek_start"))) }); + Ok(None) + } + pub const fn word_seek_length (src) { + for_each!((i, c) in char_indices(src) => if !is_word_char(c) { return Ok(Some(i)) }); + Ok(Some(src.len())) + } +}); + +dsl_type!(DslText { + fn text (&self) -> DslPerhaps<&str> { ok_flat(self.src()?.map(text_peek_only)) } +} { + pub const fn text_peek [generated]; + pub const fn text_peek_only [generated]; + pub const fn text_seek [generated]; + pub const fn text_seek_start (src) { + for_each!((i, c) in char_indices(src) => + if is_text_start(c) { return Ok(Some(i)) } else + if !is_space(c) { return Err(Unexpected(c, Some(i), None)) }); + Ok(None) + } + pub const fn text_seek_length (src) { + for_each!((i, c) in char_indices(src) => + if is_text_end(c) { return Ok(Some(i)) }); + Ok(None) + } +}); + dsl_type!(DslExpr { fn expr (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(expr_peek_inner_only))} fn head (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(peek))} @@ -172,113 +274,129 @@ dsl_type!(DslExpr { } }); -pub const fn expr_peek_inner (src: &str) -> DslPerhaps<&str> { - match expr_peek(src) { - Ok(Some(peeked)) => { - let len = peeked.len(); - let start = if len > 0 { 1 } else { 0 }; - Ok(Some(str_range(src, start, start + len.saturating_sub(2)))) - }, - e => e - } +fn ok_flat (x: Option>) -> DslPerhaps { + Ok(x.transpose()?.flatten()) } -pub const fn expr_peek_inner_only (src: &str) -> DslPerhaps<&str> { - match expr_seek(src) { - Err(e) => Err(e), - Ok(None) => Ok(None), - Ok(Some((start, length))) => { - if let Err(e) = no_trailing_non_space(src, start + length, Some("expr_peek_inner_only")) { return Err(e) } - let peeked = str_range(src, 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_word_char (c: char) -> bool { - matches!(c, 'a'..='z'|'A'..='Z'|'0'..='9'|'-'|'/'|'@'|':') -} -pub const fn is_word_end (c: char) -> bool { - is_space(c) || is_expr_end(c) -} -dsl_type!(DslWord { - fn word (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(word_peek_only))} -} { - pub const fn word_peek [generated]; - pub const fn word_peek_only [generated]; - pub const fn word_seek [generated]; - pub const fn word_seek_start (src) { - for_each!((i, c) in char_indices(src) => if - is_word_char(c) { return Ok(Some(i)) } else - if !is_space(c) { return Err(Unexpected(c, Some(i), Some("word_seek_start"))) }); - Ok(None) - } - pub const fn word_seek_length (src) { - for_each!((i, c) in char_indices(src) => if !is_word_char(c) { return Ok(Some(i)) }); - Ok(Some(src.len())) - } -}); - -pub const fn is_text_start (c: char) -> bool { matches!(c, '"') } -pub const fn is_text_end (c: char) -> bool { matches!(c, '"') } -dsl_type!(DslText { - fn text (&self) -> DslPerhaps<&str> { ok_flat(self.src()?.map(text_peek_only)) } -} { - pub const fn text_peek [generated]; - pub const fn text_peek_only [generated]; - pub const fn text_seek [generated]; - pub const fn text_seek_start (src) { - for_each!((i, c) in char_indices(src) => - if is_text_start(c) { return Ok(Some(i)) } else - if !is_space(c) { return Err(Unexpected(c, Some(i), None)) }); - Ok(None) - } - pub const fn text_seek_length (src) { - for_each!((i, c) in char_indices(src) => - if is_text_end(c) { return Ok(Some(i)) }); - Ok(None) - } -}); - -dsl_type!(DslNum { - fn num (&self) -> DslPerhaps<&str> {ok_flat(self.src()?.map(num_peek_only))} -} { - pub const fn num_peek [generated]; - pub const fn num_peek_only [generated]; - pub const fn num_seek [generated]; - pub const fn num_seek_start (src) { - for_each!((i, c) in char_indices(src) => - if is_digit(c) { return Ok(Some(i)); } else - if !is_space(c) { return Err(Unexpected(c, Some(i), None)) }); - Ok(None) - } - pub const fn num_seek_length (src) { - for_each!((i, c) in char_indices(src) => - if is_num_end(c) { return Ok(Some(i)) } else - if !is_digit(c) { return Err(Unexpected(c, Some(i), None)) }); - 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), +/// Define a DSL namespace that provides values to words and expressions. +#[macro_export] macro_rules! dsl_ns ( + // Special form for numeric types + (num |$state:ident : $State: ty| $($($num:lifetime)? $Type:ty $(=> { $( + $pat:tt => $body:expr + ),* $(,)? })?;)+) => { + $(dsl_ns!(num |$state: $State| -> $($num)? $Type { $( $($pat => $body),* )? });)+ + }; + // Special form for numeric types + (num |$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { + impl<'t> DslNs<'t, $Type> for $State { + const WORDS: DslNsMap<'t, fn (&'t $State)->Perhaps<$Type>> = + DslNsMap::new(&[$(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); + const EXPRS: DslNsMap<'t, fn (&'t $State, &str)->Perhaps<$Type>> = + DslNsMap::new(&[$(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); + fn from (&'t self, dsl: D) -> Perhaps<$Type> { + if let Ok(Some(src)) = dsl.src() { + if let Ok(num) = to_number(src) { + Ok(Some(num as $Type)) + } else if let Ok(Some(src)) = src.word() { + self.from_word(src) + } else { + self.from_expr(src) + } + } else { + Ok(None) + } + } + } + }; + // A namespace may resolve one or more types. + (|$state:ident : $State: ty| $($Type:ty $(=> { $( $pat:tt => $body:expr ),* $(,)? })? ;)+) => { + $(dsl_ns!(|$state: $State| -> $Type { $( $($pat => $body),* )? });)+ + }; + // Regular form for single type + (|$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { + impl<'t> DslNs<'t, $Type> for $State { + const WORDS: DslNsMap<'t, fn (&'t $State)->Perhaps<$Type>> = + DslNsMap::new(&[$(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); + const EXPRS: DslNsMap<'t, fn (&'t $State, &str)->Perhaps<$Type>> = + DslNsMap::new(&[$(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); + } + }; + // Symbols only. + (@word ($state:ident: $State:ty) -> $Type:ty { + $word:literal => $body:expr + }) => { + ($word, |$state|Ok(Some($body))) + }; + // Expression handlers only. + (@exp ($state:ident: $State:ty) -> $Type:ty { + ($head:literal $(,$arg:ident:$ty:ty)* $(,)*) => $body:expr + }) => { ($head, |$state, tail: &str|{ + $( + let head = tail.head()?.unwrap_or_default(); + let tail = tail.tail()?.unwrap_or_default(); + let $arg: $ty = if let Some(arg) = $state.from(&head)? { + arg + } else { + return Err(format!("{}: missing argument: {} ({}); got: {tail}", + $head, + stringify!($arg), + stringify!($Type), + ).into()) + }; + )* + Ok(Some($body)) + }) }; + // Nothing else in symbols. + (@word ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { + ("", |_|Ok(None)) // FIXME don't emit at all + }; + // Nothing else in expression handlers. + (@exp ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { + ("", |_, _|Ok(None)) // FIXME don't emit at all + }; +); + +pub trait DslNs<'t, T: 't>: 't { + /// Known symbols. + const WORDS: DslNsMap<'t, fn (&'t Self)->Perhaps> = DslNsMap::new(&[]); + /// Known expressions. + const EXPRS: DslNsMap<'t, fn (&'t Self, &str)->Perhaps> = DslNsMap::new(&[]); + /// Resolve an expression or symbol. + fn from (&'t self, dsl: D) -> Perhaps { + if let Ok(Some(src)) = dsl.src() { + if let Ok(Some(src)) = src.word() { + self.from_word(src) + } else { + self.from_expr(src) + } + } else { + Ok(None) } - iter = next; } - Ok(value) + /// Resolve a symbol if known. + fn from_word (&'t self, dsl: D) -> Perhaps { + if let Some(dsl) = dsl.word()? { + for (word, get) in Self::WORDS.0 { if dsl == *word { return get(self) } } + } + return Ok(None) + } + /// Resolve an expression if known. + fn from_expr (&'t self, dsl: D) -> Perhaps { + if let Some(head) = dsl.expr().head()? { + for (key, value) in Self::EXPRS.0.iter() { + if head == *key { + return value(self, dsl.expr().tail()?.unwrap_or("")) + } + } + } + return Ok(None) + } } -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, None, Some("parse digit"))) - }) + +/// Namespace mapping. +#[derive(Debug)] +pub struct DslNsMap<'t, T: Debug + 't>(pub &'t [(&'t str, T)]); +impl<'t, T: Debug + 't> DslNsMap<'t, T> { + /// Populate a namespace with pre-existing values. + pub const fn new (data: &'t [(&'t str, T)]) -> Self { Self(data) /* TODO a search trie */ } } diff --git a/dsl/src/dsl_ns.rs b/dsl/src/dsl_ns.rs deleted file mode 100644 index b2776aa..0000000 --- a/dsl/src/dsl_ns.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::*; -/// Define a namespace: -#[macro_export] macro_rules! dsl_ns ( - // Special form for numeric types - (num |$state:ident : $State: ty| $($($num:lifetime)? $Type:ty $(=> { $( - $pat:tt => $body:expr - ),* $(,)? })?;)+) => { - $(dsl_ns!(num |$state: $State| -> $($num)? $Type { $( $($pat => $body),* )? });)+ - }; - // Special form for numeric types - (num |$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { - impl<'t> DslNs<'t, $Type> for $State { - const WORDS: DslNsMap<'t, fn (&'t $State)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); - const EXPRS: DslNsMap<'t, fn (&'t $State, &str)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); - fn from (&'t self, dsl: D) -> Perhaps<$Type> { - if let Ok(Some(src)) = dsl.src() { - if let Ok(num) = to_number(src) { - Ok(Some(num as $Type)) - } else if let Ok(Some(src)) = src.word() { - self.from_word(src) - } else { - self.from_expr(src) - } - } else { - Ok(None) - } - } - } - }; - // A namespace may resolve one or more types. - (|$state:ident : $State: ty| $($Type:ty $(=> { $( $pat:tt => $body:expr ),* $(,)? })? ;)+) => { - $(dsl_ns!(|$state: $State| -> $Type { $( $($pat => $body),* )? });)+ - }; - // Regular form for single type - (|$state:ident : $State: ty| -> $Type:ty { $( $pat:tt => $body:expr ),* $(,)? }) => { - impl<'t> DslNs<'t, $Type> for $State { - const WORDS: DslNsMap<'t, fn (&'t $State)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@word ($state: $State) -> $Type { $pat => $body }}),*]); - const EXPRS: DslNsMap<'t, fn (&'t $State, &str)->Perhaps<$Type>> = - DslNsMap::new(&[$(dsl_ns!{@exp ($state: $State) -> $Type { $pat => $body }}),*]); - } - }; - // Symbols only. - (@word ($state:ident: $State:ty) -> $Type:ty { - $word:literal => $body:expr - }) => { - ($word, |$state|Ok(Some($body))) - }; - // Expression handlers only. - (@exp ($state:ident: $State:ty) -> $Type:ty { - ($head:literal $(,$arg:ident:$ty:ty)* $(,)*) => $body:expr - }) => { ($head, |$state, tail: &str|{ - $( - let head = tail.head()?.unwrap_or_default(); - let tail = tail.tail()?.unwrap_or_default(); - let $arg: $ty = if let Some(arg) = $state.from(&head)? { - arg - } else { - return Err(format!("{}: missing argument: {} ({}); got: {tail}", - $head, - stringify!($arg), - stringify!($Type), - ).into()) - }; - )* - Ok(Some($body)) - }) }; - // Nothing else in symbols. - (@word ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { - ("", |_|Ok(None)) // FIXME don't emit at all - }; - // Nothing else in expression handlers. - (@exp ($state:ident: $State:ty) -> $Type:ty { $pat:tt => $body:expr }) => { - ("", |_, _|Ok(None)) // FIXME don't emit at all - }; - -); - -pub trait DslNs<'t, T: 't>: 't { - /// Resolve an expression or symbol. - fn from (&'t self, dsl: D) -> Perhaps { - if let Ok(Some(src)) = dsl.src() { - if let Ok(Some(src)) = src.word() { - self.from_word(src) - } else { - self.from_expr(src) - } - } else { - Ok(None) - } - } - /// Resolve a symbol if known. - fn from_word (&'t self, dsl: D) -> Perhaps { - if let Some(dsl) = dsl.word()? { - for (word, get) in Self::WORDS.0 { if dsl == *word { return get(self) } } - } - return Ok(None) - } - /// Resolve an expression if known. - fn from_expr (&'t self, dsl: D) -> Perhaps { - if let Some(head) = dsl.expr().head()? { - for (key, value) in Self::EXPRS.0.iter() { - if head == *key { - return value(self, dsl.expr().tail()?.unwrap_or("")) - } - } - } - return Ok(None) - } - /// Known symbols. - const WORDS: DslNsMap<'t, fn (&'t Self)->Perhaps> = DslNsMap::new(&[]); - /// Known expressions. - const EXPRS: DslNsMap<'t, fn (&'t Self, &str)->Perhaps> = DslNsMap::new(&[]); -} -/// Namespace mapping. -#[derive(Debug)] -pub struct DslNsMap<'t, T: Debug + 't>(pub &'t [(&'t str, T)]); -impl<'t, T: Debug + 't> DslNsMap<'t, T> { - /// Populate a namespace. - pub const fn new (data: &'t [(&'t str, T)]) -> Self { - Self(data) // TODO build search index - } -} diff --git a/dsl/src/dsl_src.rs b/dsl/src/dsl_src.rs deleted file mode 100644 index 89b5fc9..0000000 --- a/dsl/src/dsl_src.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::*; -// Designates a string as parsable DSL. -flex_trait!(Dsl: Debug + Send + Sync + Sized { - fn src (&self) -> DslPerhaps<&str> { unreachable!("Dsl::src default impl") } -}); -// Some things that can be DSL source: -impl Dsl for String { - 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 })} -} -impl Dsl for Result { - fn src (&self) -> DslPerhaps<&str> {match self {Ok(dsl) => Ok(dsl.src()?), Err(e) => Err(*e)}} -} diff --git a/proc/src/proc_expose.rs b/proc/src/proc_expose.rs index bb6794b..cdef432 100644 --- a/proc/src/proc_expose.rs +++ b/proc/src/proc_expose.rs @@ -96,17 +96,18 @@ impl ExposeImpl { } } } else if is_num(&type_is) { - quote! { - /// Generated by [tengri_proc::expose]. - impl ::tengri::dsl::FromDsl<#state> for #t { - fn from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { - match dsl.num()? { - Some(n) => Ok(Some(n.parse::<#t>()?)), - _ => match dsl.word()? { #(#variants)* _ => Ok(None) } - } - } - } - } + quote! {} + //quote! { + ///// Generated by [tengri_proc::expose]. + //impl ::tengri::dsl::FromDsl<#state> for #t { + //fn from_dsl (state: &#state, dsl: &impl Dsl) -> Perhaps { + //match dsl.num()? { + //Some(n) => Ok(Some(n.parse::<#t>()?)), + //_ => match dsl.word()? { #(#variants)* _ => Ok(None) } + //} + //} + //} + //} } else { quote! { /// Generated by [tengri_proc::expose]. diff --git a/tengri.svg b/tengri.svg new file mode 100644 index 0000000..7ff60dd --- /dev/null +++ b/tengri.svg @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + Daily Timestamp Heatmap + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 23:00 + 22:00 + 21:00 + 20:00 + 19:00 + 18:00 + 17:00 + 16:00 + 15:00 + 14:00 + 13:00 + 12:00 + 11:00 + 10:00 + 9:00 + 8:00 + 7:00 + 6:00 + 5:00 + 4:00 + 3:00 + 2:00 + 1:00 + 0:00 + 03/02 + 03/09 + 03/16 + 03/23 + 03/30 + 04/06 + 04/13 + 04/20 + 04/27 + 05/04 + 05/11 + 05/18 + 05/25 + 06/01 + 06/08 + 06/15 + 06/22 + 06/29 + 07/06 + 07/13 + 07/20 + 07/27 + 08/03 + 08/10 + 08/17 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +